From 0a5e02a87fdabff5ff8399829ff857cae90fc1e2 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Sun, 20 Mar 2022 10:29:42 -0400
Subject: Revert "Merge remote-tracking branch 'origin/speedups2' into
 temporalmedia-mehek"

This reverts commit 1f7cf7babc76ecff5aef5fe663c48e067e85dd26, reversing
changes made to 1e3ad4de06f83eab54628de660529fefb9a0dc63.
---
 src/client/views/MainView.tsx | 50 ++++++++++++++++++++++---------------------
 1 file changed, 26 insertions(+), 24 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 8c0795881..fc1c694a2 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -5,6 +5,7 @@ import * as fa from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, configure, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
+import "normalize.css";
 import * as React from 'react';
 import * as ReactDOM from 'react-dom';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
@@ -23,7 +24,7 @@ import { DocumentManager } from '../util/DocumentManager';
 import { GroupManager } from '../util/GroupManager';
 import { HistoryUtil } from '../util/History';
 import { Hypothesis } from '../util/HypothesisUtils';
-import { ScriptingGlobals } from '../util/ScriptingGlobals';
+import { Scripting } from '../util/Scripting';
 import { SelectionManager } from '../util/SelectionManager';
 import { ColorScheme, SettingsManager } from '../util/SettingsManager';
 import { SharingManager } from '../util/SharingManager';
@@ -44,6 +45,7 @@ import { GestureOverlay } from './GestureOverlay';
 import { DASHBOARD_SELECTOR_HEIGHT, LEFT_MENU_WIDTH } from './global/globalCssVariables.scss';
 import { Colors } from './global/globalEnums';
 import { KeyManager } from './GlobalKeyHandler';
+import { InkStrokeProperties } from './InkStrokeProperties';
 import { LightboxView } from './LightboxView';
 import { LinkMenu } from './linking/LinkMenu';
 import "./MainView.scss";
@@ -163,28 +165,28 @@ export class MainView extends React.Component {
             }
         }
 
-        library.add(...[fa.faEdit, fa.faTrash, fa.faTrashAlt, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faCalendar,
-        fa.faSquare, far.faSquare as any, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faMapMarker, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock,
-        fa.faLock, fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointLeft, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard,
-        fa.faQuestion, fa.faTasks, fa.faPalette, fa.faAngleLeft, fa.faAngleRight, fa.faBell, fa.faCamera, fa.faExpand, fa.faCaretDown, fa.faCaretLeft, fa.faCaretRight,
-        fa.faCaretSquareDown, fa.faCaretSquareRight, fa.faArrowsAltH, fa.faPlus, fa.faMinus, fa.faTerminal, fa.faToggleOn, fa.faFile, fa.faLocationArrow,
-        fa.faSearch, fa.faFileDownload, fa.faFileUpload, fa.faStop, fa.faCalculator, fa.faWindowMaximize, fa.faAddressCard, fa.faQuestionCircle, fa.faArrowLeft,
-        fa.faArrowRight, fa.faArrowDown, fa.faArrowUp, fa.faBolt, fa.faBullseye, fa.faCaretUp, fa.faCat, fa.faCheck, fa.faChevronRight, fa.faChevronLeft, fa.faChevronDown, fa.faChevronUp,
-        fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt,
-        fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer,
-        fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV,
-        fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes,
-        fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
-        fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
-        fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any,
-        fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle as any,
-        fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer as any, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp,
-        fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
-        fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo,
-        fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
-        fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
-        fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines,
-        fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt]);
+        library.add(fa.faEdit, fa.faTrash, fa.faTrashAlt, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faCalendar,
+            fa.faSquare, far.faSquare as any, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faMapMarker, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock,
+            fa.faLock, fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointLeft, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard,
+            fa.faQuestion, fa.faTasks, fa.faPalette, fa.faAngleLeft, fa.faAngleRight, fa.faBell, fa.faCamera, fa.faExpand, fa.faCaretDown, fa.faCaretLeft, fa.faCaretRight,
+            fa.faCaretSquareDown, fa.faCaretSquareRight, fa.faArrowsAltH, fa.faPlus, fa.faMinus, fa.faTerminal, fa.faToggleOn, fa.faFile, fa.faLocationArrow,
+            fa.faSearch, fa.faFileDownload, fa.faFileUpload, fa.faStop, fa.faCalculator, fa.faWindowMaximize, fa.faAddressCard, fa.faQuestionCircle, fa.faArrowLeft,
+            fa.faArrowRight, fa.faArrowDown, fa.faArrowUp, fa.faBolt, fa.faBullseye, fa.faCaretUp, fa.faCat, fa.faCheck, fa.faChevronRight, fa.faChevronLeft, fa.faChevronDown, fa.faChevronUp,
+            fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt,
+            fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer,
+            fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV,
+            fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes,
+            fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
+            fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
+            fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any,
+            fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle as any,
+            fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer as any, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp,
+            fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
+            fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo,
+            fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
+            fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
+            fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines,
+            fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faSearchPlus, fa.faVolumeUp, fa.faVolumeMute, fa.faMapMarkerAlt);
         this.initAuthenticationRouters();
     }
 
@@ -694,4 +696,4 @@ export class MainView extends React.Component {
     }
 }
 
-ScriptingGlobals.add(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); });
\ No newline at end of file
+Scripting.addGlobal(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); });
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 810f86195188503b04d64f9d58ea4dfc3a639398 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 22 Mar 2022 11:32:47 -0400
Subject: fixed temporal media merge that had reverted a lot of things.

---
 .vscode/launch.json                                |     2 +-
 deploy/index.html                                  |    12 +-
 package-lock.json                                  | 33087 +++++++++++++++++--
 package.json                                       |   289 +-
 src/.DS_Store                                      |   Bin 8196 -> 8196 bytes
 src/Utils.ts                                       |     2 +-
 src/client/ClientRecommender.tsx                   |   421 -
 src/client/apis/GoogleAuthenticationManager.tsx    |     4 +-
 src/client/cognitive_services/CognitiveServices.ts |     3 +-
 src/client/documents/Documents.ts                  |    92 +-
 src/client/util/CurrentUserUtils.ts                |   248 +-
 src/client/util/DictationManager.ts                |   353 +-
 src/client/util/DocumentManager.ts                 |     8 +-
 src/client/util/DragManager.ts                     |     2 +-
 src/client/util/DropConverter.ts                   |     4 +-
 src/client/util/History.ts                         |     6 +-
 .../util/Import & Export/DirectoryImportBox.tsx    |     7 +-
 src/client/util/LinkManager.ts                     |     2 +-
 src/client/util/ScriptManager.ts                   |    12 +-
 src/client/util/Scripting.ts                       |   109 +-
 src/client/util/ScriptingGlobals.ts                |    81 +
 src/client/util/UndoManager.ts                     |     3 +-
 src/client/views/DocComponent.tsx                  |    12 +-
 src/client/views/DocumentDecorations.tsx           |    24 +-
 src/client/views/GestureOverlay.tsx                |    12 +-
 src/client/views/InkControlPtHandles.tsx           |     1 +
 src/client/views/InkingStroke.tsx                  |     7 +-
 src/client/views/Main.tsx                          |    16 +-
 src/client/views/MainView.tsx                      |    50 +-
 src/client/views/OverlayView.tsx                   |     8 +-
 src/client/views/PropertiesButtons.tsx             |    19 +
 src/client/views/PropertiesView.tsx                |     5 +-
 src/client/views/ScriptBox.tsx                     |    20 +-
 src/client/views/ScriptingRepl.tsx                 |     7 +-
 src/client/views/StyleProvider.tsx                 |     2 +-
 src/client/views/TemplateMenu.tsx                  |     6 +-
 src/client/views/animationtimeline/Keyframe.tsx    |    21 +-
 src/client/views/animationtimeline/Timeline.tsx    |     9 +-
 .../views/animationtimeline/TimelineMenu.tsx       |     5 +-
 .../views/collections/CollectionCarousel3DView.tsx |    12 +-
 .../views/collections/CollectionCarouselView.tsx   |    11 +-
 .../views/collections/CollectionDockingView.tsx    |    20 +-
 src/client/views/collections/CollectionMenu.tsx    |     4 +-
 .../views/collections/CollectionPileView.tsx       |     2 +-
 .../collections/CollectionStackedTimeline.tsx      |    38 +-
 .../views/collections/CollectionStackingView.tsx   |    17 +-
 .../views/collections/CollectionStaffView.tsx      |     2 +-
 src/client/views/collections/CollectionSubView.tsx |    10 +-
 .../views/collections/CollectionTimeView.tsx       |    15 +-
 .../views/collections/CollectionTreeView.tsx       |     6 +-
 src/client/views/collections/CollectionView.tsx    |    15 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   249 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |    23 +-
 .../collectionGrid/CollectionGridView.tsx          |     7 +-
 .../collectionLinear/CollectionLinearView.tsx      |     9 +-
 .../CollectionMulticolumnView.tsx                  |    11 +-
 .../CollectionMultirowView.tsx                     |    12 +-
 .../collectionSchema/CollectionSchemaCells.tsx     |    14 +-
 .../collectionSchema/CollectionSchemaView.tsx      |    27 +-
 .../collections/collectionSchema/SchemaTable.tsx   |     1 -
 src/client/views/nodes/AudioBox.tsx                |     9 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |    17 +-
 src/client/views/nodes/ColorBox.tsx                |     7 +-
 src/client/views/nodes/ComparisonBox.tsx           |     9 +-
 src/client/views/nodes/DocumentIcon.tsx            |     2 +-
 src/client/views/nodes/DocumentLinksButton.scss    |     4 +-
 src/client/views/nodes/DocumentLinksButton.tsx     |    24 +-
 src/client/views/nodes/DocumentView.scss           |     5 +-
 src/client/views/nodes/DocumentView.tsx            |    72 +-
 src/client/views/nodes/EquationBox.tsx             |    11 +-
 src/client/views/nodes/FilterBox.tsx               |    17 +-
 src/client/views/nodes/FunctionPlotBox.tsx         |     2 +-
 src/client/views/nodes/ImageBox.tsx                |    19 +-
 src/client/views/nodes/KeyValueBox.tsx             |     2 +-
 src/client/views/nodes/LabelBox.tsx                |     9 +-
 src/client/views/nodes/LinkAnchorBox.tsx           |     6 +-
 src/client/views/nodes/LinkBox.tsx                 |     7 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |     9 +-
 src/client/views/nodes/PDFBox.scss                 |     1 +
 src/client/views/nodes/PDFBox.tsx                  |    64 +-
 src/client/views/nodes/ScreenshotBox.tsx           |    14 +-
 src/client/views/nodes/ScriptingBox.tsx            |    34 +-
 src/client/views/nodes/SliderBox.tsx               |    15 +-
 src/client/views/nodes/VideoBox.tsx                |    41 +-
 src/client/views/nodes/WebBox.scss                 |     7 +
 src/client/views/nodes/WebBox.tsx                  |   176 +-
 src/client/views/nodes/WebBoxRenderer.js           |   395 +
 src/client/views/nodes/button/ButtonScripts.ts     |     6 +-
 src/client/views/nodes/button/FontIconBox.tsx      |   199 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |    27 +-
 .../formattedText/ProsemirrorExampleTransfer.ts    |    24 +-
 .../views/nodes/formattedText/RichTextRules.ts     |    26 +-
 src/client/views/nodes/trails/PresBox.tsx          |    12 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |    29 +-
 src/client/views/pdf/PDFViewer.tsx                 |     9 +-
 src/client/views/search/SearchBox.tsx              |    18 +-
 src/client/views/webcam/DashWebRTCVideo.tsx        |     5 +-
 src/fields/CursorField.ts                          |     2 +-
 src/fields/DateField.ts                            |     4 +-
 src/fields/Doc.ts                                  |    51 +-
 src/fields/InkField.ts                             |     6 +-
 src/fields/List.ts                                 |     6 +-
 src/fields/ObjectField.ts                          |     6 +-
 src/fields/Proxy.ts                                |     2 +-
 src/fields/RichTextField.ts                        |     6 +-
 src/fields/SchemaHeaderField.ts                    |    14 +-
 src/fields/ScriptField.ts                          |    91 +-
 src/fields/Types.ts                                |    10 +-
 src/fields/URLField.ts                             |     2 +-
 src/mobile/MobileInterface.tsx                     |    20 +-
 src/scraping/buxton/final/BuxtonImporter.ts        |   604 -
 src/server/ActionUtilities.ts                      |     2 +-
 src/server/ApiManagers/PDFManager.ts               |     5 +-
 src/server/ApiManagers/UploadManager.ts            |    19 +-
 src/server/ApiManagers/UserManager.ts              |     2 +-
 src/server/DashSession/DashSessionAgent.ts         |     2 +-
 src/server/DashSession/Session/agents/monitor.ts   |     2 +-
 .../Session/agents/promisified_ipc_manager.ts      |     2 +-
 .../DashSession/Session/agents/server_worker.ts    |     2 +-
 src/server/DashUploadUtils.ts                      |    14 +-
 src/server/Message.ts                              |     4 -
 src/server/RouteManager.ts                         |     2 +-
 src/server/Search.ts                               |     2 +-
 src/server/apis/google/GoogleApiServerUtils.ts     |     4 +-
 src/server/authentication/AuthenticationManager.ts |    15 +-
 src/server/authentication/Passport.ts              |     6 +-
 src/server/remapUrl.ts                             |     2 +-
 src/server/server_Initialization.ts                |    39 +-
 src/server/websocket.ts                            |     7 -
 webpack.config.js                                  |    45 +-
 130 files changed, 31889 insertions(+), 5920 deletions(-)
 delete mode 100644 src/client/ClientRecommender.tsx
 create mode 100644 src/client/util/ScriptingGlobals.ts
 create mode 100644 src/client/views/nodes/WebBoxRenderer.js
 delete mode 100644 src/scraping/buxton/final/BuxtonImporter.ts

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

diff --git a/.vscode/launch.json b/.vscode/launch.json
index 26c45a9d3..a7c30ca1e 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -33,7 +33,7 @@
             "type": "firefox",
             "request": "launch",
             "name": "Launch Firefox against localhost",
-            "sourceMaps": "client",
+            //"sourceMaps": "client",
             "reAttach": true,
             "url": "http://localhost:1050/login",
             "webRoot": "${workspaceFolder}",
diff --git a/deploy/index.html b/deploy/index.html
index a236de872..f8003f126 100644
--- a/deploy/index.html
+++ b/deploy/index.html
@@ -34,7 +34,7 @@
             align-content: center;
             justify-content: center;
             background-color: #BDDDF5;
-            transition: 3s;
+            transition: .3s;
             z-index: 10;
             z-index: 10;
             width: 100%;
@@ -139,11 +139,11 @@
                         if (document.getElementById("dash-progress")) document.getElementById("dash-progress").style.width = width
                         if (document.getElementById("dash-progress")) document.getElementById("dash-loader-text").innerHTML = msg
                     }
-                    setTimeout(() => dashmesg("10%", "Loading Dash..."), load / 10);
-                    setTimeout(() => dashmesg("33%", "Preparing dashboards..."), load / 3);
-                    setTimeout(() => dashmesg("50%", "Initializing scripts..."), load / 2);
-                    setTimeout(() => dashmesg("75%", "Fetching documents..."), load / 4 * 3);
-                    setTimeout(() => dashmesg("100%", "Finalising setup..."), load);
+                    // setTimeout(() => dashmesg("10%", "Loading Dash..."), load / 10);
+                    // setTimeout(() => dashmesg("33%", "Preparing dashboards..."), load / 3);
+                    // setTimeout(() => dashmesg("50%", "Initializing scripts..."), load / 2);
+                    // setTimeout(() => dashmesg("75%", "Fetching documents..."), load / 4 * 3);
+                    // setTimeout(() => dashmesg("100%", "Finalising setup..."), load);
                 </script>
             </div>
             <div id="dash-loader-text" class="dash-loader-text">
diff --git a/package-lock.json b/package-lock.json
index bbc5dc95c..5661ac03d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,8 +1,25669 @@
 {
     "name": "dash",
     "version": "1.0.0",
-    "lockfileVersion": 1,
+    "lockfileVersion": 2,
     "requires": true,
+    "packages": {
+        "": {
+            "name": "dash",
+            "version": "1.0.0",
+            "dependencies": {
+                "@fortawesome/fontawesome-svg-core": "^1.3.0",
+                "@fortawesome/free-brands-svg-icons": "^5.15.4",
+                "@fortawesome/free-regular-svg-icons": "^5.15.4",
+                "@fortawesome/free-solid-svg-icons": "^5.15.4",
+                "@fortawesome/react-fontawesome": "^0.1.17",
+                "@hig/flyout": "^1.3.1",
+                "@hig/theme-context": "^2.1.3",
+                "@hig/theme-data": "^2.23.1",
+                "@material-ui/core": "^4.12.3",
+                "@react-google-maps/api": "^2.7.0",
+                "@react-three/fiber": "^6.2.3",
+                "@types/bezier-js": "^4.1.0",
+                "@types/cors": "^2.8.12",
+                "@types/d3-axis": "^2.1.3",
+                "@types/d3-color": "^2.0.3",
+                "@types/d3-scale": "^3.3.2",
+                "@types/d3-selection": "^2.0.1",
+                "@types/dom-speech-recognition": "0.0.1",
+                "@types/formidable": "1.0.31",
+                "@types/google-maps": "^3.2.3",
+                "@types/react-reconciler": "^0.26.4",
+                "@types/reveal": "^3.3.33",
+                "@types/supercluster": "^7.1.0",
+                "@types/three": "^0.126.2",
+                "@types/web": "0.0.53",
+                "@types/webscopeio__react-textarea-autocomplete": "^4.7.2",
+                "@webscopeio/react-textarea-autocomplete": "^4.9.1",
+                "adm-zip": "^0.4.16",
+                "archiver": "^3.1.1",
+                "array-batcher": "^1.2.3",
+                "async": "^2.6.2",
+                "axios": "^0.19.2",
+                "babel": "^6.23.0",
+                "bcrypt-nodejs": "0.0.3",
+                "bezier-curve": "^1.0.0",
+                "bezier-js": "^4.1.1",
+                "bluebird": "^3.7.2",
+                "body-parser": "^1.19.2",
+                "bootstrap": "^4.6.1",
+                "browser-assert": "^1.2.1",
+                "bson": "^4.6.1",
+                "canvas": "^2.9.0",
+                "child_process": "^1.0.2",
+                "chrome": "^0.1.0",
+                "class-transformer": "^0.2.0",
+                "color": "^3.2.1",
+                "colors": "^1.4.0",
+                "connect-flash": "^0.1.1",
+                "connect-mongo": "^2.0.3",
+                "cookie-parser": "^1.4.6",
+                "cookie-session": "^2.0.0",
+                "cors": "^2.8.5",
+                "D": "^1.0.0",
+                "depcheck": "^0.9.2",
+                "equation-editor-react": "github:bobzel/equation-editor-react#useLocally",
+                "exif": "^0.6.0",
+                "exifr": "^7.1.3",
+                "express": "^4.17.3",
+                "express-flash": "0.0.2",
+                "express-session": "^1.17.2",
+                "express-validator": "^5.3.1",
+                "expressjs": "^1.0.1",
+                "file-saver": "^2.0.5",
+                "find-in-files": "^0.5.0",
+                "fit-curve": "^0.1.7",
+                "flexlayout-react": "^0.3.11",
+                "fluent-ffmpeg": "^2.1.2",
+                "forever-agent": "^0.6.1",
+                "formidable": "1.2.1",
+                "function-plot": "^1.22.8",
+                "golden-layout": "^1.5.9",
+                "google-auth-library": "^4.2.4",
+                "google-maps-react": "^2.0.6",
+                "googleapis": "^40.0.0",
+                "googlephotos": "^0.2.5",
+                "got": "^12.0.1",
+                "howler": "^2.2.3",
+                "html-to-image": "^0.1.3",
+                "html-to-text": "^5.1.1",
+                "html-webpack-plugin": "^5.5.0",
+                "http-browserify": "^1.7.0",
+                "https": "^1.0.0",
+                "https-browserify": "^1.0.0",
+                "i": "^0.3.7",
+                "image-data-uri": "^2.0.1",
+                "image-size": "^0.7.5",
+                "image-size-stream": "^1.1.0",
+                "js-datepicker": "^4.6.6",
+                "jsonschema": "^1.4.0",
+                "jszip": "^3.7.1",
+                "lodash": "^4.17.21",
+                "material-ui": "^0.20.2",
+                "memorystream": "^0.3.1",
+                "mobile-detect": "^1.4.5",
+                "mobx": "^5.15.7",
+                "mobx-react": "^5.4.4",
+                "mobx-react-devtools": "^6.1.1",
+                "mobx-utils": "^5.6.2",
+                "mongodb": "^3.7.3",
+                "mongoose": "^5.13.14",
+                "node-sass": "^4.14.1",
+                "node-stream-zip": "^1.15.0",
+                "nodemailer": "^5.1.1",
+                "nodemon": "^1.19.4",
+                "normalize.css": "^8.0.1",
+                "npm": "^6.14.16",
+                "p-limit": "^2.2.0",
+                "passport": "^0.4.0",
+                "passport-google-oauth20": "^2.0.0",
+                "passport-local": "^1.0.0",
+                "path-browserify": "^1.0.1",
+                "pdf-parse": "^1.1.1",
+                "pdfjs": "^2.4.7",
+                "pdfjs-dist": "^2.13.216",
+                "probe-image-size": "^4.0.0",
+                "process": "^0.11.10",
+                "prosemirror-commands": "^1.2.1",
+                "prosemirror-dev-tools": "^3.1.0",
+                "prosemirror-find-replace": "^0.9.0",
+                "prosemirror-history": "^1.2.0",
+                "prosemirror-inputrules": "^1.1.3",
+                "prosemirror-keymap": "^1.1.5",
+                "prosemirror-model": "^1.16.1",
+                "prosemirror-schema-list": "^1.1.6",
+                "prosemirror-state": "^1.3.4",
+                "prosemirror-transform": "^1.3.4",
+                "prosemirror-view": "^1.23.7",
+                "pug": "^2.0.4",
+                "puppeteer": "^3.3.0",
+                "query-string": "^6.14.1",
+                "raw-loader": "^1.0.0",
+                "rc-switch": "^1.9.2",
+                "react": "^16.14.0",
+                "react-audio-waveform": "0.0.5",
+                "react-autosuggest": "^9.4.3",
+                "react-beautiful-dnd": "^13.1.0",
+                "react-color": "^2.19.3",
+                "react-compound-slider": "^2.5.0",
+                "react-datepicker": "^3.8.0",
+                "react-dom": "^16.14.0",
+                "react-grid-layout": "^0.18.3",
+                "react-image-lightbox-with-rotate": "^5.1.1",
+                "react-jsx-parser": "^1.29.0",
+                "react-loading": "^2.0.3",
+                "react-measure": "^2.5.2",
+                "react-refresh-typescript": "^2.0.3",
+                "react-resizable": "^1.11.1",
+                "react-resizable-rotatable-draggable": "^0.2.0",
+                "react-reveal": "^1.2.2",
+                "react-select": "^3.2.0",
+                "react-table": "^7.7.0",
+                "readline": "^1.3.0",
+                "request": "^2.88.2",
+                "request-promise": "^4.2.6",
+                "reveal.js": "^4.3.0",
+                "rimraf": "^3.0.0",
+                "serializr": "^1.5.4",
+                "sharp": "^0.23.4",
+                "shelljs": "^0.8.5",
+                "socket.io": "^2.4.1",
+                "socket.io-client": "^2.4.0",
+                "solr-node": "^1.2.1",
+                "standard-http-error": "^2.0.1",
+                "stream-browserify": "^3.0.0",
+                "styled-components": "^4.4.1",
+                "supercluster": "^7.1.4",
+                "textarea-caret": "^3.1.0",
+                "three": "^0.127.0",
+                "tough-cookie": "^4.0.0",
+                "translate-google-api": "^1.0.4",
+                "typescript-collections": "^1.3.3",
+                "typescript-language-server": "^0.4.0",
+                "url-loader": "^1.1.2",
+                "util": "^0.12.4",
+                "uuid": "^3.4.0",
+                "valid-url": "^1.0.9",
+                "web-request": "^1.0.7",
+                "webpack-cli": "^4.9.2",
+                "webpack-dev-middleware": "^5.3.1",
+                "webrtc-adapter": "^7.7.1",
+                "wikijs": "^6.3.3",
+                "words-to-numbers": "^1.5.1",
+                "xoauth2": "^1.2.0",
+                "xregexp": "^4.4.1"
+            },
+            "devDependencies": {
+                "@types/adm-zip": "^0.4.34",
+                "@types/animejs": "^2.0.2",
+                "@types/archiver": "^3.1.1",
+                "@types/async": "^2.4.1",
+                "@types/bcrypt-nodejs": "0.0.30",
+                "@types/bluebird": "^3.5.36",
+                "@types/body-parser": "^1.19.2",
+                "@types/chai": "^4.3.0",
+                "@types/color": "^3.0.3",
+                "@types/connect-flash": "0.0.34",
+                "@types/cookie-parser": "^1.4.2",
+                "@types/cookie-session": "^2.0.44",
+                "@types/dotenv": "^6.1.1",
+                "@types/exif": "^0.6.3",
+                "@types/express": "^4.17.13",
+                "@types/express-flash": "0.0.0",
+                "@types/express-session": "^1.17.4",
+                "@types/express-validator": "^3.0.0",
+                "@types/file-saver": "^2.0.5",
+                "@types/google-maps-react": "^2.0.5",
+                "@types/jquery": "^3.5.14",
+                "@types/libxmljs": "^0.18.7",
+                "@types/lodash": "^4.14.179",
+                "@types/mobile-detect": "^1.3.4",
+                "@types/mocha": "^5.2.6",
+                "@types/mongodb": "^3.6.20",
+                "@types/mongoose": "^5.11.97",
+                "@types/node": "^10.17.60",
+                "@types/nodemailer": "^4.6.6",
+                "@types/passport": "^1.0.7",
+                "@types/passport-google-oauth20": "^2.0.11",
+                "@types/passport-local": "^1.0.34",
+                "@types/pdfjs-dist": "^2.10.378",
+                "@types/prosemirror-commands": "^1.0.4",
+                "@types/prosemirror-dev-tools": "^2.1.0",
+                "@types/prosemirror-history": "^1.0.3",
+                "@types/prosemirror-inputrules": "^1.0.4",
+                "@types/prosemirror-keymap": "^1.0.4",
+                "@types/prosemirror-menu": "^1.0.6",
+                "@types/prosemirror-model": "^1.16.1",
+                "@types/prosemirror-schema-list": "^1.0.3",
+                "@types/prosemirror-state": "^1.2.8",
+                "@types/prosemirror-transform": "^1.1.5",
+                "@types/prosemirror-view": "^1.23.1",
+                "@types/rc-switch": "^1.9.2",
+                "@types/react": "^16.14.23",
+                "@types/react-autosuggest": "^9.3.14",
+                "@types/react-color": "^2.17.6",
+                "@types/react-datepicker": "^3.1.8",
+                "@types/react-dom": "^16.9.14",
+                "@types/react-grid-layout": "^0.17.2",
+                "@types/react-measure": "^2.0.8",
+                "@types/react-select": "^3.1.2",
+                "@types/react-table": "^6.8.9",
+                "@types/request": "^2.48.8",
+                "@types/request-promise": "^4.1.48",
+                "@types/rimraf": "^2.0.5",
+                "@types/sharp": "^0.23.1",
+                "@types/shelljs": "^0.8.11",
+                "@types/socket.io": "^2.1.13",
+                "@types/socket.io-client": "^1.4.36",
+                "@types/typescript": "^2.0.0",
+                "@types/uuid": "^3.4.10",
+                "@types/valid-url": "^1.0.3",
+                "@types/webpack": "^4.41.32",
+                "@types/webpack-hot-middleware": "^2.25.6",
+                "@types/xregexp": "^4.4.0",
+                "@types/youtube": "0.0.39",
+                "awesome-typescript-loader": "^5.2.1",
+                "chai": "^4.3.6",
+                "copy-webpack-plugin": "^4.6.0",
+                "cross-env": "^5.2.1",
+                "css-loader": "^2.1.1",
+                "dotenv": "^8.6.0",
+                "file-loader": "^3.0.1",
+                "fork-ts-checker-webpack-plugin": "^1.6.0",
+                "jsdom": "^15.2.1",
+                "mocha": "^5.2.0",
+                "sass-loader": "^7.3.1",
+                "scss-loader": "0.0.1",
+                "style-loader": "^0.23.1",
+                "ts-loader": "^5.3.3",
+                "ts-node": "^7.0.1",
+                "ts-node-dev": "^1.1.8",
+                "tslint": "^5.20.1",
+                "tslint-loader": "^3.6.0",
+                "typescript": "^4.6.2",
+                "webpack": "^5.69.1",
+                "webpack-dev-server": "^3.11.3",
+                "webpack-hot-middleware": "^2.25.1"
+            }
+        },
+        "node_modules/@babel/code-frame": {
+            "version": "7.8.3",
+            "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
+            "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
+            "dependencies": {
+                "@babel/highlight": "^7.8.3"
+            }
+        },
+        "node_modules/@babel/generator": {
+            "version": "7.9.6",
+            "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.6.tgz",
+            "integrity": "sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ==",
+            "dependencies": {
+                "@babel/types": "^7.9.6",
+                "jsesc": "^2.5.1",
+                "lodash": "^4.17.13",
+                "source-map": "^0.5.0"
+            }
+        },
+        "node_modules/@babel/generator/node_modules/@babel/types": {
+            "version": "7.9.6",
+            "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.6.tgz",
+            "integrity": "sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==",
+            "dependencies": {
+                "@babel/helper-validator-identifier": "^7.9.5",
+                "lodash": "^4.17.13",
+                "to-fast-properties": "^2.0.0"
+            }
+        },
+        "node_modules/@babel/helper-annotate-as-pure": {
+            "version": "7.10.4",
+            "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz",
+            "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==",
+            "dependencies": {
+                "@babel/types": "^7.10.4"
+            }
+        },
+        "node_modules/@babel/helper-annotate-as-pure/node_modules/@babel/helper-validator-identifier": {
+            "version": "7.10.4",
+            "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+            "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw=="
+        },
+        "node_modules/@babel/helper-annotate-as-pure/node_modules/@babel/types": {
+            "version": "7.10.5",
+            "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz",
+            "integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==",
+            "dependencies": {
+                "@babel/helper-validator-identifier": "^7.10.4",
+                "lodash": "^4.17.19",
+                "to-fast-properties": "^2.0.0"
+            }
+        },
+        "node_modules/@babel/helper-annotate-as-pure/node_modules/lodash": {
+            "version": "4.17.19",
+            "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
+            "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
+        },
+        "node_modules/@babel/helper-function-name": {
+            "version": "7.9.5",
+            "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz",
+            "integrity": "sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==",
+            "dependencies": {
+                "@babel/helper-get-function-arity": "^7.8.3",
+                "@babel/template": "^7.8.3",
+                "@babel/types": "^7.9.5"
+            }
+        },
+        "node_modules/@babel/helper-get-function-arity": {
+            "version": "7.8.3",
+            "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz",
+            "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==",
+            "dependencies": {
+                "@babel/types": "^7.8.3"
+            }
+        },
+        "node_modules/@babel/helper-module-imports": {
+            "version": "7.10.3",
+            "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.3.tgz",
+            "integrity": "sha512-Jtqw5M9pahLSUWA+76nhK9OG8nwYXzhQzVIGFoNaHnXF/r4l7kz4Fl0UAW7B6mqC5myoJiBP5/YQlXQTMfHI9w==",
+            "dependencies": {
+                "@babel/types": "^7.10.3"
+            }
+        },
+        "node_modules/@babel/helper-module-imports/node_modules/@babel/helper-validator-identifier": {
+            "version": "7.10.3",
+            "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.3.tgz",
+            "integrity": "sha512-bU8JvtlYpJSBPuj1VUmKpFGaDZuLxASky3LhaKj3bmpSTY6VWooSM8msk+Z0CZoErFye2tlABF6yDkT3FOPAXw=="
+        },
+        "node_modules/@babel/helper-module-imports/node_modules/@babel/types": {
+            "version": "7.10.3",
+            "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.3.tgz",
+            "integrity": "sha512-nZxaJhBXBQ8HVoIcGsf9qWep3Oh3jCENK54V4mRF7qaJabVsAYdbTtmSD8WmAp1R6ytPiu5apMwSXyxB1WlaBA==",
+            "dependencies": {
+                "@babel/helper-validator-identifier": "^7.10.3",
+                "lodash": "^4.17.13",
+                "to-fast-properties": "^2.0.0"
+            }
+        },
+        "node_modules/@babel/helper-plugin-utils": {
+            "version": "7.16.7",
+            "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz",
+            "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==",
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@babel/helper-split-export-declaration": {
+            "version": "7.8.3",
+            "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz",
+            "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==",
+            "dependencies": {
+                "@babel/types": "^7.8.3"
+            }
+        },
+        "node_modules/@babel/helper-validator-identifier": {
+            "version": "7.9.5",
+            "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz",
+            "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g=="
+        },
+        "node_modules/@babel/highlight": {
+            "version": "7.9.0",
+            "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
+            "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
+            "dependencies": {
+                "@babel/helper-validator-identifier": "^7.9.0",
+                "chalk": "^2.0.0",
+                "js-tokens": "^4.0.0"
+            }
+        },
+        "node_modules/@babel/parser": {
+            "version": "7.9.6",
+            "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.6.tgz",
+            "integrity": "sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q==",
+            "bin": {
+                "parser": "bin/babel-parser.js"
+            },
+            "engines": {
+                "node": ">=6.0.0"
+            }
+        },
+        "node_modules/@babel/plugin-syntax-jsx": {
+            "version": "7.16.7",
+            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz",
+            "integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==",
+            "dependencies": {
+                "@babel/helper-plugin-utils": "^7.16.7"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            },
+            "peerDependencies": {
+                "@babel/core": "^7.0.0-0"
+            }
+        },
+        "node_modules/@babel/runtime": {
+            "version": "7.9.2",
+            "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
+            "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
+            "dependencies": {
+                "regenerator-runtime": "^0.13.4"
+            }
+        },
+        "node_modules/@babel/runtime-corejs3": {
+            "version": "7.17.2",
+            "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.2.tgz",
+            "integrity": "sha512-NcKtr2epxfIrNM4VOmPKO46TvDMCBhgi2CrSHaEarrz+Plk2K5r9QemmOFTGpZaoKnWoGH5MO+CzeRsih/Fcgg==",
+            "dependencies": {
+                "core-js-pure": "^3.20.2",
+                "regenerator-runtime": "^0.13.4"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@babel/template": {
+            "version": "7.8.6",
+            "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
+            "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
+            "dependencies": {
+                "@babel/code-frame": "^7.8.3",
+                "@babel/parser": "^7.8.6",
+                "@babel/types": "^7.8.6"
+            }
+        },
+        "node_modules/@babel/traverse": {
+            "version": "7.9.6",
+            "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.6.tgz",
+            "integrity": "sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg==",
+            "dependencies": {
+                "@babel/code-frame": "^7.8.3",
+                "@babel/generator": "^7.9.6",
+                "@babel/helper-function-name": "^7.9.5",
+                "@babel/helper-split-export-declaration": "^7.8.3",
+                "@babel/parser": "^7.9.6",
+                "@babel/types": "^7.9.6",
+                "debug": "^4.1.0",
+                "globals": "^11.1.0",
+                "lodash": "^4.17.13"
+            }
+        },
+        "node_modules/@babel/traverse/node_modules/@babel/types": {
+            "version": "7.9.6",
+            "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.6.tgz",
+            "integrity": "sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==",
+            "dependencies": {
+                "@babel/helper-validator-identifier": "^7.9.5",
+                "lodash": "^4.17.13",
+                "to-fast-properties": "^2.0.0"
+            }
+        },
+        "node_modules/@babel/traverse/node_modules/debug": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+            "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+            "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
+            "dependencies": {
+                "ms": "^2.1.1"
+            }
+        },
+        "node_modules/@babel/types": {
+            "version": "7.9.5",
+            "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.5.tgz",
+            "integrity": "sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg==",
+            "dependencies": {
+                "@babel/helper-validator-identifier": "^7.9.5",
+                "lodash": "^4.17.13",
+                "to-fast-properties": "^2.0.0"
+            }
+        },
+        "node_modules/@discoveryjs/json-ext": {
+            "version": "0.5.6",
+            "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz",
+            "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==",
+            "engines": {
+                "node": ">=10.0.0"
+            }
+        },
+        "node_modules/@emotion/babel-plugin": {
+            "version": "11.7.2",
+            "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.7.2.tgz",
+            "integrity": "sha512-6mGSCWi9UzXut/ZAN6lGFu33wGR3SJisNl3c0tvlmb8XChH1b2SUvxvnOh7hvLpqyRdHHU9AiazV3Cwbk5SXKQ==",
+            "dependencies": {
+                "@babel/helper-module-imports": "^7.12.13",
+                "@babel/plugin-syntax-jsx": "^7.12.13",
+                "@babel/runtime": "^7.13.10",
+                "@emotion/hash": "^0.8.0",
+                "@emotion/memoize": "^0.7.5",
+                "@emotion/serialize": "^1.0.2",
+                "babel-plugin-macros": "^2.6.1",
+                "convert-source-map": "^1.5.0",
+                "escape-string-regexp": "^4.0.0",
+                "find-root": "^1.1.0",
+                "source-map": "^0.5.7",
+                "stylis": "4.0.13"
+            },
+            "peerDependencies": {
+                "@babel/core": "^7.0.0"
+            }
+        },
+        "node_modules/@emotion/babel-plugin/node_modules/@babel/helper-module-imports": {
+            "version": "7.16.7",
+            "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz",
+            "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==",
+            "dependencies": {
+                "@babel/types": "^7.16.7"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@emotion/babel-plugin/node_modules/@babel/helper-validator-identifier": {
+            "version": "7.16.7",
+            "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz",
+            "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==",
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@emotion/babel-plugin/node_modules/@babel/runtime": {
+            "version": "7.17.2",
+            "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
+            "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
+            "dependencies": {
+                "regenerator-runtime": "^0.13.4"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@emotion/babel-plugin/node_modules/@babel/types": {
+            "version": "7.17.0",
+            "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz",
+            "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==",
+            "dependencies": {
+                "@babel/helper-validator-identifier": "^7.16.7",
+                "to-fast-properties": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@emotion/babel-plugin/node_modules/@emotion/memoize": {
+            "version": "0.7.5",
+            "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz",
+            "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ=="
+        },
+        "node_modules/@emotion/babel-plugin/node_modules/@emotion/serialize": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz",
+            "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==",
+            "dependencies": {
+                "@emotion/hash": "^0.8.0",
+                "@emotion/memoize": "^0.7.4",
+                "@emotion/unitless": "^0.7.5",
+                "@emotion/utils": "^1.0.0",
+                "csstype": "^3.0.2"
+            }
+        },
+        "node_modules/@emotion/babel-plugin/node_modules/@emotion/utils": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz",
+            "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
+        },
+        "node_modules/@emotion/babel-plugin/node_modules/csstype": {
+            "version": "3.0.10",
+            "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
+            "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
+        },
+        "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+            "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/@emotion/babel-plugin/node_modules/stylis": {
+            "version": "4.0.13",
+            "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
+            "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
+        },
+        "node_modules/@emotion/cache": {
+            "version": "10.0.29",
+            "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz",
+            "integrity": "sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==",
+            "dependencies": {
+                "@emotion/sheet": "0.9.4",
+                "@emotion/stylis": "0.8.5",
+                "@emotion/utils": "0.11.3",
+                "@emotion/weak-memoize": "0.2.5"
+            }
+        },
+        "node_modules/@emotion/core": {
+            "version": "10.3.1",
+            "resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.3.1.tgz",
+            "integrity": "sha512-447aUEjPIm0MnE6QYIaFz9VQOHSXf4Iu6EWOIqq11EAPqinkSZmfymPTmlOE3QjLv846lH4JVZBUOtwGbuQoww==",
+            "dependencies": {
+                "@babel/runtime": "^7.5.5",
+                "@emotion/cache": "^10.0.27",
+                "@emotion/css": "^10.0.27",
+                "@emotion/serialize": "^0.11.15",
+                "@emotion/sheet": "0.9.4",
+                "@emotion/utils": "0.11.3"
+            },
+            "peerDependencies": {
+                "react": ">=16.3.0"
+            }
+        },
+        "node_modules/@emotion/core/node_modules/@emotion/css": {
+            "version": "10.0.27",
+            "resolved": "https://registry.npmjs.org/@emotion/css/-/css-10.0.27.tgz",
+            "integrity": "sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==",
+            "dependencies": {
+                "@emotion/serialize": "^0.11.15",
+                "@emotion/utils": "0.11.3",
+                "babel-plugin-emotion": "^10.0.27"
+            }
+        },
+        "node_modules/@emotion/css": {
+            "version": "11.7.1",
+            "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.7.1.tgz",
+            "integrity": "sha512-RUUgPlMZunlc7SE5A6Hg+VWRzb2cU6O9xlV78KCFgcnl25s7Qz/20oQg71iKudpLqk7xj0vhbJlwcJJMT0BOZg==",
+            "dependencies": {
+                "@emotion/babel-plugin": "^11.7.1",
+                "@emotion/cache": "^11.7.1",
+                "@emotion/serialize": "^1.0.0",
+                "@emotion/sheet": "^1.0.3",
+                "@emotion/utils": "^1.0.0"
+            },
+            "peerDependencies": {
+                "@babel/core": "^7.0.0"
+            },
+            "peerDependenciesMeta": {
+                "@babel/core": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/@emotion/css/node_modules/@emotion/cache": {
+            "version": "11.7.1",
+            "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz",
+            "integrity": "sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==",
+            "dependencies": {
+                "@emotion/memoize": "^0.7.4",
+                "@emotion/sheet": "^1.1.0",
+                "@emotion/utils": "^1.0.0",
+                "@emotion/weak-memoize": "^0.2.5",
+                "stylis": "4.0.13"
+            }
+        },
+        "node_modules/@emotion/css/node_modules/@emotion/serialize": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz",
+            "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==",
+            "dependencies": {
+                "@emotion/hash": "^0.8.0",
+                "@emotion/memoize": "^0.7.4",
+                "@emotion/unitless": "^0.7.5",
+                "@emotion/utils": "^1.0.0",
+                "csstype": "^3.0.2"
+            }
+        },
+        "node_modules/@emotion/css/node_modules/@emotion/sheet": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz",
+            "integrity": "sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g=="
+        },
+        "node_modules/@emotion/css/node_modules/@emotion/utils": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz",
+            "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
+        },
+        "node_modules/@emotion/css/node_modules/csstype": {
+            "version": "3.0.10",
+            "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
+            "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
+        },
+        "node_modules/@emotion/css/node_modules/stylis": {
+            "version": "4.0.13",
+            "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
+            "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
+        },
+        "node_modules/@emotion/hash": {
+            "version": "0.8.0",
+            "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
+            "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
+        },
+        "node_modules/@emotion/is-prop-valid": {
+            "version": "0.8.8",
+            "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
+            "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
+            "dependencies": {
+                "@emotion/memoize": "0.7.4"
+            }
+        },
+        "node_modules/@emotion/memoize": {
+            "version": "0.7.4",
+            "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
+            "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw=="
+        },
+        "node_modules/@emotion/react": {
+            "version": "11.8.1",
+            "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.8.1.tgz",
+            "integrity": "sha512-XGaie4nRxmtP1BZYBXqC5JGqMYF2KRKKI7vjqNvQxyRpekVAZhb6QqrElmZCAYXH1L90lAelADSVZC4PFsrJ8Q==",
+            "dependencies": {
+                "@babel/runtime": "^7.13.10",
+                "@emotion/babel-plugin": "^11.7.1",
+                "@emotion/cache": "^11.7.1",
+                "@emotion/serialize": "^1.0.2",
+                "@emotion/sheet": "^1.1.0",
+                "@emotion/utils": "^1.1.0",
+                "@emotion/weak-memoize": "^0.2.5",
+                "hoist-non-react-statics": "^3.3.1"
+            },
+            "peerDependencies": {
+                "@babel/core": "^7.0.0",
+                "react": ">=16.8.0"
+            },
+            "peerDependenciesMeta": {
+                "@babel/core": {
+                    "optional": true
+                },
+                "@types/react": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/@emotion/react/node_modules/@babel/runtime": {
+            "version": "7.17.2",
+            "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
+            "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
+            "dependencies": {
+                "regenerator-runtime": "^0.13.4"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@emotion/react/node_modules/@emotion/cache": {
+            "version": "11.7.1",
+            "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz",
+            "integrity": "sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==",
+            "dependencies": {
+                "@emotion/memoize": "^0.7.4",
+                "@emotion/sheet": "^1.1.0",
+                "@emotion/utils": "^1.0.0",
+                "@emotion/weak-memoize": "^0.2.5",
+                "stylis": "4.0.13"
+            }
+        },
+        "node_modules/@emotion/react/node_modules/@emotion/serialize": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz",
+            "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==",
+            "dependencies": {
+                "@emotion/hash": "^0.8.0",
+                "@emotion/memoize": "^0.7.4",
+                "@emotion/unitless": "^0.7.5",
+                "@emotion/utils": "^1.0.0",
+                "csstype": "^3.0.2"
+            }
+        },
+        "node_modules/@emotion/react/node_modules/@emotion/sheet": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz",
+            "integrity": "sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g=="
+        },
+        "node_modules/@emotion/react/node_modules/@emotion/utils": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz",
+            "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
+        },
+        "node_modules/@emotion/react/node_modules/csstype": {
+            "version": "3.0.10",
+            "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
+            "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
+        },
+        "node_modules/@emotion/react/node_modules/stylis": {
+            "version": "4.0.13",
+            "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
+            "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
+        },
+        "node_modules/@emotion/serialize": {
+            "version": "0.11.16",
+            "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.16.tgz",
+            "integrity": "sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg==",
+            "dependencies": {
+                "@emotion/hash": "0.8.0",
+                "@emotion/memoize": "0.7.4",
+                "@emotion/unitless": "0.7.5",
+                "@emotion/utils": "0.11.3",
+                "csstype": "^2.5.7"
+            }
+        },
+        "node_modules/@emotion/sheet": {
+            "version": "0.9.4",
+            "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz",
+            "integrity": "sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA=="
+        },
+        "node_modules/@emotion/styled": {
+            "version": "11.8.1",
+            "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.8.1.tgz",
+            "integrity": "sha512-OghEVAYBZMpEquHZwuelXcRjRJQOVayvbmNR0zr174NHdmMgrNkLC6TljKC5h9lZLkN5WGrdUcrKlOJ4phhoTQ==",
+            "dependencies": {
+                "@babel/runtime": "^7.13.10",
+                "@emotion/babel-plugin": "^11.7.1",
+                "@emotion/is-prop-valid": "^1.1.2",
+                "@emotion/serialize": "^1.0.2",
+                "@emotion/utils": "^1.1.0"
+            },
+            "peerDependencies": {
+                "@babel/core": "^7.0.0",
+                "@emotion/react": "^11.0.0-rc.0",
+                "react": ">=16.8.0"
+            },
+            "peerDependenciesMeta": {
+                "@babel/core": {
+                    "optional": true
+                },
+                "@types/react": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/@emotion/styled/node_modules/@babel/runtime": {
+            "version": "7.17.2",
+            "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
+            "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
+            "dependencies": {
+                "regenerator-runtime": "^0.13.4"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@emotion/styled/node_modules/@emotion/is-prop-valid": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.2.tgz",
+            "integrity": "sha512-3QnhqeL+WW88YjYbQL5gUIkthuMw7a0NGbZ7wfFVk2kg/CK5w8w5FFa0RzWjyY1+sujN0NWbtSHH6OJmWHtJpQ==",
+            "dependencies": {
+                "@emotion/memoize": "^0.7.4"
+            }
+        },
+        "node_modules/@emotion/styled/node_modules/@emotion/serialize": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz",
+            "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==",
+            "dependencies": {
+                "@emotion/hash": "^0.8.0",
+                "@emotion/memoize": "^0.7.4",
+                "@emotion/unitless": "^0.7.5",
+                "@emotion/utils": "^1.0.0",
+                "csstype": "^3.0.2"
+            }
+        },
+        "node_modules/@emotion/styled/node_modules/@emotion/utils": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz",
+            "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
+        },
+        "node_modules/@emotion/styled/node_modules/csstype": {
+            "version": "3.0.10",
+            "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
+            "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
+        },
+        "node_modules/@emotion/stylis": {
+            "version": "0.8.5",
+            "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz",
+            "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ=="
+        },
+        "node_modules/@emotion/unitless": {
+            "version": "0.7.5",
+            "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
+            "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
+        },
+        "node_modules/@emotion/utils": {
+            "version": "0.11.3",
+            "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz",
+            "integrity": "sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw=="
+        },
+        "node_modules/@emotion/weak-memoize": {
+            "version": "0.2.5",
+            "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz",
+            "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA=="
+        },
+        "node_modules/@fortawesome/fontawesome-common-types": {
+            "version": "0.3.0",
+            "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.3.0.tgz",
+            "integrity": "sha512-CA3MAZBTxVsF6SkfkHXDerkhcQs0QPofy43eFdbWJJkZiq3SfiaH1msOkac59rQaqto5EqWnASboY1dBuKen5w==",
+            "deprecated": "Please upgrade to 6.1.0. https://fontawesome.com/docs/changelog/",
+            "hasInstallScript": true,
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/@fortawesome/fontawesome-svg-core": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.3.0.tgz",
+            "integrity": "sha512-UIL6crBWhjTNQcONt96ExjUnKt1D68foe3xjEensLDclqQ6YagwCRYVQdrp/hW0ALRp/5Fv/VKw+MqTUWYYvPg==",
+            "deprecated": "Please upgrade to 6.1.0. https://fontawesome.com/docs/changelog/",
+            "hasInstallScript": true,
+            "dependencies": {
+                "@fortawesome/fontawesome-common-types": "^0.3.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/@fortawesome/free-brands-svg-icons": {
+            "version": "5.15.4",
+            "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.15.4.tgz",
+            "integrity": "sha512-f1witbwycL9cTENJegcmcZRYyawAFbm8+c6IirLmwbbpqz46wyjbQYLuxOc7weXFXfB7QR8/Vd2u5R3q6JYD9g==",
+            "hasInstallScript": true,
+            "dependencies": {
+                "@fortawesome/fontawesome-common-types": "^0.2.36"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/@fortawesome/free-brands-svg-icons/node_modules/@fortawesome/fontawesome-common-types": {
+            "version": "0.2.36",
+            "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz",
+            "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==",
+            "hasInstallScript": true,
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/@fortawesome/free-regular-svg-icons": {
+            "version": "5.15.4",
+            "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.4.tgz",
+            "integrity": "sha512-9VNNnU3CXHy9XednJ3wzQp6SwNwT3XaM26oS4Rp391GsxVYA+0oDR2J194YCIWf7jNRCYKjUCOduxdceLrx+xw==",
+            "hasInstallScript": true,
+            "dependencies": {
+                "@fortawesome/fontawesome-common-types": "^0.2.36"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/@fortawesome/free-regular-svg-icons/node_modules/@fortawesome/fontawesome-common-types": {
+            "version": "0.2.36",
+            "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz",
+            "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==",
+            "hasInstallScript": true,
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/@fortawesome/free-solid-svg-icons": {
+            "version": "5.15.4",
+            "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz",
+            "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==",
+            "hasInstallScript": true,
+            "dependencies": {
+                "@fortawesome/fontawesome-common-types": "^0.2.36"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/@fortawesome/free-solid-svg-icons/node_modules/@fortawesome/fontawesome-common-types": {
+            "version": "0.2.36",
+            "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz",
+            "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==",
+            "hasInstallScript": true,
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/@fortawesome/react-fontawesome": {
+            "version": "0.1.17",
+            "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.17.tgz",
+            "integrity": "sha512-dX43Z5IvMaW7fwzU8farosYjKNGfRb2HB/DgjVBHeJZ/NSnuuaujPPx0YOdcAq+n3mqn70tyCde2HM1mqbhiuw==",
+            "dependencies": {
+                "prop-types": "^15.8.1"
+            },
+            "peerDependencies": {
+                "@fortawesome/fontawesome-svg-core": "~1 || >=1.3.0-beta1",
+                "react": ">=16.x"
+            }
+        },
+        "node_modules/@fortawesome/react-fontawesome/node_modules/prop-types": {
+            "version": "15.8.1",
+            "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+            "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+            "dependencies": {
+                "loose-envify": "^1.4.0",
+                "object-assign": "^4.1.1",
+                "react-is": "^16.13.1"
+            }
+        },
+        "node_modules/@googlemaps/js-api-loader": {
+            "version": "1.12.8",
+            "resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.12.8.tgz",
+            "integrity": "sha512-h1RNVIJkHWL4UrdJo8fwECJwSRg8CSIfTEtPdzvThcji7tAPvVBDVBPzvPKhvl8e8mWEGPhnYOt6kPfSxJL+vQ==",
+            "dependencies": {
+                "fast-deep-equal": "^3.1.3"
+            }
+        },
+        "node_modules/@googlemaps/js-api-loader/node_modules/fast-deep-equal": {
+            "version": "3.1.3",
+            "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+            "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+        },
+        "node_modules/@googlemaps/markerclusterer": {
+            "version": "1.0.12",
+            "resolved": "https://registry.npmjs.org/@googlemaps/markerclusterer/-/markerclusterer-1.0.12.tgz",
+            "integrity": "sha512-DSmN2w1ZoI+PBWfNCKYcr+zalP0uaaKIU08+UgebJYZ96X+J6CdUpD+xOVwBV1OJDiIXkneHquOZnT+V6dm+Sg==",
+            "dependencies": {
+                "@turf/clusters-dbscan": "^6.4.0",
+                "@turf/clusters-kmeans": "^6.4.0",
+                "fast-deep-equal": "^3.1.3",
+                "supercluster": "^7.1.3"
+            }
+        },
+        "node_modules/@googlemaps/markerclusterer/node_modules/fast-deep-equal": {
+            "version": "3.1.3",
+            "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+            "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+        },
+        "node_modules/@hig/flyout": {
+            "version": "1.3.1",
+            "resolved": "https://registry.npmjs.org/@hig/flyout/-/flyout-1.3.1.tgz",
+            "integrity": "sha512-dj69PnHtvXQ+IbbFv167ObHCuSVVv5ycMdFIOPMY+SCqSCssYiu1PkEI6eQxOePAb3MYhF4u7oi3Vy2XmxfkgA==",
+            "dependencies": {
+                "@hig/utils": "^0.4.1",
+                "emotion": "^10.0.0",
+                "prop-types": "^15.7.1",
+                "react-transition-group": "^2.3.1"
+            },
+            "peerDependencies": {
+                "@hig/theme-context": "^3.0.3",
+                "@hig/theme-data": "^2.22.0",
+                "react": "^15.4.1 || ^16.3.2"
+            }
+        },
+        "node_modules/@hig/theme-context": {
+            "version": "2.1.3",
+            "resolved": "https://registry.npmjs.org/@hig/theme-context/-/theme-context-2.1.3.tgz",
+            "integrity": "sha512-c0Ju+Z8C532ZZtjwOLzN+XeO+pL3kqUawu6ZG3J084MH5RM9W8JCKyMf4D9Qr38jFWoiX6u8yiSxxjV/mz9Sqw==",
+            "dependencies": {
+                "create-react-context": "^0.2.3",
+                "prop-types": "^15.6.1"
+            },
+            "peerDependencies": {
+                "@hig/theme-data": "*",
+                "react": "^15.4.1 || ^16.3.2"
+            }
+        },
+        "node_modules/@hig/theme-data": {
+            "version": "2.23.1",
+            "resolved": "https://registry.npmjs.org/@hig/theme-data/-/theme-data-2.23.1.tgz",
+            "integrity": "sha512-Yca1iCIs8U1dpkLW1VNiKOJW9fCJdb70f1dc61+R+HO1QJ8jMBu5eOpmQE1NKotJmcazhsXpo6xC11yV1crE4w==",
+            "dependencies": {
+                "tinycolor2": "^1.4.1"
+            }
+        },
+        "node_modules/@hig/utils": {
+            "version": "0.4.1",
+            "resolved": "https://registry.npmjs.org/@hig/utils/-/utils-0.4.1.tgz",
+            "integrity": "sha512-yx8HF5dLNo/WwwF5dR9P1pZu0tKhCuItrqbGbg52HpLUsptODBs/1ATT76cBaAJ7dSBCeNOKKiE4WKSrsA8bdQ==",
+            "dependencies": {
+                "emotion": "^10.0.0",
+                "lodash.memoize": "^4.1.2"
+            }
+        },
+        "node_modules/@hypnosphi/create-react-context": {
+            "version": "0.3.1",
+            "resolved": "https://registry.npmjs.org/@hypnosphi/create-react-context/-/create-react-context-0.3.1.tgz",
+            "integrity": "sha512-V1klUed202XahrWJLLOT3EXNeCpFHCcJntdFGI15ntCwau+jfT386w7OFTMaCqOgXUH1fa0w/I1oZs+i/Rfr0A==",
+            "dependencies": {
+                "gud": "^1.0.0",
+                "warning": "^4.0.3"
+            },
+            "peerDependencies": {
+                "prop-types": "^15.0.0",
+                "react": ">=0.14.0"
+            }
+        },
+        "node_modules/@hypnosphi/create-react-context/node_modules/warning": {
+            "version": "4.0.3",
+            "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+            "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+            "dependencies": {
+                "loose-envify": "^1.0.0"
+            }
+        },
+        "node_modules/@icons/material": {
+            "version": "0.2.4",
+            "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz",
+            "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==",
+            "peerDependencies": {
+                "react": "*"
+            }
+        },
+        "node_modules/@log4js-node/log4js-api": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/@log4js-node/log4js-api/-/log4js-api-1.0.2.tgz",
+            "integrity": "sha512-6SJfx949YEWooh/CUPpJ+F491y4BYJmknz4hUN1+RHvKoUEynKbRmhnwbk/VLmh4OthLLDNCyWXfbh4DG1cTXA=="
+        },
+        "node_modules/@mapbox/node-pre-gyp": {
+            "version": "1.0.8",
+            "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz",
+            "integrity": "sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg==",
+            "dependencies": {
+                "detect-libc": "^1.0.3",
+                "https-proxy-agent": "^5.0.0",
+                "make-dir": "^3.1.0",
+                "node-fetch": "^2.6.5",
+                "nopt": "^5.0.0",
+                "npmlog": "^5.0.1",
+                "rimraf": "^3.0.2",
+                "semver": "^7.3.5",
+                "tar": "^6.1.11"
+            },
+            "bin": {
+                "node-pre-gyp": "bin/node-pre-gyp"
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/ansi-regex": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/are-we-there-yet": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
+            "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
+            "dependencies": {
+                "delegates": "^1.0.0",
+                "readable-stream": "^3.6.0"
+            },
+            "engines": {
+                "node": ">=10"
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/chownr": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+            "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+            "engines": {
+                "node": ">=10"
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/emoji-regex": {
+            "version": "8.0.0",
+            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/fs-minipass": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+            "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+            "dependencies": {
+                "minipass": "^3.0.0"
+            },
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/gauge": {
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
+            "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
+            "dependencies": {
+                "aproba": "^1.0.3 || ^2.0.0",
+                "color-support": "^1.1.2",
+                "console-control-strings": "^1.0.0",
+                "has-unicode": "^2.0.1",
+                "object-assign": "^4.1.1",
+                "signal-exit": "^3.0.0",
+                "string-width": "^4.2.3",
+                "strip-ansi": "^6.0.1",
+                "wide-align": "^1.1.2"
+            },
+            "engines": {
+                "node": ">=10"
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/is-fullwidth-code-point": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+            "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/lru-cache": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+            "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+            "dependencies": {
+                "yallist": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=10"
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+            "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+            "dependencies": {
+                "semver": "^6.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": {
+            "version": "6.3.0",
+            "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+            "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+            "bin": {
+                "semver": "bin/semver.js"
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/minipass": {
+            "version": "3.1.6",
+            "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz",
+            "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==",
+            "dependencies": {
+                "yallist": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/minizlib": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+            "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+            "dependencies": {
+                "minipass": "^3.0.0",
+                "yallist": "^4.0.0"
+            },
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/mkdirp": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+            "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+            "bin": {
+                "mkdirp": "bin/cmd.js"
+            },
+            "engines": {
+                "node": ">=10"
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/node-fetch": {
+            "version": "2.6.7",
+            "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+            "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+            "dependencies": {
+                "whatwg-url": "^5.0.0"
+            },
+            "engines": {
+                "node": "4.x || >=6.0.0"
+            },
+            "peerDependencies": {
+                "encoding": "^0.1.0"
+            },
+            "peerDependenciesMeta": {
+                "encoding": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
+            "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
+            "dependencies": {
+                "abbrev": "1"
+            },
+            "bin": {
+                "nopt": "bin/nopt.js"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/npmlog": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
+            "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
+            "dependencies": {
+                "are-we-there-yet": "^2.0.0",
+                "console-control-strings": "^1.1.0",
+                "gauge": "^3.0.0",
+                "set-blocking": "^2.0.0"
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/semver": {
+            "version": "7.3.5",
+            "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+            "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+            "dependencies": {
+                "lru-cache": "^6.0.0"
+            },
+            "bin": {
+                "semver": "bin/semver.js"
+            },
+            "engines": {
+                "node": ">=10"
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/string-width": {
+            "version": "4.2.3",
+            "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+            "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+            "dependencies": {
+                "emoji-regex": "^8.0.0",
+                "is-fullwidth-code-point": "^3.0.0",
+                "strip-ansi": "^6.0.1"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/strip-ansi": {
+            "version": "6.0.1",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+            "dependencies": {
+                "ansi-regex": "^5.0.1"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/tar": {
+            "version": "6.1.11",
+            "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
+            "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
+            "dependencies": {
+                "chownr": "^2.0.0",
+                "fs-minipass": "^2.0.0",
+                "minipass": "^3.0.0",
+                "minizlib": "^2.1.1",
+                "mkdirp": "^1.0.3",
+                "yallist": "^4.0.0"
+            },
+            "engines": {
+                "node": ">= 10"
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/tr46": {
+            "version": "0.0.3",
+            "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+            "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/webidl-conversions": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+            "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/whatwg-url": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+            "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
+            "dependencies": {
+                "tr46": "~0.0.3",
+                "webidl-conversions": "^3.0.0"
+            }
+        },
+        "node_modules/@mapbox/node-pre-gyp/node_modules/yallist": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+            "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+        },
+        "node_modules/@material-ui/core": {
+            "version": "4.12.3",
+            "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.3.tgz",
+            "integrity": "sha512-sdpgI/PL56QVsEJldwEe4FFaFTLUqN+rd7sSZiRCdx2E/C7z5yK0y/khAWVBH24tXwto7I1hCzNWfJGZIYJKnw==",
+            "deprecated": "You can now upgrade to @mui/material. See the guide: https://mui.com/guides/migration-v4/",
+            "dependencies": {
+                "@babel/runtime": "^7.4.4",
+                "@material-ui/styles": "^4.11.4",
+                "@material-ui/system": "^4.12.1",
+                "@material-ui/types": "5.1.0",
+                "@material-ui/utils": "^4.11.2",
+                "@types/react-transition-group": "^4.2.0",
+                "clsx": "^1.0.4",
+                "hoist-non-react-statics": "^3.3.2",
+                "popper.js": "1.16.1-lts",
+                "prop-types": "^15.7.2",
+                "react-is": "^16.8.0 || ^17.0.0",
+                "react-transition-group": "^4.4.0"
+            },
+            "engines": {
+                "node": ">=8.0.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/material-ui"
+            },
+            "peerDependencies": {
+                "@types/react": "^16.8.6 || ^17.0.0",
+                "react": "^16.8.0 || ^17.0.0",
+                "react-dom": "^16.8.0 || ^17.0.0"
+            },
+            "peerDependenciesMeta": {
+                "@types/react": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/@material-ui/core/node_modules/csstype": {
+            "version": "3.0.10",
+            "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
+            "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
+        },
+        "node_modules/@material-ui/core/node_modules/dom-helpers": {
+            "version": "5.2.1",
+            "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+            "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+            "dependencies": {
+                "@babel/runtime": "^7.8.7",
+                "csstype": "^3.0.2"
+            }
+        },
+        "node_modules/@material-ui/core/node_modules/react-transition-group": {
+            "version": "4.4.2",
+            "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz",
+            "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==",
+            "dependencies": {
+                "@babel/runtime": "^7.5.5",
+                "dom-helpers": "^5.0.1",
+                "loose-envify": "^1.4.0",
+                "prop-types": "^15.6.2"
+            },
+            "peerDependencies": {
+                "react": ">=16.6.0",
+                "react-dom": ">=16.6.0"
+            }
+        },
+        "node_modules/@material-ui/styles": {
+            "version": "4.11.4",
+            "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.4.tgz",
+            "integrity": "sha512-KNTIZcnj/zprG5LW0Sao7zw+yG3O35pviHzejMdcSGCdWbiO8qzRgOYL8JAxAsWBKOKYwVZxXtHWaB5T2Kvxew==",
+            "deprecated": "You can now upgrade to @mui/styles. See the guide: https://mui.com/guides/migration-v4/",
+            "dependencies": {
+                "@babel/runtime": "^7.4.4",
+                "@emotion/hash": "^0.8.0",
+                "@material-ui/types": "5.1.0",
+                "@material-ui/utils": "^4.11.2",
+                "clsx": "^1.0.4",
+                "csstype": "^2.5.2",
+                "hoist-non-react-statics": "^3.3.2",
+                "jss": "^10.5.1",
+                "jss-plugin-camel-case": "^10.5.1",
+                "jss-plugin-default-unit": "^10.5.1",
+                "jss-plugin-global": "^10.5.1",
+                "jss-plugin-nested": "^10.5.1",
+                "jss-plugin-props-sort": "^10.5.1",
+                "jss-plugin-rule-value-function": "^10.5.1",
+                "jss-plugin-vendor-prefixer": "^10.5.1",
+                "prop-types": "^15.7.2"
+            },
+            "engines": {
+                "node": ">=8.0.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/material-ui"
+            },
+            "peerDependencies": {
+                "@types/react": "^16.8.6 || ^17.0.0",
+                "react": "^16.8.0 || ^17.0.0",
+                "react-dom": "^16.8.0 || ^17.0.0"
+            },
+            "peerDependenciesMeta": {
+                "@types/react": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/@material-ui/system": {
+            "version": "4.12.1",
+            "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.1.tgz",
+            "integrity": "sha512-lUdzs4q9kEXZGhbN7BptyiS1rLNHe6kG9o8Y307HCvF4sQxbCgpL2qi+gUk+yI8a2DNk48gISEQxoxpgph0xIw==",
+            "deprecated": "You can now upgrade to @mui/system. See the guide: https://mui.com/guides/migration-v4/",
+            "dependencies": {
+                "@babel/runtime": "^7.4.4",
+                "@material-ui/utils": "^4.11.2",
+                "csstype": "^2.5.2",
+                "prop-types": "^15.7.2"
+            },
+            "engines": {
+                "node": ">=8.0.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/material-ui"
+            },
+            "peerDependencies": {
+                "@types/react": "^16.8.6 || ^17.0.0",
+                "react": "^16.8.0 || ^17.0.0",
+                "react-dom": "^16.8.0 || ^17.0.0"
+            },
+            "peerDependenciesMeta": {
+                "@types/react": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/@material-ui/types": {
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz",
+            "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==",
+            "peerDependencies": {
+                "@types/react": "*"
+            },
+            "peerDependenciesMeta": {
+                "@types/react": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/@material-ui/utils": {
+            "version": "4.11.2",
+            "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.2.tgz",
+            "integrity": "sha512-Uul8w38u+PICe2Fg2pDKCaIG7kOyhowZ9vjiC1FsVwPABTW8vPPKfF6OvxRq3IiBaI1faOJmgdvMG7rMJARBhA==",
+            "dependencies": {
+                "@babel/runtime": "^7.4.4",
+                "prop-types": "^15.7.2",
+                "react-is": "^16.8.0 || ^17.0.0"
+            },
+            "engines": {
+                "node": ">=8.0.0"
+            },
+            "peerDependencies": {
+                "react": "^16.8.0 || ^17.0.0",
+                "react-dom": "^16.8.0 || ^17.0.0"
+            }
+        },
+        "node_modules/@react-google-maps/api": {
+            "version": "2.7.0",
+            "resolved": "https://registry.npmjs.org/@react-google-maps/api/-/api-2.7.0.tgz",
+            "integrity": "sha512-Fb/l7aR69ObYvzhpt1d1931454ylRkTPKF1C8GfLvXtg19wc36zTev2Afbahd+qAdyKuQyDdf5/SQyMjYBHI/A==",
+            "dependencies": {
+                "@googlemaps/js-api-loader": "1.12.8",
+                "@googlemaps/markerclusterer": "1.0.12",
+                "@react-google-maps/infobox": "2.6.0",
+                "@react-google-maps/marker-clusterer": "2.6.0",
+                "@types/google.maps": "3.46.1",
+                "invariant": "2.2.4"
+            },
+            "peerDependencies": {
+                "react": "^16.6.3 || ^17.0.0",
+                "react-dom": "^16.6.3 || ^17.0.0"
+            }
+        },
+        "node_modules/@react-google-maps/infobox": {
+            "version": "2.6.0",
+            "resolved": "https://registry.npmjs.org/@react-google-maps/infobox/-/infobox-2.6.0.tgz",
+            "integrity": "sha512-2dXVZsuZ2PjelVg+slpUSYPnvoSIySwf4P/wScGijjqu6bpvhyX9AivitsF7zHfbwnZ0gAPX1Q8xtQhKgawkEg=="
+        },
+        "node_modules/@react-google-maps/marker-clusterer": {
+            "version": "2.6.0",
+            "resolved": "https://registry.npmjs.org/@react-google-maps/marker-clusterer/-/marker-clusterer-2.6.0.tgz",
+            "integrity": "sha512-EjeoCM+U/6W9qoGWqurcDbCubqTEDTFET7Jd3XcLHqTLvFAE473x2YbQg98mJXKNgPKcPTNEcTiYYGiIz9tEjA=="
+        },
+        "node_modules/@react-three/fiber": {
+            "version": "6.2.3",
+            "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-6.2.3.tgz",
+            "integrity": "sha512-XuL3683iVcwjm3enour57k4OjkA1MQztqpRL2oQh30v7ygf1vJg95gKo8cqqgPJctc2pZAMUW1eRpr+Xqjwqrg==",
+            "dependencies": {
+                "@babel/runtime": "^7.13.10",
+                "react-merge-refs": "^1.1.0",
+                "react-reconciler": "^0.26.2",
+                "react-three-fiber": "0.0.0-deprecated",
+                "react-use-measure": "^2.0.4",
+                "resize-observer-polyfill": "^1.5.1",
+                "scheduler": "^0.20.2",
+                "use-asset": "^1.0.4",
+                "utility-types": "^3.10.0",
+                "zustand": "^3.5.1"
+            },
+            "peerDependencies": {
+                "react": ">=17.0",
+                "react-dom": ">=17.0",
+                "three": ">=0.126"
+            },
+            "peerDependenciesMeta": {
+                "react-dom": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/@react-three/fiber/node_modules/@babel/runtime": {
+            "version": "7.17.2",
+            "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
+            "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
+            "dependencies": {
+                "regenerator-runtime": "^0.13.4"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/@rkusa/linebreak": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/@rkusa/linebreak/-/linebreak-1.0.0.tgz",
+            "integrity": "sha512-yCSm87XA1aYMgfcABSxcIkk3JtCw3AihNceHY+DnZGLvVP/g2z3UWZbi0xIoYpZWAJEVPr5Zt3QE37Q80wF1pA==",
+            "dependencies": {
+                "unicode-trie": "^0.3.0"
+            }
+        },
+        "node_modules/@sindresorhus/is": {
+            "version": "4.6.0",
+            "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+            "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sindresorhus/is?sponsor=1"
+            }
+        },
+        "node_modules/@socket.io/component-emitter": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz",
+            "integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==",
+            "dev": true
+        },
+        "node_modules/@szmarczak/http-timer": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz",
+            "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==",
+            "dependencies": {
+                "defer-to-connect": "^2.0.1"
+            },
+            "engines": {
+                "node": ">=14.16"
+            }
+        },
+        "node_modules/@turf/clone": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-6.5.0.tgz",
+            "integrity": "sha512-mzVtTFj/QycXOn6ig+annKrM6ZlimreKYz6f/GSERytOpgzodbQyOgkfwru100O1KQhhjSudKK4DsQ0oyi9cTw==",
+            "dependencies": {
+                "@turf/helpers": "^6.5.0"
+            },
+            "funding": {
+                "url": "https://opencollective.com/turf"
+            }
+        },
+        "node_modules/@turf/clusters-dbscan": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/clusters-dbscan/-/clusters-dbscan-6.5.0.tgz",
+            "integrity": "sha512-SxZEE4kADU9DqLRiT53QZBBhu8EP9skviSyl+FGj08Y01xfICM/RR9ACUdM0aEQimhpu+ZpRVcUK+2jtiCGrYQ==",
+            "dependencies": {
+                "@turf/clone": "^6.5.0",
+                "@turf/distance": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "density-clustering": "1.3.0"
+            },
+            "funding": {
+                "url": "https://opencollective.com/turf"
+            }
+        },
+        "node_modules/@turf/clusters-kmeans": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/clusters-kmeans/-/clusters-kmeans-6.5.0.tgz",
+            "integrity": "sha512-DwacD5+YO8kwDPKaXwT9DV46tMBVNsbi1IzdajZu1JDSWoN7yc7N9Qt88oi+p30583O0UPVkAK+A10WAQv4mUw==",
+            "dependencies": {
+                "@turf/clone": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "skmeans": "0.9.7"
+            },
+            "funding": {
+                "url": "https://opencollective.com/turf"
+            }
+        },
+        "node_modules/@turf/distance": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-6.5.0.tgz",
+            "integrity": "sha512-xzykSLfoURec5qvQJcfifw/1mJa+5UwByZZ5TZ8iaqjGYN0vomhV9aiSLeYdUGtYRESZ+DYC/OzY+4RclZYgMg==",
+            "dependencies": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            },
+            "funding": {
+                "url": "https://opencollective.com/turf"
+            }
+        },
+        "node_modules/@turf/helpers": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.5.0.tgz",
+            "integrity": "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw==",
+            "funding": {
+                "url": "https://opencollective.com/turf"
+            }
+        },
+        "node_modules/@turf/invariant": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.5.0.tgz",
+            "integrity": "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg==",
+            "dependencies": {
+                "@turf/helpers": "^6.5.0"
+            },
+            "funding": {
+                "url": "https://opencollective.com/turf"
+            }
+        },
+        "node_modules/@turf/meta": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.5.0.tgz",
+            "integrity": "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA==",
+            "dependencies": {
+                "@turf/helpers": "^6.5.0"
+            },
+            "funding": {
+                "url": "https://opencollective.com/turf"
+            }
+        },
+        "node_modules/@types/adm-zip": {
+            "version": "0.4.34",
+            "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.4.34.tgz",
+            "integrity": "sha512-8ToYLLAYhkRfcmmljrKi22gT2pqu7hGMDtORP1emwIEGmgUTZOsaDjzWFzW5N2frcFRz/50CWt4zA1CxJ73pmQ==",
+            "dev": true,
+            "dependencies": {
+                "@types/node": "*"
+            }
+        },
+        "node_modules/@types/animejs": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/@types/animejs/-/animejs-2.0.2.tgz",
+            "integrity": "sha512-ACymFQ5qgSrZNR1Fqjk7Wv9gH6dFgejn2gpLkkceWxTKzivRJsshX4xhBVALgvF79gUdXiMCRIdusN728XpeGA==",
+            "dev": true
+        },
+        "node_modules/@types/archiver": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-3.1.1.tgz",
+            "integrity": "sha512-TzVZ9204sH1TuFylfr1cw/AA/3/VldAAXswEwKLXUOzA9mDg+m6gHF9EaqKNlozcjc6knX5m1KAqJzksPLSEfw==",
+            "dev": true,
+            "dependencies": {
+                "@types/glob": "*"
+            }
+        },
+        "node_modules/@types/assert": {
+            "version": "1.5.6",
+            "resolved": "https://registry.npmjs.org/@types/assert/-/assert-1.5.6.tgz",
+            "integrity": "sha512-Y7gDJiIqb9qKUHfBQYOWGngUpLORtirAVPuj/CWJrU2C6ZM4/y3XLwuwfGMF8s7QzW746LQZx23m0+1FSgjfug=="
+        },
+        "node_modules/@types/async": {
+            "version": "2.4.2",
+            "resolved": "https://registry.npmjs.org/@types/async/-/async-2.4.2.tgz",
+            "integrity": "sha512-bWBbC7VG2jdjbgZMX0qpds8U/3h3anfIqE81L8jmVrgFZw/urEDnBA78ymGGKTTK6ciBXmmJ/xlok+Re41S8ww==",
+            "dev": true
+        },
+        "node_modules/@types/babel-types": {
+            "version": "7.0.7",
+            "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.7.tgz",
+            "integrity": "sha512-dBtBbrc+qTHy1WdfHYjBwRln4+LWqASWakLHsWHR2NWHIFkv4W3O070IGoGLEBrJBvct3r0L1BUPuvURi7kYUQ=="
+        },
+        "node_modules/@types/babylon": {
+            "version": "6.16.5",
+            "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.5.tgz",
+            "integrity": "sha512-xH2e58elpj1X4ynnKp9qSnWlsRTIs6n3tgLGNfwAGHwePw0mulHQllV34n0T25uYSu1k0hRKkWXF890B1yS47w==",
+            "dependencies": {
+                "@types/babel-types": "*"
+            }
+        },
+        "node_modules/@types/bcrypt-nodejs": {
+            "version": "0.0.30",
+            "resolved": "https://registry.npmjs.org/@types/bcrypt-nodejs/-/bcrypt-nodejs-0.0.30.tgz",
+            "integrity": "sha1-TN2WtJKTs5MhIuS34pVD415rrlg=",
+            "dev": true
+        },
+        "node_modules/@types/bezier-js": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/@types/bezier-js/-/bezier-js-4.1.0.tgz",
+            "integrity": "sha512-ElU16s8E6Pr6magp8ihwH1O8pbUJASbMND/qgUc9RsLmP3lMLHiDMRXdjtaObwW5GPtOVYOsXDUIhTIluT+yaw=="
+        },
+        "node_modules/@types/bluebird": {
+            "version": "3.5.36",
+            "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.36.tgz",
+            "integrity": "sha512-HBNx4lhkxN7bx6P0++W8E289foSu8kO8GCk2unhuVggO+cE7rh9DhZUyPhUxNRG9m+5B5BTKxZQ5ZP92x/mx9Q==",
+            "dev": true
+        },
+        "node_modules/@types/body-parser": {
+            "version": "1.19.2",
+            "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
+            "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
+            "dev": true,
+            "dependencies": {
+                "@types/connect": "*",
+                "@types/node": "*"
+            }
+        },
+        "node_modules/@types/bson": {
+            "version": "4.2.0",
+            "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.2.0.tgz",
+            "integrity": "sha512-ELCPqAdroMdcuxqwMgUpifQyRoTpyYCNr1V9xKyF40VsBobsj+BbWNRvwGchMgBPGqkw655ypkjj2MEF5ywVwg==",
+            "deprecated": "This is a stub types definition. bson provides its own type definitions, so you do not need this installed.",
+            "dependencies": {
+                "bson": "*"
+            }
+        },
+        "node_modules/@types/cacheable-request": {
+            "version": "6.0.2",
+            "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz",
+            "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==",
+            "dependencies": {
+                "@types/http-cache-semantics": "*",
+                "@types/keyv": "*",
+                "@types/node": "*",
+                "@types/responselike": "*"
+            }
+        },
+        "node_modules/@types/caseless": {
+            "version": "0.12.2",
+            "resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz",
+            "integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==",
+            "dev": true
+        },
+        "node_modules/@types/chai": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz",
+            "integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==",
+            "dev": true
+        },
+        "node_modules/@types/color": {
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/@types/color/-/color-3.0.3.tgz",
+            "integrity": "sha512-X//qzJ3d3Zj82J9sC/C18ZY5f43utPbAJ6PhYt/M7uG6etcF6MRpKdN880KBy43B0BMzSfeT96MzrsNjFI3GbA==",
+            "dev": true,
+            "dependencies": {
+                "@types/color-convert": "*"
+            }
+        },
+        "node_modules/@types/color-convert": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/@types/color-convert/-/color-convert-2.0.0.tgz",
+            "integrity": "sha512-m7GG7IKKGuJUXvkZ1qqG3ChccdIM/qBBo913z+Xft0nKCX4hAU/IxKwZBU4cpRZ7GS5kV4vOblUkILtSShCPXQ==",
+            "dev": true,
+            "dependencies": {
+                "@types/color-name": "*"
+            }
+        },
+        "node_modules/@types/color-name": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
+            "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
+        },
+        "node_modules/@types/connect": {
+            "version": "3.4.33",
+            "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
+            "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
+            "dev": true,
+            "dependencies": {
+                "@types/node": "*"
+            }
+        },
+        "node_modules/@types/connect-flash": {
+            "version": "0.0.34",
+            "resolved": "https://registry.npmjs.org/@types/connect-flash/-/connect-flash-0.0.34.tgz",
+            "integrity": "sha512-QC93TwnTZ0sk//bfT81o7U4GOedbOZAcgvqi0v1vJqCESC8tqIVnhzB1CHiAUBUWFjoxG5JQF0TYaNa6DMb6Ig==",
+            "dev": true,
+            "dependencies": {
+                "@types/express": "*"
+            }
+        },
+        "node_modules/@types/cookie-parser": {
+            "version": "1.4.2",
+            "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.2.tgz",
+            "integrity": "sha512-uwcY8m6SDQqciHsqcKDGbo10GdasYsPCYkH3hVegj9qAah6pX5HivOnOuI3WYmyQMnOATV39zv/Ybs0bC/6iVg==",
+            "dev": true,
+            "dependencies": {
+                "@types/express": "*"
+            }
+        },
+        "node_modules/@types/cookie-session": {
+            "version": "2.0.44",
+            "resolved": "https://registry.npmjs.org/@types/cookie-session/-/cookie-session-2.0.44.tgz",
+            "integrity": "sha512-3DheOZ41pql6raSIkqEPphJdhA2dX2bkS+s2Qacv8YMKkoCbAIEXbsDil7351ARzMqvfyDUGNeHGiRZveIzhqQ==",
+            "dev": true,
+            "dependencies": {
+                "@types/express": "*",
+                "@types/keygrip": "*"
+            }
+        },
+        "node_modules/@types/cors": {
+            "version": "2.8.12",
+            "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
+            "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw=="
+        },
+        "node_modules/@types/d3-axis": {
+            "version": "2.1.3",
+            "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-2.1.3.tgz",
+            "integrity": "sha512-QjXjwZ0xzyrW2ndkmkb09ErgWDEYtbLBKGui73QLMFm3woqWpxptfD5Y7vqQdybMcu7WEbjZ5q+w2w5+uh2IjA==",
+            "dependencies": {
+                "@types/d3-selection": "^2"
+            }
+        },
+        "node_modules/@types/d3-color": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-2.0.3.tgz",
+            "integrity": "sha512-+0EtEjBfKEDtH9Rk3u3kLOUXM5F+iZK+WvASPb0MhIZl8J8NUvGeZRwKCXl+P3HkYx5TdU4YtcibpqHkSR9n7w=="
+        },
+        "node_modules/@types/d3-scale": {
+            "version": "3.3.2",
+            "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-3.3.2.tgz",
+            "integrity": "sha512-gGqr7x1ost9px3FvIfUMi5XA/F/yAf4UkUDtdQhpH92XCT0Oa7zkkRzY61gPVJq+DxpHn/btouw5ohWkbBsCzQ==",
+            "dependencies": {
+                "@types/d3-time": "^2"
+            }
+        },
+        "node_modules/@types/d3-selection": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-2.0.1.tgz",
+            "integrity": "sha512-3mhtPnGE+c71rl/T5HMy+ykg7migAZ4T6gzU0HxpgBFKcasBrSnwRbYV1/UZR6o5fkpySxhWxAhd7yhjj8jL7g=="
+        },
+        "node_modules/@types/d3-time": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.1.1.tgz",
+            "integrity": "sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg=="
+        },
+        "node_modules/@types/dom-speech-recognition": {
+            "version": "0.0.1",
+            "resolved": "https://registry.npmjs.org/@types/dom-speech-recognition/-/dom-speech-recognition-0.0.1.tgz",
+            "integrity": "sha512-udCxb8DvjcDKfk1WTBzDsxFbLgYxmQGKrE/ricoMqHRNjSlSUCcamVTA5lIQqzY10mY5qCY0QDwBfFEwhfoDPw=="
+        },
+        "node_modules/@types/dotenv": {
+            "version": "6.1.1",
+            "resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-6.1.1.tgz",
+            "integrity": "sha512-ftQl3DtBvqHl9L16tpqqzA4YzCSXZfi7g8cQceTz5rOlYtk/IZbFjAv3mLOQlNIgOaylCQWQoBdDQHPgEBJPHg==",
+            "dev": true,
+            "dependencies": {
+                "@types/node": "*"
+            }
+        },
+        "node_modules/@types/engine.io": {
+            "version": "3.1.7",
+            "resolved": "https://registry.npmjs.org/@types/engine.io/-/engine.io-3.1.7.tgz",
+            "integrity": "sha512-qNjVXcrp+1sS8YpRUa714r0pgzOwESdW5UjHL7D/2ZFdBX0BXUXtg1LUrp+ylvqbvMcMWUy73YpRoxPN2VoKAQ==",
+            "dev": true,
+            "dependencies": {
+                "@types/node": "*"
+            }
+        },
+        "node_modules/@types/eslint": {
+            "version": "8.4.1",
+            "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz",
+            "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==",
+            "dependencies": {
+                "@types/estree": "*",
+                "@types/json-schema": "*"
+            }
+        },
+        "node_modules/@types/eslint-scope": {
+            "version": "3.7.3",
+            "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz",
+            "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==",
+            "dependencies": {
+                "@types/eslint": "*",
+                "@types/estree": "*"
+            }
+        },
+        "node_modules/@types/estree": {
+            "version": "0.0.51",
+            "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz",
+            "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ=="
+        },
+        "node_modules/@types/events": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
+            "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g=="
+        },
+        "node_modules/@types/exif": {
+            "version": "0.6.3",
+            "resolved": "https://registry.npmjs.org/@types/exif/-/exif-0.6.3.tgz",
+            "integrity": "sha512-OMthUry1d2/gpXL2H3RGaj7KWRw0EWdxI0OANeUWbYMotBxljFuAl3eydCgDaBYTyD+d4Q3atdVcmimwS6KvGg==",
+            "dev": true,
+            "dependencies": {
+                "@types/node": "*"
+            }
+        },
+        "node_modules/@types/express": {
+            "version": "4.17.13",
+            "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
+            "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==",
+            "dev": true,
+            "dependencies": {
+                "@types/body-parser": "*",
+                "@types/express-serve-static-core": "^4.17.18",
+                "@types/qs": "*",
+                "@types/serve-static": "*"
+            }
+        },
+        "node_modules/@types/express-flash": {
+            "version": "0.0.0",
+            "resolved": "https://registry.npmjs.org/@types/express-flash/-/express-flash-0.0.0.tgz",
+            "integrity": "sha512-zs1xXRIZOjghUBriJPSnhPmfDpqf/EQxT21ggi/9XZ9/RHYrUi+5vK2jnQrP2pD1abbuZvm7owLICiNCLBQzEQ==",
+            "dev": true,
+            "dependencies": {
+                "@types/connect-flash": "*",
+                "@types/express": "*"
+            }
+        },
+        "node_modules/@types/express-serve-static-core": {
+            "version": "4.17.28",
+            "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz",
+            "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==",
+            "dev": true,
+            "dependencies": {
+                "@types/node": "*",
+                "@types/qs": "*",
+                "@types/range-parser": "*"
+            }
+        },
+        "node_modules/@types/express-session": {
+            "version": "1.17.4",
+            "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.4.tgz",
+            "integrity": "sha512-7cNlSI8+oOBUHTfPXMwDxF/Lchx5aJ3ho7+p9jJZYVg9dVDJFh3qdMXmJtRsysnvS+C6x46k9DRYmrmCkE+MVg==",
+            "dev": true,
+            "dependencies": {
+                "@types/express": "*"
+            }
+        },
+        "node_modules/@types/express-validator": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/@types/express-validator/-/express-validator-3.0.0.tgz",
+            "integrity": "sha512-LusnB0YhTXpBT25PXyGPQlK7leE1e41Vezq1hHEUwjfkopM1Pkv2X2Ppxqh9c+w/HZ6Udzki8AJotKNjDTGdkQ==",
+            "deprecated": "This is a stub types definition for express-validator (https://github.com/ctavan/express-validator). express-validator provides its own type definitions, so you don't need @types/express-validator installed!",
+            "dev": true,
+            "dependencies": {
+                "express-validator": "*"
+            }
+        },
+        "node_modules/@types/file-saver": {
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.5.tgz",
+            "integrity": "sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==",
+            "dev": true
+        },
+        "node_modules/@types/formidable": {
+            "version": "1.0.31",
+            "resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-1.0.31.tgz",
+            "integrity": "sha512-dIhM5t8lRP0oWe2HF8MuPvdd1TpPTjhDMAqemcq6oIZQCBQTovhBAdTQ5L5veJB4pdQChadmHuxtB0YzqvfU3Q==",
+            "dependencies": {
+                "@types/events": "*",
+                "@types/node": "*"
+            }
+        },
+        "node_modules/@types/geojson": {
+            "version": "7946.0.8",
+            "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz",
+            "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA=="
+        },
+        "node_modules/@types/glob": {
+            "version": "7.2.0",
+            "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
+            "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
+            "dev": true,
+            "dependencies": {
+                "@types/minimatch": "*",
+                "@types/node": "*"
+            }
+        },
+        "node_modules/@types/google-maps": {
+            "version": "3.2.3",
+            "resolved": "https://registry.npmjs.org/@types/google-maps/-/google-maps-3.2.3.tgz",
+            "integrity": "sha512-+GR/7sMvo2bENS26H4t2QqoCqE4FE5qhsG8aaDM9mJAlsorkLyaFdTQra8VkmS1RNE09fx9c1kac+GKG0RtA2A==",
+            "dependencies": {
+                "@types/google.maps": "*"
+            }
+        },
+        "node_modules/@types/google-maps-react": {
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/@types/google-maps-react/-/google-maps-react-2.0.5.tgz",
+            "integrity": "sha512-qcsYlHiNH169Vf7jmkEwbzBDqBfqqzYkTgK1vL7qkWVZI04wFESADYVITuQunrZ9swY/SG+tTWUIXMlY4W8byw==",
+            "deprecated": "This is a stub types definition. google-maps-react provides its own type definitions, so you do not need this installed.",
+            "dev": true,
+            "dependencies": {
+                "google-maps-react": "*"
+            }
+        },
+        "node_modules/@types/google.maps": {
+            "version": "3.46.1",
+            "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.46.1.tgz",
+            "integrity": "sha512-GAa5ZWYgXG50yLXybb7A824esGm/L0LKHS7qD0qkP0IA/Qp5r922P9tmYcbCkGEf3Zgf7Ukbp7l08/IGIJuQwQ=="
+        },
+        "node_modules/@types/hoist-non-react-statics": {
+            "version": "3.3.1",
+            "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
+            "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
+            "dependencies": {
+                "@types/react": "*",
+                "hoist-non-react-statics": "^3.3.0"
+            }
+        },
+        "node_modules/@types/html-minifier-terser": {
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+            "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg=="
+        },
+        "node_modules/@types/http-cache-semantics": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
+            "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="
+        },
+        "node_modules/@types/jquery": {
+            "version": "3.5.14",
+            "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.14.tgz",
+            "integrity": "sha512-X1gtMRMbziVQkErhTQmSe2jFwwENA/Zr+PprCkF63vFq+Yt5PZ4AlKqgmeNlwgn7dhsXEK888eIW2520EpC+xg==",
+            "dev": true,
+            "dependencies": {
+                "@types/sizzle": "*"
+            }
+        },
+        "node_modules/@types/jsdom": {
+            "version": "16.2.14",
+            "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-16.2.14.tgz",
+            "integrity": "sha512-6BAy1xXEmMuHeAJ4Fv4yXKwBDTGTOseExKE3OaHiNycdHdZw59KfYzrt0DkDluvwmik1HRt6QS7bImxUmpSy+w==",
+            "dependencies": {
+                "@types/node": "*",
+                "@types/parse5": "*",
+                "@types/tough-cookie": "*"
+            }
+        },
+        "node_modules/@types/json-schema": {
+            "version": "7.0.9",
+            "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
+            "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ=="
+        },
+        "node_modules/@types/keygrip": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz",
+            "integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==",
+            "dev": true
+        },
+        "node_modules/@types/keyv": {
+            "version": "3.1.3",
+            "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz",
+            "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==",
+            "dependencies": {
+                "@types/node": "*"
+            }
+        },
+        "node_modules/@types/libxmljs": {
+            "version": "0.18.7",
+            "resolved": "https://registry.npmjs.org/@types/libxmljs/-/libxmljs-0.18.7.tgz",
+            "integrity": "sha512-g88L5tS5UJKfzmPZbvGckcULvPHzguy24lFFTXUVm2UfezQBC5adHmA15RlTx0y0+s/+TyrCV1oPvxn+KqfH8A==",
+            "dev": true,
+            "dependencies": {
+                "@types/node": "*"
+            }
+        },
+        "node_modules/@types/lodash": {
+            "version": "4.14.179",
+            "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.179.tgz",
+            "integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w==",
+            "dev": true
+        },
+        "node_modules/@types/mime": {
+            "version": "1.3.2",
+            "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
+            "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
+            "dev": true
+        },
+        "node_modules/@types/minimatch": {
+            "version": "3.0.5",
+            "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
+            "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
+            "dev": true
+        },
+        "node_modules/@types/mobile-detect": {
+            "version": "1.3.4",
+            "resolved": "https://registry.npmjs.org/@types/mobile-detect/-/mobile-detect-1.3.4.tgz",
+            "integrity": "sha512-MGBTvT5c7aH8eX6szFYP3dWPryNLt5iGlo31XNaJtt8o6jsg6tjn99eEMq9l8T6cPZymsr+J4Jth8+/G/04ZDw==",
+            "deprecated": "This is a stub types definition for mobile-detect (http://hgoebl.github.io/mobile-detect.js/). mobile-detect provides its own type definitions, so you don't need @types/mobile-detect installed!",
+            "dev": true,
+            "dependencies": {
+                "mobile-detect": "*"
+            }
+        },
+        "node_modules/@types/mocha": {
+            "version": "5.2.7",
+            "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz",
+            "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==",
+            "dev": true
+        },
+        "node_modules/@types/mongodb": {
+            "version": "3.6.20",
+            "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz",
+            "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==",
+            "dependencies": {
+                "@types/bson": "*",
+                "@types/node": "*"
+            }
+        },
+        "node_modules/@types/mongoose": {
+            "version": "5.11.97",
+            "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.11.97.tgz",
+            "integrity": "sha512-cqwOVYT3qXyLiGw7ueU2kX9noE8DPGRY6z8eUxudhXY8NZ7DMKYAxyZkLSevGfhCX3dO/AoX5/SO9lAzfjon0Q==",
+            "deprecated": "Mongoose publishes its own types, so you do not need to install this package.",
+            "dev": true,
+            "dependencies": {
+                "mongoose": "*"
+            }
+        },
+        "node_modules/@types/node": {
+            "version": "10.17.60",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
+            "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="
+        },
+        "node_modules/@types/nodemailer": {
+            "version": "4.6.8",
+            "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-4.6.8.tgz",
+            "integrity": "sha512-IX1P3bxDP1VIdZf6/kIWYNmSejkYm9MOyMEtoDFi4DVzKjJ3kY4GhOcOAKs6lZRjqVVmF9UjPOZXuQczlpZThw==",
+            "dev": true,
+            "dependencies": {
+                "@types/node": "*"
+            }
+        },
+        "node_modules/@types/oauth": {
+            "version": "0.9.1",
+            "resolved": "https://registry.npmjs.org/@types/oauth/-/oauth-0.9.1.tgz",
+            "integrity": "sha512-a1iY62/a3yhZ7qH7cNUsxoI3U/0Fe9+RnuFrpTKr+0WVOzbKlSLojShCKe20aOD1Sppv+i8Zlq0pLDuTJnwS4A==",
+            "dev": true,
+            "dependencies": {
+                "@types/node": "*"
+            }
+        },
+        "node_modules/@types/orderedmap": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/@types/orderedmap/-/orderedmap-1.0.0.tgz",
+            "integrity": "sha512-dxKo80TqYx3YtBipHwA/SdFmMMyLCnP+5mkEqN0eMjcTBzHkiiX0ES118DsjDBjvD+zeSsSU9jULTZ+frog+Gw==",
+            "dev": true
+        },
+        "node_modules/@types/parse-json": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
+            "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
+        },
+        "node_modules/@types/parse5": {
+            "version": "6.0.3",
+            "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz",
+            "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g=="
+        },
+        "node_modules/@types/passport": {
+            "version": "1.0.7",
+            "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.7.tgz",
+            "integrity": "sha512-JtswU8N3kxBYgo+n9of7C97YQBT+AYPP2aBfNGTzABqPAZnK/WOAaKfh3XesUYMZRrXFuoPc2Hv0/G/nQFveHw==",
+            "dev": true,
+            "dependencies": {
+                "@types/express": "*"
+            }
+        },
+        "node_modules/@types/passport-google-oauth20": {
+            "version": "2.0.11",
+            "resolved": "https://registry.npmjs.org/@types/passport-google-oauth20/-/passport-google-oauth20-2.0.11.tgz",
+            "integrity": "sha512-9XMT1GfwhZL7UQEiCepLef55RNPHkbrCtsU7rsWPTEOsmu5qVIW8nSemtB4p+P24CuOhA+IKkv8LsPThYghGww==",
+            "dev": true,
+            "dependencies": {
+                "@types/express": "*",
+                "@types/passport": "*",
+                "@types/passport-oauth2": "*"
+            }
+        },
+        "node_modules/@types/passport-local": {
+            "version": "1.0.34",
+            "resolved": "https://registry.npmjs.org/@types/passport-local/-/passport-local-1.0.34.tgz",
+            "integrity": "sha512-PSc07UdYx+jhadySxxIYWuv6sAnY5e+gesn/5lkPKfBeGuIYn9OPR+AAEDq73VRUh6NBTpvE/iPE62rzZUslog==",
+            "dev": true,
+            "dependencies": {
+                "@types/express": "*",
+                "@types/passport": "*",
+                "@types/passport-strategy": "*"
+            }
+        },
+        "node_modules/@types/passport-oauth2": {
+            "version": "1.4.11",
+            "resolved": "https://registry.npmjs.org/@types/passport-oauth2/-/passport-oauth2-1.4.11.tgz",
+            "integrity": "sha512-KUNwmGhe/3xPbjkzkPwwcPmyFwfyiSgtV1qOrPBLaU4i4q9GSCdAOyCbkFG0gUxAyEmYwqo9OAF/rjPjJ6ImdA==",
+            "dev": true,
+            "dependencies": {
+                "@types/express": "*",
+                "@types/oauth": "*",
+                "@types/passport": "*"
+            }
+        },
+        "node_modules/@types/passport-strategy": {
+            "version": "0.2.35",
+            "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz",
+            "integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==",
+            "dev": true,
+            "dependencies": {
+                "@types/express": "*",
+                "@types/passport": "*"
+            }
+        },
+        "node_modules/@types/pdfjs-dist": {
+            "version": "2.10.378",
+            "resolved": "https://registry.npmjs.org/@types/pdfjs-dist/-/pdfjs-dist-2.10.378.tgz",
+            "integrity": "sha512-TRdIPqdsvKmPla44kVy4jv5Nt5vjMfVjbIEke1CRULIrwKNRC4lIiZvNYDJvbUMNCFPNIUcOKhXTyMJrX18IMA==",
+            "deprecated": "This is a stub types definition. pdfjs-dist provides its own type definitions, so you do not need this installed.",
+            "dev": true,
+            "dependencies": {
+                "pdfjs-dist": "*"
+            }
+        },
+        "node_modules/@types/prop-types": {
+            "version": "15.7.4",
+            "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
+            "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ=="
+        },
+        "node_modules/@types/prosemirror-commands": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-commands/-/prosemirror-commands-1.0.4.tgz",
+            "integrity": "sha512-utDNYB3EXLjAfYIcRWJe6pn3kcQ5kG4RijbT/0Y/TFOm6yhvYS/D9eJVnijdg9LDjykapcezchxGRqFD5LcyaQ==",
+            "dev": true,
+            "dependencies": {
+                "@types/prosemirror-model": "*",
+                "@types/prosemirror-state": "*",
+                "@types/prosemirror-view": "*"
+            }
+        },
+        "node_modules/@types/prosemirror-dev-tools": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-dev-tools/-/prosemirror-dev-tools-2.1.0.tgz",
+            "integrity": "sha512-OhnSaC4yrrEMLPRUkEWcHAIPVqgKlLkE4kISqL3cHeAYxASouSPvPMLqhBIbWkGwaozy43DjjVC1OXkxTo+y5Q==",
+            "dev": true,
+            "dependencies": {
+                "@types/prosemirror-state": "*",
+                "@types/prosemirror-view": "*"
+            }
+        },
+        "node_modules/@types/prosemirror-history": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-history/-/prosemirror-history-1.0.3.tgz",
+            "integrity": "sha512-5TloMDRavgLjOAKXp1Li8u0xcsspzbT1Cm9F2pwHOkgvQOz1jWQb2VIXO7RVNsFjLBZdIXlyfSLivro3DuMWXg==",
+            "dev": true,
+            "dependencies": {
+                "@types/prosemirror-model": "*",
+                "@types/prosemirror-state": "*"
+            }
+        },
+        "node_modules/@types/prosemirror-inputrules": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-inputrules/-/prosemirror-inputrules-1.0.4.tgz",
+            "integrity": "sha512-lJIMpOjO47SYozQybUkpV6QmfuQt7GZKHtVrvS+mR5UekA8NMC5HRIVMyaIauJLWhKU6oaNjpVaXdw41kh165g==",
+            "dev": true,
+            "dependencies": {
+                "@types/prosemirror-model": "*",
+                "@types/prosemirror-state": "*"
+            }
+        },
+        "node_modules/@types/prosemirror-keymap": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-keymap/-/prosemirror-keymap-1.0.4.tgz",
+            "integrity": "sha512-ycevwkqUh+jEQtPwqO7sWGcm+Sybmhu8MpBsM8DlO3+YTKnXbKA6SDz/+q14q1wK3UA8lHJyfR+v+GPxfUSemg==",
+            "dev": true,
+            "dependencies": {
+                "@types/prosemirror-commands": "*",
+                "@types/prosemirror-model": "*",
+                "@types/prosemirror-state": "*",
+                "@types/prosemirror-view": "*"
+            }
+        },
+        "node_modules/@types/prosemirror-menu": {
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-menu/-/prosemirror-menu-1.0.6.tgz",
+            "integrity": "sha512-teKfCPnnbYeB8DJiUIof4N0f8kyWbQP3BagEBRP6r+7/o0fiqHsnrex8GXuCSNNUqlpSpDQkJy52aq4jvzmDpg==",
+            "dev": true,
+            "dependencies": {
+                "@types/prosemirror-model": "*",
+                "@types/prosemirror-state": "*",
+                "@types/prosemirror-view": "*"
+            }
+        },
+        "node_modules/@types/prosemirror-model": {
+            "version": "1.16.1",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-model/-/prosemirror-model-1.16.1.tgz",
+            "integrity": "sha512-SrrCe2cHlYrQ9o55e2i/c3wt1yRajTTpRLvzfmB+2DWjWEbBLTByVWyjrdpKtQTxAaTeU2aeDGo1iuwl/jF27w==",
+            "dev": true,
+            "dependencies": {
+                "@types/orderedmap": "*"
+            }
+        },
+        "node_modules/@types/prosemirror-schema-list": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-schema-list/-/prosemirror-schema-list-1.0.3.tgz",
+            "integrity": "sha512-uWybOf+M2Ea7rlbs0yLsS4YJYNGXYtn4N+w8HCw3Vvfl6wBAROzlMt0gV/D/VW/7J/LlAjwMezuGe8xi24HzXA==",
+            "dev": true,
+            "dependencies": {
+                "@types/orderedmap": "*",
+                "@types/prosemirror-model": "*",
+                "@types/prosemirror-state": "*"
+            }
+        },
+        "node_modules/@types/prosemirror-state": {
+            "version": "1.2.8",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-state/-/prosemirror-state-1.2.8.tgz",
+            "integrity": "sha512-mq9uyQWcpu8jeamO6Callrdvf/e1H/aRLR2kZWSpZrPHctEsxWHBbluD/wqVjXBRIOoMHLf6ZvOkrkmGLoCHVA==",
+            "dev": true,
+            "dependencies": {
+                "@types/prosemirror-model": "*",
+                "@types/prosemirror-transform": "*",
+                "@types/prosemirror-view": "*"
+            }
+        },
+        "node_modules/@types/prosemirror-transform": {
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-transform/-/prosemirror-transform-1.1.5.tgz",
+            "integrity": "sha512-Wr2HXaEF4JPklWpC17RTxE6PxyU54Taqk5FMhK1ojgcN93J+GpkYW8s0mD3rl7KfTmlhVwZPCHE9o0cYf2Go5A==",
+            "dev": true,
+            "dependencies": {
+                "@types/prosemirror-model": "*"
+            }
+        },
+        "node_modules/@types/prosemirror-view": {
+            "version": "1.23.1",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-view/-/prosemirror-view-1.23.1.tgz",
+            "integrity": "sha512-6e1B2oKUnhmZPUrsVvYjDqeVjE6jGezygjtoHsAK4ZENAxHzHqy5NT4jUvdPTWjCYeH0t2Y7pSfRPNrPIyQX4A==",
+            "dev": true,
+            "dependencies": {
+                "@types/prosemirror-model": "*",
+                "@types/prosemirror-state": "*",
+                "@types/prosemirror-transform": "*"
+            }
+        },
+        "node_modules/@types/qs": {
+            "version": "6.9.7",
+            "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+            "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
+            "dev": true
+        },
+        "node_modules/@types/range-parser": {
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
+            "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
+            "dev": true
+        },
+        "node_modules/@types/rc-switch": {
+            "version": "1.9.2",
+            "resolved": "https://registry.npmjs.org/@types/rc-switch/-/rc-switch-1.9.2.tgz",
+            "integrity": "sha512-+2e+KP23qqJ4RMb63aeb866cPPZv8g7rCu2NYJlwyxRveH3Gm/OT25Zj3BfRLViCrxwGhpwKq49ugiUBZkr9AQ==",
+            "dev": true,
+            "dependencies": {
+                "@types/react": "*"
+            }
+        },
+        "node_modules/@types/react": {
+            "version": "16.14.23",
+            "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.23.tgz",
+            "integrity": "sha512-WngBZLuSkP4IAgPi0HOsGCHo6dn3CcuLQnCfC17VbA7YBgipZiZoTOhObwl/93DsFW0Y2a/ZXeonpW4DxirEJg==",
+            "dependencies": {
+                "@types/prop-types": "*",
+                "@types/scheduler": "*",
+                "csstype": "^3.0.2"
+            }
+        },
+        "node_modules/@types/react-autosuggest": {
+            "version": "9.3.14",
+            "resolved": "https://registry.npmjs.org/@types/react-autosuggest/-/react-autosuggest-9.3.14.tgz",
+            "integrity": "sha512-cvGpKaQaNsFbDxTwP56VKVj2FO6SpJ9PsrQtlVzN7aVa/SsMZoQrBLEUx5HQKfIS4Zupb6K4tHmIyTjF7AEcow==",
+            "dev": true,
+            "dependencies": {
+                "@types/react": "*"
+            }
+        },
+        "node_modules/@types/react-color": {
+            "version": "2.17.6",
+            "resolved": "https://registry.npmjs.org/@types/react-color/-/react-color-2.17.6.tgz",
+            "integrity": "sha512-5CEKnrpvgZz8v5UYbpLgBeK+V6K7KdszksSDK6mNjL/8wrsqQfIyKB45CQOAEYtRgn+tWqTeqbUtvFWjXDpURQ==",
+            "dev": true,
+            "dependencies": {
+                "@types/react": "*",
+                "@types/reactcss": "*"
+            }
+        },
+        "node_modules/@types/react-datepicker": {
+            "version": "3.1.8",
+            "resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-3.1.8.tgz",
+            "integrity": "sha512-RFEg7++xhosMq02i2lsuaUPEbZGn66U3dxtvw9LU/ZRqLkBGr9Ft2LTz6vbeYYVtaBdOr0NcQatOLnlfUaS8kw==",
+            "dev": true,
+            "dependencies": {
+                "@types/react": "*",
+                "date-fns": "^2.0.1",
+                "popper.js": "^1.14.1"
+            }
+        },
+        "node_modules/@types/react-datepicker/node_modules/popper.js": {
+            "version": "1.16.1",
+            "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
+            "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
+            "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1",
+            "dev": true,
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/popperjs"
+            }
+        },
+        "node_modules/@types/react-dom": {
+            "version": "16.9.14",
+            "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.14.tgz",
+            "integrity": "sha512-FIX2AVmPTGP30OUJ+0vadeIFJJ07Mh1m+U0rxfgyW34p3rTlXI+nlenvAxNn4BP36YyI9IJ/+UJ7Wu22N1pI7A==",
+            "dev": true,
+            "dependencies": {
+                "@types/react": "^16"
+            }
+        },
+        "node_modules/@types/react-grid-layout": {
+            "version": "0.17.2",
+            "resolved": "https://registry.npmjs.org/@types/react-grid-layout/-/react-grid-layout-0.17.2.tgz",
+            "integrity": "sha512-iLKQ5rI9KyPGs/BlWmeBfoikRnoOmTuddK8V5mOCgJ46e2JaecZuPhuIX1kV0mkQSk6X3XoEZALVW0XQbz9g5Q==",
+            "dev": true,
+            "dependencies": {
+                "@types/react": "*"
+            }
+        },
+        "node_modules/@types/react-measure": {
+            "version": "2.0.8",
+            "resolved": "https://registry.npmjs.org/@types/react-measure/-/react-measure-2.0.8.tgz",
+            "integrity": "sha512-Pu4/hQ/1AKVN6efoawtcM+l376WYOI8e1fiM6ir4pdLkHilDCkJLjUGvAm0mWKJ0GE6hzu55yCrcJ/xNyEdFwA==",
+            "dev": true,
+            "dependencies": {
+                "@types/react": "*"
+            }
+        },
+        "node_modules/@types/react-reconciler": {
+            "version": "0.26.4",
+            "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.4.tgz",
+            "integrity": "sha512-bdx4aIBkQRDAnzc23JBFeZmVpmfLJHfHikmQukEt9qs4bQtq9f+PDbNwhR9u74FkIUyIDz1I1qJ8OF6RwadKpw==",
+            "dependencies": {
+                "@types/react": "*"
+            }
+        },
+        "node_modules/@types/react-redux": {
+            "version": "7.1.22",
+            "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.22.tgz",
+            "integrity": "sha512-GxIA1kM7ClU73I6wg9IRTVwSO9GS+SAKZKe0Enj+82HMU6aoESFU2HNAdNi3+J53IaOHPiUfT3kSG4L828joDQ==",
+            "dependencies": {
+                "@types/hoist-non-react-statics": "^3.3.0",
+                "@types/react": "*",
+                "hoist-non-react-statics": "^3.3.0",
+                "redux": "^4.0.0"
+            }
+        },
+        "node_modules/@types/react-select": {
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/@types/react-select/-/react-select-3.1.2.tgz",
+            "integrity": "sha512-ygvR/2FL87R2OLObEWFootYzkvm67LRA+URYEAcBuvKk7IXmdsnIwSGm60cVXGaqkJQHozb2Cy1t94tCYb6rJA==",
+            "dev": true,
+            "dependencies": {
+                "@types/react": "*",
+                "@types/react-dom": "*",
+                "@types/react-transition-group": "*"
+            }
+        },
+        "node_modules/@types/react-table": {
+            "version": "6.8.9",
+            "resolved": "https://registry.npmjs.org/@types/react-table/-/react-table-6.8.9.tgz",
+            "integrity": "sha512-fVQXjy/EYDbgraScgjDONA291McKqGrw0R0NeK639fx2bS4T19TnXMjg3FjOPlkI3qYTQtFTPADlRYysaQIMpA==",
+            "dev": true,
+            "dependencies": {
+                "@types/react": "*"
+            }
+        },
+        "node_modules/@types/react-transition-group": {
+            "version": "4.4.4",
+            "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz",
+            "integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==",
+            "dependencies": {
+                "@types/react": "*"
+            }
+        },
+        "node_modules/@types/react/node_modules/csstype": {
+            "version": "3.0.10",
+            "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
+            "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
+        },
+        "node_modules/@types/reactcss": {
+            "version": "1.2.6",
+            "resolved": "https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.6.tgz",
+            "integrity": "sha512-qaIzpCuXNWomGR1Xq8SCFTtF4v8V27Y6f+b9+bzHiv087MylI/nTCqqdChNeWS7tslgROmYB7yeiruWX7WnqNg==",
+            "dev": true,
+            "dependencies": {
+                "@types/react": "*"
+            }
+        },
+        "node_modules/@types/request": {
+            "version": "2.48.8",
+            "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.8.tgz",
+            "integrity": "sha512-whjk1EDJPcAR2kYHRbFl/lKeeKYTi05A15K9bnLInCVroNDCtXce57xKdI0/rQaA3K+6q0eFyUBPmqfSndUZdQ==",
+            "dev": true,
+            "dependencies": {
+                "@types/caseless": "*",
+                "@types/node": "*",
+                "@types/tough-cookie": "*",
+                "form-data": "^2.5.0"
+            }
+        },
+        "node_modules/@types/request-promise": {
+            "version": "4.1.48",
+            "resolved": "https://registry.npmjs.org/@types/request-promise/-/request-promise-4.1.48.tgz",
+            "integrity": "sha512-sLsfxfwP5G3E3U64QXxKwA6ctsxZ7uKyl4I28pMj3JvV+ztWECRns73GL71KMOOJME5u1A5Vs5dkBqyiR1Zcnw==",
+            "dev": true,
+            "dependencies": {
+                "@types/bluebird": "*",
+                "@types/request": "*"
+            }
+        },
+        "node_modules/@types/responselike": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
+            "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
+            "dependencies": {
+                "@types/node": "*"
+            }
+        },
+        "node_modules/@types/reveal": {
+            "version": "3.3.33",
+            "resolved": "https://registry.npmjs.org/@types/reveal/-/reveal-3.3.33.tgz",
+            "integrity": "sha512-lKbezA9Oa5LfdSRwFDc/FHEGH4+FjiXh/a/PCSZAmN+KCeQJL/3ClOdAQwOxt3zdHc8XyioT+cNvIOletwRI7A=="
+        },
+        "node_modules/@types/rimraf": {
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-2.0.5.tgz",
+            "integrity": "sha512-YyP+VfeaqAyFmXoTh3HChxOQMyjByRMsHU7kc5KOJkSlXudhMhQIALbYV7rHh/l8d2lX3VUQzprrcAgWdRuU8g==",
+            "dev": true,
+            "dependencies": {
+                "@types/glob": "*",
+                "@types/node": "*"
+            }
+        },
+        "node_modules/@types/scheduler": {
+            "version": "0.16.2",
+            "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
+            "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
+        },
+        "node_modules/@types/serve-static": {
+            "version": "1.13.10",
+            "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz",
+            "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==",
+            "dev": true,
+            "dependencies": {
+                "@types/mime": "^1",
+                "@types/node": "*"
+            }
+        },
+        "node_modules/@types/sharp": {
+            "version": "0.23.1",
+            "resolved": "https://registry.npmjs.org/@types/sharp/-/sharp-0.23.1.tgz",
+            "integrity": "sha512-iBRM9RjRF9pkIkukk6imlxfaKMRuiRND8L0yYKl5PJu5uLvxuNzp5f0x8aoTG5VX85M8O//BwbttzFVZL1j/FQ==",
+            "dev": true,
+            "dependencies": {
+                "@types/node": "*"
+            }
+        },
+        "node_modules/@types/shelljs": {
+            "version": "0.8.11",
+            "resolved": "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.8.11.tgz",
+            "integrity": "sha512-x9yaMvEh5BEaZKeVQC4vp3l+QoFj3BXcd4aYfuKSzIIyihjdVARAadYy3SMNIz0WCCdS2vB9JL/U6GQk5PaxQw==",
+            "dev": true,
+            "dependencies": {
+                "@types/glob": "*",
+                "@types/node": "*"
+            }
+        },
+        "node_modules/@types/sizzle": {
+            "version": "2.3.3",
+            "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz",
+            "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==",
+            "dev": true
+        },
+        "node_modules/@types/socket.io": {
+            "version": "2.1.13",
+            "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.13.tgz",
+            "integrity": "sha512-JRgH3nCgsWel4OPANkhH8TelpXvacAJ9VeryjuqCDiaVDMpLysd6sbt0dr6Z15pqH3p2YpOT3T1C5vQ+O/7uyg==",
+            "dev": true,
+            "dependencies": {
+                "@types/engine.io": "*",
+                "@types/node": "*",
+                "@types/socket.io-parser": "*"
+            }
+        },
+        "node_modules/@types/socket.io-client": {
+            "version": "1.4.36",
+            "resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.36.tgz",
+            "integrity": "sha512-ZJWjtFBeBy1kRSYpVbeGYTElf6BqPQUkXDlHHD4k/42byCN5Rh027f4yARHCink9sKAkbtGZXEAmR0ZCnc2/Ag==",
+            "dev": true
+        },
+        "node_modules/@types/socket.io-parser": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/@types/socket.io-parser/-/socket.io-parser-3.0.0.tgz",
+            "integrity": "sha512-Ry/rbTE6HQNL9eu3LpL1Ocup5VexXu1bSSGlSho/IR5LuRc8YvxwSNJ3JxqTltVJEATLbZkMQETSbxfKNgp4Ew==",
+            "deprecated": "This is a stub types definition. socket.io-parser provides its own type definitions, so you do not need this installed.",
+            "dev": true,
+            "dependencies": {
+                "socket.io-parser": "*"
+            }
+        },
+        "node_modules/@types/source-list-map": {
+            "version": "0.1.2",
+            "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
+            "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==",
+            "dev": true
+        },
+        "node_modules/@types/strip-bom": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz",
+            "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=",
+            "dev": true
+        },
+        "node_modules/@types/strip-json-comments": {
+            "version": "0.0.30",
+            "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz",
+            "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==",
+            "dev": true
+        },
+        "node_modules/@types/supercluster": {
+            "version": "7.1.0",
+            "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.0.tgz",
+            "integrity": "sha512-6JapQ2GmEkH66r23BK49I+u6zczVDGTtiJEVvKDYZVSm/vepWaJuTq6BXzJ6I4agG5s8vA1KM7m/gXWDg03O4Q==",
+            "dependencies": {
+                "@types/geojson": "*"
+            }
+        },
+        "node_modules/@types/tapable": {
+            "version": "1.0.8",
+            "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.8.tgz",
+            "integrity": "sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==",
+            "dev": true
+        },
+        "node_modules/@types/three": {
+            "version": "0.126.2",
+            "resolved": "https://registry.npmjs.org/@types/three/-/three-0.126.2.tgz",
+            "integrity": "sha512-6JqTgijtfXcTJik8NtiNxr2L90ex6ElM00qilOGeUcrEsJLOdzLJSIkXHUYS+KPAYQYtRJQKD6XaXds3HjS+gg=="
+        },
+        "node_modules/@types/tough-cookie": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz",
+            "integrity": "sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg=="
+        },
+        "node_modules/@types/typescript": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/@types/typescript/-/typescript-2.0.0.tgz",
+            "integrity": "sha1-xDNTnJi64oaCswfqp6D9IRW4PCg=",
+            "deprecated": "This is a stub types definition for TypeScript (https://github.com/Microsoft/TypeScript). TypeScript provides its own type definitions, so you don't need @types/typescript installed!",
+            "dev": true,
+            "dependencies": {
+                "typescript": "*"
+            }
+        },
+        "node_modules/@types/uglify-js": {
+            "version": "3.13.1",
+            "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.1.tgz",
+            "integrity": "sha512-O3MmRAk6ZuAKa9CHgg0Pr0+lUOqoMLpc9AS4R8ano2auvsg7IE8syF3Xh/NPr26TWklxYcqoEEFdzLLs1fV9PQ==",
+            "dev": true,
+            "dependencies": {
+                "source-map": "^0.6.1"
+            }
+        },
+        "node_modules/@types/uglify-js/node_modules/source-map": {
+            "version": "0.6.1",
+            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+            "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/@types/uuid": {
+            "version": "3.4.10",
+            "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.10.tgz",
+            "integrity": "sha512-BgeaZuElf7DEYZhWYDTc/XcLZXdVgFkVSTa13BqKvbnmUrxr3TJFKofUxCtDO9UQOdhnV+HPOESdHiHKZOJV1A==",
+            "dev": true
+        },
+        "node_modules/@types/valid-url": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/@types/valid-url/-/valid-url-1.0.3.tgz",
+            "integrity": "sha512-+33x29mg+ecU88ODdWpqaie2upIuRkhujVLA7TuJjM823cNMbeggfI6NhxewaRaRF8dy+g33e4uIg/m5Mb3xDQ==",
+            "dev": true
+        },
+        "node_modules/@types/web": {
+            "version": "0.0.53",
+            "resolved": "https://registry.npmjs.org/@types/web/-/web-0.0.53.tgz",
+            "integrity": "sha512-ULJJgHevSiiD/QaSWcpAfWHXHEaeXUXGGJ82XVderV4TAWdI22NFyjWcRfzJwjEIdUi0gvfVRlTFDeLTZpvF4Q=="
+        },
+        "node_modules/@types/webpack": {
+            "version": "4.41.32",
+            "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.32.tgz",
+            "integrity": "sha512-cb+0ioil/7oz5//7tZUSwbrSAN/NWHrQylz5cW8G0dWTcF/g+/dSdMlKVZspBYuMAN1+WnwHrkxiRrLcwd0Heg==",
+            "dev": true,
+            "dependencies": {
+                "@types/node": "*",
+                "@types/tapable": "^1",
+                "@types/uglify-js": "*",
+                "@types/webpack-sources": "*",
+                "anymatch": "^3.0.0",
+                "source-map": "^0.6.0"
+            }
+        },
+        "node_modules/@types/webpack-hot-middleware": {
+            "version": "2.25.6",
+            "resolved": "https://registry.npmjs.org/@types/webpack-hot-middleware/-/webpack-hot-middleware-2.25.6.tgz",
+            "integrity": "sha512-1Q9ClNvZR30HIsEAHYQL3bXJK1K7IsrqjGMTfIureFjphsGOZ3TkbeoCupbCmi26pSLjVTPHp+pFrJNpOkBSVg==",
+            "dev": true,
+            "dependencies": {
+                "@types/connect": "*",
+                "tapable": "^2.2.0",
+                "webpack": "^5"
+            }
+        },
+        "node_modules/@types/webpack-hot-middleware/node_modules/tapable": {
+            "version": "2.2.1",
+            "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+            "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+            "dev": true,
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/@types/webpack-sources": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-3.2.0.tgz",
+            "integrity": "sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg==",
+            "dev": true,
+            "dependencies": {
+                "@types/node": "*",
+                "@types/source-list-map": "*",
+                "source-map": "^0.7.3"
+            }
+        },
+        "node_modules/@types/webpack-sources/node_modules/source-map": {
+            "version": "0.7.3",
+            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+            "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+            "dev": true,
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/@types/webpack/node_modules/anymatch": {
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+            "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+            "dev": true,
+            "dependencies": {
+                "normalize-path": "^3.0.0",
+                "picomatch": "^2.0.4"
+            },
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/@types/webpack/node_modules/source-map": {
+            "version": "0.6.1",
+            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+            "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/@types/webscopeio__react-textarea-autocomplete": {
+            "version": "4.7.2",
+            "resolved": "https://registry.npmjs.org/@types/webscopeio__react-textarea-autocomplete/-/webscopeio__react-textarea-autocomplete-4.7.2.tgz",
+            "integrity": "sha512-e1DZGD+eH19BnllTWCGXAdrMa2kI53wEMuhn/d+wUmnu8//ZI6BiuK/EPdw07fI4+tlyo5qdPZdXdpkoXHJVOw==",
+            "dependencies": {
+                "@types/react": "*"
+            }
+        },
+        "node_modules/@types/xregexp": {
+            "version": "4.4.0",
+            "resolved": "https://registry.npmjs.org/@types/xregexp/-/xregexp-4.4.0.tgz",
+            "integrity": "sha512-RJJHNci1sRRq8nZjWxzCbQdLhJVq+JcDHpsdzoTtFAR9qdsMhAWqKQ1NHsNcenKDtLsOwCBe/kfSKM82yXtocg==",
+            "deprecated": "This is a stub types definition. xregexp provides its own type definitions, so you do not need this installed.",
+            "dev": true,
+            "dependencies": {
+                "xregexp": "*"
+            }
+        },
+        "node_modules/@types/yauzl": {
+            "version": "2.9.1",
+            "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz",
+            "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==",
+            "optional": true,
+            "dependencies": {
+                "@types/node": "*"
+            }
+        },
+        "node_modules/@types/youtube": {
+            "version": "0.0.39",
+            "resolved": "https://registry.npmjs.org/@types/youtube/-/youtube-0.0.39.tgz",
+            "integrity": "sha512-naVjyTZT/CoNwEQW3+mf9E/x6sgB7QIPbRDtVlPUpHlmuQTk+j6gQBy0T5CRkV8oC0GYQjMgfr3VdueTSVLTjw==",
+            "dev": true
+        },
+        "node_modules/@webassemblyjs/ast": {
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
+            "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==",
+            "dependencies": {
+                "@webassemblyjs/helper-numbers": "1.11.1",
+                "@webassemblyjs/helper-wasm-bytecode": "1.11.1"
+            }
+        },
+        "node_modules/@webassemblyjs/floating-point-hex-parser": {
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz",
+            "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ=="
+        },
+        "node_modules/@webassemblyjs/helper-api-error": {
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz",
+            "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg=="
+        },
+        "node_modules/@webassemblyjs/helper-buffer": {
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz",
+            "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA=="
+        },
+        "node_modules/@webassemblyjs/helper-numbers": {
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz",
+            "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==",
+            "dependencies": {
+                "@webassemblyjs/floating-point-hex-parser": "1.11.1",
+                "@webassemblyjs/helper-api-error": "1.11.1",
+                "@xtuc/long": "4.2.2"
+            }
+        },
+        "node_modules/@webassemblyjs/helper-wasm-bytecode": {
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz",
+            "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q=="
+        },
+        "node_modules/@webassemblyjs/helper-wasm-section": {
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz",
+            "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==",
+            "dependencies": {
+                "@webassemblyjs/ast": "1.11.1",
+                "@webassemblyjs/helper-buffer": "1.11.1",
+                "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+                "@webassemblyjs/wasm-gen": "1.11.1"
+            }
+        },
+        "node_modules/@webassemblyjs/ieee754": {
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz",
+            "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==",
+            "dependencies": {
+                "@xtuc/ieee754": "^1.2.0"
+            }
+        },
+        "node_modules/@webassemblyjs/leb128": {
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz",
+            "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==",
+            "dependencies": {
+                "@xtuc/long": "4.2.2"
+            }
+        },
+        "node_modules/@webassemblyjs/utf8": {
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz",
+            "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ=="
+        },
+        "node_modules/@webassemblyjs/wasm-edit": {
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz",
+            "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==",
+            "dependencies": {
+                "@webassemblyjs/ast": "1.11.1",
+                "@webassemblyjs/helper-buffer": "1.11.1",
+                "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+                "@webassemblyjs/helper-wasm-section": "1.11.1",
+                "@webassemblyjs/wasm-gen": "1.11.1",
+                "@webassemblyjs/wasm-opt": "1.11.1",
+                "@webassemblyjs/wasm-parser": "1.11.1",
+                "@webassemblyjs/wast-printer": "1.11.1"
+            }
+        },
+        "node_modules/@webassemblyjs/wasm-gen": {
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz",
+            "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==",
+            "dependencies": {
+                "@webassemblyjs/ast": "1.11.1",
+                "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+                "@webassemblyjs/ieee754": "1.11.1",
+                "@webassemblyjs/leb128": "1.11.1",
+                "@webassemblyjs/utf8": "1.11.1"
+            }
+        },
+        "node_modules/@webassemblyjs/wasm-opt": {
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz",
+            "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==",
+            "dependencies": {
+                "@webassemblyjs/ast": "1.11.1",
+                "@webassemblyjs/helper-buffer": "1.11.1",
+                "@webassemblyjs/wasm-gen": "1.11.1",
+                "@webassemblyjs/wasm-parser": "1.11.1"
+            }
+        },
+        "node_modules/@webassemblyjs/wasm-parser": {
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz",
+            "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==",
+            "dependencies": {
+                "@webassemblyjs/ast": "1.11.1",
+                "@webassemblyjs/helper-api-error": "1.11.1",
+                "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+                "@webassemblyjs/ieee754": "1.11.1",
+                "@webassemblyjs/leb128": "1.11.1",
+                "@webassemblyjs/utf8": "1.11.1"
+            }
+        },
+        "node_modules/@webassemblyjs/wast-printer": {
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz",
+            "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==",
+            "dependencies": {
+                "@webassemblyjs/ast": "1.11.1",
+                "@xtuc/long": "4.2.2"
+            }
+        },
+        "node_modules/@webpack-cli/configtest": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.1.tgz",
+            "integrity": "sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg==",
+            "peerDependencies": {
+                "webpack": "4.x.x || 5.x.x",
+                "webpack-cli": "4.x.x"
+            }
+        },
+        "node_modules/@webpack-cli/info": {
+            "version": "1.4.1",
+            "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.1.tgz",
+            "integrity": "sha512-PKVGmazEq3oAo46Q63tpMr4HipI3OPfP7LiNOEJg963RMgT0rqheag28NCML0o3GIzA3DmxP1ZIAv9oTX1CUIA==",
+            "dependencies": {
+                "envinfo": "^7.7.3"
+            },
+            "peerDependencies": {
+                "webpack-cli": "4.x.x"
+            }
+        },
+        "node_modules/@webpack-cli/serve": {
+            "version": "1.6.1",
+            "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.1.tgz",
+            "integrity": "sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw==",
+            "peerDependencies": {
+                "webpack-cli": "4.x.x"
+            },
+            "peerDependenciesMeta": {
+                "webpack-dev-server": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/@webscopeio/react-textarea-autocomplete": {
+            "version": "4.9.1",
+            "resolved": "https://registry.npmjs.org/@webscopeio/react-textarea-autocomplete/-/react-textarea-autocomplete-4.9.1.tgz",
+            "integrity": "sha512-g5cnRRXQQBGH5Km/0eSq8xFlnHZeG2WVRr+uu/3PKbHcgvNUIHKEy2tjOA9m4MK8KFzbFtfnj2MqdPf9R+5/Cw==",
+            "dependencies": {
+                "custom-event": "^1.0.1",
+                "textarea-caret": "3.0.2"
+            },
+            "peerDependencies": {
+                "prop-types": "^15.0.0",
+                "react": "^16.0.0 || ^17",
+                "react-dom": "^16.0.0 || ^17"
+            }
+        },
+        "node_modules/@webscopeio/react-textarea-autocomplete/node_modules/textarea-caret": {
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/textarea-caret/-/textarea-caret-3.0.2.tgz",
+            "integrity": "sha1-82DEhpmqGr9xhoCkOjGoUGZcLK8="
+        },
+        "node_modules/@xtuc/ieee754": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+            "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA=="
+        },
+        "node_modules/@xtuc/long": {
+            "version": "4.2.2",
+            "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+            "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
+        },
+        "node_modules/abab": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz",
+            "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==",
+            "dev": true
+        },
+        "node_modules/abbrev": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+            "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
+        },
+        "node_modules/abort-controller": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+            "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+            "dependencies": {
+                "event-target-shim": "^5.0.0"
+            },
+            "engines": {
+                "node": ">=6.5"
+            }
+        },
+        "node_modules/accepts": {
+            "version": "1.3.8",
+            "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+            "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+            "dependencies": {
+                "mime-types": "~2.1.34",
+                "negotiator": "0.6.3"
+            },
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/accepts/node_modules/mime-db": {
+            "version": "1.51.0",
+            "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
+            "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==",
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/accepts/node_modules/mime-types": {
+            "version": "2.1.34",
+            "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
+            "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
+            "dependencies": {
+                "mime-db": "1.51.0"
+            },
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/acorn": {
+            "version": "3.3.0",
+            "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
+            "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
+            "bin": {
+                "acorn": "bin/acorn"
+            },
+            "engines": {
+                "node": ">=0.4.0"
+            }
+        },
+        "node_modules/acorn-globals": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz",
+            "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=",
+            "dependencies": {
+                "acorn": "^4.0.4"
+            }
+        },
+        "node_modules/acorn-globals/node_modules/acorn": {
+            "version": "4.0.13",
+            "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
+            "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=",
+            "bin": {
+                "acorn": "bin/acorn"
+            },
+            "engines": {
+                "node": ">=0.4.0"
+            }
+        },
+        "node_modules/acorn-import-assertions": {
+            "version": "1.8.0",
+            "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
+            "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
+            "peerDependencies": {
+                "acorn": "^8"
+            }
+        },
+        "node_modules/acorn-jsx": {
+            "version": "5.3.2",
+            "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+            "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+            "peerDependencies": {
+                "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+            }
+        },
+        "node_modules/acorn-walk": {
+            "version": "6.2.0",
+            "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz",
+            "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.4.0"
+            }
+        },
+        "node_modules/adm-zip": {
+            "version": "0.4.16",
+            "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz",
+            "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==",
+            "engines": {
+                "node": ">=0.3.0"
+            }
+        },
+        "node_modules/after": {
+            "version": "0.8.2",
+            "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
+            "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
+        },
+        "node_modules/agent-base": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz",
+            "integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==",
+            "dependencies": {
+                "debug": "4"
+            },
+            "engines": {
+                "node": ">= 6.0.0"
+            }
+        },
+        "node_modules/agent-base/node_modules/debug": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+            "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+            "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
+            "dependencies": {
+                "ms": "^2.1.1"
+            }
+        },
+        "node_modules/ajv": {
+            "version": "6.12.2",
+            "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
+            "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==",
+            "dependencies": {
+                "fast-deep-equal": "^3.1.1",
+                "fast-json-stable-stringify": "^2.0.0",
+                "json-schema-traverse": "^0.4.1",
+                "uri-js": "^4.2.2"
+            }
+        },
+        "node_modules/ajv-errors": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
+            "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
+            "peerDependencies": {
+                "ajv": ">=5.0.0"
+            }
+        },
+        "node_modules/ajv-formats": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
+            "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+            "dependencies": {
+                "ajv": "^8.0.0"
+            },
+            "peerDependencies": {
+                "ajv": "^8.0.0"
+            },
+            "peerDependenciesMeta": {
+                "ajv": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/ajv-formats/node_modules/ajv": {
+            "version": "8.10.0",
+            "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz",
+            "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==",
+            "dependencies": {
+                "fast-deep-equal": "^3.1.1",
+                "json-schema-traverse": "^1.0.0",
+                "require-from-string": "^2.0.2",
+                "uri-js": "^4.2.2"
+            },
+            "funding": {
+                "type": "github",
+                "url": "https://github.com/sponsors/epoberezkin"
+            }
+        },
+        "node_modules/ajv-formats/node_modules/json-schema-traverse": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+            "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+        },
+        "node_modules/ajv-keywords": {
+            "version": "3.4.1",
+            "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz",
+            "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
+            "peerDependencies": {
+                "ajv": "^6.9.1"
+            }
+        },
+        "node_modules/align-text": {
+            "version": "0.1.4",
+            "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
+            "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
+            "dependencies": {
+                "kind-of": "^3.0.2",
+                "longest": "^1.0.1",
+                "repeat-string": "^1.5.2"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/align-text/node_modules/is-buffer": {
+            "version": "1.1.6",
+            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+            "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+        },
+        "node_modules/align-text/node_modules/kind-of": {
+            "version": "3.2.2",
+            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+            "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+            "dependencies": {
+                "is-buffer": "^1.1.5"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/amdefine": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
+            "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
+            "engines": {
+                "node": ">=0.4.2"
+            }
+        },
+        "node_modules/ansi-align": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz",
+            "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=",
+            "dependencies": {
+                "string-width": "^2.0.0"
+            }
+        },
+        "node_modules/ansi-colors": {
+            "version": "3.2.3",
+            "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
+            "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/ansi-escape-sequences": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-4.1.0.tgz",
+            "integrity": "sha512-dzW9kHxH011uBsidTXd14JXgzye/YLb2LzeKZ4bsgl/Knwx8AtbSFkkGxagdNOoh0DlqHCmfiEjWKBaqjOanVw==",
+            "dependencies": {
+                "array-back": "^3.0.1"
+            },
+            "engines": {
+                "node": ">=8.0.0"
+            }
+        },
+        "node_modules/ansi-escape-sequences/node_modules/array-back": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz",
+            "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/ansi-html-community": {
+            "version": "0.0.8",
+            "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz",
+            "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==",
+            "dev": true,
+            "engines": [
+                "node >= 0.8.0"
+            ],
+            "bin": {
+                "ansi-html": "bin/ansi-html"
+            }
+        },
+        "node_modules/ansi-regex": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+            "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/ansi-styles": {
+            "version": "3.2.1",
+            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+            "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+            "dependencies": {
+                "color-convert": "^1.9.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/any-promise": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+            "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
+        },
+        "node_modules/anymatch": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+            "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+            "dependencies": {
+                "micromatch": "^3.1.4",
+                "normalize-path": "^2.1.1"
+            }
+        },
+        "node_modules/anymatch/node_modules/normalize-path": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+            "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+            "dependencies": {
+                "remove-trailing-separator": "^1.0.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/aproba": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+            "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+        },
+        "node_modules/archiver": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/archiver/-/archiver-3.1.1.tgz",
+            "integrity": "sha512-5Hxxcig7gw5Jod/8Gq0OneVgLYET+oNHcxgWItq4TbhOzRLKNAFUb9edAftiMKXvXfCB0vbGrJdZDNq0dWMsxg==",
+            "dependencies": {
+                "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.2"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/archiver-utils": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz",
+            "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/archiver-utils/node_modules/readable-stream": {
+            "version": "2.3.7",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/are-we-there-yet": {
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
+            "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
+            "dependencies": {
+                "delegates": "^1.0.0",
+                "readable-stream": "^2.0.6"
+            }
+        },
+        "node_modules/are-we-there-yet/node_modules/readable-stream": {
+            "version": "2.3.7",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/arg": {
+            "version": "4.1.3",
+            "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+            "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+            "dev": true
+        },
+        "node_modules/argparse": {
+            "version": "1.0.10",
+            "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+            "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+            "dependencies": {
+                "sprintf-js": "~1.0.2"
+            }
+        },
+        "node_modules/arr-diff": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+            "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/arr-flatten": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+            "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/arr-union": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+            "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/array-back": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz",
+            "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==",
+            "dependencies": {
+                "typical": "^2.6.1"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/array-batcher": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/array-batcher/-/array-batcher-1.2.3.tgz",
+            "integrity": "sha512-/IOrwn4ZJi7YqTZrs3k+wQN5nKhjtTqL5ZKkzB+sKJlPeJzpMnRc3o8T9yt8/ZJiSldd+PwTHjM+//UsaszOOw==",
+            "dependencies": {
+                "@types/node": "^12.7.5",
+                "chai": "^4.2.0",
+                "mocha": "^6.2.0",
+                "request": "^2.88.0",
+                "request-promise": "^4.2.4"
+            }
+        },
+        "node_modules/array-batcher/node_modules/@types/node": {
+            "version": "12.12.37",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.37.tgz",
+            "integrity": "sha512-4mXKoDptrXAwZErQHrLzpe0FN/0Wmf5JRniSVIdwUrtDf9wnmEV1teCNLBo/TwuXhkK/bVegoEn/wmb+x0AuPg=="
+        },
+        "node_modules/array-batcher/node_modules/glob": {
+            "version": "7.1.3",
+            "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+            "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/array-batcher/node_modules/mocha": {
+            "version": "6.2.3",
+            "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.3.tgz",
+            "integrity": "sha512-0R/3FvjIGH3eEuG17ccFPk117XL2rWxatr81a57D+r/x2uTYZRbdZ4oVidEUMh2W2TJDa7MdAb12Lm2/qrKajg==",
+            "dependencies": {
+                "ansi-colors": "3.2.3",
+                "browser-stdout": "1.3.1",
+                "debug": "3.2.6",
+                "diff": "3.5.0",
+                "escape-string-regexp": "1.0.5",
+                "find-up": "3.0.0",
+                "glob": "7.1.3",
+                "growl": "1.10.5",
+                "he": "1.2.0",
+                "js-yaml": "3.13.1",
+                "log-symbols": "2.2.0",
+                "minimatch": "3.0.4",
+                "mkdirp": "0.5.4",
+                "ms": "2.1.1",
+                "node-environment-flags": "1.0.5",
+                "object.assign": "4.1.0",
+                "strip-json-comments": "2.0.1",
+                "supports-color": "6.0.0",
+                "which": "1.3.1",
+                "wide-align": "1.1.3",
+                "yargs": "13.3.2",
+                "yargs-parser": "13.1.2",
+                "yargs-unparser": "1.6.0"
+            },
+            "bin": {
+                "_mocha": "bin/_mocha",
+                "mocha": "bin/mocha"
+            },
+            "engines": {
+                "node": ">= 6.0.0"
+            }
+        },
+        "node_modules/array-batcher/node_modules/supports-color": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz",
+            "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==",
+            "dependencies": {
+                "has-flag": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/array-equal": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
+            "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
+            "dev": true
+        },
+        "node_modules/array-find-index": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
+            "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/array-flatten": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+            "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+        },
+        "node_modules/array-union": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+            "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+            "dev": true,
+            "dependencies": {
+                "array-uniq": "^1.0.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/array-uniq": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+            "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
+            "dev": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/array-unique": {
+            "version": "0.3.2",
+            "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+            "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/arraybuffer.slice": {
+            "version": "0.0.7",
+            "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
+            "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
+        },
+        "node_modules/arrify": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
+            "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/asap": {
+            "version": "2.0.6",
+            "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+            "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
+        },
+        "node_modules/asn1": {
+            "version": "0.2.6",
+            "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
+            "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
+            "dependencies": {
+                "safer-buffer": "~2.1.0"
+            }
+        },
+        "node_modules/assert-plus": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+            "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+            "engines": {
+                "node": ">=0.8"
+            }
+        },
+        "node_modules/assertion-error": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+            "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/assign-symbols": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+            "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/async": {
+            "version": "2.6.3",
+            "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
+            "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+            "dependencies": {
+                "lodash": "^4.17.14"
+            }
+        },
+        "node_modules/async-each": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
+            "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ=="
+        },
+        "node_modules/async-foreach": {
+            "version": "0.1.3",
+            "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz",
+            "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/async-limiter": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
+            "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
+            "dev": true
+        },
+        "node_modules/asynckit": {
+            "version": "0.4.0",
+            "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+            "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+        },
+        "node_modules/atob": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+            "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+            "bin": {
+                "atob": "bin/atob.js"
+            },
+            "engines": {
+                "node": ">= 4.5.0"
+            }
+        },
+        "node_modules/available-typed-arrays": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
+            "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/awesome-typescript-loader": {
+            "version": "5.2.1",
+            "resolved": "https://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-5.2.1.tgz",
+            "integrity": "sha512-slv66OAJB8orL+UUaTI3pKlLorwIvS4ARZzYR9iJJyGsEgOqueMfOMdKySWzZ73vIkEe3fcwFgsKMg4d8zyb1g==",
+            "dev": true,
+            "dependencies": {
+                "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"
+            },
+            "peerDependencies": {
+                "typescript": "^2.7 || ^3"
+            }
+        },
+        "node_modules/aws-sign2": {
+            "version": "0.7.0",
+            "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+            "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/aws4": {
+            "version": "1.11.0",
+            "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
+            "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA=="
+        },
+        "node_modules/axios": {
+            "version": "0.19.2",
+            "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
+            "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
+            "deprecated": "Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410",
+            "dependencies": {
+                "follow-redirects": "1.5.10"
+            }
+        },
+        "node_modules/axios/node_modules/debug": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+            "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+            "dependencies": {
+                "ms": "2.0.0"
+            }
+        },
+        "node_modules/axios/node_modules/follow-redirects": {
+            "version": "1.5.10",
+            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
+            "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
+            "dependencies": {
+                "debug": "=3.1.0"
+            },
+            "engines": {
+                "node": ">=4.0"
+            }
+        },
+        "node_modules/axios/node_modules/ms": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        },
+        "node_modules/babel": {
+            "version": "6.23.0",
+            "resolved": "https://registry.npmjs.org/babel/-/babel-6.23.0.tgz",
+            "integrity": "sha1-0NHn2APpdHZb7qMjLU4VPA77kPQ=",
+            "deprecated": "In 6.x, the babel package has been deprecated in favor of babel-cli. Check https://opencollective.com/babel to support the Babel maintainers",
+            "bin": {
+                "babel": "lib/cli.js",
+                "babel-external-helpers": "lib/cli.js",
+                "babel-node": "lib/cli.js"
+            }
+        },
+        "node_modules/babel-code-frame": {
+            "version": "6.26.0",
+            "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
+            "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
+            "dev": true,
+            "dependencies": {
+                "chalk": "^1.1.3",
+                "esutils": "^2.0.2",
+                "js-tokens": "^3.0.2"
+            }
+        },
+        "node_modules/babel-code-frame/node_modules/ansi-regex": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+            "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+            "dev": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/babel-code-frame/node_modules/ansi-styles": {
+            "version": "2.2.1",
+            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+            "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+            "dev": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/babel-code-frame/node_modules/chalk": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+            "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+            "dev": true,
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/babel-code-frame/node_modules/js-tokens": {
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
+            "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
+            "dev": true
+        },
+        "node_modules/babel-code-frame/node_modules/strip-ansi": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+            "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+            "dev": true,
+            "dependencies": {
+                "ansi-regex": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/babel-code-frame/node_modules/supports-color": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+            "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+            "dev": true,
+            "engines": {
+                "node": ">=0.8.0"
+            }
+        },
+        "node_modules/babel-plugin-emotion": {
+            "version": "10.2.2",
+            "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.2.2.tgz",
+            "integrity": "sha512-SMSkGoqTbTyUTDeuVuPIWifPdUGkTk1Kf9BWRiXIOIcuyMfsdp2EjeiiFvOzX8NOBvEh/ypKYvUh2rkgAJMCLA==",
+            "dependencies": {
+                "@babel/helper-module-imports": "^7.0.0",
+                "@emotion/hash": "0.8.0",
+                "@emotion/memoize": "0.7.4",
+                "@emotion/serialize": "^0.11.16",
+                "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"
+            }
+        },
+        "node_modules/babel-plugin-macros": {
+            "version": "2.8.0",
+            "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz",
+            "integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==",
+            "dependencies": {
+                "@babel/runtime": "^7.7.2",
+                "cosmiconfig": "^6.0.0",
+                "resolve": "^1.12.0"
+            }
+        },
+        "node_modules/babel-plugin-styled-components": {
+            "version": "1.10.7",
+            "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.10.7.tgz",
+            "integrity": "sha512-MBMHGcIA22996n9hZRf/UJLVVgkEOITuR2SvjHLb5dSTUyR4ZRGn+ngITapes36FI3WLxZHfRhkA1ffHxihOrg==",
+            "dependencies": {
+                "@babel/helper-annotate-as-pure": "^7.0.0",
+                "@babel/helper-module-imports": "^7.0.0",
+                "babel-plugin-syntax-jsx": "^6.18.0",
+                "lodash": "^4.17.11"
+            },
+            "peerDependencies": {
+                "styled-components": ">= 2"
+            }
+        },
+        "node_modules/babel-plugin-syntax-jsx": {
+            "version": "6.18.0",
+            "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
+            "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY="
+        },
+        "node_modules/babel-runtime": {
+            "version": "6.26.0",
+            "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
+            "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+            "dependencies": {
+                "core-js": "^2.4.0",
+                "regenerator-runtime": "^0.11.0"
+            }
+        },
+        "node_modules/babel-runtime/node_modules/core-js": {
+            "version": "2.6.12",
+            "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
+            "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
+            "deprecated": "core-js@<3.4 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.",
+            "hasInstallScript": true
+        },
+        "node_modules/babel-runtime/node_modules/regenerator-runtime": {
+            "version": "0.11.1",
+            "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+            "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
+        },
+        "node_modules/babel-types": {
+            "version": "6.26.0",
+            "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
+            "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
+            "dependencies": {
+                "babel-runtime": "^6.26.0",
+                "esutils": "^2.0.2",
+                "lodash": "^4.17.4",
+                "to-fast-properties": "^1.0.3"
+            }
+        },
+        "node_modules/babel-types/node_modules/to-fast-properties": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
+            "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/babylon": {
+            "version": "6.18.0",
+            "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
+            "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
+            "bin": {
+                "babylon": "bin/babylon.js"
+            }
+        },
+        "node_modules/backo2": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
+            "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
+        },
+        "node_modules/balanced-match": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+            "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+        },
+        "node_modules/base": {
+            "version": "0.11.2",
+            "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+            "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/base/node_modules/define-property": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+            "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+            "dependencies": {
+                "is-descriptor": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/base/node_modules/is-accessor-descriptor": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+            "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+            "dependencies": {
+                "kind-of": "^6.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/base/node_modules/is-data-descriptor": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+            "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+            "dependencies": {
+                "kind-of": "^6.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/base/node_modules/is-descriptor": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+            "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+            "dependencies": {
+                "is-accessor-descriptor": "^1.0.0",
+                "is-data-descriptor": "^1.0.0",
+                "kind-of": "^6.0.2"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/base16": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz",
+            "integrity": "sha1-4pf2DX7BAUp6lxo568ipjAtoHnA="
+        },
+        "node_modules/Base64": {
+            "version": "0.2.1",
+            "resolved": "https://registry.npmjs.org/Base64/-/Base64-0.2.1.tgz",
+            "integrity": "sha1-ujpCMHCOGGcFBl5mur3Uw1z2ACg="
+        },
+        "node_modules/base64-arraybuffer": {
+            "version": "0.1.4",
+            "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
+            "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=",
+            "engines": {
+                "node": ">= 0.6.0"
+            }
+        },
+        "node_modules/base64-js": {
+            "version": "1.3.1",
+            "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
+            "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
+        },
+        "node_modules/base64id": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
+            "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
+            "engines": {
+                "node": "^4.5.0 || >= 5.9"
+            }
+        },
+        "node_modules/base64url": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz",
+            "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==",
+            "engines": {
+                "node": ">=6.0.0"
+            }
+        },
+        "node_modules/batch": {
+            "version": "0.6.1",
+            "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
+            "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=",
+            "dev": true
+        },
+        "node_modules/bcrypt-nodejs": {
+            "version": "0.0.3",
+            "resolved": "https://registry.npmjs.org/bcrypt-nodejs/-/bcrypt-nodejs-0.0.3.tgz",
+            "integrity": "sha1-xgkX8m3CNWYVZsaBBhwwPCsohCs=",
+            "deprecated": "bcrypt-nodejs is no longer actively maintained. Please use bcrypt or bcryptjs. See https://github.com/kelektiv/node.bcrypt.js/wiki/bcrypt-vs-brypt.js to learn more about these two options"
+        },
+        "node_modules/bcrypt-pbkdf": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+            "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+            "dependencies": {
+                "tweetnacl": "^0.14.3"
+            }
+        },
+        "node_modules/bezier-curve": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/bezier-curve/-/bezier-curve-1.0.0.tgz",
+            "integrity": "sha1-o9+v6rEqlMRicw1QeYxSqEBdc3k="
+        },
+        "node_modules/bezier-js": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/bezier-js/-/bezier-js-4.1.1.tgz",
+            "integrity": "sha512-oVOS6SSFFFlfnZdzC+lsfvhs/RRcbxJ47U04M4s5QIBaJmr3YWmTIL3qmrOK9uW+nUUcl9Jccmo/xpTrG+bBoQ==",
+            "funding": {
+                "type": "individual",
+                "url": "https://github.com/Pomax/bezierjs/blob/master/FUNDING.md"
+            }
+        },
+        "node_modules/big.js": {
+            "version": "5.2.2",
+            "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
+            "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/bignumber.js": {
+            "version": "7.2.1",
+            "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz",
+            "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/binary-extensions": {
+            "version": "1.13.1",
+            "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+            "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/bl": {
+            "version": "4.0.2",
+            "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz",
+            "integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==",
+            "dependencies": {
+                "buffer": "^5.5.0",
+                "inherits": "^2.0.4",
+                "readable-stream": "^3.4.0"
+            }
+        },
+        "node_modules/blob": {
+            "version": "0.0.5",
+            "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
+            "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
+        },
+        "node_modules/block-stream": {
+            "version": "0.0.9",
+            "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
+            "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
+            "dependencies": {
+                "inherits": "~2.0.0"
+            },
+            "engines": {
+                "node": "0.4 || >=0.5.8"
+            }
+        },
+        "node_modules/bluebird": {
+            "version": "3.7.2",
+            "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+            "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
+        },
+        "node_modules/blueimp-load-image": {
+            "version": "2.31.0",
+            "resolved": "https://registry.npmjs.org/blueimp-load-image/-/blueimp-load-image-2.31.0.tgz",
+            "integrity": "sha512-6sTGh1OiUmuH8ftAYvUzALivoOmcnahinGmjZFI4puZVowXoKTn/bXtth7N1skW5AlezEOfjgFH4lNXHeNRQog=="
+        },
+        "node_modules/body-parser": {
+            "version": "1.19.2",
+            "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz",
+            "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==",
+            "dependencies": {
+                "bytes": "3.1.2",
+                "content-type": "~1.0.4",
+                "debug": "2.6.9",
+                "depd": "~1.1.2",
+                "http-errors": "1.8.1",
+                "iconv-lite": "0.4.24",
+                "on-finished": "~2.3.0",
+                "qs": "6.9.7",
+                "raw-body": "2.4.3",
+                "type-is": "~1.6.18"
+            },
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/body-parser/node_modules/debug": {
+            "version": "2.6.9",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+            "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+            "dependencies": {
+                "ms": "2.0.0"
+            }
+        },
+        "node_modules/body-parser/node_modules/ms": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        },
+        "node_modules/body-parser/node_modules/qs": {
+            "version": "6.9.7",
+            "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz",
+            "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==",
+            "engines": {
+                "node": ">=0.6"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/bonjour": {
+            "version": "3.5.0",
+            "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
+            "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=",
+            "dev": true,
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/bonjour/node_modules/array-flatten": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
+            "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==",
+            "dev": true
+        },
+        "node_modules/boolbase": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+            "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
+        },
+        "node_modules/bootstrap": {
+            "version": "4.6.1",
+            "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.1.tgz",
+            "integrity": "sha512-0dj+VgI9Ecom+rvvpNZ4MUZJz8dcX7WCX+eTID9+/8HgOkv3dsRzi8BGeZJCQU6flWQVYxwTQnEZFrmJSEO7og==",
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/bootstrap"
+            },
+            "peerDependencies": {
+                "jquery": "1.9.1 - 3",
+                "popper.js": "^1.16.1"
+            }
+        },
+        "node_modules/bowser": {
+            "version": "1.9.4",
+            "resolved": "https://registry.npmjs.org/bowser/-/bowser-1.9.4.tgz",
+            "integrity": "sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ=="
+        },
+        "node_modules/boxen": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz",
+            "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/boxen/node_modules/camelcase": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+            "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/brace-expansion": {
+            "version": "1.1.11",
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+            "dependencies": {
+                "balanced-match": "^1.0.0",
+                "concat-map": "0.0.1"
+            }
+        },
+        "node_modules/braces": {
+            "version": "2.3.2",
+            "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+            "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/braces/node_modules/extend-shallow": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+            "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+            "dependencies": {
+                "is-extendable": "^0.1.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/braces/node_modules/is-extendable": {
+            "version": "0.1.1",
+            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+            "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/browser-assert": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/browser-assert/-/browser-assert-1.2.1.tgz",
+            "integrity": "sha1-mqpaKox0aFwq4Fv+Ru/WBvBowgA="
+        },
+        "node_modules/browser-process-hrtime": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
+            "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==",
+            "dev": true
+        },
+        "node_modules/browser-stdout": {
+            "version": "1.3.1",
+            "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+            "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw=="
+        },
+        "node_modules/browserslist": {
+            "version": "4.19.3",
+            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.3.tgz",
+            "integrity": "sha512-XK3X4xtKJ+Txj8G5c30B4gsm71s69lqXlkYui4s6EkKxuv49qjYlY6oVd+IFJ73d4YymtM3+djvvt/R/iJwwDg==",
+            "dependencies": {
+                "caniuse-lite": "^1.0.30001312",
+                "electron-to-chromium": "^1.4.71",
+                "escalade": "^3.1.1",
+                "node-releases": "^2.0.2",
+                "picocolors": "^1.0.0"
+            },
+            "bin": {
+                "browserslist": "cli.js"
+            },
+            "engines": {
+                "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/browserslist"
+            }
+        },
+        "node_modules/bson": {
+            "version": "4.6.1",
+            "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.1.tgz",
+            "integrity": "sha512-I1LQ7Hz5zgwR4QquilLNZwbhPw0Apx7i7X9kGMBTsqPdml/03Q9NBtD9nt/19ahjlphktQImrnderxqpzeVDjw==",
+            "dependencies": {
+                "buffer": "^5.6.0"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/buffer": {
+            "version": "5.6.0",
+            "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
+            "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
+            "dependencies": {
+                "base64-js": "^1.0.2",
+                "ieee754": "^1.1.4"
+            }
+        },
+        "node_modules/buffer-crc32": {
+            "version": "0.2.13",
+            "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+            "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/buffer-equal-constant-time": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+            "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
+        },
+        "node_modules/buffer-from": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+            "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
+        },
+        "node_modules/buffer-indexof": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz",
+            "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==",
+            "dev": true
+        },
+        "node_modules/buffer-shims": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz",
+            "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E="
+        },
+        "node_modules/built-in-math-eval": {
+            "version": "0.3.0",
+            "resolved": "https://registry.npmjs.org/built-in-math-eval/-/built-in-math-eval-0.3.0.tgz",
+            "integrity": "sha1-JA3CHLOJQ5WIxhxGDrAHZJfvxBw=",
+            "dependencies": {
+                "math-codegen": "^0.3.5"
+            }
+        },
+        "node_modules/builtin-modules": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+            "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+            "dev": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/bytes": {
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+            "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/cacache": {
+            "version": "10.0.4",
+            "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz",
+            "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==",
+            "dev": true,
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/cacache/node_modules/lru-cache": {
+            "version": "4.1.5",
+            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+            "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+            "dev": true,
+            "dependencies": {
+                "pseudomap": "^1.0.2",
+                "yallist": "^2.1.2"
+            }
+        },
+        "node_modules/cacache/node_modules/rimraf": {
+            "version": "2.7.1",
+            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+            "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+            "dev": true,
+            "dependencies": {
+                "glob": "^7.1.3"
+            },
+            "bin": {
+                "rimraf": "bin.js"
+            }
+        },
+        "node_modules/cacache/node_modules/yallist": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+            "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+            "dev": true
+        },
+        "node_modules/cache-base": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+            "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/cacheable-lookup": {
+            "version": "6.0.4",
+            "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.0.4.tgz",
+            "integrity": "sha512-mbcDEZCkv2CZF4G01kr8eBd/5agkt9oCqz75tJMSIsquvRZ2sL6Hi5zGVKi/0OSC9oO1GHfJ2AV0ZIOY9vye0A==",
+            "engines": {
+                "node": ">=10.6.0"
+            }
+        },
+        "node_modules/cacheable-request": {
+            "version": "7.0.2",
+            "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz",
+            "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==",
+            "dependencies": {
+                "clone-response": "^1.0.2",
+                "get-stream": "^5.1.0",
+                "http-cache-semantics": "^4.0.0",
+                "keyv": "^4.0.0",
+                "lowercase-keys": "^2.0.0",
+                "normalize-url": "^6.0.1",
+                "responselike": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/cacheable-request/node_modules/get-stream": {
+            "version": "5.2.0",
+            "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+            "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+            "dependencies": {
+                "pump": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/cacheable-request/node_modules/lowercase-keys": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+            "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/call-bind": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+            "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+            "dependencies": {
+                "function-bind": "^1.1.1",
+                "get-intrinsic": "^1.0.2"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/caller-callsite": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
+            "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
+            "dependencies": {
+                "callsites": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/caller-callsite/node_modules/callsites": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
+            "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/caller-path": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
+            "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
+            "dependencies": {
+                "caller-callsite": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/callsites": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+            "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/camel-case": {
+            "version": "4.1.2",
+            "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
+            "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
+            "dependencies": {
+                "pascal-case": "^3.1.2",
+                "tslib": "^2.0.3"
+            }
+        },
+        "node_modules/camel-case/node_modules/tslib": {
+            "version": "2.3.1",
+            "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+            "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+        },
+        "node_modules/camelcase": {
+            "version": "5.3.1",
+            "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+            "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/camelcase-keys": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
+            "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
+            "dependencies": {
+                "camelcase": "^2.0.0",
+                "map-obj": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/camelcase-keys/node_modules/camelcase": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
+            "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/camelize": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz",
+            "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
+        },
+        "node_modules/caniuse-lite": {
+            "version": "1.0.30001312",
+            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz",
+            "integrity": "sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ==",
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/browserslist"
+            }
+        },
+        "node_modules/canvas": {
+            "version": "2.9.0",
+            "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.9.0.tgz",
+            "integrity": "sha512-0l93g7uxp7rMyr7H+XRQ28A3ud0dKIUTIEkUe1Dxh4rjUYN7B93+SjC3r1PDKA18xcQN87OFGgUnyw7LSgNLSQ==",
+            "hasInstallScript": true,
+            "dependencies": {
+                "@mapbox/node-pre-gyp": "^1.0.0",
+                "nan": "^2.15.0",
+                "simple-get": "^3.0.3"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/canvas/node_modules/nan": {
+            "version": "2.15.0",
+            "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
+            "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ=="
+        },
+        "node_modules/capture-stack-trace": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz",
+            "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/caseless": {
+            "version": "0.12.0",
+            "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+            "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
+        },
+        "node_modules/center-align": {
+            "version": "0.1.3",
+            "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
+            "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
+            "dependencies": {
+                "align-text": "^0.1.3",
+                "lazy-cache": "^1.0.3"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/chai": {
+            "version": "4.3.6",
+            "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz",
+            "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==",
+            "dependencies": {
+                "assertion-error": "^1.1.0",
+                "check-error": "^1.0.2",
+                "deep-eql": "^3.0.1",
+                "get-func-name": "^2.0.0",
+                "loupe": "^2.3.1",
+                "pathval": "^1.1.1",
+                "type-detect": "^4.0.5"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/chain-function": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/chain-function/-/chain-function-1.0.1.tgz",
+            "integrity": "sha512-SxltgMwL9uCko5/ZCLiyG2B7R9fY4pDZUw7hJ4MhirdjBLosoDqkWABi3XMucddHdLiFJMb7PD2MZifZriuMTg=="
+        },
+        "node_modules/chalk": {
+            "version": "2.4.2",
+            "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+            "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+            "dependencies": {
+                "ansi-styles": "^3.2.1",
+                "escape-string-regexp": "^1.0.5",
+                "supports-color": "^5.3.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/change-emitter": {
+            "version": "0.1.6",
+            "resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz",
+            "integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU="
+        },
+        "node_modules/character-parser": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz",
+            "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=",
+            "dependencies": {
+                "is-regex": "^1.0.3"
+            }
+        },
+        "node_modules/check-error": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
+            "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/child_process": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz",
+            "integrity": "sha1-sffn/HPSXn/R1FWtyU4UODAYK1o="
+        },
+        "node_modules/chokidar": {
+            "version": "2.1.8",
+            "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+            "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+            "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies",
+            "dependencies": {
+                "anymatch": "^2.0.0",
+                "async-each": "^1.0.1",
+                "braces": "^2.3.2",
+                "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"
+            },
+            "optionalDependencies": {
+                "fsevents": "^1.2.7"
+            }
+        },
+        "node_modules/chokidar/node_modules/bindings": {
+            "version": "1.5.0",
+            "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+            "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+            "optional": true,
+            "dependencies": {
+                "file-uri-to-path": "1.0.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents": {
+            "version": "1.2.12",
+            "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz",
+            "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==",
+            "bundleDependencies": [
+                "node-pre-gyp"
+            ],
+            "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.",
+            "hasInstallScript": true,
+            "optional": true,
+            "os": [
+                "darwin"
+            ],
+            "dependencies": {
+                "bindings": "^1.5.0",
+                "nan": "^2.12.1",
+                "node-pre-gyp": "*"
+            },
+            "engines": {
+                "node": ">= 4.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/abbrev": {
+            "version": "1.1.1",
+            "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/ansi-regex": {
+            "version": "2.1.1",
+            "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+            "inBundle": true,
+            "optional": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/aproba": {
+            "version": "1.2.0",
+            "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/are-we-there-yet": {
+            "version": "1.1.5",
+            "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "delegates": "^1.0.0",
+                "readable-stream": "^2.0.6"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/balanced-match": {
+            "version": "1.0.0",
+            "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/brace-expansion": {
+            "version": "1.1.11",
+            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "balanced-match": "^1.0.0",
+                "concat-map": "0.0.1"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/chownr": {
+            "version": "1.1.4",
+            "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/code-point-at": {
+            "version": "1.1.0",
+            "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
+            "inBundle": true,
+            "optional": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/concat-map": {
+            "version": "0.0.1",
+            "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/console-control-strings": {
+            "version": "1.1.0",
+            "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/core-util-is": {
+            "version": "1.0.2",
+            "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/debug": {
+            "version": "3.2.6",
+            "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+            "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "ms": "^2.1.1"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/deep-extend": {
+            "version": "0.6.0",
+            "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+            "inBundle": true,
+            "optional": true,
+            "engines": {
+                "node": ">=4.0.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/delegates": {
+            "version": "1.0.0",
+            "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/detect-libc": {
+            "version": "1.0.3",
+            "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
+            "inBundle": true,
+            "optional": true,
+            "bin": {
+                "detect-libc": "bin/detect-libc.js"
+            },
+            "engines": {
+                "node": ">=0.10"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/fs-minipass": {
+            "version": "1.2.7",
+            "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "minipass": "^2.6.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/fs.realpath": {
+            "version": "1.0.0",
+            "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/gauge": {
+            "version": "2.7.4",
+            "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/glob": {
+            "version": "7.1.6",
+            "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": "*"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/has-unicode": {
+            "version": "2.0.1",
+            "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/iconv-lite": {
+            "version": "0.4.24",
+            "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "safer-buffer": ">= 2.1.2 < 3"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/ignore-walk": {
+            "version": "3.0.3",
+            "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "minimatch": "^3.0.4"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/inflight": {
+            "version": "1.0.6",
+            "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "once": "^1.3.0",
+                "wrappy": "1"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/inherits": {
+            "version": "2.0.4",
+            "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/ini": {
+            "version": "1.3.5",
+            "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+            "deprecated": "Please update to ini >=1.3.6 to avoid a prototype pollution issue",
+            "inBundle": true,
+            "optional": true,
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/is-fullwidth-code-point": {
+            "version": "1.0.0",
+            "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "number-is-nan": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/isarray": {
+            "version": "1.0.0",
+            "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/minimatch": {
+            "version": "3.0.4",
+            "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "brace-expansion": "^1.1.7"
+            },
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/minimist": {
+            "version": "1.2.5",
+            "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/minipass": {
+            "version": "2.9.0",
+            "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "safe-buffer": "^5.1.2",
+                "yallist": "^3.0.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/minizlib": {
+            "version": "1.3.3",
+            "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "minipass": "^2.9.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/mkdirp": {
+            "version": "0.5.3",
+            "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==",
+            "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "minimist": "^1.2.5"
+            },
+            "bin": {
+                "mkdirp": "bin/cmd.js"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/ms": {
+            "version": "2.1.2",
+            "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/needle": {
+            "version": "2.3.3",
+            "integrity": "sha512-EkY0GeSq87rWp1hoq/sH/wnTWgFVhYlnIkbJ0YJFfRgEFlz2RraCjBpFQ+vrEgEdp0ThfyHADmkChEhcb7PKyw==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "debug": "^3.2.6",
+                "iconv-lite": "^0.4.4",
+                "sax": "^1.2.4"
+            },
+            "bin": {
+                "needle": "bin/needle"
+            },
+            "engines": {
+                "node": ">= 4.4.x"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/node-pre-gyp": {
+            "version": "0.14.0",
+            "integrity": "sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==",
+            "deprecated": "Please upgrade to @mapbox/node-pre-gyp: the non-scoped node-pre-gyp package is deprecated and only the @mapbox scoped package will recieve updates in the future",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "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.4.2"
+            },
+            "bin": {
+                "node-pre-gyp": "bin/node-pre-gyp"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/nopt": {
+            "version": "4.0.3",
+            "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "abbrev": "1",
+                "osenv": "^0.1.4"
+            },
+            "bin": {
+                "nopt": "bin/nopt.js"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/npm-bundled": {
+            "version": "1.1.1",
+            "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "npm-normalize-package-bin": "^1.0.1"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/npm-normalize-package-bin": {
+            "version": "1.0.1",
+            "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/npm-packlist": {
+            "version": "1.4.8",
+            "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "ignore-walk": "^3.0.1",
+                "npm-bundled": "^1.0.1",
+                "npm-normalize-package-bin": "^1.0.1"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/npmlog": {
+            "version": "4.1.2",
+            "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "are-we-there-yet": "~1.1.2",
+                "console-control-strings": "~1.1.0",
+                "gauge": "~2.7.3",
+                "set-blocking": "~2.0.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/number-is-nan": {
+            "version": "1.0.1",
+            "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+            "inBundle": true,
+            "optional": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/object-assign": {
+            "version": "4.1.1",
+            "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+            "inBundle": true,
+            "optional": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/once": {
+            "version": "1.4.0",
+            "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "wrappy": "1"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/os-homedir": {
+            "version": "1.0.2",
+            "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+            "inBundle": true,
+            "optional": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/os-tmpdir": {
+            "version": "1.0.2",
+            "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+            "inBundle": true,
+            "optional": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/osenv": {
+            "version": "0.1.5",
+            "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "os-homedir": "^1.0.0",
+                "os-tmpdir": "^1.0.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/path-is-absolute": {
+            "version": "1.0.1",
+            "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+            "inBundle": true,
+            "optional": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/process-nextick-args": {
+            "version": "2.0.1",
+            "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/rc": {
+            "version": "1.2.8",
+            "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "deep-extend": "^0.6.0",
+                "ini": "~1.3.0",
+                "minimist": "^1.2.0",
+                "strip-json-comments": "~2.0.1"
+            },
+            "bin": {
+                "rc": "cli.js"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/readable-stream": {
+            "version": "2.3.7",
+            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/rimraf": {
+            "version": "2.7.1",
+            "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "glob": "^7.1.3"
+            },
+            "bin": {
+                "rimraf": "bin.js"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/safe-buffer": {
+            "version": "5.1.2",
+            "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/safer-buffer": {
+            "version": "2.1.2",
+            "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/sax": {
+            "version": "1.2.4",
+            "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/semver": {
+            "version": "5.7.1",
+            "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+            "inBundle": true,
+            "optional": true,
+            "bin": {
+                "semver": "bin/semver"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/set-blocking": {
+            "version": "2.0.0",
+            "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/signal-exit": {
+            "version": "3.0.2",
+            "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/string_decoder": {
+            "version": "1.1.1",
+            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "safe-buffer": "~5.1.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/string-width": {
+            "version": "1.0.2",
+            "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "code-point-at": "^1.0.0",
+                "is-fullwidth-code-point": "^1.0.0",
+                "strip-ansi": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/strip-ansi": {
+            "version": "3.0.1",
+            "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "ansi-regex": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/strip-json-comments": {
+            "version": "2.0.1",
+            "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+            "inBundle": true,
+            "optional": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/tar": {
+            "version": "4.4.13",
+            "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=4.5"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/util-deprecate": {
+            "version": "1.0.2",
+            "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/wide-align": {
+            "version": "1.1.3",
+            "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+            "inBundle": true,
+            "optional": true,
+            "dependencies": {
+                "string-width": "^1.0.2 || 2"
+            }
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/wrappy": {
+            "version": "1.0.2",
+            "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chokidar/node_modules/fsevents/node_modules/yallist": {
+            "version": "3.1.1",
+            "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+            "inBundle": true,
+            "optional": true
+        },
+        "node_modules/chownr": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+            "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
+        },
+        "node_modules/chrome": {
+            "version": "0.1.0",
+            "resolved": "https://registry.npmjs.org/chrome/-/chrome-0.1.0.tgz",
+            "integrity": "sha1-9h2beS/v6MGUxwVt3BAscmqGQyk=",
+            "dependencies": {
+                "exeq": "^2.2.0",
+                "plist": "^1.1.0"
+            },
+            "bin": {
+                "chrome": "index.js"
+            }
+        },
+        "node_modules/chrome-trace-event": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
+            "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
+            "engines": {
+                "node": ">=6.0"
+            }
+        },
+        "node_modules/ci-info": {
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz",
+            "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A=="
+        },
+        "node_modules/clamp": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz",
+            "integrity": "sha1-ZqDmQBGBbjcZaCj9yMjBRzEshjQ="
+        },
+        "node_modules/class-transformer": {
+            "version": "0.2.3",
+            "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.2.3.tgz",
+            "integrity": "sha512-qsP+0xoavpOlJHuYsQJsN58HXSl8Jvveo+T37rEvCEeRfMWoytAyR0Ua/YsFgpM6AZYZ/og2PJwArwzJl1aXtQ=="
+        },
+        "node_modules/class-utils": {
+            "version": "0.3.6",
+            "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+            "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+            "dependencies": {
+                "arr-union": "^3.1.0",
+                "define-property": "^0.2.5",
+                "isobject": "^3.0.0",
+                "static-extend": "^0.1.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/class-utils/node_modules/define-property": {
+            "version": "0.2.5",
+            "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+            "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+            "dependencies": {
+                "is-descriptor": "^0.1.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/classnames": {
+            "version": "2.2.6",
+            "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
+            "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
+        },
+        "node_modules/clean-css": {
+            "version": "4.2.3",
+            "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
+            "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==",
+            "dependencies": {
+                "source-map": "~0.6.0"
+            },
+            "engines": {
+                "node": ">= 4.0"
+            }
+        },
+        "node_modules/clean-css/node_modules/source-map": {
+            "version": "0.6.1",
+            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+            "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/cli-boxes": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
+            "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/cliss": {
+            "version": "0.0.2",
+            "resolved": "https://registry.npmjs.org/cliss/-/cliss-0.0.2.tgz",
+            "integrity": "sha512-6rj9pgdukjT994Md13JCUAgTk91abAKrygL9sAvmHY4F6AKMOV8ccGaxhUUfcBuyg3sundWnn3JE0Mc9W6ZYqw==",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/cliss/node_modules/camelcase": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+            "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/cliss/node_modules/yargs-parser": {
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz",
+            "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=",
+            "dependencies": {
+                "camelcase": "^4.1.0"
+            }
+        },
+        "node_modules/cliui": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+            "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+            "dependencies": {
+                "string-width": "^3.1.0",
+                "strip-ansi": "^5.2.0",
+                "wrap-ansi": "^5.1.0"
+            }
+        },
+        "node_modules/cliui/node_modules/ansi-regex": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+            "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/cliui/node_modules/string-width": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+            "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+            "dependencies": {
+                "emoji-regex": "^7.0.1",
+                "is-fullwidth-code-point": "^2.0.0",
+                "strip-ansi": "^5.1.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/cliui/node_modules/strip-ansi": {
+            "version": "5.2.0",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+            "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+            "dependencies": {
+                "ansi-regex": "^4.1.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/clj-fuzzy": {
+            "version": "0.3.3",
+            "resolved": "https://registry.npmjs.org/clj-fuzzy/-/clj-fuzzy-0.3.3.tgz",
+            "integrity": "sha1-seU0MJHFIC28UlMoY+HEp4RX8D0=",
+            "deprecated": "use the `talisman` library instead"
+        },
+        "node_modules/clone-deep": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
+            "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
+            "dependencies": {
+                "is-plain-object": "^2.0.4",
+                "kind-of": "^6.0.2",
+                "shallow-clone": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/clone-response": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
+            "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
+            "dependencies": {
+                "mimic-response": "^1.0.0"
+            }
+        },
+        "node_modules/clone-response/node_modules/mimic-response": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
+            "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/clsx": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
+            "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/code-point-at": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+            "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/collection-visit": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+            "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+            "dependencies": {
+                "map-visit": "^1.0.0",
+                "object-visit": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/color": {
+            "version": "3.2.1",
+            "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
+            "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
+            "dependencies": {
+                "color-convert": "^1.9.3",
+                "color-string": "^1.6.0"
+            }
+        },
+        "node_modules/color-convert": {
+            "version": "1.9.3",
+            "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+            "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+            "dependencies": {
+                "color-name": "1.1.3"
+            }
+        },
+        "node_modules/color-name": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+            "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
+        },
+        "node_modules/color-string": {
+            "version": "1.9.0",
+            "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz",
+            "integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==",
+            "dependencies": {
+                "color-name": "^1.0.0",
+                "simple-swizzle": "^0.2.2"
+            }
+        },
+        "node_modules/color-support": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+            "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
+            "bin": {
+                "color-support": "bin.js"
+            }
+        },
+        "node_modules/colorette": {
+            "version": "2.0.16",
+            "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz",
+            "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g=="
+        },
+        "node_modules/colors": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
+            "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
+            "engines": {
+                "node": ">=0.1.90"
+            }
+        },
+        "node_modules/combined-stream": {
+            "version": "1.0.8",
+            "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+            "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+            "dependencies": {
+                "delayed-stream": "~1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/command-exists": {
+            "version": "1.2.6",
+            "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.6.tgz",
+            "integrity": "sha512-Qst/zUUNmS/z3WziPxyqjrcz09pm+2Knbs5mAZL4VAE0sSrNY1/w8+/YxeHcoBTsO6iojA6BW7eFf27Eg2MRuw=="
+        },
+        "node_modules/command-line-usage": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-4.1.0.tgz",
+            "integrity": "sha512-MxS8Ad995KpdAC0Jopo/ovGIroV/m0KHwzKfXxKag6FHOkGsH8/lv5yjgablcRxCJJC0oJeUMuO/gmaq+Wq46g==",
+            "dependencies": {
+                "ansi-escape-sequences": "^4.0.0",
+                "array-back": "^2.0.0",
+                "table-layout": "^0.4.2",
+                "typical": "^2.6.1"
+            },
+            "engines": {
+                "node": ">=4.0.0"
+            }
+        },
+        "node_modules/commander": {
+            "version": "2.20.3",
+            "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+            "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+        },
+        "node_modules/commondir": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+            "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
+            "dev": true
+        },
+        "node_modules/component-bind": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
+            "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
+        },
+        "node_modules/component-emitter": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+            "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
+        },
+        "node_modules/component-inherit": {
+            "version": "0.0.3",
+            "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
+            "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
+        },
+        "node_modules/compress-commons": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-2.1.1.tgz",
+            "integrity": "sha512-eVw6n7CnEMFzc3duyFVrQEuY1BlHR3rYsSztyG32ibGMW722i3C6IizEGMFmfMU+A+fALvBIwxN3czffTcdA+Q==",
+            "dependencies": {
+                "buffer-crc32": "^0.2.13",
+                "crc32-stream": "^3.0.1",
+                "normalize-path": "^3.0.0",
+                "readable-stream": "^2.3.6"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/compress-commons/node_modules/readable-stream": {
+            "version": "2.3.7",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/compressible": {
+            "version": "2.0.18",
+            "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+            "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+            "dev": true,
+            "dependencies": {
+                "mime-db": ">= 1.43.0 < 2"
+            },
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/compression": {
+            "version": "1.7.4",
+            "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
+            "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
+            "dev": true,
+            "dependencies": {
+                "accepts": "~1.3.5",
+                "bytes": "3.0.0",
+                "compressible": "~2.0.16",
+                "debug": "2.6.9",
+                "on-headers": "~1.0.2",
+                "safe-buffer": "5.1.2",
+                "vary": "~1.1.2"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/compression/node_modules/bytes": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+            "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
+            "dev": true,
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/compression/node_modules/debug": {
+            "version": "2.6.9",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+            "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+            "dev": true,
+            "dependencies": {
+                "ms": "2.0.0"
+            }
+        },
+        "node_modules/compression/node_modules/ms": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+            "dev": true
+        },
+        "node_modules/concat-map": {
+            "version": "0.0.1",
+            "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+            "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+        },
+        "node_modules/concat-stream": {
+            "version": "1.6.2",
+            "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+            "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+            "engines": [
+                "node >= 0.8"
+            ],
+            "dependencies": {
+                "buffer-from": "^1.0.0",
+                "inherits": "^2.0.3",
+                "readable-stream": "^2.2.2",
+                "typedarray": "^0.0.6"
+            }
+        },
+        "node_modules/concat-stream/node_modules/readable-stream": {
+            "version": "2.3.7",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/configstore": {
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz",
+            "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/connect-flash": {
+            "version": "0.1.1",
+            "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz",
+            "integrity": "sha1-2GMPJtlaf4UfmVax6MxnMvO2qjA=",
+            "engines": {
+                "node": ">= 0.4.0"
+            }
+        },
+        "node_modules/connect-history-api-fallback": {
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz",
+            "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.8"
+            }
+        },
+        "node_modules/connect-mongo": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/connect-mongo/-/connect-mongo-2.0.3.tgz",
+            "integrity": "sha512-Vs+QZ/6X6gbCrP1Ls7Oh/wlyY6pgpbPSrUKF5yRT+zd+4GZPNbjNquxquZ+Clv2+03HBXE7T4lVM0PUcaBhihg==",
+            "dependencies": {
+                "mongodb": "^2.0.36"
+            }
+        },
+        "node_modules/connect-mongo/node_modules/mongodb": {
+            "version": "2.2.36",
+            "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.36.tgz",
+            "integrity": "sha512-P2SBLQ8Z0PVx71ngoXwo12+FiSfbNfGOClAao03/bant5DgLNkOPAck5IaJcEk4gKlQhDEURzfR3xuBG1/B+IA==",
+            "dependencies": {
+                "es6-promise": "3.2.1",
+                "mongodb-core": "2.1.20",
+                "readable-stream": "2.2.7"
+            },
+            "engines": {
+                "node": ">=0.10.3"
+            }
+        },
+        "node_modules/connect-mongo/node_modules/process-nextick-args": {
+            "version": "1.0.7",
+            "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
+            "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
+        },
+        "node_modules/connect-mongo/node_modules/readable-stream": {
+            "version": "2.2.7",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz",
+            "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/connect-mongo/node_modules/string_decoder": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+            "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+            "dependencies": {
+                "safe-buffer": "~5.1.0"
+            }
+        },
+        "node_modules/console-control-strings": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+            "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
+        },
+        "node_modules/constantinople": {
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.1.2.tgz",
+            "integrity": "sha512-yePcBqEFhLOqSBtwYOGGS1exHo/s1xjekXiinh4itpNQGCu4KA1euPh1fg07N2wMITZXQkBz75Ntdt1ctGZouw==",
+            "dependencies": {
+                "@types/babel-types": "^7.0.0",
+                "@types/babylon": "^6.16.2",
+                "babel-types": "^6.26.0",
+                "babylon": "^6.18.0"
+            }
+        },
+        "node_modules/content-disposition": {
+            "version": "0.5.4",
+            "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+            "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+            "dependencies": {
+                "safe-buffer": "5.2.1"
+            },
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/content-disposition/node_modules/safe-buffer": {
+            "version": "5.2.1",
+            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+            "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/feross"
+                },
+                {
+                    "type": "patreon",
+                    "url": "https://www.patreon.com/feross"
+                },
+                {
+                    "type": "consulting",
+                    "url": "https://feross.org/support"
+                }
+            ]
+        },
+        "node_modules/content-type": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+            "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/convert-source-map": {
+            "version": "1.8.0",
+            "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz",
+            "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==",
+            "dependencies": {
+                "safe-buffer": "~5.1.1"
+            }
+        },
+        "node_modules/cookie": {
+            "version": "0.4.1",
+            "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
+            "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/cookie-parser": {
+            "version": "1.4.6",
+            "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
+            "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
+            "dependencies": {
+                "cookie": "0.4.1",
+                "cookie-signature": "1.0.6"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/cookie-session": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/cookie-session/-/cookie-session-2.0.0.tgz",
+            "integrity": "sha512-hKvgoThbw00zQOleSlUr2qpvuNweoqBtxrmx0UFosx6AGi9lYtLoA+RbsvknrEX8Pr6MDbdWAb2j6SnMn+lPsg==",
+            "dependencies": {
+                "cookies": "0.8.0",
+                "debug": "3.2.7",
+                "on-headers": "~1.0.2",
+                "safe-buffer": "5.2.1"
+            },
+            "engines": {
+                "node": ">= 0.10"
+            }
+        },
+        "node_modules/cookie-session/node_modules/debug": {
+            "version": "3.2.7",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+            "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+            "dependencies": {
+                "ms": "^2.1.1"
+            }
+        },
+        "node_modules/cookie-session/node_modules/safe-buffer": {
+            "version": "5.2.1",
+            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+            "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/feross"
+                },
+                {
+                    "type": "patreon",
+                    "url": "https://www.patreon.com/feross"
+                },
+                {
+                    "type": "consulting",
+                    "url": "https://feross.org/support"
+                }
+            ]
+        },
+        "node_modules/cookie-signature": {
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+            "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+        },
+        "node_modules/cookies": {
+            "version": "0.8.0",
+            "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.8.0.tgz",
+            "integrity": "sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow==",
+            "dependencies": {
+                "depd": "~2.0.0",
+                "keygrip": "~1.1.0"
+            },
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/cookies/node_modules/depd": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+            "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/copy-concurrently": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
+            "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
+            "dev": true,
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/copy-concurrently/node_modules/rimraf": {
+            "version": "2.7.1",
+            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+            "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+            "dev": true,
+            "dependencies": {
+                "glob": "^7.1.3"
+            },
+            "bin": {
+                "rimraf": "bin.js"
+            }
+        },
+        "node_modules/copy-descriptor": {
+            "version": "0.1.1",
+            "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+            "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/copy-webpack-plugin": {
+            "version": "4.6.0",
+            "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz",
+            "integrity": "sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==",
+            "dev": true,
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">= 4"
+            }
+        },
+        "node_modules/copy-webpack-plugin/node_modules/p-limit": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+            "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+            "dev": true,
+            "dependencies": {
+                "p-try": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/copy-webpack-plugin/node_modules/p-try": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+            "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+            "dev": true,
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/core-js": {
+            "version": "1.2.7",
+            "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
+            "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=",
+            "deprecated": "core-js@<3.4 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js."
+        },
+        "node_modules/core-js-pure": {
+            "version": "3.21.1",
+            "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz",
+            "integrity": "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==",
+            "hasInstallScript": true,
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/core-js"
+            }
+        },
+        "node_modules/core-util-is": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+            "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+        },
+        "node_modules/cors": {
+            "version": "2.8.5",
+            "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+            "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+            "dependencies": {
+                "object-assign": "^4",
+                "vary": "^1"
+            },
+            "engines": {
+                "node": ">= 0.10"
+            }
+        },
+        "node_modules/cosmiconfig": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
+            "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
+            "dependencies": {
+                "@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"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/crc": {
+            "version": "3.8.0",
+            "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz",
+            "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==",
+            "dependencies": {
+                "buffer": "^5.1.0"
+            }
+        },
+        "node_modules/crc32-stream": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-3.0.1.tgz",
+            "integrity": "sha512-mctvpXlbzsvK+6z8kJwSJ5crm7yBwrQMTybJzMw1O4lLGJqjlDCXY2Zw7KheiA6XBEcBmfLx1D88mjRGVJtY9w==",
+            "dependencies": {
+                "crc": "^3.4.4",
+                "readable-stream": "^3.4.0"
+            },
+            "engines": {
+                "node": ">= 6.9.0"
+            }
+        },
+        "node_modules/create-emotion": {
+            "version": "10.0.27",
+            "resolved": "https://registry.npmjs.org/create-emotion/-/create-emotion-10.0.27.tgz",
+            "integrity": "sha512-fIK73w82HPPn/RsAij7+Zt8eCE8SptcJ3WoRMfxMtjteYxud8GDTKKld7MYwAX2TVhrw29uR1N/bVGxeStHILg==",
+            "dependencies": {
+                "@emotion/cache": "^10.0.27",
+                "@emotion/serialize": "^0.11.15",
+                "@emotion/sheet": "0.9.4",
+                "@emotion/utils": "0.11.3"
+            }
+        },
+        "node_modules/create-error-class": {
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
+            "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
+            "dependencies": {
+                "capture-stack-trace": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/create-react-context": {
+            "version": "0.2.3",
+            "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.2.3.tgz",
+            "integrity": "sha512-CQBmD0+QGgTaxDL3OX1IDXYqjkp2It4RIbcb99jS6AEg27Ga+a9G3JtK6SIu0HBwPLZlmwt9F7UwWA4Bn92Rag==",
+            "dependencies": {
+                "fbjs": "^0.8.0",
+                "gud": "^1.0.0"
+            },
+            "peerDependencies": {
+                "prop-types": "^15.0.0",
+                "react": "^0.14.0 || ^15.0.0 || ^16.0.0"
+            }
+        },
+        "node_modules/create-require": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+            "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+            "dev": true
+        },
+        "node_modules/cross-env": {
+            "version": "5.2.1",
+            "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.1.tgz",
+            "integrity": "sha512-1yHhtcfAd1r4nwQgknowuUNfIT9E8dOMMspC36g45dN+iD1blloi7xp8X/xAIDnjHWyt1uQ8PHk2fkNaym7soQ==",
+            "dev": true,
+            "dependencies": {
+                "cross-spawn": "^6.0.5"
+            },
+            "bin": {
+                "cross-env": "dist/bin/cross-env.js",
+                "cross-env-shell": "dist/bin/cross-env-shell.js"
+            },
+            "engines": {
+                "node": ">=4.0"
+            }
+        },
+        "node_modules/cross-env/node_modules/cross-spawn": {
+            "version": "6.0.5",
+            "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+            "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+            "dev": true,
+            "dependencies": {
+                "nice-try": "^1.0.4",
+                "path-key": "^2.0.1",
+                "semver": "^5.5.0",
+                "shebang-command": "^1.2.0",
+                "which": "^1.2.9"
+            },
+            "engines": {
+                "node": ">=4.8"
+            }
+        },
+        "node_modules/cross-fetch": {
+            "version": "3.1.5",
+            "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
+            "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
+            "dependencies": {
+                "node-fetch": "2.6.7"
+            }
+        },
+        "node_modules/cross-fetch/node_modules/node-fetch": {
+            "version": "2.6.7",
+            "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+            "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+            "dependencies": {
+                "whatwg-url": "^5.0.0"
+            },
+            "engines": {
+                "node": "4.x || >=6.0.0"
+            },
+            "peerDependencies": {
+                "encoding": "^0.1.0"
+            },
+            "peerDependenciesMeta": {
+                "encoding": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/cross-fetch/node_modules/tr46": {
+            "version": "0.0.3",
+            "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+            "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
+        },
+        "node_modules/cross-fetch/node_modules/webidl-conversions": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+            "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
+        },
+        "node_modules/cross-fetch/node_modules/whatwg-url": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+            "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
+            "dependencies": {
+                "tr46": "~0.0.3",
+                "webidl-conversions": "^3.0.0"
+            }
+        },
+        "node_modules/cross-spawn": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz",
+            "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=",
+            "dependencies": {
+                "lru-cache": "^4.0.1",
+                "which": "^1.2.9"
+            }
+        },
+        "node_modules/cross-spawn/node_modules/lru-cache": {
+            "version": "4.1.5",
+            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+            "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+            "dependencies": {
+                "pseudomap": "^1.0.2",
+                "yallist": "^2.1.2"
+            }
+        },
+        "node_modules/cross-spawn/node_modules/yallist": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+            "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
+        },
+        "node_modules/crypto-random-string": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
+            "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/css-box-model": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
+            "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==",
+            "dependencies": {
+                "tiny-invariant": "^1.0.6"
+            }
+        },
+        "node_modules/css-color-keywords": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
+            "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/css-in-js-utils": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz",
+            "integrity": "sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==",
+            "dependencies": {
+                "hyphenate-style-name": "^1.0.2",
+                "isobject": "^3.0.1"
+            }
+        },
+        "node_modules/css-loader": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-2.1.1.tgz",
+            "integrity": "sha512-OcKJU/lt232vl1P9EEDamhoO9iKY3tIjY5GU+XDLblAykTdgs6Ux9P1hTHve8nFKy5KPpOXOsVI/hIwi3841+w==",
+            "dev": true,
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">= 6.9.0"
+            },
+            "peerDependencies": {
+                "webpack": "^4.0.0"
+            }
+        },
+        "node_modules/css-loader/node_modules/schema-utils": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+            "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+            "dev": true,
+            "dependencies": {
+                "ajv": "^6.1.0",
+                "ajv-errors": "^1.0.0",
+                "ajv-keywords": "^3.1.0"
+            },
+            "engines": {
+                "node": ">= 4"
+            }
+        },
+        "node_modules/css-select": {
+            "version": "4.2.1",
+            "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz",
+            "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==",
+            "dependencies": {
+                "boolbase": "^1.0.0",
+                "css-what": "^5.1.0",
+                "domhandler": "^4.3.0",
+                "domutils": "^2.8.0",
+                "nth-check": "^2.0.1"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/fb55"
+            }
+        },
+        "node_modules/css-select/node_modules/dom-serializer": {
+            "version": "1.3.2",
+            "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
+            "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
+            "dependencies": {
+                "domelementtype": "^2.0.1",
+                "domhandler": "^4.2.0",
+                "entities": "^2.0.0"
+            },
+            "funding": {
+                "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+            }
+        },
+        "node_modules/css-select/node_modules/domelementtype": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
+            "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/fb55"
+                }
+            ]
+        },
+        "node_modules/css-select/node_modules/domhandler": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz",
+            "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==",
+            "dependencies": {
+                "domelementtype": "^2.2.0"
+            },
+            "engines": {
+                "node": ">= 4"
+            },
+            "funding": {
+                "url": "https://github.com/fb55/domhandler?sponsor=1"
+            }
+        },
+        "node_modules/css-select/node_modules/domutils": {
+            "version": "2.8.0",
+            "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+            "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+            "dependencies": {
+                "dom-serializer": "^1.0.1",
+                "domelementtype": "^2.2.0",
+                "domhandler": "^4.2.0"
+            },
+            "funding": {
+                "url": "https://github.com/fb55/domutils?sponsor=1"
+            }
+        },
+        "node_modules/css-select/node_modules/entities": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+            "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+            "funding": {
+                "url": "https://github.com/fb55/entities?sponsor=1"
+            }
+        },
+        "node_modules/css-to-react-native": {
+            "version": "2.3.2",
+            "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.3.2.tgz",
+            "integrity": "sha512-VOFaeZA053BqvvvqIA8c9n0+9vFppVBAHCp6JgFTtTMU3Mzi+XnelJ9XC9ul3BqFzZyQ5N+H0SnwsWT2Ebchxw==",
+            "dependencies": {
+                "camelize": "^1.0.0",
+                "css-color-keywords": "^1.0.0",
+                "postcss-value-parser": "^3.3.0"
+            }
+        },
+        "node_modules/css-vendor": {
+            "version": "2.0.8",
+            "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz",
+            "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==",
+            "dependencies": {
+                "@babel/runtime": "^7.8.3",
+                "is-in-browser": "^1.0.2"
+            }
+        },
+        "node_modules/css-what": {
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
+            "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==",
+            "engines": {
+                "node": ">= 6"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/fb55"
+            }
+        },
+        "node_modules/cssesc": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+            "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+            "dev": true,
+            "bin": {
+                "cssesc": "bin/cssesc"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/cssom": {
+            "version": "0.4.4",
+            "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
+            "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==",
+            "dev": true
+        },
+        "node_modules/cssstyle": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.2.0.tgz",
+            "integrity": "sha512-sEb3XFPx3jNnCAMtqrXPDeSgQr+jojtCeNf8cvMNMh1cG970+lljssvQDzPq6lmmJu2Vhqood/gtEomBiHOGnA==",
+            "dev": true,
+            "dependencies": {
+                "cssom": "~0.3.6"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/cssstyle/node_modules/cssom": {
+            "version": "0.3.8",
+            "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
+            "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
+            "dev": true
+        },
+        "node_modules/csstype": {
+            "version": "2.6.19",
+            "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.19.tgz",
+            "integrity": "sha512-ZVxXaNy28/k3kJg0Fou5MiYpp88j7H9hLZp8PDC3jV0WFjfH5E9xHb56L0W59cPbKbcHXeP4qyT8PrHp8t6LcQ=="
+        },
+        "node_modules/currently-unhandled": {
+            "version": "0.4.1",
+            "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
+            "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
+            "dependencies": {
+                "array-find-index": "^1.0.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/custom-event": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
+            "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU="
+        },
+        "node_modules/cyclist": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
+            "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
+            "dev": true
+        },
+        "node_modules/D": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/D/-/D-1.0.0.tgz",
+            "integrity": "sha512-nQvrCBu7K2pSSEtIM0EEF03FVjcczCXInMt3moLNFbjlWx6bZrX72uT6/1uAXDbnzGUAx9gTyDiQ+vrFi663oA==",
+            "deprecated": "Package no longer supported. Contact support@npmjs.com for more info."
+        },
+        "node_modules/d3-array": {
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
+            "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
+        },
+        "node_modules/d3-axis": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-2.1.0.tgz",
+            "integrity": "sha512-z/G2TQMyuf0X3qP+Mh+2PimoJD41VOCjViJzT0BHeL/+JQAofkiWZbWxlwFGb1N8EN+Cl/CW+MUKbVzr1689Cw=="
+        },
+        "node_modules/d3-color": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz",
+            "integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ=="
+        },
+        "node_modules/d3-dispatch": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-2.0.0.tgz",
+            "integrity": "sha512-S/m2VsXI7gAti2pBoLClFFTMOO1HTtT0j99AuXLoGFKO6deHDdnv6ZGTxSTTUTgO1zVcv82fCOtDjYK4EECmWA=="
+        },
+        "node_modules/d3-drag": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-2.0.0.tgz",
+            "integrity": "sha512-g9y9WbMnF5uqB9qKqwIIa/921RYWzlUDv9Jl1/yONQwxbOfszAWTCm8u7HOTgJgRDXiRZN56cHT9pd24dmXs8w==",
+            "dependencies": {
+                "d3-dispatch": "1 - 2",
+                "d3-selection": "2"
+            }
+        },
+        "node_modules/d3-ease": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-2.0.0.tgz",
+            "integrity": "sha512-68/n9JWarxXkOWMshcT5IcjbB+agblQUaIsbnXmrzejn2O82n3p2A9R2zEB9HIEFWKFwPAEDDN8gR0VdSAyyAQ=="
+        },
+        "node_modules/d3-format": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz",
+            "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA=="
+        },
+        "node_modules/d3-interpolate": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-2.0.1.tgz",
+            "integrity": "sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==",
+            "dependencies": {
+                "d3-color": "1 - 2"
+            }
+        },
+        "node_modules/d3-path": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-2.0.0.tgz",
+            "integrity": "sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA=="
+        },
+        "node_modules/d3-scale": {
+            "version": "3.3.0",
+            "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz",
+            "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==",
+            "dependencies": {
+                "d3-array": "^2.3.0",
+                "d3-format": "1 - 2",
+                "d3-interpolate": "1.2.0 - 2",
+                "d3-time": "^2.1.1",
+                "d3-time-format": "2 - 3"
+            }
+        },
+        "node_modules/d3-scale/node_modules/d3-array": {
+            "version": "2.12.1",
+            "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
+            "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
+            "dependencies": {
+                "internmap": "^1.0.0"
+            }
+        },
+        "node_modules/d3-selection": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-2.0.0.tgz",
+            "integrity": "sha512-XoGGqhLUN/W14NmaqcO/bb1nqjDAw5WtSYb2X8wiuQWvSZUsUVYsOSkOybUrNvcBjaywBdYPy03eXHMXjk9nZA=="
+        },
+        "node_modules/d3-shape": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-2.1.0.tgz",
+            "integrity": "sha512-PnjUqfM2PpskbSLTJvAzp2Wv4CZsnAgTfcVRTwW03QR3MkXF8Uo7B1y/lWkAsmbKwuecto++4NlsYcvYpXpTHA==",
+            "dependencies": {
+                "d3-path": "1 - 2"
+            }
+        },
+        "node_modules/d3-time": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz",
+            "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==",
+            "dependencies": {
+                "d3-array": "2"
+            }
+        },
+        "node_modules/d3-time-format": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz",
+            "integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==",
+            "dependencies": {
+                "d3-time": "1 - 2"
+            }
+        },
+        "node_modules/d3-time/node_modules/d3-array": {
+            "version": "2.12.1",
+            "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
+            "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
+            "dependencies": {
+                "internmap": "^1.0.0"
+            }
+        },
+        "node_modules/d3-timer": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-2.0.0.tgz",
+            "integrity": "sha512-TO4VLh0/420Y/9dO3+f9abDEFYeCUr2WZRlxJvbp4HPTQcSylXNiL6yZa9FIUvV1yRiFufl1bszTCLDqv9PWNA=="
+        },
+        "node_modules/d3-transition": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-2.0.0.tgz",
+            "integrity": "sha512-42ltAGgJesfQE3u9LuuBHNbGrI/AJjNL2OAUdclE70UE6Vy239GCBEYD38uBPoLeNsOhFStGpPI0BAOV+HMxog==",
+            "dependencies": {
+                "d3-color": "1 - 2",
+                "d3-dispatch": "1 - 2",
+                "d3-ease": "1 - 2",
+                "d3-interpolate": "1 - 2",
+                "d3-timer": "1 - 2"
+            },
+            "peerDependencies": {
+                "d3-selection": "2"
+            }
+        },
+        "node_modules/d3-zoom": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-2.0.0.tgz",
+            "integrity": "sha512-fFg7aoaEm9/jf+qfstak0IYpnesZLiMX6GZvXtUSdv8RH2o4E2qeelgdU09eKS6wGuiGMfcnMI0nTIqWzRHGpw==",
+            "dependencies": {
+                "d3-dispatch": "1 - 2",
+                "d3-drag": "2",
+                "d3-interpolate": "1 - 2",
+                "d3-selection": "2",
+                "d3-transition": "2"
+            }
+        },
+        "node_modules/dashdash": {
+            "version": "1.14.1",
+            "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+            "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+            "dependencies": {
+                "assert-plus": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10"
+            }
+        },
+        "node_modules/data-urls": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz",
+            "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==",
+            "dev": true,
+            "dependencies": {
+                "abab": "^2.0.0",
+                "whatwg-mimetype": "^2.2.0",
+                "whatwg-url": "^7.0.0"
+            }
+        },
+        "node_modules/date-fns": {
+            "version": "2.28.0",
+            "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz",
+            "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==",
+            "engines": {
+                "node": ">=0.11"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/date-fns"
+            }
+        },
+        "node_modules/de-indent": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
+            "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0="
+        },
+        "node_modules/debounce": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
+            "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
+        },
+        "node_modules/debug": {
+            "version": "3.2.6",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+            "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+            "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
+            "dependencies": {
+                "ms": "^2.1.1"
+            }
+        },
+        "node_modules/decamelize": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+            "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/decode-uri-component": {
+            "version": "0.2.0",
+            "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+            "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
+            "engines": {
+                "node": ">=0.10"
+            }
+        },
+        "node_modules/decompress-response": {
+            "version": "4.2.1",
+            "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
+            "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
+            "dependencies": {
+                "mimic-response": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/deep-eql": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
+            "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
+            "dependencies": {
+                "type-detect": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=0.12"
+            }
+        },
+        "node_modules/deep-equal": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
+            "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
+            "dependencies": {
+                "is-arguments": "^1.0.4",
+                "is-date-object": "^1.0.1",
+                "is-regex": "^1.0.4",
+                "object-is": "^1.0.1",
+                "object-keys": "^1.1.1",
+                "regexp.prototype.flags": "^1.2.0"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/deep-extend": {
+            "version": "0.6.0",
+            "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+            "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+            "engines": {
+                "node": ">=4.0.0"
+            }
+        },
+        "node_modules/deep-is": {
+            "version": "0.1.3",
+            "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+            "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+            "dev": true
+        },
+        "node_modules/deepmerge": {
+            "version": "2.2.1",
+            "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
+            "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/default-gateway": {
+            "version": "4.2.0",
+            "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz",
+            "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==",
+            "dev": true,
+            "dependencies": {
+                "execa": "^1.0.0",
+                "ip-regex": "^2.1.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/default-gateway/node_modules/cross-spawn": {
+            "version": "6.0.5",
+            "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+            "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+            "dev": true,
+            "dependencies": {
+                "nice-try": "^1.0.4",
+                "path-key": "^2.0.1",
+                "semver": "^5.5.0",
+                "shebang-command": "^1.2.0",
+                "which": "^1.2.9"
+            },
+            "engines": {
+                "node": ">=4.8"
+            }
+        },
+        "node_modules/default-gateway/node_modules/execa": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
+            "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
+            "dev": true,
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/default-gateway/node_modules/get-stream": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+            "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+            "dev": true,
+            "dependencies": {
+                "pump": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/defer-to-connect": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
+            "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
+            "engines": {
+                "node": ">=10"
+            }
+        },
+        "node_modules/define-properties": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+            "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+            "dependencies": {
+                "object-keys": "^1.0.12"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/define-property": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+            "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+            "dependencies": {
+                "is-descriptor": "^1.0.2",
+                "isobject": "^3.0.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/define-property/node_modules/is-accessor-descriptor": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+            "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+            "dependencies": {
+                "kind-of": "^6.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/define-property/node_modules/is-data-descriptor": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+            "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+            "dependencies": {
+                "kind-of": "^6.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/define-property/node_modules/is-descriptor": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+            "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+            "dependencies": {
+                "is-accessor-descriptor": "^1.0.0",
+                "is-data-descriptor": "^1.0.0",
+                "kind-of": "^6.0.2"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/del": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz",
+            "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==",
+            "dev": true,
+            "dependencies": {
+                "@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"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/del/node_modules/globby": {
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+            "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
+            "dev": true,
+            "dependencies": {
+                "array-union": "^1.0.1",
+                "glob": "^7.0.3",
+                "object-assign": "^4.0.1",
+                "pify": "^2.0.0",
+                "pinkie-promise": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/del/node_modules/globby/node_modules/pify": {
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+            "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+            "dev": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/del/node_modules/rimraf": {
+            "version": "2.7.1",
+            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+            "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+            "dev": true,
+            "dependencies": {
+                "glob": "^7.1.3"
+            },
+            "bin": {
+                "rimraf": "bin.js"
+            }
+        },
+        "node_modules/delayed-stream": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+            "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+            "engines": {
+                "node": ">=0.4.0"
+            }
+        },
+        "node_modules/delegates": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+            "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
+        },
+        "node_modules/denque": {
+            "version": "1.5.1",
+            "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz",
+            "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==",
+            "engines": {
+                "node": ">=0.10"
+            }
+        },
+        "node_modules/density-clustering": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/density-clustering/-/density-clustering-1.3.0.tgz",
+            "integrity": "sha1-3J9ZyPCrl+FiSsZJMP0xlIF9ysU="
+        },
+        "node_modules/depcheck": {
+            "version": "0.9.2",
+            "resolved": "https://registry.npmjs.org/depcheck/-/depcheck-0.9.2.tgz",
+            "integrity": "sha512-w5f+lSZqLJJkk58s44eOd0Vor7hLZot4PlFL0y2JsIX5LuHQ2eAjHlDVeGBD4Mj6ZQSKakvKWRRCcPlvrdU2Sg==",
+            "dependencies": {
+                "@babel/parser": "^7.7.7",
+                "@babel/traverse": "^7.7.4",
+                "builtin-modules": "^3.0.0",
+                "camelcase": "^5.3.1",
+                "cosmiconfig": "^5.2.1",
+                "debug": "^4.1.1",
+                "deps-regex": "^0.1.4",
+                "js-yaml": "^3.4.2",
+                "lodash": "^4.17.15",
+                "minimatch": "^3.0.2",
+                "node-sass-tilde-importer": "^1.0.2",
+                "please-upgrade-node": "^3.2.0",
+                "require-package-name": "^2.0.1",
+                "resolve": "^1.14.1",
+                "vue-template-compiler": "^2.6.11",
+                "walkdir": "^0.4.1",
+                "yargs": "^15.0.2"
+            },
+            "bin": {
+                "depcheck": "bin/depcheck.js"
+            },
+            "engines": {
+                "node": ">=10"
+            }
+        },
+        "node_modules/depcheck/node_modules/ansi-regex": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+            "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/depcheck/node_modules/ansi-styles": {
+            "version": "4.2.1",
+            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+            "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+            "dependencies": {
+                "@types/color-name": "^1.1.1",
+                "color-convert": "^2.0.1"
+            },
+            "engines": {
+                "node": ">=8"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+            }
+        },
+        "node_modules/depcheck/node_modules/builtin-modules": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz",
+            "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/depcheck/node_modules/cliui": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+            "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+            "dependencies": {
+                "string-width": "^4.2.0",
+                "strip-ansi": "^6.0.0",
+                "wrap-ansi": "^6.2.0"
+            }
+        },
+        "node_modules/depcheck/node_modules/color-convert": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+            "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+            "dependencies": {
+                "color-name": "~1.1.4"
+            },
+            "engines": {
+                "node": ">=7.0.0"
+            }
+        },
+        "node_modules/depcheck/node_modules/color-name": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+            "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+        },
+        "node_modules/depcheck/node_modules/cosmiconfig": {
+            "version": "5.2.1",
+            "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
+            "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
+            "dependencies": {
+                "import-fresh": "^2.0.0",
+                "is-directory": "^0.3.1",
+                "js-yaml": "^3.13.1",
+                "parse-json": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/depcheck/node_modules/debug": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+            "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+            "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
+            "dependencies": {
+                "ms": "^2.1.1"
+            }
+        },
+        "node_modules/depcheck/node_modules/emoji-regex": {
+            "version": "8.0.0",
+            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+            "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+        },
+        "node_modules/depcheck/node_modules/find-up": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+            "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+            "dependencies": {
+                "locate-path": "^5.0.0",
+                "path-exists": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/depcheck/node_modules/import-fresh": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
+            "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
+            "dependencies": {
+                "caller-path": "^2.0.0",
+                "resolve-from": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/depcheck/node_modules/is-fullwidth-code-point": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+            "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/depcheck/node_modules/locate-path": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+            "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+            "dependencies": {
+                "p-locate": "^4.1.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/depcheck/node_modules/p-locate": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+            "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+            "dependencies": {
+                "p-limit": "^2.2.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/depcheck/node_modules/parse-json": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+            "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+            "dependencies": {
+                "error-ex": "^1.3.1",
+                "json-parse-better-errors": "^1.0.1"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/depcheck/node_modules/path-exists": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+            "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/depcheck/node_modules/resolve-from": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+            "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/depcheck/node_modules/string-width": {
+            "version": "4.2.0",
+            "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+            "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+            "dependencies": {
+                "emoji-regex": "^8.0.0",
+                "is-fullwidth-code-point": "^3.0.0",
+                "strip-ansi": "^6.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/depcheck/node_modules/strip-ansi": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+            "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+            "dependencies": {
+                "ansi-regex": "^5.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/depcheck/node_modules/wrap-ansi": {
+            "version": "6.2.0",
+            "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+            "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+            "dependencies": {
+                "ansi-styles": "^4.0.0",
+                "string-width": "^4.1.0",
+                "strip-ansi": "^6.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/depcheck/node_modules/yargs": {
+            "version": "15.3.1",
+            "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz",
+            "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==",
+            "dependencies": {
+                "cliui": "^6.0.0",
+                "decamelize": "^1.2.0",
+                "find-up": "^4.1.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": "^4.2.0",
+                "which-module": "^2.0.0",
+                "y18n": "^4.0.0",
+                "yargs-parser": "^18.1.1"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/depcheck/node_modules/yargs-parser": {
+            "version": "18.1.3",
+            "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+            "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+            "dependencies": {
+                "camelcase": "^5.0.0",
+                "decamelize": "^1.2.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/depd": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+            "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/deps-regex": {
+            "version": "0.1.4",
+            "resolved": "https://registry.npmjs.org/deps-regex/-/deps-regex-0.1.4.tgz",
+            "integrity": "sha1-UYZnt2kUYKXn4KNBvnbrfOgJAYQ="
+        },
+        "node_modules/destroy": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+            "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+        },
+        "node_modules/detect-libc": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+            "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
+            "bin": {
+                "detect-libc": "bin/detect-libc.js"
+            },
+            "engines": {
+                "node": ">=0.10"
+            }
+        },
+        "node_modules/detect-node": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
+            "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
+            "dev": true
+        },
+        "node_modules/diff": {
+            "version": "3.5.0",
+            "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
+            "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
+            "engines": {
+                "node": ">=0.3.1"
+            }
+        },
+        "node_modules/diff-match-patch": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz",
+            "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw=="
+        },
+        "node_modules/dir-glob": {
+            "version": "2.2.2",
+            "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz",
+            "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==",
+            "dev": true,
+            "dependencies": {
+                "path-type": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/dir-glob/node_modules/path-type": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+            "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
+            "dev": true,
+            "dependencies": {
+                "pify": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/dir-glob/node_modules/pify": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+            "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+            "dev": true,
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/dns-equal": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
+            "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=",
+            "dev": true
+        },
+        "node_modules/dns-packet": {
+            "version": "1.3.4",
+            "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz",
+            "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==",
+            "dev": true,
+            "dependencies": {
+                "ip": "^1.1.0",
+                "safe-buffer": "^5.0.1"
+            }
+        },
+        "node_modules/dns-txt": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz",
+            "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=",
+            "dev": true,
+            "dependencies": {
+                "buffer-indexof": "^1.0.0"
+            }
+        },
+        "node_modules/doctypes": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz",
+            "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk="
+        },
+        "node_modules/dom-converter": {
+            "version": "0.2.0",
+            "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
+            "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==",
+            "dependencies": {
+                "utila": "~0.4"
+            }
+        },
+        "node_modules/dom-helpers": {
+            "version": "3.4.0",
+            "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
+            "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==",
+            "dependencies": {
+                "@babel/runtime": "^7.1.2"
+            }
+        },
+        "node_modules/dom-serializer": {
+            "version": "0.2.2",
+            "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
+            "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==",
+            "dependencies": {
+                "domelementtype": "^2.0.1",
+                "entities": "^2.0.0"
+            }
+        },
+        "node_modules/dom-serializer/node_modules/domelementtype": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz",
+            "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ=="
+        },
+        "node_modules/dom-serializer/node_modules/entities": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
+            "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw=="
+        },
+        "node_modules/domelementtype": {
+            "version": "1.3.1",
+            "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
+            "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w=="
+        },
+        "node_modules/domexception": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz",
+            "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
+            "dev": true,
+            "dependencies": {
+                "webidl-conversions": "^4.0.2"
+            }
+        },
+        "node_modules/domhandler": {
+            "version": "2.4.2",
+            "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
+            "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
+            "dependencies": {
+                "domelementtype": "1"
+            }
+        },
+        "node_modules/domutils": {
+            "version": "1.7.0",
+            "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
+            "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==",
+            "dependencies": {
+                "dom-serializer": "0",
+                "domelementtype": "1"
+            }
+        },
+        "node_modules/dot-case": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
+            "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+            "dependencies": {
+                "no-case": "^3.0.4",
+                "tslib": "^2.0.3"
+            }
+        },
+        "node_modules/dot-case/node_modules/tslib": {
+            "version": "2.3.1",
+            "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+            "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+        },
+        "node_modules/dot-prop": {
+            "version": "4.2.0",
+            "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
+            "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==",
+            "dependencies": {
+                "is-obj": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/dotenv": {
+            "version": "8.6.0",
+            "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz",
+            "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==",
+            "dev": true,
+            "engines": {
+                "node": ">=10"
+            }
+        },
+        "node_modules/double-bits": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/double-bits/-/double-bits-1.1.1.tgz",
+            "integrity": "sha1-WKu6RUlNpND6Nrc60RoobJGEscY="
+        },
+        "node_modules/duplexer3": {
+            "version": "0.1.4",
+            "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
+            "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
+        },
+        "node_modules/duplexify": {
+            "version": "3.7.1",
+            "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
+            "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
+            "dev": true,
+            "dependencies": {
+                "end-of-stream": "^1.0.0",
+                "inherits": "^2.0.1",
+                "readable-stream": "^2.0.0",
+                "stream-shift": "^1.0.0"
+            }
+        },
+        "node_modules/duplexify/node_modules/readable-stream": {
+            "version": "2.3.7",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+            "dev": true,
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/dynamic-dedupe": {
+            "version": "0.3.0",
+            "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz",
+            "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=",
+            "dev": true,
+            "dependencies": {
+                "xtend": "^4.0.0"
+            }
+        },
+        "node_modules/ecc-jsbn": {
+            "version": "0.1.2",
+            "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+            "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+            "dependencies": {
+                "jsbn": "~0.1.0",
+                "safer-buffer": "^2.1.0"
+            }
+        },
+        "node_modules/ecdsa-sig-formatter": {
+            "version": "1.0.11",
+            "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+            "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+            "dependencies": {
+                "safe-buffer": "^5.0.1"
+            }
+        },
+        "node_modules/ee-first": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+            "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+        },
+        "node_modules/electron-to-chromium": {
+            "version": "1.4.75",
+            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.75.tgz",
+            "integrity": "sha512-LxgUNeu3BVU7sXaKjUDD9xivocQLxFtq6wgERrutdY/yIOps3ODOZExK1jg8DTEg4U8TUCb5MLGeWFOYuxjF3Q=="
+        },
+        "node_modules/emoji-regex": {
+            "version": "7.0.3",
+            "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+            "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
+        },
+        "node_modules/emojis-list": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
+            "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
+            "engines": {
+                "node": ">= 4"
+            }
+        },
+        "node_modules/emotion": {
+            "version": "10.0.27",
+            "resolved": "https://registry.npmjs.org/emotion/-/emotion-10.0.27.tgz",
+            "integrity": "sha512-2xdDzdWWzue8R8lu4G76uWX5WhyQuzATon9LmNeCy/2BHVC6dsEpfhN1a0qhELgtDVdjyEA6J8Y/VlI5ZnaH0g==",
+            "dependencies": {
+                "babel-plugin-emotion": "^10.0.27",
+                "create-emotion": "^10.0.27"
+            }
+        },
+        "node_modules/encodeurl": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+            "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/encoding": {
+            "version": "0.1.12",
+            "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
+            "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
+            "dependencies": {
+                "iconv-lite": "~0.4.13"
+            }
+        },
+        "node_modules/end-of-stream": {
+            "version": "1.4.4",
+            "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+            "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+            "dependencies": {
+                "once": "^1.4.0"
+            }
+        },
+        "node_modules/engine.io": {
+            "version": "3.5.0",
+            "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.5.0.tgz",
+            "integrity": "sha512-21HlvPUKaitDGE4GXNtQ7PLP0Sz4aWLddMPw2VTyFz1FVZqu/kZsJUO8WNpKuE/OCL7nkfRaOui2ZCJloGznGA==",
+            "dependencies": {
+                "accepts": "~1.3.4",
+                "base64id": "2.0.0",
+                "cookie": "~0.4.1",
+                "debug": "~4.1.0",
+                "engine.io-parser": "~2.2.0",
+                "ws": "~7.4.2"
+            },
+            "engines": {
+                "node": ">=8.0.0"
+            }
+        },
+        "node_modules/engine.io-client": {
+            "version": "3.5.2",
+            "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.2.tgz",
+            "integrity": "sha512-QEqIp+gJ/kMHeUun7f5Vv3bteRHppHH/FMBQX/esFj/fuYfjyUKWGMo3VCvIP/V8bE9KcjHmRZrhIz2Z9oNsDA==",
+            "dependencies": {
+                "component-emitter": "~1.3.0",
+                "component-inherit": "0.0.3",
+                "debug": "~3.1.0",
+                "engine.io-parser": "~2.2.0",
+                "has-cors": "1.1.0",
+                "indexof": "0.0.1",
+                "parseqs": "0.0.6",
+                "parseuri": "0.0.6",
+                "ws": "~7.4.2",
+                "xmlhttprequest-ssl": "~1.6.2",
+                "yeast": "0.1.2"
+            }
+        },
+        "node_modules/engine.io-client/node_modules/debug": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+            "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+            "dependencies": {
+                "ms": "2.0.0"
+            }
+        },
+        "node_modules/engine.io-client/node_modules/ms": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        },
+        "node_modules/engine.io-client/node_modules/ws": {
+            "version": "7.4.6",
+            "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
+            "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
+            "engines": {
+                "node": ">=8.3.0"
+            },
+            "peerDependencies": {
+                "bufferutil": "^4.0.1",
+                "utf-8-validate": "^5.0.2"
+            },
+            "peerDependenciesMeta": {
+                "bufferutil": {
+                    "optional": true
+                },
+                "utf-8-validate": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/engine.io-parser": {
+            "version": "2.2.1",
+            "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz",
+            "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==",
+            "dependencies": {
+                "after": "0.8.2",
+                "arraybuffer.slice": "~0.0.7",
+                "base64-arraybuffer": "0.1.4",
+                "blob": "0.0.5",
+                "has-binary2": "~1.0.2"
+            }
+        },
+        "node_modules/engine.io/node_modules/debug": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+            "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+            "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
+            "dependencies": {
+                "ms": "^2.1.1"
+            }
+        },
+        "node_modules/engine.io/node_modules/ws": {
+            "version": "7.4.6",
+            "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
+            "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
+            "engines": {
+                "node": ">=8.3.0"
+            },
+            "peerDependencies": {
+                "bufferutil": "^4.0.1",
+                "utf-8-validate": "^5.0.2"
+            },
+            "peerDependenciesMeta": {
+                "bufferutil": {
+                    "optional": true
+                },
+                "utf-8-validate": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/enhanced-resolve": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz",
+            "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==",
+            "dev": true,
+            "dependencies": {
+                "graceful-fs": "^4.1.2",
+                "memory-fs": "^0.5.0",
+                "tapable": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/entities": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
+            "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w=="
+        },
+        "node_modules/envinfo": {
+            "version": "7.8.1",
+            "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz",
+            "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==",
+            "bin": {
+                "envinfo": "dist/cli.js"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/equation-editor-react": {
+            "version": "0.0.6",
+            "resolved": "git+ssh://git@github.com/bobzel/equation-editor-react.git#75915e852b4b36a6a4cd3e1cbc80598da6b65227",
+            "integrity": "sha512-ctU569x4psKy2sfv9GmNX1HMA9HMUZH8bzt0pku1fYyhx5MZ9E/GXlcB5svqvFo+ZeaySEe4+DdDwm5kVP4ySA==",
+            "license": "MIT",
+            "dependencies": {
+                "jquery": "^3.4.1",
+                "mathquill": "^0.10.1-a"
+            },
+            "peerDependencies": {
+                "react": "^16.0.0",
+                "react-dom": "^16.0.0"
+            }
+        },
+        "node_modules/errno": {
+            "version": "0.1.7",
+            "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
+            "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
+            "dev": true,
+            "dependencies": {
+                "prr": "~1.0.1"
+            },
+            "bin": {
+                "errno": "cli.js"
+            }
+        },
+        "node_modules/error-ex": {
+            "version": "1.3.2",
+            "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+            "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+            "dependencies": {
+                "is-arrayish": "^0.2.1"
+            }
+        },
+        "node_modules/es-abstract": {
+            "version": "1.17.5",
+            "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz",
+            "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==",
+            "dependencies": {
+                "es-to-primitive": "^1.2.1",
+                "function-bind": "^1.1.1",
+                "has": "^1.0.3",
+                "has-symbols": "^1.0.1",
+                "is-callable": "^1.1.5",
+                "is-regex": "^1.0.5",
+                "object-inspect": "^1.7.0",
+                "object-keys": "^1.1.1",
+                "object.assign": "^4.1.0",
+                "string.prototype.trimleft": "^2.1.1",
+                "string.prototype.trimright": "^2.1.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/es-module-lexer": {
+            "version": "0.9.3",
+            "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
+            "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ=="
+        },
+        "node_modules/es-to-primitive": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+            "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+            "dependencies": {
+                "is-callable": "^1.1.4",
+                "is-date-object": "^1.0.1",
+                "is-symbol": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/es5-ext": {
+            "version": "0.10.59",
+            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.59.tgz",
+            "integrity": "sha512-cOgyhW0tIJyQY1Kfw6Kr0viu9ZlUctVchRMZ7R0HiH3dxTSp5zJDLecwxUqPUrGKMsgBI1wd1FL+d9Jxfi4cLw==",
+            "dev": true,
+            "hasInstallScript": true,
+            "dependencies": {
+                "es6-iterator": "^2.0.3",
+                "es6-symbol": "^3.1.3",
+                "next-tick": "^1.1.0"
+            },
+            "engines": {
+                "node": ">=0.10"
+            }
+        },
+        "node_modules/es6-iterator": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+            "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
+            "dev": true,
+            "dependencies": {
+                "d": "1",
+                "es5-ext": "^0.10.35",
+                "es6-symbol": "^3.1.1"
+            }
+        },
+        "node_modules/es6-iterator/node_modules/d": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+            "dev": true,
+            "dependencies": {
+                "es5-ext": "^0.10.50",
+                "type": "^1.0.1"
+            }
+        },
+        "node_modules/es6-promise": {
+            "version": "3.2.1",
+            "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
+            "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q="
+        },
+        "node_modules/es6-symbol": {
+            "version": "3.1.3",
+            "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
+            "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
+            "dev": true,
+            "dependencies": {
+                "d": "^1.0.1",
+                "ext": "^1.1.2"
+            }
+        },
+        "node_modules/es6-symbol/node_modules/d": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+            "dev": true,
+            "dependencies": {
+                "es5-ext": "^0.10.50",
+                "type": "^1.0.1"
+            }
+        },
+        "node_modules/escalade": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+            "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/escape-html": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+            "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+        },
+        "node_modules/escape-string-regexp": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+            "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+            "engines": {
+                "node": ">=0.8.0"
+            }
+        },
+        "node_modules/escodegen": {
+            "version": "1.14.1",
+            "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz",
+            "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==",
+            "dev": true,
+            "dependencies": {
+                "esprima": "^4.0.1",
+                "estraverse": "^4.2.0",
+                "esutils": "^2.0.2",
+                "optionator": "^0.8.1"
+            },
+            "bin": {
+                "escodegen": "bin/escodegen.js",
+                "esgenerate": "bin/esgenerate.js"
+            },
+            "engines": {
+                "node": ">=4.0"
+            },
+            "optionalDependencies": {
+                "source-map": "~0.6.1"
+            }
+        },
+        "node_modules/escodegen/node_modules/source-map": {
+            "version": "0.6.1",
+            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+            "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+            "dev": true,
+            "optional": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/eslint-scope": {
+            "version": "5.1.1",
+            "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+            "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+            "dependencies": {
+                "esrecurse": "^4.3.0",
+                "estraverse": "^4.1.1"
+            },
+            "engines": {
+                "node": ">=8.0.0"
+            }
+        },
+        "node_modules/esprima": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+            "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+            "bin": {
+                "esparse": "bin/esparse.js",
+                "esvalidate": "bin/esvalidate.js"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/esrecurse": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+            "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+            "dependencies": {
+                "estraverse": "^5.2.0"
+            },
+            "engines": {
+                "node": ">=4.0"
+            }
+        },
+        "node_modules/esrecurse/node_modules/estraverse": {
+            "version": "5.3.0",
+            "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+            "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+            "engines": {
+                "node": ">=4.0"
+            }
+        },
+        "node_modules/estraverse": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+            "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+            "engines": {
+                "node": ">=4.0"
+            }
+        },
+        "node_modules/esutils": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+            "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/etag": {
+            "version": "1.8.1",
+            "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+            "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/event-target-shim": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+            "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/eventemitter3": {
+            "version": "4.0.7",
+            "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+            "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+            "dev": true
+        },
+        "node_modules/events": {
+            "version": "3.3.0",
+            "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+            "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+            "engines": {
+                "node": ">=0.8.x"
+            }
+        },
+        "node_modules/eventsource": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz",
+            "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==",
+            "dev": true,
+            "dependencies": {
+                "original": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.12.0"
+            }
+        },
+        "node_modules/execa": {
+            "version": "0.7.0",
+            "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
+            "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/execa/node_modules/cross-spawn": {
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
+            "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+            "dependencies": {
+                "lru-cache": "^4.0.1",
+                "shebang-command": "^1.2.0",
+                "which": "^1.2.9"
+            }
+        },
+        "node_modules/execa/node_modules/lru-cache": {
+            "version": "4.1.5",
+            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+            "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+            "dependencies": {
+                "pseudomap": "^1.0.2",
+                "yallist": "^2.1.2"
+            }
+        },
+        "node_modules/execa/node_modules/yallist": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+            "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
+        },
+        "node_modules/exenv": {
+            "version": "1.2.2",
+            "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
+            "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50="
+        },
+        "node_modules/exeq": {
+            "version": "2.4.0",
+            "resolved": "https://registry.npmjs.org/exeq/-/exeq-2.4.0.tgz",
+            "integrity": "sha1-Td8qaEZIxCeteZNJzzO9dTWPiEo=",
+            "dependencies": {
+                "bluebird": "^3.0.3",
+                "native-or-bluebird": "^1.2.0"
+            },
+            "engines": {
+                "node": ">= 0.10.0"
+            }
+        },
+        "node_modules/exif": {
+            "version": "0.6.0",
+            "resolved": "https://registry.npmjs.org/exif/-/exif-0.6.0.tgz",
+            "integrity": "sha1-YKYmaAdlQst+T1cZnUrG830sX0o=",
+            "dependencies": {
+                "debug": "^2.2"
+            }
+        },
+        "node_modules/exif/node_modules/debug": {
+            "version": "2.6.9",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+            "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+            "dependencies": {
+                "ms": "2.0.0"
+            }
+        },
+        "node_modules/exif/node_modules/ms": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        },
+        "node_modules/exifr": {
+            "version": "7.1.3",
+            "resolved": "https://registry.npmjs.org/exifr/-/exifr-7.1.3.tgz",
+            "integrity": "sha512-g/aje2noHivrRSLbAUtBPWFbxKdKhgj/xr1vATDdUXPOFYJlQ62Ft0oy+72V6XLIpDJfHs6gXLbBLAolqOXYRw=="
+        },
+        "node_modules/expand-brackets": {
+            "version": "2.1.4",
+            "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+            "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/expand-brackets/node_modules/debug": {
+            "version": "2.6.9",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+            "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+            "dependencies": {
+                "ms": "2.0.0"
+            }
+        },
+        "node_modules/expand-brackets/node_modules/define-property": {
+            "version": "0.2.5",
+            "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+            "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+            "dependencies": {
+                "is-descriptor": "^0.1.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/expand-brackets/node_modules/extend-shallow": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+            "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+            "dependencies": {
+                "is-extendable": "^0.1.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/expand-brackets/node_modules/is-extendable": {
+            "version": "0.1.1",
+            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+            "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/expand-brackets/node_modules/ms": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        },
+        "node_modules/expand-template": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
+            "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/express": {
+            "version": "4.17.3",
+            "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz",
+            "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==",
+            "dependencies": {
+                "accepts": "~1.3.8",
+                "array-flatten": "1.1.1",
+                "body-parser": "1.19.2",
+                "content-disposition": "0.5.4",
+                "content-type": "~1.0.4",
+                "cookie": "0.4.2",
+                "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",
+                "fresh": "0.5.2",
+                "merge-descriptors": "1.0.1",
+                "methods": "~1.1.2",
+                "on-finished": "~2.3.0",
+                "parseurl": "~1.3.3",
+                "path-to-regexp": "0.1.7",
+                "proxy-addr": "~2.0.7",
+                "qs": "6.9.7",
+                "range-parser": "~1.2.1",
+                "safe-buffer": "5.2.1",
+                "send": "0.17.2",
+                "serve-static": "1.14.2",
+                "setprototypeof": "1.2.0",
+                "statuses": "~1.5.0",
+                "type-is": "~1.6.18",
+                "utils-merge": "1.0.1",
+                "vary": "~1.1.2"
+            },
+            "engines": {
+                "node": ">= 0.10.0"
+            }
+        },
+        "node_modules/express-flash": {
+            "version": "0.0.2",
+            "resolved": "https://registry.npmjs.org/express-flash/-/express-flash-0.0.2.tgz",
+            "integrity": "sha1-I9GovPP5DXB5KOSJ+Whp7K0KzaI=",
+            "dependencies": {
+                "connect-flash": "0.1.x"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/express-session": {
+            "version": "1.17.2",
+            "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz",
+            "integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==",
+            "dependencies": {
+                "cookie": "0.4.1",
+                "cookie-signature": "1.0.6",
+                "debug": "2.6.9",
+                "depd": "~2.0.0",
+                "on-headers": "~1.0.2",
+                "parseurl": "~1.3.3",
+                "safe-buffer": "5.2.1",
+                "uid-safe": "~2.1.5"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/express-session/node_modules/debug": {
+            "version": "2.6.9",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+            "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+            "dependencies": {
+                "ms": "2.0.0"
+            }
+        },
+        "node_modules/express-session/node_modules/depd": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+            "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/express-session/node_modules/ms": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        },
+        "node_modules/express-session/node_modules/safe-buffer": {
+            "version": "5.2.1",
+            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+            "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/feross"
+                },
+                {
+                    "type": "patreon",
+                    "url": "https://www.patreon.com/feross"
+                },
+                {
+                    "type": "consulting",
+                    "url": "https://feross.org/support"
+                }
+            ]
+        },
+        "node_modules/express-validator": {
+            "version": "5.3.1",
+            "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-5.3.1.tgz",
+            "integrity": "sha512-g8xkipBF6VxHbO1+ksC7nxUU7+pWif0+OZXjZTybKJ/V0aTVhuCoHbyhIPgSYVldwQLocGExPtB2pE0DqK4jsw==",
+            "dependencies": {
+                "lodash": "^4.17.10",
+                "validator": "^10.4.0"
+            },
+            "engines": {
+                "node": ">= 6.0.0"
+            }
+        },
+        "node_modules/express/node_modules/cookie": {
+            "version": "0.4.2",
+            "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+            "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/express/node_modules/debug": {
+            "version": "2.6.9",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+            "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+            "dependencies": {
+                "ms": "2.0.0"
+            }
+        },
+        "node_modules/express/node_modules/ms": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        },
+        "node_modules/express/node_modules/qs": {
+            "version": "6.9.7",
+            "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz",
+            "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==",
+            "engines": {
+                "node": ">=0.6"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/express/node_modules/safe-buffer": {
+            "version": "5.2.1",
+            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+            "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/feross"
+                },
+                {
+                    "type": "patreon",
+                    "url": "https://www.patreon.com/feross"
+                },
+                {
+                    "type": "consulting",
+                    "url": "https://feross.org/support"
+                }
+            ]
+        },
+        "node_modules/expressjs": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/expressjs/-/expressjs-1.0.1.tgz",
+            "integrity": "sha1-IgMoRpoY31rWFeK3oM6ZXxf7ru8=",
+            "deprecated": "This is a typosquat on the popular Express package. This is not maintained nor is the original Express package."
+        },
+        "node_modules/ext": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
+            "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
+            "dev": true,
+            "dependencies": {
+                "type": "^2.0.0"
+            }
+        },
+        "node_modules/ext/node_modules/type": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz",
+            "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==",
+            "dev": true
+        },
+        "node_modules/extend": {
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+            "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
+        },
+        "node_modules/extend-shallow": {
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+            "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+            "dependencies": {
+                "assign-symbols": "^1.0.0",
+                "is-extendable": "^1.0.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/extglob": {
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+            "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/extglob/node_modules/define-property": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+            "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+            "dependencies": {
+                "is-descriptor": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/extglob/node_modules/extend-shallow": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+            "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+            "dependencies": {
+                "is-extendable": "^0.1.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/extglob/node_modules/is-accessor-descriptor": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+            "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+            "dependencies": {
+                "kind-of": "^6.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/extglob/node_modules/is-data-descriptor": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+            "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+            "dependencies": {
+                "kind-of": "^6.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/extglob/node_modules/is-descriptor": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+            "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+            "dependencies": {
+                "is-accessor-descriptor": "^1.0.0",
+                "is-data-descriptor": "^1.0.0",
+                "kind-of": "^6.0.2"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/extglob/node_modules/is-extendable": {
+            "version": "0.1.1",
+            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+            "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/extract-zip": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
+            "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
+            "dependencies": {
+                "debug": "^4.1.1",
+                "get-stream": "^5.1.0",
+                "yauzl": "^2.10.0"
+            },
+            "bin": {
+                "extract-zip": "cli.js"
+            },
+            "engines": {
+                "node": ">= 10.17.0"
+            },
+            "optionalDependencies": {
+                "@types/yauzl": "^2.9.1"
+            }
+        },
+        "node_modules/extract-zip/node_modules/debug": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+            "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+            "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
+            "dependencies": {
+                "ms": "^2.1.1"
+            }
+        },
+        "node_modules/extract-zip/node_modules/get-stream": {
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz",
+            "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==",
+            "dependencies": {
+                "pump": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/extsprintf": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+            "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+            "engines": [
+                "node >=0.6.0"
+            ]
+        },
+        "node_modules/fast-deep-equal": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
+            "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA=="
+        },
+        "node_modules/fast-json-stable-stringify": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+            "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
+        },
+        "node_modules/fast-levenshtein": {
+            "version": "2.0.6",
+            "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+            "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+            "dev": true
+        },
+        "node_modules/fast-text-encoding": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.2.tgz",
+            "integrity": "sha512-5rQdinSsycpzvAoHga2EDn+LRX1d5xLFsuNG0Kg61JrAT/tASXcLL0nf/33v+sAxlQcfYmWbTURa1mmAf55jGw=="
+        },
+        "node_modules/fastest-levenshtein": {
+            "version": "1.0.12",
+            "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz",
+            "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow=="
+        },
+        "node_modules/faye-websocket": {
+            "version": "0.11.4",
+            "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
+            "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
+            "dev": true,
+            "dependencies": {
+                "websocket-driver": ">=0.5.1"
+            },
+            "engines": {
+                "node": ">=0.8.0"
+            }
+        },
+        "node_modules/fbjs": {
+            "version": "0.8.17",
+            "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
+            "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/fd-slicer": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+            "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
+            "dependencies": {
+                "pend": "~1.2.0"
+            }
+        },
+        "node_modules/file-loader": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz",
+            "integrity": "sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw==",
+            "dev": true,
+            "dependencies": {
+                "loader-utils": "^1.0.2",
+                "schema-utils": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 6.9.0"
+            },
+            "peerDependencies": {
+                "webpack": "^4.0.0"
+            }
+        },
+        "node_modules/file-loader/node_modules/schema-utils": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+            "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+            "dev": true,
+            "dependencies": {
+                "ajv": "^6.1.0",
+                "ajv-errors": "^1.0.0",
+                "ajv-keywords": "^3.1.0"
+            },
+            "engines": {
+                "node": ">= 4"
+            }
+        },
+        "node_modules/file-saver": {
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
+            "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
+        },
+        "node_modules/file-uri-to-path": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+            "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+            "optional": true
+        },
+        "node_modules/fill-range": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+            "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+            "dependencies": {
+                "extend-shallow": "^2.0.1",
+                "is-number": "^3.0.0",
+                "repeat-string": "^1.6.1",
+                "to-regex-range": "^2.1.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/fill-range/node_modules/extend-shallow": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+            "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+            "dependencies": {
+                "is-extendable": "^0.1.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/fill-range/node_modules/is-extendable": {
+            "version": "0.1.1",
+            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+            "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/filter-obj": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
+            "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/finalhandler": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+            "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/finalhandler/node_modules/debug": {
+            "version": "2.6.9",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+            "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+            "dependencies": {
+                "ms": "2.0.0"
+            }
+        },
+        "node_modules/finalhandler/node_modules/ms": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        },
+        "node_modules/find": {
+            "version": "0.1.7",
+            "resolved": "https://registry.npmjs.org/find/-/find-0.1.7.tgz",
+            "integrity": "sha1-yGyHrxqxjyIrvjjeyGy8dg0Wpvs=",
+            "dependencies": {
+                "traverse-chain": "~0.1.0"
+            }
+        },
+        "node_modules/find-cache-dir": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz",
+            "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=",
+            "dev": true,
+            "dependencies": {
+                "commondir": "^1.0.1",
+                "make-dir": "^1.0.0",
+                "pkg-dir": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/find-in-files": {
+            "version": "0.5.0",
+            "resolved": "https://registry.npmjs.org/find-in-files/-/find-in-files-0.5.0.tgz",
+            "integrity": "sha512-VraTc6HdtdSHmAp0yJpAy20yPttGKzyBWc7b7FPnnsX9TOgmKx0g9xajizpF/iuu4IvNK4TP0SpyBT9zAlwG+g==",
+            "dependencies": {
+                "find": "^0.1.5",
+                "q": "^1.0.1"
+            }
+        },
+        "node_modules/find-parent-dir": {
+            "version": "0.3.0",
+            "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.0.tgz",
+            "integrity": "sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ="
+        },
+        "node_modules/find-root": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
+            "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
+        },
+        "node_modules/find-up": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+            "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+            "dependencies": {
+                "locate-path": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/fit-curve": {
+            "version": "0.1.7",
+            "resolved": "https://registry.npmjs.org/fit-curve/-/fit-curve-0.1.7.tgz",
+            "integrity": "sha512-Md3b3ReA/qJwwYvKXeHpOV1fhPqwhJ9/29zc9lfi8ijyg00J0S9C7+5XMZDFhlDAKbwOvVgBxVIG1EHoMSC3vw=="
+        },
+        "node_modules/flat": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
+            "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
+            "deprecated": "Fixed a prototype pollution security issue in 4.1.0, please upgrade to ^4.1.1 or ^5.0.1.",
+            "dependencies": {
+                "is-buffer": "~2.0.3"
+            },
+            "bin": {
+                "flat": "cli.js"
+            }
+        },
+        "node_modules/flexlayout-react": {
+            "version": "0.3.11",
+            "resolved": "https://registry.npmjs.org/flexlayout-react/-/flexlayout-react-0.3.11.tgz",
+            "integrity": "sha512-V+rEfyYJBqNk9oBgotPoXg8rmom4/ji9Mvr6f7T8sIJs83RuyK1D3oOrya2ydZIUCP2T6BIvj7rFTmRRkzpU8w==",
+            "peerDependencies": {
+                "react": "^16.13.1",
+                "react-dom": "^16.13.1"
+            }
+        },
+        "node_modules/fluent-ffmpeg": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.2.tgz",
+            "integrity": "sha1-yVLeIkD4EuvaCqgAbXd27irPfXQ=",
+            "dependencies": {
+                "async": ">=0.2.9",
+                "which": "^1.1.1"
+            },
+            "engines": {
+                "node": ">=0.8.0"
+            }
+        },
+        "node_modules/flush-write-stream": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
+            "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
+            "dev": true,
+            "dependencies": {
+                "inherits": "^2.0.3",
+                "readable-stream": "^2.3.6"
+            }
+        },
+        "node_modules/flush-write-stream/node_modules/readable-stream": {
+            "version": "2.3.7",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+            "dev": true,
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/follow-redirects": {
+            "version": "1.12.1",
+            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz",
+            "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg==",
+            "funding": [
+                {
+                    "type": "individual",
+                    "url": "https://github.com/sponsors/RubenVerborgh"
+                }
+            ],
+            "engines": {
+                "node": ">=4.0"
+            }
+        },
+        "node_modules/for-each-property": {
+            "version": "0.0.4",
+            "resolved": "https://registry.npmjs.org/for-each-property/-/for-each-property-0.0.4.tgz",
+            "integrity": "sha1-z6hXrsFCLh0Sb/CHhPz2Jim8g/Y=",
+            "dependencies": {
+                "get-prototype-chain": "^1.0.1"
+            }
+        },
+        "node_modules/for-each-property-deep": {
+            "version": "0.0.3",
+            "resolved": "https://registry.npmjs.org/for-each-property-deep/-/for-each-property-deep-0.0.3.tgz",
+            "integrity": "sha1-MTCaSvw4qcygbxsiP1PWSm0IP60=",
+            "dependencies": {
+                "for-each-property": "0.0.4"
+            }
+        },
+        "node_modules/for-in": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+            "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/foreach": {
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
+            "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k="
+        },
+        "node_modules/forever-agent": {
+            "version": "0.6.1",
+            "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+            "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/fork-ts-checker-webpack-plugin": {
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-1.6.0.tgz",
+            "integrity": "sha512-vqOY5gakcoon2s12V7MMe01OPwfgqulUWFzm+geQaPPOBKjW1I7aqqoBVlU0ECn97liMB0ECs16pRdIGe9qdRw==",
+            "dev": true,
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=6.11.5",
+                "yarn": ">=1.0.0"
+            }
+        },
+        "node_modules/form-data": {
+            "version": "2.5.1",
+            "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
+            "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
+            "dev": true,
+            "dependencies": {
+                "asynckit": "^0.4.0",
+                "combined-stream": "^1.0.6",
+                "mime-types": "^2.1.12"
+            },
+            "engines": {
+                "node": ">= 0.12"
+            }
+        },
+        "node_modules/form-data-encoder": {
+            "version": "1.7.1",
+            "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz",
+            "integrity": "sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg=="
+        },
+        "node_modules/formidable": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz",
+            "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==",
+            "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau"
+        },
+        "node_modules/forwarded": {
+            "version": "0.2.0",
+            "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+            "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/fragment-cache": {
+            "version": "0.2.1",
+            "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+            "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+            "dependencies": {
+                "map-cache": "^0.2.2"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/fresh": {
+            "version": "0.5.2",
+            "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+            "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/from2": {
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
+            "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
+            "dev": true,
+            "dependencies": {
+                "inherits": "^2.0.1",
+                "readable-stream": "^2.0.0"
+            }
+        },
+        "node_modules/from2/node_modules/readable-stream": {
+            "version": "2.3.7",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+            "dev": true,
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/fs-constants": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+            "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
+        },
+        "node_modules/fs-monkey": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz",
+            "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q=="
+        },
+        "node_modules/fs-write-stream-atomic": {
+            "version": "1.0.10",
+            "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
+            "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
+            "dev": true,
+            "dependencies": {
+                "graceful-fs": "^4.1.2",
+                "iferr": "^0.1.5",
+                "imurmurhash": "^0.1.4",
+                "readable-stream": "1 || 2"
+            }
+        },
+        "node_modules/fs-write-stream-atomic/node_modules/readable-stream": {
+            "version": "2.3.7",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+            "dev": true,
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/fs.realpath": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+            "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+        },
+        "node_modules/fsevents": {
+            "version": "2.3.2",
+            "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+            "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+            "dev": true,
+            "hasInstallScript": true,
+            "optional": true,
+            "os": [
+                "darwin"
+            ],
+            "engines": {
+                "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+            }
+        },
+        "node_modules/fstream": {
+            "version": "1.0.12",
+            "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
+            "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
+            "dependencies": {
+                "graceful-fs": "^4.1.2",
+                "inherits": "~2.0.0",
+                "mkdirp": ">=0.5 0",
+                "rimraf": "2"
+            },
+            "engines": {
+                "node": ">=0.6"
+            }
+        },
+        "node_modules/fstream/node_modules/rimraf": {
+            "version": "2.7.1",
+            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+            "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+            "dependencies": {
+                "glob": "^7.1.3"
+            },
+            "bin": {
+                "rimraf": "bin.js"
+            }
+        },
+        "node_modules/function-bind": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+            "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+        },
+        "node_modules/function-plot": {
+            "version": "1.22.8",
+            "resolved": "https://registry.npmjs.org/function-plot/-/function-plot-1.22.8.tgz",
+            "integrity": "sha512-VGqY3Lxu+LoC2ZvdesEiK8n0jMThjEI1gAGHOcWcRED5ip0Kt018swygtKl+lmBlbvmXDXOhYW6KOPa0dbky2w==",
+            "dependencies": {
+                "built-in-math-eval": "^0.3.0",
+                "clamp": "^1.0.1",
+                "d3-axis": "^2.0.0",
+                "d3-color": "^2.0.0",
+                "d3-format": "^2.0.0",
+                "d3-interpolate": "^2.0.1",
+                "d3-scale": "^3.2.2",
+                "d3-selection": "^2.0.0",
+                "d3-shape": "^2.0.0",
+                "d3-zoom": "^2.0.0",
+                "interval-arithmetic-eval": "^0.4.7"
+            }
+        },
+        "node_modules/gauge": {
+            "version": "2.7.4",
+            "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+            "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/gauge/node_modules/ansi-regex": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+            "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/gauge/node_modules/is-fullwidth-code-point": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+            "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+            "dependencies": {
+                "number-is-nan": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/gauge/node_modules/string-width": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+            "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+            "dependencies": {
+                "code-point-at": "^1.0.0",
+                "is-fullwidth-code-point": "^1.0.0",
+                "strip-ansi": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/gauge/node_modules/strip-ansi": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+            "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+            "dependencies": {
+                "ansi-regex": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/gaxios": {
+            "version": "2.3.4",
+            "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.3.4.tgz",
+            "integrity": "sha512-US8UMj8C5pRnao3Zykc4AAVr+cffoNKRTg9Rsf2GiuZCW69vgJj38VK2PzlPuQU73FZ/nTk9/Av6/JGcE1N9vA==",
+            "dependencies": {
+                "abort-controller": "^3.0.0",
+                "extend": "^3.0.2",
+                "https-proxy-agent": "^5.0.0",
+                "is-stream": "^2.0.0",
+                "node-fetch": "^2.3.0"
+            },
+            "engines": {
+                "node": ">=8.10.0"
+            }
+        },
+        "node_modules/gaxios/node_modules/is-stream": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
+            "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/gaxios/node_modules/node-fetch": {
+            "version": "2.6.0",
+            "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
+            "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==",
+            "engines": {
+                "node": "4.x || >=6.0.0"
+            }
+        },
+        "node_modules/gaze": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
+            "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==",
+            "dependencies": {
+                "globule": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 4.0.0"
+            }
+        },
+        "node_modules/gcp-metadata": {
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-2.0.4.tgz",
+            "integrity": "sha512-p1lXhJvcKvJHWfQXhkd4Za1kyXRsGZA0JH7Cjs07W9hrg84d/j5tqQhbGewlSLx9gNyuQUid69uLux48YbggLg==",
+            "dependencies": {
+                "gaxios": "^2.0.1",
+                "json-bigint": "^0.3.0"
+            },
+            "engines": {
+                "node": ">=8.10.0"
+            }
+        },
+        "node_modules/get-caller-file": {
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+            "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+            "engines": {
+                "node": "6.* || 8.* || >= 10.*"
+            }
+        },
+        "node_modules/get-func-name": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
+            "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/get-intrinsic": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+            "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+            "dependencies": {
+                "function-bind": "^1.1.1",
+                "has": "^1.0.3",
+                "has-symbols": "^1.0.1"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/get-node-dimensions": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/get-node-dimensions/-/get-node-dimensions-1.2.1.tgz",
+            "integrity": "sha512-2MSPMu7S1iOTL+BOa6K1S62hB2zUAYNF/lV0gSVlOaacd087lc6nR1H1r0e3B1CerTo+RceOmi1iJW+vp21xcQ=="
+        },
+        "node_modules/get-prototype-chain": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/get-prototype-chain/-/get-prototype-chain-1.0.1.tgz",
+            "integrity": "sha1-oXGhFeoeSQbG7ThDofABwYUQQW8=",
+            "engines": {
+                "node": ">=4.0.0"
+            }
+        },
+        "node_modules/get-stdin": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz",
+            "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=",
+            "engines": {
+                "node": ">=0.12.0"
+            }
+        },
+        "node_modules/get-stream": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+            "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/get-symbol-description": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
+            "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "get-intrinsic": "^1.1.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/get-value": {
+            "version": "2.0.6",
+            "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+            "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/getpass": {
+            "version": "0.1.7",
+            "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+            "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+            "dependencies": {
+                "assert-plus": "^1.0.0"
+            }
+        },
+        "node_modules/github-from-package": {
+            "version": "0.0.0",
+            "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
+            "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4="
+        },
+        "node_modules/glob": {
+            "version": "7.1.6",
+            "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+            "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": "*"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            }
+        },
+        "node_modules/glob-parent": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+            "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+            "dependencies": {
+                "is-glob": "^3.1.0",
+                "path-dirname": "^1.0.0"
+            }
+        },
+        "node_modules/glob-parent/node_modules/is-glob": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+            "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+            "dependencies": {
+                "is-extglob": "^2.1.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/glob-to-regexp": {
+            "version": "0.4.1",
+            "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+            "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
+        },
+        "node_modules/global-dirs": {
+            "version": "0.1.1",
+            "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
+            "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
+            "dependencies": {
+                "ini": "^1.3.4"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/globals": {
+            "version": "11.12.0",
+            "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+            "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/globby": {
+            "version": "7.1.1",
+            "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz",
+            "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=",
+            "dev": true,
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/globby/node_modules/pify": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+            "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+            "dev": true,
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/globule": {
+            "version": "1.3.1",
+            "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz",
+            "integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==",
+            "dependencies": {
+                "glob": "~7.1.1",
+                "lodash": "~4.17.12",
+                "minimatch": "~3.0.2"
+            },
+            "engines": {
+                "node": ">= 0.10"
+            }
+        },
+        "node_modules/golden-layout": {
+            "version": "1.5.9",
+            "resolved": "https://registry.npmjs.org/golden-layout/-/golden-layout-1.5.9.tgz",
+            "integrity": "sha1-o5vB9qZ+b4hreXwBbdkk6UJrp38=",
+            "dependencies": {
+                "jquery": "*"
+            }
+        },
+        "node_modules/google-auth-library": {
+            "version": "4.2.6",
+            "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-4.2.6.tgz",
+            "integrity": "sha512-oJ6tCA9rbsYeIVY+mcLPFHa2hatz3XO6idYIrlI/KhhlMxZrO3tKyU8O2Pxu5KnSBBP7Wj4HtbM1LLKngNFaFw==",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=8.10.0"
+            }
+        },
+        "node_modules/google-maps-react": {
+            "version": "2.0.6",
+            "resolved": "https://registry.npmjs.org/google-maps-react/-/google-maps-react-2.0.6.tgz",
+            "integrity": "sha512-M8Eo9WndfQEfxcmm6yRq03qdJgw1x6rQmJ9DN+a+xPQ3K7yNDGkVDbinrf4/8vcox7nELbeopbm4bpefKewWfQ==",
+            "peerDependencies": {
+                "react": "~0.14.8 || ^15.0.0 || ^16.0.0",
+                "react-dom": "~0.14.8 || ^15.0.0 || ^16.0.0"
+            }
+        },
+        "node_modules/google-p12-pem": {
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.4.tgz",
+            "integrity": "sha512-S4blHBQWZRnEW44OcR7TL9WR+QCqByRvhNDZ/uuQfpxywfupikf/miba8js1jZi6ZOGv5slgSuoshCWh6EMDzg==",
+            "dependencies": {
+                "node-forge": "^0.9.0"
+            },
+            "bin": {
+                "gp12-pem": "build/src/bin/gp12-pem.js"
+            },
+            "engines": {
+                "node": ">=8.10.0"
+            }
+        },
+        "node_modules/googleapis": {
+            "version": "40.0.1",
+            "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-40.0.1.tgz",
+            "integrity": "sha512-B6qZVCautOOspEhru9GZ814I+ztkGWyA4ZEUfaXwXHBruX/HAWqedbsuUEx1w3nCECywK/FLTNUdcbH9zpaMaw==",
+            "dependencies": {
+                "google-auth-library": "^4.0.0",
+                "googleapis-common": "^2.0.2"
+            },
+            "engines": {
+                "node": ">=8.10.0"
+            }
+        },
+        "node_modules/googleapis-common": {
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-2.0.4.tgz",
+            "integrity": "sha512-8RRkxr24v1jIKCC1onFWA8RGnwFV55m3Qpil9DLX1yLc9e5qvOJsRoDOhhD2e7jFRONYEhT/BzT8vJZANqSr9w==",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=8.10.0"
+            }
+        },
+        "node_modules/googleapis-common/node_modules/qs": {
+            "version": "6.9.3",
+            "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz",
+            "integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==",
+            "engines": {
+                "node": ">=0.6"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/googlephotos": {
+            "version": "0.2.5",
+            "resolved": "https://registry.npmjs.org/googlephotos/-/googlephotos-0.2.5.tgz",
+            "integrity": "sha512-XPmD3gu8aMVuwauKVzzahD2Vn8Cn8WtBRGgSF5J9A85Fn6N2GM0OToxWbEoTfyKahK+ryGHGcIYzDX98ndxE9g==",
+            "dependencies": {
+                "lodash.chunk": "^4.2.0",
+                "request": "^2.86.0",
+                "request-promise": "^4.2.2"
+            },
+            "engines": {
+                "node": ">=6.0.0"
+            }
+        },
+        "node_modules/got": {
+            "version": "12.0.1",
+            "resolved": "https://registry.npmjs.org/got/-/got-12.0.1.tgz",
+            "integrity": "sha512-1Zhoh+lDej3t7Ks1BP/Jufn+rNqdiHQgUOcTxHzg2Dao1LQfp5S4Iq0T3iBxN4Zdo7QqCJL+WJUNzDX6rCP2Ew==",
+            "dependencies": {
+                "@sindresorhus/is": "^4.2.0",
+                "@szmarczak/http-timer": "^5.0.1",
+                "@types/cacheable-request": "^6.0.2",
+                "@types/responselike": "^1.0.0",
+                "cacheable-lookup": "^6.0.4",
+                "cacheable-request": "^7.0.2",
+                "decompress-response": "^6.0.0",
+                "form-data-encoder": "1.7.1",
+                "get-stream": "^6.0.1",
+                "http2-wrapper": "^2.1.9",
+                "lowercase-keys": "^3.0.0",
+                "p-cancelable": "^3.0.0",
+                "responselike": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=14.16"
+            },
+            "funding": {
+                "url": "https://github.com/sindresorhus/got?sponsor=1"
+            }
+        },
+        "node_modules/got/node_modules/decompress-response": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+            "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+            "dependencies": {
+                "mimic-response": "^3.1.0"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/got/node_modules/get-stream": {
+            "version": "6.0.1",
+            "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+            "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/got/node_modules/mimic-response": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+            "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/graceful-fs": {
+            "version": "4.2.4",
+            "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+            "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw=="
+        },
+        "node_modules/growl": {
+            "version": "1.10.5",
+            "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+            "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+            "engines": {
+                "node": ">=4.x"
+            }
+        },
+        "node_modules/gtoken": {
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-3.0.2.tgz",
+            "integrity": "sha512-BOBi6Zz31JfxhSHRZBIDdbwIbOPyux10WxJHdx8wz/FMP1zyN1xFrsAWsgcLe5ww5v/OZu/MePUEZAjgJXSauA==",
+            "dependencies": {
+                "gaxios": "^2.0.0",
+                "google-p12-pem": "^2.0.0",
+                "jws": "^3.1.5",
+                "mime": "^2.2.0"
+            },
+            "engines": {
+                "node": ">=8.10.0"
+            }
+        },
+        "node_modules/gtoken/node_modules/mime": {
+            "version": "2.4.4",
+            "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
+            "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==",
+            "bin": {
+                "mime": "cli.js"
+            },
+            "engines": {
+                "node": ">=4.0.0"
+            }
+        },
+        "node_modules/gud": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
+            "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw=="
+        },
+        "node_modules/handle-thing": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
+            "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==",
+            "dev": true
+        },
+        "node_modules/har-schema": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+            "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/har-validator": {
+            "version": "5.1.5",
+            "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+            "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+            "deprecated": "this library is no longer supported",
+            "dependencies": {
+                "ajv": "^6.12.3",
+                "har-schema": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/har-validator/node_modules/ajv": {
+            "version": "6.12.6",
+            "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+            "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+            "dependencies": {
+                "fast-deep-equal": "^3.1.1",
+                "fast-json-stable-stringify": "^2.0.0",
+                "json-schema-traverse": "^0.4.1",
+                "uri-js": "^4.2.2"
+            },
+            "funding": {
+                "type": "github",
+                "url": "https://github.com/sponsors/epoberezkin"
+            }
+        },
+        "node_modules/has": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+            "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+            "dependencies": {
+                "function-bind": "^1.1.1"
+            },
+            "engines": {
+                "node": ">= 0.4.0"
+            }
+        },
+        "node_modules/has-ansi": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+            "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+            "dependencies": {
+                "ansi-regex": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/has-ansi/node_modules/ansi-regex": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+            "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/has-bigints": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
+            "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==",
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/has-binary2": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
+            "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
+            "dependencies": {
+                "isarray": "2.0.1"
+            }
+        },
+        "node_modules/has-binary2/node_modules/isarray": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+            "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+        },
+        "node_modules/has-cors": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
+            "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
+        },
+        "node_modules/has-flag": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+            "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/has-symbols": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+            "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/has-tostringtag": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
+            "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+            "dependencies": {
+                "has-symbols": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/has-tostringtag/node_modules/has-symbols": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
+            "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/has-unicode": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+            "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
+        },
+        "node_modules/has-value": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+            "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+            "dependencies": {
+                "get-value": "^2.0.6",
+                "has-values": "^1.0.0",
+                "isobject": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/has-values": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+            "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+            "dependencies": {
+                "is-number": "^3.0.0",
+                "kind-of": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/has-values/node_modules/is-buffer": {
+            "version": "1.1.6",
+            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+            "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+        },
+        "node_modules/has-values/node_modules/kind-of": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+            "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+            "dependencies": {
+                "is-buffer": "^1.1.5"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/he": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+            "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+            "bin": {
+                "he": "bin/he"
+            }
+        },
+        "node_modules/hoist-non-react-statics": {
+            "version": "3.3.2",
+            "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+            "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+            "dependencies": {
+                "react-is": "^16.7.0"
+            }
+        },
+        "node_modules/hosted-git-info": {
+            "version": "2.8.8",
+            "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
+            "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg=="
+        },
+        "node_modules/howler": {
+            "version": "2.2.3",
+            "resolved": "https://registry.npmjs.org/howler/-/howler-2.2.3.tgz",
+            "integrity": "sha512-QM0FFkw0LRX1PR8pNzJVAY25JhIWvbKMBFM4gqk+QdV+kPXOhleWGCB6AiAF/goGjIHK2e/nIElplvjQwhr0jg=="
+        },
+        "node_modules/hpack.js": {
+            "version": "2.1.6",
+            "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
+            "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=",
+            "dev": true,
+            "dependencies": {
+                "inherits": "^2.0.1",
+                "obuf": "^1.0.0",
+                "readable-stream": "^2.0.1",
+                "wbuf": "^1.1.0"
+            }
+        },
+        "node_modules/hpack.js/node_modules/readable-stream": {
+            "version": "2.3.7",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+            "dev": true,
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/html": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/html/-/html-1.0.0.tgz",
+            "integrity": "sha1-pUT6nqVJK/s6LMqCEKEL57WvH2E=",
+            "dependencies": {
+                "concat-stream": "^1.4.7"
+            },
+            "bin": {
+                "html": "bin/html.js"
+            }
+        },
+        "node_modules/html-encoding-sniffer": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
+            "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==",
+            "dev": true,
+            "dependencies": {
+                "whatwg-encoding": "^1.0.1"
+            }
+        },
+        "node_modules/html-entities": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz",
+            "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==",
+            "dev": true
+        },
+        "node_modules/html-minifier-terser": {
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+            "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==",
+            "dependencies": {
+                "camel-case": "^4.1.2",
+                "clean-css": "^5.2.2",
+                "commander": "^8.3.0",
+                "he": "^1.2.0",
+                "param-case": "^3.0.4",
+                "relateurl": "^0.2.7",
+                "terser": "^5.10.0"
+            },
+            "bin": {
+                "html-minifier-terser": "cli.js"
+            },
+            "engines": {
+                "node": ">=12"
+            }
+        },
+        "node_modules/html-minifier-terser/node_modules/clean-css": {
+            "version": "5.2.4",
+            "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.2.4.tgz",
+            "integrity": "sha512-nKseG8wCzEuji/4yrgM/5cthL9oTDc5UOQyFMvW/Q53oP6gLH690o1NbuTh6Y18nujr7BxlsFuS7gXLnLzKJGg==",
+            "dependencies": {
+                "source-map": "~0.6.0"
+            },
+            "engines": {
+                "node": ">= 10.0"
+            }
+        },
+        "node_modules/html-minifier-terser/node_modules/commander": {
+            "version": "8.3.0",
+            "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+            "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+            "engines": {
+                "node": ">= 12"
+            }
+        },
+        "node_modules/html-minifier-terser/node_modules/source-map": {
+            "version": "0.6.1",
+            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+            "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/html-to-image": {
+            "version": "0.1.3",
+            "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-0.1.3.tgz",
+            "integrity": "sha512-8JTGEAAdJGL/nlp3wb/WI8fLMx2dHKOFZMdsvdon23D45ZdtsXDeRm39Wddf04ludQe3OPmvjMJ9nPjI/7hPlg=="
+        },
+        "node_modules/html-to-text": {
+            "version": "5.1.1",
+            "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-5.1.1.tgz",
+            "integrity": "sha512-Bci6bD/JIfZSvG4s0gW/9mMKwBRoe/1RWLxUME/d6WUSZCdY7T60bssf/jFf7EYXRyqU4P5xdClVqiYU0/ypdA==",
+            "dependencies": {
+                "he": "^1.2.0",
+                "htmlparser2": "^3.10.1",
+                "lodash": "^4.17.11",
+                "minimist": "^1.2.0"
+            },
+            "bin": {
+                "html-to-text": "bin/cli.js"
+            },
+            "engines": {
+                "node": ">= 4.0.0"
+            }
+        },
+        "node_modules/html-webpack-plugin": {
+            "version": "5.5.0",
+            "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz",
+            "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==",
+            "dependencies": {
+                "@types/html-minifier-terser": "^6.0.0",
+                "html-minifier-terser": "^6.0.2",
+                "lodash": "^4.17.21",
+                "pretty-error": "^4.0.0",
+                "tapable": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=10.13.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/html-webpack-plugin"
+            },
+            "peerDependencies": {
+                "webpack": "^5.20.0"
+            }
+        },
+        "node_modules/html-webpack-plugin/node_modules/tapable": {
+            "version": "2.2.1",
+            "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+            "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/htmlparser2": {
+            "version": "3.10.1",
+            "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
+            "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/http-browserify": {
+            "version": "1.7.0",
+            "resolved": "https://registry.npmjs.org/http-browserify/-/http-browserify-1.7.0.tgz",
+            "integrity": "sha1-M3la3nLfiKz7/TZ3PO/tp2RzWyA=",
+            "dependencies": {
+                "Base64": "~0.2.0",
+                "inherits": "~2.0.1"
+            }
+        },
+        "node_modules/http-cache-semantics": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
+            "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
+        },
+        "node_modules/http-deceiver": {
+            "version": "1.2.7",
+            "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
+            "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=",
+            "dev": true
+        },
+        "node_modules/http-errors": {
+            "version": "1.8.1",
+            "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
+            "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
+            "dependencies": {
+                "depd": "~1.1.2",
+                "inherits": "2.0.4",
+                "setprototypeof": "1.2.0",
+                "statuses": ">= 1.5.0 < 2",
+                "toidentifier": "1.0.1"
+            },
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/http-parser-js": {
+            "version": "0.5.5",
+            "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz",
+            "integrity": "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==",
+            "dev": true
+        },
+        "node_modules/http-proxy": {
+            "version": "1.18.1",
+            "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+            "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+            "dev": true,
+            "dependencies": {
+                "eventemitter3": "^4.0.0",
+                "follow-redirects": "^1.0.0",
+                "requires-port": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=8.0.0"
+            }
+        },
+        "node_modules/http-proxy-middleware": {
+            "version": "0.19.1",
+            "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz",
+            "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==",
+            "dev": true,
+            "dependencies": {
+                "http-proxy": "^1.17.0",
+                "is-glob": "^4.0.0",
+                "lodash": "^4.17.11",
+                "micromatch": "^3.1.10"
+            },
+            "engines": {
+                "node": ">=4.0.0"
+            }
+        },
+        "node_modules/http-signature": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+            "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+            "dependencies": {
+                "assert-plus": "^1.0.0",
+                "jsprim": "^1.2.2",
+                "sshpk": "^1.7.0"
+            },
+            "engines": {
+                "node": ">=0.8",
+                "npm": ">=1.3.7"
+            }
+        },
+        "node_modules/http2-wrapper": {
+            "version": "2.1.10",
+            "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.1.10.tgz",
+            "integrity": "sha512-QHgsdYkieKp+6JbXP25P+tepqiHYd+FVnDwXpxi/BlUcoIB0nsmTOymTNvETuTO+pDuwcSklPE72VR3DqV+Haw==",
+            "dependencies": {
+                "quick-lru": "^5.1.1",
+                "resolve-alpn": "^1.2.0"
+            },
+            "engines": {
+                "node": ">=10.19.0"
+            }
+        },
+        "node_modules/https": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz",
+            "integrity": "sha1-PDfHrhqO65ZpBKKtHpdaGUt+06Q="
+        },
+        "node_modules/https-browserify": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
+            "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM="
+        },
+        "node_modules/https-proxy-agent": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
+            "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
+            "dependencies": {
+                "agent-base": "6",
+                "debug": "4"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/https-proxy-agent/node_modules/debug": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+            "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+            "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
+            "dependencies": {
+                "ms": "^2.1.1"
+            }
+        },
+        "node_modules/human-signals": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+            "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+            "engines": {
+                "node": ">=10.17.0"
+            }
+        },
+        "node_modules/hyntax": {
+            "version": "1.1.9",
+            "resolved": "https://registry.npmjs.org/hyntax/-/hyntax-1.1.9.tgz",
+            "integrity": "sha512-xjxyDLbVDdLgjPnl4NM+Iu6il3UPmk6PNCBXruQKeuKDc/HtaZx1hk1AtMgw3vsn9YnLZRfoBpPxYMXcoT5KAA==",
+            "engines": {
+                "node": ">=6.11.1",
+                "npm": ">=5.3.0"
+            }
+        },
+        "node_modules/hyphenate-style-name": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz",
+            "integrity": "sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ=="
+        },
+        "node_modules/i": {
+            "version": "0.3.7",
+            "resolved": "https://registry.npmjs.org/i/-/i-0.3.7.tgz",
+            "integrity": "sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q==",
+            "engines": {
+                "node": ">=0.4"
+            }
+        },
+        "node_modules/iconv-lite": {
+            "version": "0.4.24",
+            "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+            "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+            "dependencies": {
+                "safer-buffer": ">= 2.1.2 < 3"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/icss-replace-symbols": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz",
+            "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=",
+            "dev": true
+        },
+        "node_modules/icss-utils": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz",
+            "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==",
+            "dev": true,
+            "dependencies": {
+                "postcss": "^7.0.14"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/ieee754": {
+            "version": "1.1.13",
+            "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+            "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
+        },
+        "node_modules/iferr": {
+            "version": "0.1.5",
+            "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
+            "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
+            "dev": true
+        },
+        "node_modules/ignore": {
+            "version": "3.3.10",
+            "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz",
+            "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==",
+            "dev": true
+        },
+        "node_modules/ignore-by-default": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
+            "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk="
+        },
+        "node_modules/image-data-uri": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/image-data-uri/-/image-data-uri-2.0.1.tgz",
+            "integrity": "sha512-BZh721F2Q5TwBdwpiqrBrHEdj8daj8KuMZK/DOCyqQlz1CqFhhuZWbK5ZCUnAvFJr8LaKHTaWl9ja3/a3DC2Ew==",
+            "dependencies": {
+                "fs-extra": "^0.26.7",
+                "magicli": "0.0.8",
+                "mime-types": "^2.1.18",
+                "request": "^2.88.0"
+            },
+            "bin": {
+                "image-data-uri": "bin/magicli.js"
+            }
+        },
+        "node_modules/image-data-uri/node_modules/fs-extra": {
+            "version": "0.26.7",
+            "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz",
+            "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=",
+            "dependencies": {
+                "graceful-fs": "^4.1.2",
+                "jsonfile": "^2.1.0",
+                "klaw": "^1.0.0",
+                "path-is-absolute": "^1.0.0",
+                "rimraf": "^2.2.8"
+            }
+        },
+        "node_modules/image-data-uri/node_modules/jsonfile": {
+            "version": "2.4.0",
+            "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
+            "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
+            "optionalDependencies": {
+                "graceful-fs": "^4.1.6"
+            }
+        },
+        "node_modules/image-data-uri/node_modules/rimraf": {
+            "version": "2.7.1",
+            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+            "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+            "dependencies": {
+                "glob": "^7.1.3"
+            },
+            "bin": {
+                "rimraf": "bin.js"
+            }
+        },
+        "node_modules/image-size": {
+            "version": "0.7.5",
+            "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.7.5.tgz",
+            "integrity": "sha512-Hiyv+mXHfFEP7LzUL/llg9RwFxxY+o9N3JVLIeG5E7iFIFAalxvRU9UZthBdYDEVnzHMgjnKJPPpay5BWf1g9g==",
+            "bin": {
+                "image-size": "bin/image-size.js"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/image-size-stream": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/image-size-stream/-/image-size-stream-1.1.0.tgz",
+            "integrity": "sha1-Ivou2mbG31AQh0bacUkmSy0l+Gs=",
+            "dependencies": {
+                "image-size": "git+https://github.com/netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1",
+                "readable-stream": "^1.0.33",
+                "tryit": "^1.0.1"
+            }
+        },
+        "node_modules/image-size-stream/node_modules/image-size": {
+            "version": "0.3.5",
+            "resolved": "git+ssh://git@github.com/netroy/image-size.git#da2c863807a3e9602617bdd357b0de3ab4a064c1",
+            "integrity": "sha512-UrxyP0H+R9mM8uExKWWHVIfJALOnowdrbcoBEYC9kcKsFA2xfoFX4II9h1RaMujOrZ3J9NxcqF/svaZXL3ePhw==",
+            "license": "MIT",
+            "bin": {
+                "image-size": "bin/image-size"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/image-size-stream/node_modules/isarray": {
+            "version": "0.0.1",
+            "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+            "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+        },
+        "node_modules/image-size-stream/node_modules/readable-stream": {
+            "version": "1.1.14",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+            "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+            "dependencies": {
+                "core-util-is": "~1.0.0",
+                "inherits": "~2.0.1",
+                "isarray": "0.0.1",
+                "string_decoder": "~0.10.x"
+            }
+        },
+        "node_modules/image-size-stream/node_modules/string_decoder": {
+            "version": "0.10.31",
+            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+            "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+        },
+        "node_modules/immediate": {
+            "version": "3.0.6",
+            "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+            "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
+        },
+        "node_modules/import-fresh": {
+            "version": "3.3.0",
+            "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+            "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+            "dependencies": {
+                "parent-module": "^1.0.0",
+                "resolve-from": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/import-lazy": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
+            "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/import-local": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
+            "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==",
+            "dev": true,
+            "dependencies": {
+                "pkg-dir": "^3.0.0",
+                "resolve-cwd": "^2.0.0"
+            },
+            "bin": {
+                "import-local-fixture": "fixtures/cli.js"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/import-local/node_modules/pkg-dir": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+            "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+            "dev": true,
+            "dependencies": {
+                "find-up": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/imurmurhash": {
+            "version": "0.1.4",
+            "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+            "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+            "engines": {
+                "node": ">=0.8.19"
+            }
+        },
+        "node_modules/in-publish": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz",
+            "integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==",
+            "bin": {
+                "in-install": "in-install.js",
+                "in-publish": "in-publish.js",
+                "not-in-install": "not-in-install.js",
+                "not-in-publish": "not-in-publish.js"
+            }
+        },
+        "node_modules/indent-string": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
+            "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
+            "dependencies": {
+                "repeating": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/indexes-of": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
+            "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
+            "dev": true
+        },
+        "node_modules/indexof": {
+            "version": "0.0.1",
+            "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
+            "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
+        },
+        "node_modules/inflight": {
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+            "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+            "dependencies": {
+                "once": "^1.3.0",
+                "wrappy": "1"
+            }
+        },
+        "node_modules/infobox-parser": {
+            "version": "3.6.2",
+            "resolved": "https://registry.npmjs.org/infobox-parser/-/infobox-parser-3.6.2.tgz",
+            "integrity": "sha512-lasdwvbtjCtDDO6mArAs/ueFEnBJRyo2UbZPAkd5rEG5NVJ3XFCOvbMwNTT/rJlFv1+ORw8D3UvZV4brpgATCg==",
+            "dependencies": {
+                "camelcase": "^4.1.0"
+            },
+            "funding": {
+                "type": "individual",
+                "url": "https://www.buymeacoffee.com/2tmRKi9"
+            }
+        },
+        "node_modules/infobox-parser/node_modules/camelcase": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+            "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/inherits": {
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+            "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+        },
+        "node_modules/ini": {
+            "version": "1.3.5",
+            "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+            "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+            "deprecated": "Please update to ini >=1.3.6 to avoid a prototype pollution issue",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/inline-style-prefixer": {
+            "version": "3.0.8",
+            "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-3.0.8.tgz",
+            "integrity": "sha1-hVG45bTVcyROZqNLBPfTIHaitTQ=",
+            "dependencies": {
+                "bowser": "^1.7.3",
+                "css-in-js-utils": "^2.0.0"
+            }
+        },
+        "node_modules/inspect-function": {
+            "version": "0.2.2",
+            "resolved": "https://registry.npmjs.org/inspect-function/-/inspect-function-0.2.2.tgz",
+            "integrity": "sha1-hdoMUli8TDMK4yg7Z0fgdZ2QpjU=",
+            "dependencies": {
+                "split-skip": "0.0.1",
+                "unpack-string": "0.0.2"
+            }
+        },
+        "node_modules/inspect-function/node_modules/split-skip": {
+            "version": "0.0.1",
+            "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.1.tgz",
+            "integrity": "sha1-gK2ONumOV2RUzDtmfB3SXYZejwA="
+        },
+        "node_modules/inspect-parameters-declaration": {
+            "version": "0.0.9",
+            "resolved": "https://registry.npmjs.org/inspect-parameters-declaration/-/inspect-parameters-declaration-0.0.9.tgz",
+            "integrity": "sha512-c3jrKKA1rwwrsjdGMAo2hFWV0vNe3/RKHxpE/OBt41LP3ynOVI1qmgxpZYK5SQu3jtWCyaho8L7AZzCjJ4mEUw==",
+            "dependencies": {
+                "magicli": "0.0.5",
+                "split-skip": "0.0.2",
+                "stringify-parameters": "0.0.4",
+                "unpack-string": "0.0.2"
+            },
+            "bin": {
+                "inspect-parameters-declaration": "bin/cli.js"
+            }
+        },
+        "node_modules/inspect-parameters-declaration/node_modules/magicli": {
+            "version": "0.0.5",
+            "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz",
+            "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=",
+            "dependencies": {
+                "commander": "^2.9.0",
+                "get-stdin": "^5.0.1",
+                "inspect-function": "^0.2.1",
+                "pipe-functions": "^1.2.0"
+            }
+        },
+        "node_modules/inspect-property": {
+            "version": "0.0.6",
+            "resolved": "https://registry.npmjs.org/inspect-property/-/inspect-property-0.0.6.tgz",
+            "integrity": "sha512-LgjHkRl9W6bj2n+kWrAOgvCYPTYt+LanE4rtd/vKNq6yEb+SvVV7UTLzoSPpDX6/U1cAz7VfqPr+lPAIz7wHaQ==",
+            "dependencies": {
+                "for-each-property": "0.0.4",
+                "for-each-property-deep": "0.0.3",
+                "inspect-function": "^0.3.1"
+            }
+        },
+        "node_modules/inspect-property/node_modules/inspect-function": {
+            "version": "0.3.4",
+            "resolved": "https://registry.npmjs.org/inspect-function/-/inspect-function-0.3.4.tgz",
+            "integrity": "sha512-s0RsbJqK/sNZ+U1mykGoTickog3ea1A9Qk4mXniogOBu4PgkkZ56elScO7QC/r8D94lhGmJ2NyDI1ipOA/uq/g==",
+            "dependencies": {
+                "inspect-parameters-declaration": "0.0.8",
+                "magicli": "0.0.8",
+                "split-skip": "0.0.1",
+                "stringify-parameters": "0.0.4",
+                "unpack-string": "0.0.2"
+            },
+            "bin": {
+                "inspect-function": "bin/magicli.js"
+            }
+        },
+        "node_modules/inspect-property/node_modules/inspect-parameters-declaration": {
+            "version": "0.0.8",
+            "resolved": "https://registry.npmjs.org/inspect-parameters-declaration/-/inspect-parameters-declaration-0.0.8.tgz",
+            "integrity": "sha512-W4QzN1LgFmasKOM+NoLlDd2OAZM3enNZlVUOXoGQKmYBDFgxoPDOyebF55ALaf8avyM9TavNwibXxg347RrzCg==",
+            "dependencies": {
+                "magicli": "0.0.5",
+                "split-skip": "0.0.2",
+                "stringify-parameters": "0.0.4",
+                "unpack-string": "0.0.2"
+            },
+            "bin": {
+                "inspect-parameters-declaration": "bin/cli.js"
+            }
+        },
+        "node_modules/inspect-property/node_modules/inspect-parameters-declaration/node_modules/inspect-function": {
+            "version": "0.2.2",
+            "resolved": "https://registry.npmjs.org/inspect-function/-/inspect-function-0.2.2.tgz",
+            "integrity": "sha1-hdoMUli8TDMK4yg7Z0fgdZ2QpjU=",
+            "dependencies": {
+                "split-skip": "0.0.1",
+                "unpack-string": "0.0.2"
+            }
+        },
+        "node_modules/inspect-property/node_modules/inspect-parameters-declaration/node_modules/inspect-function/node_modules/split-skip": {
+            "version": "0.0.1",
+            "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.1.tgz",
+            "integrity": "sha1-gK2ONumOV2RUzDtmfB3SXYZejwA="
+        },
+        "node_modules/inspect-property/node_modules/inspect-parameters-declaration/node_modules/magicli": {
+            "version": "0.0.5",
+            "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz",
+            "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=",
+            "dependencies": {
+                "commander": "^2.9.0",
+                "get-stdin": "^5.0.1",
+                "inspect-function": "^0.2.1",
+                "pipe-functions": "^1.2.0"
+            }
+        },
+        "node_modules/inspect-property/node_modules/inspect-parameters-declaration/node_modules/split-skip": {
+            "version": "0.0.2",
+            "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.2.tgz",
+            "integrity": "sha1-2J2Iu9L3Pka1FYqjcKVhIk6A1GE="
+        },
+        "node_modules/inspect-property/node_modules/split-skip": {
+            "version": "0.0.1",
+            "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.1.tgz",
+            "integrity": "sha1-gK2ONumOV2RUzDtmfB3SXYZejwA="
+        },
+        "node_modules/internal-ip": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz",
+            "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==",
+            "dev": true,
+            "dependencies": {
+                "default-gateway": "^4.2.0",
+                "ipaddr.js": "^1.9.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/internal-slot": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz",
+            "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==",
+            "dependencies": {
+                "get-intrinsic": "^1.1.0",
+                "has": "^1.0.3",
+                "side-channel": "^1.0.4"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/internmap": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz",
+            "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="
+        },
+        "node_modules/interpret": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
+            "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
+            "engines": {
+                "node": ">= 0.10"
+            }
+        },
+        "node_modules/interval-arithmetic": {
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/interval-arithmetic/-/interval-arithmetic-1.0.6.tgz",
+            "integrity": "sha512-eVotDGYPNiEaJ63oa4CeEHgOczZJ3gNHqG5wfVQ2o8sN2CEczQyR82Sjey/Bp36x8e7PtBcBvitcMnw6VUpjgQ==",
+            "dependencies": {
+                "@types/assert": "^1.4.6",
+                "is-safe-integer": "^2.0.0",
+                "nextafter": "^1.0.0",
+                "typedarray": "0.0.6"
+            }
+        },
+        "node_modules/interval-arithmetic-eval": {
+            "version": "0.4.7",
+            "resolved": "https://registry.npmjs.org/interval-arithmetic-eval/-/interval-arithmetic-eval-0.4.7.tgz",
+            "integrity": "sha512-ClK+N4efbsgjlZR8h0qd0LQbyzUzJ9IkrjmTnD5MVb4Ytebd0lesoVP4AxLclcsEI+nIieskQ8cepHIWUPaRhQ==",
+            "dependencies": {
+                "interval-arithmetic": "^1.0.6",
+                "math-codegen": "^0.3.5"
+            }
+        },
+        "node_modules/invariant": {
+            "version": "2.2.4",
+            "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+            "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+            "dependencies": {
+                "loose-envify": "^1.0.0"
+            }
+        },
+        "node_modules/ip": {
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
+            "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
+            "dev": true
+        },
+        "node_modules/ip-regex": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
+            "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
+            "dev": true,
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/ipaddr.js": {
+            "version": "1.9.1",
+            "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+            "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+            "engines": {
+                "node": ">= 0.10"
+            }
+        },
+        "node_modules/is-absolute-url": {
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz",
+            "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==",
+            "dev": true,
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/is-accessor-descriptor": {
+            "version": "0.1.6",
+            "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+            "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+            "dependencies": {
+                "kind-of": "^3.0.2"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-accessor-descriptor/node_modules/is-buffer": {
+            "version": "1.1.6",
+            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+            "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+        },
+        "node_modules/is-accessor-descriptor/node_modules/kind-of": {
+            "version": "3.2.2",
+            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+            "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+            "dependencies": {
+                "is-buffer": "^1.1.5"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-arguments": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
+            "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "has-tostringtag": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-arrayish": {
+            "version": "0.2.1",
+            "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+            "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
+        },
+        "node_modules/is-bigint": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
+            "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
+            "dependencies": {
+                "has-bigints": "^1.0.1"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-binary-path": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+            "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+            "dependencies": {
+                "binary-extensions": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-boolean-object": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
+            "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "has-tostringtag": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-buffer": {
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
+            "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/is-callable": {
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
+            "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-ci": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz",
+            "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==",
+            "dependencies": {
+                "ci-info": "^1.5.0"
+            },
+            "bin": {
+                "is-ci": "bin.js"
+            }
+        },
+        "node_modules/is-data-descriptor": {
+            "version": "0.1.4",
+            "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+            "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+            "dependencies": {
+                "kind-of": "^3.0.2"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-data-descriptor/node_modules/is-buffer": {
+            "version": "1.1.6",
+            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+            "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+        },
+        "node_modules/is-data-descriptor/node_modules/kind-of": {
+            "version": "3.2.2",
+            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+            "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+            "dependencies": {
+                "is-buffer": "^1.1.5"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-date-object": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
+            "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-descriptor": {
+            "version": "0.1.6",
+            "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+            "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+            "dependencies": {
+                "is-accessor-descriptor": "^0.1.6",
+                "is-data-descriptor": "^0.1.4",
+                "kind-of": "^5.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-descriptor/node_modules/kind-of": {
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+            "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-directory": {
+            "version": "0.3.1",
+            "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
+            "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-expression": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz",
+            "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=",
+            "dependencies": {
+                "acorn": "~4.0.2",
+                "object-assign": "^4.0.1"
+            }
+        },
+        "node_modules/is-expression/node_modules/acorn": {
+            "version": "4.0.13",
+            "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
+            "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=",
+            "bin": {
+                "acorn": "bin/acorn"
+            },
+            "engines": {
+                "node": ">=0.4.0"
+            }
+        },
+        "node_modules/is-extendable": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+            "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+            "dependencies": {
+                "is-plain-object": "^2.0.4"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-extglob": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+            "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-finite": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz",
+            "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==",
+            "engines": {
+                "node": ">=0.10.0"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/is-fullwidth-code-point": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+            "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/is-generator-function": {
+            "version": "1.0.10",
+            "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
+            "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
+            "dependencies": {
+                "has-tostringtag": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-glob": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+            "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+            "dependencies": {
+                "is-extglob": "^2.1.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-in-browser": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz",
+            "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU="
+        },
+        "node_modules/is-installed-globally": {
+            "version": "0.1.0",
+            "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz",
+            "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=",
+            "dependencies": {
+                "global-dirs": "^0.1.0",
+                "is-path-inside": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/is-negative-zero": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
+            "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-npm": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz",
+            "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-number": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+            "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+            "dependencies": {
+                "kind-of": "^3.0.2"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-number-object": {
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz",
+            "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==",
+            "dependencies": {
+                "has-tostringtag": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-number/node_modules/is-buffer": {
+            "version": "1.1.6",
+            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+            "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+        },
+        "node_modules/is-number/node_modules/kind-of": {
+            "version": "3.2.2",
+            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+            "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+            "dependencies": {
+                "is-buffer": "^1.1.5"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-obj": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+            "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-path-cwd": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
+            "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==",
+            "dev": true,
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/is-path-in-cwd": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz",
+            "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==",
+            "dev": true,
+            "dependencies": {
+                "is-path-inside": "^2.1.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/is-path-in-cwd/node_modules/is-path-inside": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz",
+            "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==",
+            "dev": true,
+            "dependencies": {
+                "path-is-inside": "^1.0.2"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/is-path-inside": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
+            "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
+            "dependencies": {
+                "path-is-inside": "^1.0.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-plain-object": {
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+            "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+            "dependencies": {
+                "isobject": "^3.0.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-promise": {
+            "version": "2.2.2",
+            "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
+            "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ=="
+        },
+        "node_modules/is-redirect": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
+            "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-regex": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
+            "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
+            "dependencies": {
+                "has": "^1.0.3"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-retry-allowed": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz",
+            "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-safe-integer": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/is-safe-integer/-/is-safe-integer-2.0.0.tgz",
+            "integrity": "sha512-eDaA39/1+3SNtYTRP28lRYOHMwiB1gfqXQaXcf/+f4mLwKgm8TTDkwJldsdtbgrK1R5CoDbf6AQ0KqP7BKoGtQ==",
+            "deprecated": "This package is no longer relevant as ES2015 support is widespread.",
+            "dependencies": {
+                "max-safe-integer": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-shared-array-buffer": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz",
+            "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==",
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-stream": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+            "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-string": {
+            "version": "1.0.7",
+            "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
+            "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
+            "dependencies": {
+                "has-tostringtag": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-symbol": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
+            "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+            "dependencies": {
+                "has-symbols": "^1.0.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-typed-array": {
+            "version": "1.1.8",
+            "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz",
+            "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==",
+            "dependencies": {
+                "available-typed-arrays": "^1.0.5",
+                "call-bind": "^1.0.2",
+                "es-abstract": "^1.18.5",
+                "foreach": "^2.0.5",
+                "has-tostringtag": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-typed-array/node_modules/es-abstract": {
+            "version": "1.19.1",
+            "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz",
+            "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==",
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "es-to-primitive": "^1.2.1",
+                "function-bind": "^1.1.1",
+                "get-intrinsic": "^1.1.1",
+                "get-symbol-description": "^1.0.0",
+                "has": "^1.0.3",
+                "has-symbols": "^1.0.2",
+                "internal-slot": "^1.0.3",
+                "is-callable": "^1.2.4",
+                "is-negative-zero": "^2.0.1",
+                "is-regex": "^1.1.4",
+                "is-shared-array-buffer": "^1.0.1",
+                "is-string": "^1.0.7",
+                "is-weakref": "^1.0.1",
+                "object-inspect": "^1.11.0",
+                "object-keys": "^1.1.1",
+                "object.assign": "^4.1.2",
+                "string.prototype.trimend": "^1.0.4",
+                "string.prototype.trimstart": "^1.0.4",
+                "unbox-primitive": "^1.0.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-typed-array/node_modules/has-symbols": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+            "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-typed-array/node_modules/is-callable": {
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz",
+            "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-typed-array/node_modules/is-regex": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+            "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "has-tostringtag": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-typed-array/node_modules/object-inspect": {
+            "version": "1.12.0",
+            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
+            "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==",
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-typed-array/node_modules/object.assign": {
+            "version": "4.1.2",
+            "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+            "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+            "dependencies": {
+                "call-bind": "^1.0.0",
+                "define-properties": "^1.1.3",
+                "has-symbols": "^1.0.1",
+                "object-keys": "^1.1.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-typed-array/node_modules/string.prototype.trimend": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
+            "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-typed-array/node_modules/string.prototype.trimstart": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
+            "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-typedarray": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+            "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
+        },
+        "node_modules/is-utf8": {
+            "version": "0.2.1",
+            "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+            "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI="
+        },
+        "node_modules/is-weakref": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
+            "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
+            "dependencies": {
+                "call-bind": "^1.0.2"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/is-what": {
+            "version": "3.10.0",
+            "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.10.0.tgz",
+            "integrity": "sha512-U4RYCXNOmATQHlOPlOCHCfXyKEFIPqvyaKDqYRuLbD6EYKcTTfc3YXkAYjzOVxO3zt34L+Wh2feIyWrYiZ7kng=="
+        },
+        "node_modules/is-windows": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+            "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/is-wsl": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+            "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
+            "dev": true,
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/isarray": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+            "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+        },
+        "node_modules/isexe": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+            "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
+        },
+        "node_modules/isobject": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+            "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/isomorphic-fetch": {
+            "version": "2.2.1",
+            "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
+            "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
+            "dependencies": {
+                "node-fetch": "^1.0.1",
+                "whatwg-fetch": ">=0.10.0"
+            }
+        },
+        "node_modules/isstream": {
+            "version": "0.1.2",
+            "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+            "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+        },
+        "node_modules/its-set": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/its-set/-/its-set-1.2.3.tgz",
+            "integrity": "sha512-UQc+xLLn+0a8KKRXRj3OS2kERK8G7zcayPpPULqZnPwuJ1hGWEO8+j0T5eycu7DKXYjezw3pyF8oV1fJkJxV5w==",
+            "dependencies": {
+                "babel-runtime": "6.x.x",
+                "lodash.get": "^4.4.2"
+            }
+        },
+        "node_modules/jest-worker": {
+            "version": "27.5.1",
+            "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+            "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+            "dependencies": {
+                "@types/node": "*",
+                "merge-stream": "^2.0.0",
+                "supports-color": "^8.0.0"
+            },
+            "engines": {
+                "node": ">= 10.13.0"
+            }
+        },
+        "node_modules/jest-worker/node_modules/has-flag": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+            "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/jest-worker/node_modules/supports-color": {
+            "version": "8.1.1",
+            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+            "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+            "dependencies": {
+                "has-flag": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/chalk/supports-color?sponsor=1"
+            }
+        },
+        "node_modules/jquery": {
+            "version": "3.5.1",
+            "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz",
+            "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg=="
+        },
+        "node_modules/js-base64": {
+            "version": "2.5.2",
+            "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz",
+            "integrity": "sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ=="
+        },
+        "node_modules/js-datepicker": {
+            "version": "4.6.6",
+            "resolved": "https://registry.npmjs.org/js-datepicker/-/js-datepicker-4.6.6.tgz",
+            "integrity": "sha512-kLsE2oHfQM6pzdWNA7Y8HwCHaZIVURItPds7YVJnbe1z1OmQFzN7lfg6yFbDDWOyNjdHZGzWDy3jSDN2W0pNZQ=="
+        },
+        "node_modules/js-stringify": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz",
+            "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds="
+        },
+        "node_modules/js-tokens": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+            "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+        },
+        "node_modules/js-yaml": {
+            "version": "3.13.1",
+            "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+            "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+            "dependencies": {
+                "argparse": "^1.0.7",
+                "esprima": "^4.0.0"
+            },
+            "bin": {
+                "js-yaml": "bin/js-yaml.js"
+            }
+        },
+        "node_modules/jsbn": {
+            "version": "0.1.1",
+            "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+            "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
+        },
+        "node_modules/jsdom": {
+            "version": "15.2.1",
+            "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-15.2.1.tgz",
+            "integrity": "sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g==",
+            "dev": true,
+            "dependencies": {
+                "abab": "^2.0.0",
+                "acorn": "^7.1.0",
+                "acorn-globals": "^4.3.2",
+                "array-equal": "^1.0.0",
+                "cssom": "^0.4.1",
+                "cssstyle": "^2.0.0",
+                "data-urls": "^1.1.0",
+                "domexception": "^1.0.1",
+                "escodegen": "^1.11.1",
+                "html-encoding-sniffer": "^1.0.2",
+                "nwsapi": "^2.2.0",
+                "parse5": "5.1.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"
+            },
+            "engines": {
+                "node": ">=8"
+            },
+            "peerDependencies": {
+                "canvas": "^2.5.0"
+            },
+            "peerDependenciesMeta": {
+                "canvas": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/jsdom/node_modules/acorn": {
+            "version": "7.1.1",
+            "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
+            "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==",
+            "dev": true,
+            "bin": {
+                "acorn": "bin/acorn"
+            },
+            "engines": {
+                "node": ">=0.4.0"
+            }
+        },
+        "node_modules/jsdom/node_modules/acorn-globals": {
+            "version": "4.3.4",
+            "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz",
+            "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==",
+            "dev": true,
+            "dependencies": {
+                "acorn": "^6.0.1",
+                "acorn-walk": "^6.0.1"
+            }
+        },
+        "node_modules/jsdom/node_modules/acorn-globals/node_modules/acorn": {
+            "version": "6.4.1",
+            "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
+            "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
+            "dev": true,
+            "bin": {
+                "acorn": "bin/acorn"
+            },
+            "engines": {
+                "node": ">=0.4.0"
+            }
+        },
+        "node_modules/jsdom/node_modules/parse5": {
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
+            "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==",
+            "dev": true
+        },
+        "node_modules/jsdom/node_modules/tough-cookie": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
+            "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==",
+            "dev": true,
+            "dependencies": {
+                "ip-regex": "^2.1.0",
+                "psl": "^1.1.28",
+                "punycode": "^2.1.1"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/jsesc": {
+            "version": "2.5.2",
+            "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+            "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+            "bin": {
+                "jsesc": "bin/jsesc"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/json-bigint": {
+            "version": "0.3.0",
+            "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz",
+            "integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=",
+            "dependencies": {
+                "bignumber.js": "^7.0.0"
+            }
+        },
+        "node_modules/json-buffer": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+            "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
+        },
+        "node_modules/json-parse-better-errors": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+            "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
+        },
+        "node_modules/json-parse-even-better-errors": {
+            "version": "2.3.1",
+            "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+            "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
+        },
+        "node_modules/json-schema": {
+            "version": "0.4.0",
+            "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+            "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="
+        },
+        "node_modules/json-schema-traverse": {
+            "version": "0.4.1",
+            "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+            "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+        },
+        "node_modules/json-stringify-safe": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+            "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
+        },
+        "node_modules/jsondiffpatch": {
+            "version": "0.4.1",
+            "resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.4.1.tgz",
+            "integrity": "sha512-t0etAxTUk1w5MYdNOkZBZ8rvYYN5iL+2dHCCx/DpkFm/bW28M6y5nUS83D4XdZiHy35Fpaw6LBb+F88fHZnVCw==",
+            "dependencies": {
+                "chalk": "^2.3.0",
+                "diff-match-patch": "^1.0.0"
+            },
+            "bin": {
+                "jsondiffpatch": "bin/jsondiffpatch"
+            },
+            "engines": {
+                "node": ">=8.17.0"
+            }
+        },
+        "node_modules/jsonschema": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.0.tgz",
+            "integrity": "sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw==",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/jsprim": {
+            "version": "1.4.2",
+            "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
+            "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
+            "dependencies": {
+                "assert-plus": "1.0.0",
+                "extsprintf": "1.3.0",
+                "json-schema": "0.4.0",
+                "verror": "1.10.0"
+            },
+            "engines": {
+                "node": ">=0.6.0"
+            }
+        },
+        "node_modules/jss": {
+            "version": "10.9.0",
+            "resolved": "https://registry.npmjs.org/jss/-/jss-10.9.0.tgz",
+            "integrity": "sha512-YpzpreB6kUunQBbrlArlsMpXYyndt9JATbt95tajx0t4MTJJcCJdd4hdNpHmOIDiUJrF/oX5wtVFrS3uofWfGw==",
+            "dependencies": {
+                "@babel/runtime": "^7.3.1",
+                "csstype": "^3.0.2",
+                "is-in-browser": "^1.1.3",
+                "tiny-warning": "^1.0.2"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/jss"
+            }
+        },
+        "node_modules/jss-plugin-camel-case": {
+            "version": "10.9.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.9.0.tgz",
+            "integrity": "sha512-UH6uPpnDk413/r/2Olmw4+y54yEF2lRIV8XIZyuYpgPYTITLlPOsq6XB9qeqv+75SQSg3KLocq5jUBXW8qWWww==",
+            "dependencies": {
+                "@babel/runtime": "^7.3.1",
+                "hyphenate-style-name": "^1.0.3",
+                "jss": "10.9.0"
+            }
+        },
+        "node_modules/jss-plugin-default-unit": {
+            "version": "10.9.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.9.0.tgz",
+            "integrity": "sha512-7Ju4Q9wJ/MZPsxfu4T84mzdn7pLHWeqoGd/D8O3eDNNJ93Xc8PxnLmV8s8ZPNRYkLdxZqKtm1nPQ0BM4JRlq2w==",
+            "dependencies": {
+                "@babel/runtime": "^7.3.1",
+                "jss": "10.9.0"
+            }
+        },
+        "node_modules/jss-plugin-global": {
+            "version": "10.9.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.9.0.tgz",
+            "integrity": "sha512-4G8PHNJ0x6nwAFsEzcuVDiBlyMsj2y3VjmFAx/uHk/R/gzJV+yRHICjT4MKGGu1cJq2hfowFWCyrr/Gg37FbgQ==",
+            "dependencies": {
+                "@babel/runtime": "^7.3.1",
+                "jss": "10.9.0"
+            }
+        },
+        "node_modules/jss-plugin-nested": {
+            "version": "10.9.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.9.0.tgz",
+            "integrity": "sha512-2UJnDrfCZpMYcpPYR16oZB7VAC6b/1QLsRiAutOt7wJaaqwCBvNsosLEu/fUyKNQNGdvg2PPJFDO5AX7dwxtoA==",
+            "dependencies": {
+                "@babel/runtime": "^7.3.1",
+                "jss": "10.9.0",
+                "tiny-warning": "^1.0.2"
+            }
+        },
+        "node_modules/jss-plugin-props-sort": {
+            "version": "10.9.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.9.0.tgz",
+            "integrity": "sha512-7A76HI8bzwqrsMOJTWKx/uD5v+U8piLnp5bvru7g/3ZEQOu1+PjHvv7bFdNO3DwNPC9oM0a//KwIJsIcDCjDzw==",
+            "dependencies": {
+                "@babel/runtime": "^7.3.1",
+                "jss": "10.9.0"
+            }
+        },
+        "node_modules/jss-plugin-rule-value-function": {
+            "version": "10.9.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.9.0.tgz",
+            "integrity": "sha512-IHJv6YrEf8pRzkY207cPmdbBstBaE+z8pazhPShfz0tZSDtRdQua5jjg6NMz3IbTasVx9FdnmptxPqSWL5tyJg==",
+            "dependencies": {
+                "@babel/runtime": "^7.3.1",
+                "jss": "10.9.0",
+                "tiny-warning": "^1.0.2"
+            }
+        },
+        "node_modules/jss-plugin-vendor-prefixer": {
+            "version": "10.9.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.9.0.tgz",
+            "integrity": "sha512-MbvsaXP7iiVdYVSEoi+blrW+AYnTDvHTW6I6zqi7JcwXdc6I9Kbm234nEblayhF38EftoenbM+5218pidmC5gA==",
+            "dependencies": {
+                "@babel/runtime": "^7.3.1",
+                "css-vendor": "^2.0.8",
+                "jss": "10.9.0"
+            }
+        },
+        "node_modules/jss/node_modules/csstype": {
+            "version": "3.0.10",
+            "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
+            "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
+        },
+        "node_modules/jstransformer": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz",
+            "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=",
+            "dependencies": {
+                "is-promise": "^2.0.0",
+                "promise": "^7.0.1"
+            }
+        },
+        "node_modules/jszip": {
+            "version": "3.7.1",
+            "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz",
+            "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==",
+            "dependencies": {
+                "lie": "~3.3.0",
+                "pako": "~1.0.2",
+                "readable-stream": "~2.3.6",
+                "set-immediate-shim": "~1.0.1"
+            }
+        },
+        "node_modules/jszip/node_modules/readable-stream": {
+            "version": "2.3.7",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/jwa": {
+            "version": "1.4.1",
+            "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+            "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+            "dependencies": {
+                "buffer-equal-constant-time": "1.0.1",
+                "ecdsa-sig-formatter": "1.0.11",
+                "safe-buffer": "^5.0.1"
+            }
+        },
+        "node_modules/jws": {
+            "version": "3.2.2",
+            "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+            "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+            "dependencies": {
+                "jwa": "^1.4.1",
+                "safe-buffer": "^5.0.1"
+            }
+        },
+        "node_modules/kareem": {
+            "version": "2.3.2",
+            "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz",
+            "integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ=="
+        },
+        "node_modules/kdbush": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz",
+            "integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew=="
+        },
+        "node_modules/keycode": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz",
+            "integrity": "sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ="
+        },
+        "node_modules/keygrip": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
+            "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==",
+            "dependencies": {
+                "tsscmp": "1.0.6"
+            },
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/keyv": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz",
+            "integrity": "sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==",
+            "dependencies": {
+                "json-buffer": "3.0.1"
+            }
+        },
+        "node_modules/killable": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
+            "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==",
+            "dev": true
+        },
+        "node_modules/kind-of": {
+            "version": "6.0.3",
+            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+            "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/klaw": {
+            "version": "1.3.1",
+            "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
+            "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=",
+            "optionalDependencies": {
+                "graceful-fs": "^4.1.9"
+            }
+        },
+        "node_modules/latest-version": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz",
+            "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=",
+            "dependencies": {
+                "package-json": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/lazy-cache": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
+            "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/lazystream": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz",
+            "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=",
+            "dependencies": {
+                "readable-stream": "^2.0.5"
+            },
+            "engines": {
+                "node": ">= 0.6.3"
+            }
+        },
+        "node_modules/lazystream/node_modules/readable-stream": {
+            "version": "2.3.7",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/levn": {
+            "version": "0.3.0",
+            "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+            "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+            "dev": true,
+            "dependencies": {
+                "prelude-ls": "~1.1.2",
+                "type-check": "~0.3.2"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/lie": {
+            "version": "3.3.0",
+            "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+            "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+            "dependencies": {
+                "immediate": "~3.0.5"
+            }
+        },
+        "node_modules/lines-and-columns": {
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+            "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
+        },
+        "node_modules/load-json-file": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+            "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
+            "dependencies": {
+                "graceful-fs": "^4.1.2",
+                "parse-json": "^2.2.0",
+                "pify": "^2.0.0",
+                "pinkie-promise": "^2.0.0",
+                "strip-bom": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/load-json-file/node_modules/parse-json": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+            "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+            "dependencies": {
+                "error-ex": "^1.2.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/load-json-file/node_modules/pify": {
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+            "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/loader-runner": {
+            "version": "4.2.0",
+            "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz",
+            "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==",
+            "engines": {
+                "node": ">=6.11.5"
+            }
+        },
+        "node_modules/loader-utils": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
+            "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
+            "dependencies": {
+                "big.js": "^5.2.2",
+                "emojis-list": "^3.0.0",
+                "json5": "^1.0.1"
+            },
+            "engines": {
+                "node": ">=4.0.0"
+            }
+        },
+        "node_modules/loader-utils/node_modules/json5": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+            "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+            "dependencies": {
+                "minimist": "^1.2.0"
+            },
+            "bin": {
+                "json5": "lib/cli.js"
+            }
+        },
+        "node_modules/locate-path": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+            "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+            "dependencies": {
+                "p-locate": "^3.0.0",
+                "path-exists": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/lodash": {
+            "version": "4.17.21",
+            "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+            "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+        },
+        "node_modules/lodash-es": {
+            "version": "4.17.21",
+            "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+            "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
+        },
+        "node_modules/lodash._getnative": {
+            "version": "3.9.1",
+            "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
+            "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U="
+        },
+        "node_modules/lodash.chunk": {
+            "version": "4.2.0",
+            "resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz",
+            "integrity": "sha1-ZuXOH3btJ7QwPYxlEujRIW6BBrw="
+        },
+        "node_modules/lodash.curry": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz",
+            "integrity": "sha1-JI42By7ekGUB11lmIAqG2riyMXA="
+        },
+        "node_modules/lodash.debounce": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-3.1.1.tgz",
+            "integrity": "sha1-gSIRw3ipTMKdWqTjNGzwv846ffU=",
+            "dependencies": {
+                "lodash._getnative": "^3.0.0"
+            }
+        },
+        "node_modules/lodash.defaults": {
+            "version": "4.2.0",
+            "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
+            "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw="
+        },
+        "node_modules/lodash.difference": {
+            "version": "4.5.0",
+            "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
+            "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw="
+        },
+        "node_modules/lodash.flatten": {
+            "version": "4.4.0",
+            "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
+            "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
+        },
+        "node_modules/lodash.flow": {
+            "version": "3.5.0",
+            "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz",
+            "integrity": "sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o="
+        },
+        "node_modules/lodash.get": {
+            "version": "4.4.2",
+            "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+            "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
+        },
+        "node_modules/lodash.isequal": {
+            "version": "4.5.0",
+            "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+            "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+        },
+        "node_modules/lodash.isplainobject": {
+            "version": "4.0.6",
+            "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+            "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
+        },
+        "node_modules/lodash.memoize": {
+            "version": "4.1.2",
+            "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+            "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4="
+        },
+        "node_modules/lodash.merge": {
+            "version": "4.6.2",
+            "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+            "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
+        },
+        "node_modules/lodash.padend": {
+            "version": "4.6.1",
+            "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz",
+            "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4="
+        },
+        "node_modules/lodash.sortby": {
+            "version": "4.7.0",
+            "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
+            "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
+            "dev": true
+        },
+        "node_modules/lodash.throttle": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+            "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
+        },
+        "node_modules/lodash.union": {
+            "version": "4.6.0",
+            "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
+            "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg="
+        },
+        "node_modules/log-symbols": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
+            "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==",
+            "dependencies": {
+                "chalk": "^2.0.1"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/loglevel": {
+            "version": "1.6.8",
+            "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz",
+            "integrity": "sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==",
+            "dev": true,
+            "engines": {
+                "node": ">= 0.6.0"
+            },
+            "funding": {
+                "type": "tidelift",
+                "url": "https://tidelift.com/subscription/pkg/npm-loglevel?utm_medium=referral&utm_source=npm_fund"
+            }
+        },
+        "node_modules/loglevelnext": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/loglevelnext/-/loglevelnext-1.0.5.tgz",
+            "integrity": "sha512-V/73qkPuJmx4BcBF19xPBr+0ZRVBhc4POxvZTZdMeXpJ4NItXSJ/MSwuFT0kQJlCbXvdlZoQQ/418bS1y9Jh6A==",
+            "dev": true,
+            "dependencies": {
+                "es6-symbol": "^3.1.1",
+                "object.assign": "^4.1.0"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/longest": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
+            "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/loose-envify": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+            "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+            "dependencies": {
+                "js-tokens": "^3.0.0 || ^4.0.0"
+            },
+            "bin": {
+                "loose-envify": "cli.js"
+            }
+        },
+        "node_modules/loud-rejection": {
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
+            "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
+            "dependencies": {
+                "currently-unhandled": "^0.4.1",
+                "signal-exit": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/loupe": {
+            "version": "2.3.4",
+            "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz",
+            "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==",
+            "dependencies": {
+                "get-func-name": "^2.0.0"
+            }
+        },
+        "node_modules/lower-case": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+            "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+            "dependencies": {
+                "tslib": "^2.0.3"
+            }
+        },
+        "node_modules/lower-case/node_modules/tslib": {
+            "version": "2.3.1",
+            "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+            "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+        },
+        "node_modules/lowercase-keys": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
+            "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==",
+            "engines": {
+                "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/lru-cache": {
+            "version": "5.1.1",
+            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+            "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+            "dependencies": {
+                "yallist": "^3.0.2"
+            }
+        },
+        "node_modules/magicli": {
+            "version": "0.0.8",
+            "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.8.tgz",
+            "integrity": "sha512-x/eBenweAHF+DsYy172sK4doRxZl0yrJnfxhLJiN7H6hPM3Ya0PfI6uBZshZ3ScFFSQD7HXgBqMdbnXKEZsO1g==",
+            "dependencies": {
+                "cliss": "0.0.2",
+                "find-up": "^2.1.0",
+                "for-each-property": "0.0.4",
+                "inspect-property": "0.0.6"
+            }
+        },
+        "node_modules/magicli/node_modules/find-up": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+            "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+            "dependencies": {
+                "locate-path": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/magicli/node_modules/locate-path": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+            "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+            "dependencies": {
+                "p-locate": "^2.0.0",
+                "path-exists": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/magicli/node_modules/p-limit": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+            "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+            "dependencies": {
+                "p-try": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/magicli/node_modules/p-locate": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+            "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+            "dependencies": {
+                "p-limit": "^1.1.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/magicli/node_modules/p-try": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+            "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/make-dir": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
+            "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
+            "dependencies": {
+                "pify": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/make-dir/node_modules/pify": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+            "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/make-error": {
+            "version": "1.3.6",
+            "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+            "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+            "dev": true
+        },
+        "node_modules/map-cache": {
+            "version": "0.2.2",
+            "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+            "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/map-obj": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
+            "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/map-visit": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+            "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+            "dependencies": {
+                "object-visit": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/material-colors": {
+            "version": "1.2.6",
+            "resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
+            "integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
+        },
+        "node_modules/material-ui": {
+            "version": "0.20.2",
+            "resolved": "https://registry.npmjs.org/material-ui/-/material-ui-0.20.2.tgz",
+            "integrity": "sha512-VeqgQkdvtK193w+FFvXDEwlVxI4rWk83eWbpYLeOIHDPWr3rbB9B075JRnJt/8IsI2X8q5Aia5W3+7m4KkleDg==",
+            "deprecated": "You can now upgrade to @material-ui/core",
+            "dependencies": {
+                "babel-runtime": "^6.23.0",
+                "inline-style-prefixer": "^3.0.8",
+                "keycode": "^2.1.8",
+                "lodash.merge": "^4.6.0",
+                "lodash.throttle": "^4.1.1",
+                "prop-types": "^15.5.7",
+                "react-event-listener": "^0.6.2",
+                "react-transition-group": "^1.2.1",
+                "recompose": "^0.26.0",
+                "simple-assign": "^0.1.0",
+                "warning": "^3.0.0"
+            },
+            "peerDependencies": {
+                "react": "^15.4.0 || ^16.0.0",
+                "react-dom": "^15.4.0 || ^16.0.0"
+            }
+        },
+        "node_modules/material-ui/node_modules/react-transition-group": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-1.2.1.tgz",
+            "integrity": "sha512-CWaL3laCmgAFdxdKbhhps+c0HRGF4c+hdM4H23+FI1QBNUyx/AMeIJGWorehPNSaKnQNOAxL7PQmqMu78CDj3Q==",
+            "dependencies": {
+                "chain-function": "^1.0.0",
+                "dom-helpers": "^3.2.0",
+                "loose-envify": "^1.3.1",
+                "prop-types": "^15.5.6",
+                "warning": "^3.0.0"
+            },
+            "peerDependencies": {
+                "react": "^15.0.0 || ^16.0.0",
+                "react-dom": "^15.0.0 || ^16.0.0"
+            }
+        },
+        "node_modules/math-codegen": {
+            "version": "0.3.5",
+            "resolved": "https://registry.npmjs.org/math-codegen/-/math-codegen-0.3.5.tgz",
+            "integrity": "sha1-R5nuRnfe0Ud2bQA8ykt4ee3UDMo=",
+            "dependencies": {
+                "extend": "^3.0.0",
+                "mr-parser": "^0.2.1"
+            }
+        },
+        "node_modules/mathquill": {
+            "version": "0.10.1-a",
+            "resolved": "https://registry.npmjs.org/mathquill/-/mathquill-0.10.1-a.tgz",
+            "integrity": "sha1-vyylaQEAY6w0vNXVKa3Ag3zVPD8=",
+            "dependencies": {
+                "jquery": "^1.12.3"
+            }
+        },
+        "node_modules/mathquill/node_modules/jquery": {
+            "version": "1.12.4",
+            "resolved": "https://registry.npmjs.org/jquery/-/jquery-1.12.4.tgz",
+            "integrity": "sha1-AeHfuikP5z3rp3zurLD5ui/sngw="
+        },
+        "node_modules/max-safe-integer": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/max-safe-integer/-/max-safe-integer-1.0.1.tgz",
+            "integrity": "sha1-84BgvixWPYwC5tSK85Ei/YO29BA=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/media-typer": {
+            "version": "0.3.0",
+            "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+            "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/memfs": {
+            "version": "3.4.1",
+            "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz",
+            "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==",
+            "dependencies": {
+                "fs-monkey": "1.0.3"
+            },
+            "engines": {
+                "node": ">= 4.0.0"
+            }
+        },
+        "node_modules/memoize-one": {
+            "version": "5.1.1",
+            "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz",
+            "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA=="
+        },
+        "node_modules/memory-fs": {
+            "version": "0.5.0",
+            "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
+            "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
+            "dev": true,
+            "dependencies": {
+                "errno": "^0.1.3",
+                "readable-stream": "^2.0.1"
+            },
+            "engines": {
+                "node": ">=4.3.0 <5.0.0 || >=5.10"
+            }
+        },
+        "node_modules/memory-fs/node_modules/readable-stream": {
+            "version": "2.3.7",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+            "dev": true,
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/memory-pager": {
+            "version": "1.5.0",
+            "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
+            "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
+            "optional": true
+        },
+        "node_modules/memorystream": {
+            "version": "0.3.1",
+            "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
+            "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=",
+            "engines": {
+                "node": ">= 0.10.0"
+            }
+        },
+        "node_modules/meow": {
+            "version": "3.7.0",
+            "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
+            "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/merge-anything": {
+            "version": "2.4.4",
+            "resolved": "https://registry.npmjs.org/merge-anything/-/merge-anything-2.4.4.tgz",
+            "integrity": "sha512-l5XlriUDJKQT12bH+rVhAHjwIuXWdAIecGwsYjv2LJo+dA1AeRTmeQS+3QBpO6lEthBMDi2IUMpLC1yyRvGlwQ==",
+            "dependencies": {
+                "is-what": "^3.3.1"
+            }
+        },
+        "node_modules/merge-descriptors": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+            "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+        },
+        "node_modules/merge-stream": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+            "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
+        },
+        "node_modules/methods": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+            "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/microevent.ts": {
+            "version": "0.1.1",
+            "resolved": "https://registry.npmjs.org/microevent.ts/-/microevent.ts-0.1.1.tgz",
+            "integrity": "sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g==",
+            "dev": true
+        },
+        "node_modules/micromatch": {
+            "version": "3.1.10",
+            "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+            "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/mime": {
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+            "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+            "bin": {
+                "mime": "cli.js"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/mime-db": {
+            "version": "1.44.0",
+            "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
+            "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==",
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/mime-types": {
+            "version": "2.1.27",
+            "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
+            "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
+            "dependencies": {
+                "mime-db": "1.44.0"
+            },
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/mimic-fn": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+            "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/mimic-response": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
+            "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
+            "engines": {
+                "node": ">=8"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/minimalistic-assert": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+            "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+            "dev": true
+        },
+        "node_modules/minimatch": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+            "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+            "dependencies": {
+                "brace-expansion": "^1.1.7"
+            },
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/minimist": {
+            "version": "1.2.5",
+            "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+            "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+        },
+        "node_modules/mississippi": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz",
+            "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==",
+            "dev": true,
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=4.0.0"
+            }
+        },
+        "node_modules/mississippi/node_modules/pump": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
+            "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+            "dev": true,
+            "dependencies": {
+                "end-of-stream": "^1.1.0",
+                "once": "^1.3.1"
+            }
+        },
+        "node_modules/mixin-deep": {
+            "version": "1.3.2",
+            "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+            "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+            "dependencies": {
+                "for-in": "^1.0.2",
+                "is-extendable": "^1.0.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/mkdirp": {
+            "version": "0.5.4",
+            "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz",
+            "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==",
+            "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)",
+            "dependencies": {
+                "minimist": "^1.2.5"
+            },
+            "bin": {
+                "mkdirp": "bin/cmd.js"
+            }
+        },
+        "node_modules/mkdirp-classic": {
+            "version": "0.5.2",
+            "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz",
+            "integrity": "sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g=="
+        },
+        "node_modules/mobile-detect": {
+            "version": "1.4.5",
+            "resolved": "https://registry.npmjs.org/mobile-detect/-/mobile-detect-1.4.5.tgz",
+            "integrity": "sha512-yc0LhH6tItlvfLBugVUEtgawwFU2sIe+cSdmRJJCTMZ5GEJyLxNyC/NIOAOGk67Fa8GNpOttO3Xz/1bHpXFD/g=="
+        },
+        "node_modules/mobx": {
+            "version": "5.15.7",
+            "resolved": "https://registry.npmjs.org/mobx/-/mobx-5.15.7.tgz",
+            "integrity": "sha512-wyM3FghTkhmC+hQjyPGGFdpehrcX1KOXsDuERhfK2YbJemkUhEB+6wzEN639T21onxlfYBmriA1PFnvxTUhcKw==",
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/mobx"
+            }
+        },
+        "node_modules/mobx-react": {
+            "version": "5.4.4",
+            "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-5.4.4.tgz",
+            "integrity": "sha512-2mTzpyEjVB/RGk2i6KbcmP4HWcAUFox5ZRCrGvSyz49w20I4C4qql63grPpYrS9E9GKwgydBHQlA4y665LuRCQ==",
+            "dependencies": {
+                "hoist-non-react-statics": "^3.0.0",
+                "react-lifecycles-compat": "^3.0.2"
+            },
+            "peerDependencies": {
+                "mobx": "^4.0.0 || ^5.0.0",
+                "react": "^0.13.0 || ^0.14.0 || ^15.0.0 || ^16.0.0"
+            }
+        },
+        "node_modules/mobx-react-devtools": {
+            "version": "6.1.1",
+            "resolved": "https://registry.npmjs.org/mobx-react-devtools/-/mobx-react-devtools-6.1.1.tgz",
+            "integrity": "sha512-nc5IXLdEUFLn3wZal65KF3/JFEFd+mbH4KTz/IG5BOPyw7jo8z29w/8qm7+wiCyqVfUIgJ1gL4+HVKmcXIOgqA==",
+            "peerDependencies": {
+                "mobx": "^4.3.1 || ^5.0.0",
+                "mobx-react": "^4.0.0 || ^5.0.0"
+            }
+        },
+        "node_modules/mobx-utils": {
+            "version": "5.6.2",
+            "resolved": "https://registry.npmjs.org/mobx-utils/-/mobx-utils-5.6.2.tgz",
+            "integrity": "sha512-a/WlXyGkp6F12b01sTarENpxbmlRgPHFyR1Xv2bsSjQBm5dcOtd16ONb40/vOqck8L99NHpI+C9MXQ+SZ8f+yw==",
+            "peerDependencies": {
+                "mobx": "^4.13.1 || ^5.13.1"
+            }
+        },
+        "node_modules/mocha": {
+            "version": "5.2.0",
+            "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz",
+            "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==",
+            "dev": true,
+            "dependencies": {
+                "browser-stdout": "1.3.1",
+                "commander": "2.15.1",
+                "debug": "3.1.0",
+                "diff": "3.5.0",
+                "escape-string-regexp": "1.0.5",
+                "glob": "7.1.2",
+                "growl": "1.10.5",
+                "he": "1.1.1",
+                "minimatch": "3.0.4",
+                "mkdirp": "0.5.1",
+                "supports-color": "5.4.0"
+            },
+            "bin": {
+                "_mocha": "bin/_mocha",
+                "mocha": "bin/mocha"
+            },
+            "engines": {
+                "node": ">= 4.0.0"
+            }
+        },
+        "node_modules/mocha/node_modules/commander": {
+            "version": "2.15.1",
+            "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
+            "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
+            "dev": true
+        },
+        "node_modules/mocha/node_modules/debug": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+            "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+            "dev": true,
+            "dependencies": {
+                "ms": "2.0.0"
+            }
+        },
+        "node_modules/mocha/node_modules/glob": {
+            "version": "7.1.2",
+            "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
+            "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+            "dev": true,
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/mocha/node_modules/he": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
+            "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
+            "dev": true,
+            "bin": {
+                "he": "bin/he"
+            }
+        },
+        "node_modules/mocha/node_modules/minimist": {
+            "version": "0.0.8",
+            "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+            "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+            "dev": true
+        },
+        "node_modules/mocha/node_modules/mkdirp": {
+            "version": "0.5.1",
+            "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+            "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+            "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)",
+            "dev": true,
+            "dependencies": {
+                "minimist": "0.0.8"
+            },
+            "bin": {
+                "mkdirp": "bin/cmd.js"
+            }
+        },
+        "node_modules/mocha/node_modules/ms": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+            "dev": true
+        },
+        "node_modules/mocha/node_modules/supports-color": {
+            "version": "5.4.0",
+            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
+            "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+            "dev": true,
+            "dependencies": {
+                "has-flag": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/mongodb": {
+            "version": "3.7.3",
+            "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.3.tgz",
+            "integrity": "sha512-Psm+g3/wHXhjBEktkxXsFMZvd3nemI0r3IPsE0bU+4//PnvNWKkzhZcEsbPcYiWqe8XqXJJEg4Tgtr7Raw67Yw==",
+            "dependencies": {
+                "bl": "^2.2.1",
+                "bson": "^1.1.4",
+                "denque": "^1.4.1",
+                "optional-require": "^1.1.8",
+                "safe-buffer": "^5.1.2"
+            },
+            "engines": {
+                "node": ">=4"
+            },
+            "optionalDependencies": {
+                "saslprep": "^1.0.0"
+            },
+            "peerDependenciesMeta": {
+                "aws4": {
+                    "optional": true
+                },
+                "bson-ext": {
+                    "optional": true
+                },
+                "kerberos": {
+                    "optional": true
+                },
+                "mongodb-client-encryption": {
+                    "optional": true
+                },
+                "mongodb-extjson": {
+                    "optional": true
+                },
+                "snappy": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/mongodb-core": {
+            "version": "2.1.20",
+            "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.20.tgz",
+            "integrity": "sha512-IN57CX5/Q1bhDq6ShAR6gIv4koFsZP7L8WOK1S0lR0pVDQaScffSMV5jxubLsmZ7J+UdqmykKw4r9hG3XQEGgQ==",
+            "dependencies": {
+                "bson": "~1.0.4",
+                "require_optional": "~1.0.0"
+            }
+        },
+        "node_modules/mongodb-core/node_modules/bson": {
+            "version": "1.0.9",
+            "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.9.tgz",
+            "integrity": "sha512-IQX9/h7WdMBIW/q/++tGd+emQr0XMdeZ6icnT/74Xk9fnabWn+gZgpE+9V+gujL3hhJOoNrnDVY7tWdzc7NUTg==",
+            "deprecated": "Fixed a critical issue with BSON serialization documented in CVE-2019-2391, see https://bit.ly/2KcpXdo for more details",
+            "engines": {
+                "node": ">=0.6.19"
+            }
+        },
+        "node_modules/mongodb/node_modules/bl": {
+            "version": "2.2.1",
+            "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz",
+            "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==",
+            "dependencies": {
+                "readable-stream": "^2.3.5",
+                "safe-buffer": "^5.1.1"
+            }
+        },
+        "node_modules/mongodb/node_modules/bson": {
+            "version": "1.1.6",
+            "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz",
+            "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==",
+            "engines": {
+                "node": ">=0.6.19"
+            }
+        },
+        "node_modules/mongodb/node_modules/optional-require": {
+            "version": "1.1.8",
+            "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.8.tgz",
+            "integrity": "sha512-jq83qaUb0wNg9Krv1c5OQ+58EK+vHde6aBPzLvPPqJm89UQWsvSuFy9X/OSNJnFeSOKo7btE0n8Nl2+nE+z5nA==",
+            "dependencies": {
+                "require-at": "^1.0.6"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/mongodb/node_modules/readable-stream": {
+            "version": "2.3.7",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/mongoose": {
+            "version": "5.13.14",
+            "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.14.tgz",
+            "integrity": "sha512-j+BlQjjxgZg0iWn42kLeZTB91OejcxWpY2Z50bsZTiKJ7HHcEtcY21Godw496GMkBqJMTzmW7G/kZ04mW+Cb7Q==",
+            "dependencies": {
+                "@types/bson": "1.x || 4.0.x",
+                "@types/mongodb": "^3.5.27",
+                "bson": "^1.1.4",
+                "kareem": "2.3.2",
+                "mongodb": "3.7.3",
+                "mongoose-legacy-pluralize": "1.0.2",
+                "mpath": "0.8.4",
+                "mquery": "3.2.5",
+                "ms": "2.1.2",
+                "optional-require": "1.0.x",
+                "regexp-clone": "1.0.0",
+                "safe-buffer": "5.2.1",
+                "sift": "13.5.2",
+                "sliced": "1.0.1"
+            },
+            "engines": {
+                "node": ">=4.0.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/mongoose"
+            }
+        },
+        "node_modules/mongoose-legacy-pluralize": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz",
+            "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==",
+            "peerDependencies": {
+                "mongoose": "*"
+            }
+        },
+        "node_modules/mongoose/node_modules/@types/bson": {
+            "version": "4.0.5",
+            "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.5.tgz",
+            "integrity": "sha512-vVLwMUqhYJSQ/WKcE60eFqcyuWse5fGH+NMAXHuKrUAPoryq3ATxk5o4bgYNtg5aOM4APVg7Hnb3ASqUYG0PKg==",
+            "dependencies": {
+                "@types/node": "*"
+            }
+        },
+        "node_modules/mongoose/node_modules/bson": {
+            "version": "1.1.6",
+            "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz",
+            "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg==",
+            "engines": {
+                "node": ">=0.6.19"
+            }
+        },
+        "node_modules/mongoose/node_modules/ms": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+            "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+        },
+        "node_modules/mongoose/node_modules/safe-buffer": {
+            "version": "5.2.1",
+            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+            "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/feross"
+                },
+                {
+                    "type": "patreon",
+                    "url": "https://www.patreon.com/feross"
+                },
+                {
+                    "type": "consulting",
+                    "url": "https://feross.org/support"
+                }
+            ]
+        },
+        "node_modules/move-concurrently": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
+            "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
+            "dev": true,
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/move-concurrently/node_modules/rimraf": {
+            "version": "2.7.1",
+            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+            "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+            "dev": true,
+            "dependencies": {
+                "glob": "^7.1.3"
+            },
+            "bin": {
+                "rimraf": "bin.js"
+            }
+        },
+        "node_modules/mpath": {
+            "version": "0.8.4",
+            "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.4.tgz",
+            "integrity": "sha512-DTxNZomBcTWlrMW76jy1wvV37X/cNNxPW1y2Jzd4DZkAaC5ZGsm8bfGfNOthcDuRJujXLqiuS6o3Tpy0JEoh7g==",
+            "engines": {
+                "node": ">=4.0.0"
+            }
+        },
+        "node_modules/mquery": {
+            "version": "3.2.5",
+            "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.5.tgz",
+            "integrity": "sha512-VjOKHHgU84wij7IUoZzFRU07IAxd5kWJaDmyUzQlbjHjyoeK5TNeeo8ZsFDtTYnSgpW6n/nMNIHvE3u8Lbrf4A==",
+            "dependencies": {
+                "bluebird": "3.5.1",
+                "debug": "3.1.0",
+                "regexp-clone": "^1.0.0",
+                "safe-buffer": "5.1.2",
+                "sliced": "1.0.1"
+            },
+            "engines": {
+                "node": ">=4.0.0"
+            }
+        },
+        "node_modules/mquery/node_modules/bluebird": {
+            "version": "3.5.1",
+            "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
+            "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
+        },
+        "node_modules/mquery/node_modules/debug": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+            "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+            "dependencies": {
+                "ms": "2.0.0"
+            }
+        },
+        "node_modules/mquery/node_modules/ms": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        },
+        "node_modules/mr-parser": {
+            "version": "0.2.1",
+            "resolved": "https://registry.npmjs.org/mr-parser/-/mr-parser-0.2.1.tgz",
+            "integrity": "sha1-hhi5ukF+KOn0OaQcaVtVTq/u2Sc="
+        },
+        "node_modules/ms": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+            "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+        },
+        "node_modules/multicast-dns": {
+            "version": "6.2.3",
+            "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz",
+            "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==",
+            "dev": true,
+            "dependencies": {
+                "dns-packet": "^1.3.1",
+                "thunky": "^1.0.2"
+            },
+            "bin": {
+                "multicast-dns": "cli.js"
+            }
+        },
+        "node_modules/multicast-dns-service-types": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
+            "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
+            "dev": true
+        },
+        "node_modules/nan": {
+            "version": "2.14.1",
+            "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz",
+            "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw=="
+        },
+        "node_modules/nanoid": {
+            "version": "2.1.11",
+            "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz",
+            "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA=="
+        },
+        "node_modules/nanomatch": {
+            "version": "1.2.13",
+            "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+            "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/napi-build-utils": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
+            "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
+        },
+        "node_modules/native-or-bluebird": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/native-or-bluebird/-/native-or-bluebird-1.2.0.tgz",
+            "integrity": "sha1-OcR7/Xgl0fuf+tMiEK4l2q3xAck=",
+            "deprecated": "'native-or-bluebird' is deprecated. Please use 'any-promise' instead."
+        },
+        "node_modules/negotiator": {
+            "version": "0.6.3",
+            "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+            "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/neo-async": {
+            "version": "2.6.1",
+            "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
+            "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==",
+            "dev": true
+        },
+        "node_modules/next-tick": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
+            "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
+        },
+        "node_modules/nextafter": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/nextafter/-/nextafter-1.0.0.tgz",
+            "integrity": "sha1-t9d7U1MQ4+CX5gJauwqQNHfsGjo=",
+            "dependencies": {
+                "double-bits": "^1.1.0"
+            }
+        },
+        "node_modules/nice-try": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+            "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+            "dev": true
+        },
+        "node_modules/no-case": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+            "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+            "dependencies": {
+                "lower-case": "^2.0.2",
+                "tslib": "^2.0.3"
+            }
+        },
+        "node_modules/no-case/node_modules/tslib": {
+            "version": "2.3.1",
+            "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+            "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+        },
+        "node_modules/node-abi": {
+            "version": "2.16.0",
+            "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.16.0.tgz",
+            "integrity": "sha512-+sa0XNlWDA6T+bDLmkCUYn6W5k5W6BPRL6mqzSCs6H/xUgtl4D5x2fORKDzopKiU6wsyn/+wXlRXwXeSp+mtoA==",
+            "dependencies": {
+                "semver": "^5.4.1"
+            }
+        },
+        "node_modules/node-ensure": {
+            "version": "0.0.0",
+            "resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz",
+            "integrity": "sha1-7K52QVDemYYexcgQ/V0Jaxg5Mqc="
+        },
+        "node_modules/node-environment-flags": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz",
+            "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==",
+            "dependencies": {
+                "object.getownpropertydescriptors": "^2.0.3",
+                "semver": "^5.7.0"
+            }
+        },
+        "node_modules/node-fetch": {
+            "version": "1.7.3",
+            "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
+            "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
+            "dependencies": {
+                "encoding": "^0.1.11",
+                "is-stream": "^1.0.1"
+            }
+        },
+        "node_modules/node-forge": {
+            "version": "0.9.1",
+            "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.1.tgz",
+            "integrity": "sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ==",
+            "engines": {
+                "node": ">= 4.5.0"
+            }
+        },
+        "node_modules/node-gyp": {
+            "version": "3.8.0",
+            "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz",
+            "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==",
+            "dependencies": {
+                "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"
+            },
+            "bin": {
+                "node-gyp": "bin/node-gyp.js"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/node-gyp/node_modules/nopt": {
+            "version": "3.0.6",
+            "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
+            "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
+            "dependencies": {
+                "abbrev": "1"
+            },
+            "bin": {
+                "nopt": "bin/nopt.js"
+            }
+        },
+        "node_modules/node-gyp/node_modules/rimraf": {
+            "version": "2.7.1",
+            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+            "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+            "dependencies": {
+                "glob": "^7.1.3"
+            },
+            "bin": {
+                "rimraf": "bin.js"
+            }
+        },
+        "node_modules/node-gyp/node_modules/semver": {
+            "version": "5.3.0",
+            "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
+            "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
+            "bin": {
+                "semver": "bin/semver"
+            }
+        },
+        "node_modules/node-gyp/node_modules/tar": {
+            "version": "2.2.2",
+            "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
+            "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
+            "deprecated": "This version of tar is no longer supported, and will not receive security updates. Please upgrade asap.",
+            "dependencies": {
+                "block-stream": "*",
+                "fstream": "^1.0.12",
+                "inherits": "2"
+            }
+        },
+        "node_modules/node-releases": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz",
+            "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg=="
+        },
+        "node_modules/node-sass": {
+            "version": "4.14.1",
+            "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.14.1.tgz",
+            "integrity": "sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g==",
+            "hasInstallScript": true,
+            "dependencies": {
+                "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.15",
+                "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.5",
+                "stdout-stream": "^1.4.0",
+                "true-case-path": "^1.0.2"
+            },
+            "bin": {
+                "node-sass": "bin/node-sass"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/node-sass-tilde-importer": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/node-sass-tilde-importer/-/node-sass-tilde-importer-1.0.2.tgz",
+            "integrity": "sha512-Swcmr38Y7uB78itQeBm3mThjxBy9/Ah/ykPIaURY/L6Nec9AyRoL/jJ7ECfMR+oZeCTVQNxVMu/aHU+TLRVbdg==",
+            "dependencies": {
+                "find-parent-dir": "^0.3.0"
+            }
+        },
+        "node_modules/node-sass/node_modules/ansi-regex": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+            "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/node-sass/node_modules/ansi-styles": {
+            "version": "2.2.1",
+            "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+            "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/node-sass/node_modules/chalk": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+            "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/node-sass/node_modules/get-stdin": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
+            "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/node-sass/node_modules/strip-ansi": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+            "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+            "dependencies": {
+                "ansi-regex": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/node-sass/node_modules/supports-color": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+            "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+            "engines": {
+                "node": ">=0.8.0"
+            }
+        },
+        "node_modules/node-stream-zip": {
+            "version": "1.15.0",
+            "resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz",
+            "integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==",
+            "engines": {
+                "node": ">=0.12.0"
+            },
+            "funding": {
+                "type": "github",
+                "url": "https://github.com/sponsors/antelle"
+            }
+        },
+        "node_modules/nodemailer": {
+            "version": "5.1.1",
+            "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-5.1.1.tgz",
+            "integrity": "sha512-hKGCoeNdFL2W7S76J/Oucbw0/qRlfG815tENdhzcqTpSjKgAN91mFOqU2lQUflRRxFM7iZvCyaFcAR9noc/CqQ==",
+            "engines": {
+                "node": ">=6.0.0"
+            }
+        },
+        "node_modules/nodemon": {
+            "version": "1.19.4",
+            "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.19.4.tgz",
+            "integrity": "sha512-VGPaqQBNk193lrJFotBU8nvWZPqEZY2eIzymy2jjY0fJ9qIsxA0sxQ8ATPl0gZC645gijYEc1jtZvpS8QWzJGQ==",
+            "hasInstallScript": true,
+            "dependencies": {
+                "chokidar": "^2.1.8",
+                "debug": "^3.2.6",
+                "ignore-by-default": "^1.0.1",
+                "minimatch": "^3.0.4",
+                "pstree.remy": "^1.1.7",
+                "semver": "^5.7.1",
+                "supports-color": "^5.5.0",
+                "touch": "^3.1.0",
+                "undefsafe": "^2.0.2",
+                "update-notifier": "^2.5.0"
+            },
+            "bin": {
+                "nodemon": "bin/nodemon.js"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/noop-logger": {
+            "version": "0.1.1",
+            "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
+            "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI="
+        },
+        "node_modules/normalize-package-data": {
+            "version": "2.5.0",
+            "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+            "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+            "dependencies": {
+                "hosted-git-info": "^2.1.4",
+                "resolve": "^1.10.0",
+                "semver": "2 || 3 || 4 || 5",
+                "validate-npm-package-license": "^3.0.1"
+            }
+        },
+        "node_modules/normalize-path": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+            "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/normalize-url": {
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
+            "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/normalize.css": {
+            "version": "8.0.1",
+            "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz",
+            "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg=="
+        },
+        "node_modules/npm": {
+            "version": "6.14.16",
+            "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.16.tgz",
+            "integrity": "sha512-LMiLGYsVNJfVPlQg7v2NYjG7iRIapcLv+oMunlq7fkXVx0BATCjRu7XyWl0G+iuZzHy4CjtM32QB8ox8juTgaw==",
+            "bundleDependencies": [
+                "abbrev",
+                "ansicolors",
+                "ansistyles",
+                "aproba",
+                "archy",
+                "bin-links",
+                "bluebird",
+                "byte-size",
+                "cacache",
+                "call-limit",
+                "chownr",
+                "ci-info",
+                "cli-columns",
+                "cli-table3",
+                "cmd-shim",
+                "columnify",
+                "config-chain",
+                "debuglog",
+                "detect-indent",
+                "detect-newline",
+                "dezalgo",
+                "editor",
+                "figgy-pudding",
+                "find-npm-prefix",
+                "fs-vacuum",
+                "fs-write-stream-atomic",
+                "gentle-fs",
+                "glob",
+                "graceful-fs",
+                "has-unicode",
+                "hosted-git-info",
+                "iferr",
+                "imurmurhash",
+                "infer-owner",
+                "inflight",
+                "inherits",
+                "ini",
+                "init-package-json",
+                "is-cidr",
+                "json-parse-better-errors",
+                "JSONStream",
+                "lazy-property",
+                "libcipm",
+                "libnpm",
+                "libnpmaccess",
+                "libnpmhook",
+                "libnpmorg",
+                "libnpmsearch",
+                "libnpmteam",
+                "libnpx",
+                "lock-verify",
+                "lockfile",
+                "lodash._baseindexof",
+                "lodash._baseuniq",
+                "lodash._bindcallback",
+                "lodash._cacheindexof",
+                "lodash._createcache",
+                "lodash._getnative",
+                "lodash.clonedeep",
+                "lodash.restparam",
+                "lodash.union",
+                "lodash.uniq",
+                "lodash.without",
+                "lru-cache",
+                "meant",
+                "mississippi",
+                "mkdirp",
+                "move-concurrently",
+                "node-gyp",
+                "nopt",
+                "normalize-package-data",
+                "npm-audit-report",
+                "npm-cache-filename",
+                "npm-install-checks",
+                "npm-lifecycle",
+                "npm-package-arg",
+                "npm-packlist",
+                "npm-pick-manifest",
+                "npm-profile",
+                "npm-registry-fetch",
+                "npm-user-validate",
+                "npmlog",
+                "once",
+                "opener",
+                "osenv",
+                "pacote",
+                "path-is-inside",
+                "promise-inflight",
+                "qrcode-terminal",
+                "query-string",
+                "qw",
+                "read-cmd-shim",
+                "read-installed",
+                "read-package-json",
+                "read-package-tree",
+                "read",
+                "readable-stream",
+                "readdir-scoped-modules",
+                "request",
+                "retry",
+                "rimraf",
+                "safe-buffer",
+                "semver",
+                "sha",
+                "slide",
+                "sorted-object",
+                "sorted-union-stream",
+                "ssri",
+                "stringify-package",
+                "tar",
+                "text-table",
+                "tiny-relative-date",
+                "uid-number",
+                "umask",
+                "unique-filename",
+                "unpipe",
+                "update-notifier",
+                "uuid",
+                "validate-npm-package-license",
+                "validate-npm-package-name",
+                "which",
+                "worker-farm",
+                "write-file-atomic"
+            ],
+            "dependencies": {
+                "abbrev": "~1.1.1",
+                "ansicolors": "~0.3.2",
+                "ansistyles": "~0.1.3",
+                "aproba": "^2.0.0",
+                "archy": "~1.0.0",
+                "bin-links": "^1.1.8",
+                "bluebird": "^3.5.5",
+                "byte-size": "^5.0.1",
+                "cacache": "^12.0.3",
+                "call-limit": "^1.1.1",
+                "chownr": "^1.1.4",
+                "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.1",
+                "glob": "^7.1.6",
+                "graceful-fs": "^4.2.4",
+                "has-unicode": "~2.0.1",
+                "hosted-git-info": "^2.8.9",
+                "iferr": "^1.0.2",
+                "imurmurhash": "*",
+                "infer-owner": "^1.0.4",
+                "inflight": "~1.0.6",
+                "inherits": "^2.0.4",
+                "ini": "^1.3.8",
+                "init-package-json": "^1.10.3",
+                "is-cidr": "^3.0.0",
+                "json-parse-better-errors": "^1.0.2",
+                "JSONStream": "^1.3.5",
+                "lazy-property": "~1.0.0",
+                "libcipm": "^4.0.8",
+                "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.4",
+                "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.2",
+                "mississippi": "^3.0.0",
+                "mkdirp": "^0.5.5",
+                "move-concurrently": "^1.0.1",
+                "node-gyp": "^5.1.0",
+                "nopt": "^4.0.3",
+                "normalize-package-data": "^2.5.0",
+                "npm-audit-report": "^1.3.3",
+                "npm-cache-filename": "~1.0.2",
+                "npm-install-checks": "^3.0.2",
+                "npm-lifecycle": "^3.1.5",
+                "npm-package-arg": "^6.1.1",
+                "npm-packlist": "^1.4.8",
+                "npm-pick-manifest": "^3.0.2",
+                "npm-profile": "^4.0.4",
+                "npm-registry-fetch": "^4.0.7",
+                "npm-user-validate": "^1.0.1",
+                "npmlog": "~4.1.2",
+                "once": "~1.4.0",
+                "opener": "^1.5.2",
+                "osenv": "^0.1.5",
+                "pacote": "^9.5.12",
+                "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.6.0",
+                "readdir-scoped-modules": "^1.1.0",
+                "request": "^2.88.0",
+                "retry": "^0.12.0",
+                "rimraf": "^2.7.1",
+                "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.2",
+                "stringify-package": "^1.0.1",
+                "tar": "^4.4.19",
+                "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"
+            },
+            "bin": {
+                "npm": "bin/npm-cli.js",
+                "npx": "bin/npx-cli.js"
+            },
+            "engines": {
+                "node": "6 >=6.2.0 || 8 || >=9.3.0"
+            }
+        },
+        "node_modules/npm-run-path": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
+            "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+            "dependencies": {
+                "path-key": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/abbrev": {
+            "version": "1.1.1",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/agent-base": {
+            "version": "4.3.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "es6-promisify": "^5.0.0"
+            },
+            "engines": {
+                "node": ">= 4.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/agentkeepalive": {
+            "version": "3.5.2",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "humanize-ms": "^1.2.1"
+            },
+            "engines": {
+                "node": ">= 4.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/ansi-align": {
+            "version": "2.0.0",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "string-width": "^2.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/ansi-regex": {
+            "version": "2.1.1",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/ansi-styles": {
+            "version": "3.2.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "color-convert": "^1.9.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/ansicolors": {
+            "version": "0.3.2",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/ansistyles": {
+            "version": "0.1.3",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/aproba": {
+            "version": "2.0.0",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/archy": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/are-we-there-yet": {
+            "version": "1.1.4",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "delegates": "^1.0.0",
+                "readable-stream": "^2.0.6"
+            }
+        },
+        "node_modules/npm/node_modules/are-we-there-yet/node_modules/readable-stream": {
+            "version": "2.3.6",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/are-we-there-yet/node_modules/string_decoder": {
+            "version": "1.1.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "safe-buffer": "~5.1.0"
+            }
+        },
+        "node_modules/npm/node_modules/asap": {
+            "version": "2.0.6",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/asn1": {
+            "version": "0.2.4",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "safer-buffer": "~2.1.0"
+            }
+        },
+        "node_modules/npm/node_modules/assert-plus": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.8"
+            }
+        },
+        "node_modules/npm/node_modules/asynckit": {
+            "version": "0.4.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/aws-sign2": {
+            "version": "0.7.0",
+            "inBundle": true,
+            "license": "Apache-2.0",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/npm/node_modules/aws4": {
+            "version": "1.8.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/balanced-match": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/bcrypt-pbkdf": {
+            "version": "1.0.2",
+            "inBundle": true,
+            "license": "BSD-3-Clause",
+            "optional": true,
+            "dependencies": {
+                "tweetnacl": "^0.14.3"
+            }
+        },
+        "node_modules/npm/node_modules/bin-links": {
+            "version": "1.1.8",
+            "inBundle": true,
+            "license": "Artistic-2.0",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/bluebird": {
+            "version": "3.5.5",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/boxen": {
+            "version": "1.3.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/brace-expansion": {
+            "version": "1.1.11",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "balanced-match": "^1.0.0",
+                "concat-map": "0.0.1"
+            }
+        },
+        "node_modules/npm/node_modules/buffer-from": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/builtins": {
+            "version": "1.0.3",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/byline": {
+            "version": "5.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/byte-size": {
+            "version": "5.0.1",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/cacache": {
+            "version": "12.0.3",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/call-limit": {
+            "version": "1.1.1",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/camelcase": {
+            "version": "4.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/capture-stack-trace": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/caseless": {
+            "version": "0.12.0",
+            "inBundle": true,
+            "license": "Apache-2.0"
+        },
+        "node_modules/npm/node_modules/chalk": {
+            "version": "2.4.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "ansi-styles": "^3.2.1",
+                "escape-string-regexp": "^1.0.5",
+                "supports-color": "^5.3.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/chownr": {
+            "version": "1.1.4",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/ci-info": {
+            "version": "2.0.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/cidr-regex": {
+            "version": "2.0.10",
+            "inBundle": true,
+            "license": "BSD-2-Clause",
+            "dependencies": {
+                "ip-regex": "^2.1.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/cli-boxes": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/cli-columns": {
+            "version": "3.1.2",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "string-width": "^2.0.0",
+                "strip-ansi": "^3.0.1"
+            },
+            "engines": {
+                "node": ">= 4"
+            }
+        },
+        "node_modules/npm/node_modules/cli-table3": {
+            "version": "0.5.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "object-assign": "^4.1.0",
+                "string-width": "^2.1.1"
+            },
+            "engines": {
+                "node": ">=6"
+            },
+            "optionalDependencies": {
+                "colors": "^1.1.2"
+            }
+        },
+        "node_modules/npm/node_modules/cliui": {
+            "version": "5.0.0",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "string-width": "^3.1.0",
+                "strip-ansi": "^5.2.0",
+                "wrap-ansi": "^5.1.0"
+            }
+        },
+        "node_modules/npm/node_modules/cliui/node_modules/ansi-regex": {
+            "version": "4.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/cliui/node_modules/is-fullwidth-code-point": {
+            "version": "2.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/cliui/node_modules/string-width": {
+            "version": "3.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "emoji-regex": "^7.0.1",
+                "is-fullwidth-code-point": "^2.0.0",
+                "strip-ansi": "^5.1.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/cliui/node_modules/strip-ansi": {
+            "version": "5.2.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "ansi-regex": "^4.1.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/clone": {
+            "version": "1.0.4",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.8"
+            }
+        },
+        "node_modules/npm/node_modules/cmd-shim": {
+            "version": "3.0.3",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "graceful-fs": "^4.1.2",
+                "mkdirp": "~0.5.0"
+            }
+        },
+        "node_modules/npm/node_modules/code-point-at": {
+            "version": "1.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/color-convert": {
+            "version": "1.9.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "color-name": "^1.1.1"
+            }
+        },
+        "node_modules/npm/node_modules/color-name": {
+            "version": "1.1.3",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/colors": {
+            "version": "1.3.3",
+            "inBundle": true,
+            "license": "MIT",
+            "optional": true,
+            "engines": {
+                "node": ">=0.1.90"
+            }
+        },
+        "node_modules/npm/node_modules/columnify": {
+            "version": "1.5.4",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "strip-ansi": "^3.0.0",
+                "wcwidth": "^1.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/combined-stream": {
+            "version": "1.0.6",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "delayed-stream": "~1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/npm/node_modules/concat-map": {
+            "version": "0.0.1",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/concat-stream": {
+            "version": "1.6.2",
+            "engines": [
+                "node >= 0.8"
+            ],
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "buffer-from": "^1.0.0",
+                "inherits": "^2.0.3",
+                "readable-stream": "^2.2.2",
+                "typedarray": "^0.0.6"
+            }
+        },
+        "node_modules/npm/node_modules/concat-stream/node_modules/readable-stream": {
+            "version": "2.3.6",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/concat-stream/node_modules/string_decoder": {
+            "version": "1.1.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "safe-buffer": "~5.1.0"
+            }
+        },
+        "node_modules/npm/node_modules/config-chain": {
+            "version": "1.1.12",
+            "inBundle": true,
+            "dependencies": {
+                "ini": "^1.3.4",
+                "proto-list": "~1.2.1"
+            }
+        },
+        "node_modules/npm/node_modules/configstore": {
+            "version": "3.1.5",
+            "inBundle": true,
+            "license": "BSD-2-Clause",
+            "dependencies": {
+                "dot-prop": "^4.2.1",
+                "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"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/console-control-strings": {
+            "version": "1.1.0",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/copy-concurrently": {
+            "version": "1.0.5",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/copy-concurrently/node_modules/aproba": {
+            "version": "1.2.0",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/copy-concurrently/node_modules/iferr": {
+            "version": "0.1.5",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/core-util-is": {
+            "version": "1.0.2",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/create-error-class": {
+            "version": "3.0.2",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "capture-stack-trace": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/cross-spawn": {
+            "version": "5.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "lru-cache": "^4.0.1",
+                "shebang-command": "^1.2.0",
+                "which": "^1.2.9"
+            }
+        },
+        "node_modules/npm/node_modules/cross-spawn/node_modules/lru-cache": {
+            "version": "4.1.5",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "pseudomap": "^1.0.2",
+                "yallist": "^2.1.2"
+            }
+        },
+        "node_modules/npm/node_modules/cross-spawn/node_modules/yallist": {
+            "version": "2.1.2",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/crypto-random-string": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/cyclist": {
+            "version": "0.2.2",
+            "inBundle": true
+        },
+        "node_modules/npm/node_modules/dashdash": {
+            "version": "1.14.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "assert-plus": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10"
+            }
+        },
+        "node_modules/npm/node_modules/debug": {
+            "version": "3.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "ms": "2.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/debug/node_modules/ms": {
+            "version": "2.0.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/debuglog": {
+            "version": "1.0.1",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/npm/node_modules/decamelize": {
+            "version": "1.2.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/decode-uri-component": {
+            "version": "0.2.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10"
+            }
+        },
+        "node_modules/npm/node_modules/deep-extend": {
+            "version": "0.6.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/defaults": {
+            "version": "1.0.3",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "clone": "^1.0.2"
+            }
+        },
+        "node_modules/npm/node_modules/define-properties": {
+            "version": "1.1.3",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "object-keys": "^1.0.12"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/npm/node_modules/delayed-stream": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.4.0"
+            }
+        },
+        "node_modules/npm/node_modules/delegates": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/detect-indent": {
+            "version": "5.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/detect-newline": {
+            "version": "2.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/dezalgo": {
+            "version": "1.0.3",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "asap": "^2.0.0",
+                "wrappy": "1"
+            }
+        },
+        "node_modules/npm/node_modules/dot-prop": {
+            "version": "4.2.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "is-obj": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/dotenv": {
+            "version": "5.0.1",
+            "inBundle": true,
+            "license": "BSD-2-Clause",
+            "engines": {
+                "node": ">=4.6.0"
+            }
+        },
+        "node_modules/npm/node_modules/duplexer3": {
+            "version": "0.1.4",
+            "inBundle": true,
+            "license": "BSD-3-Clause"
+        },
+        "node_modules/npm/node_modules/duplexify": {
+            "version": "3.6.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "end-of-stream": "^1.0.0",
+                "inherits": "^2.0.1",
+                "readable-stream": "^2.0.0",
+                "stream-shift": "^1.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/duplexify/node_modules/readable-stream": {
+            "version": "2.3.6",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/duplexify/node_modules/string_decoder": {
+            "version": "1.1.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "safe-buffer": "~5.1.0"
+            }
+        },
+        "node_modules/npm/node_modules/ecc-jsbn": {
+            "version": "0.1.2",
+            "inBundle": true,
+            "license": "MIT",
+            "optional": true,
+            "dependencies": {
+                "jsbn": "~0.1.0",
+                "safer-buffer": "^2.1.0"
+            }
+        },
+        "node_modules/npm/node_modules/editor": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/emoji-regex": {
+            "version": "7.0.3",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/encoding": {
+            "version": "0.1.12",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "iconv-lite": "~0.4.13"
+            }
+        },
+        "node_modules/npm/node_modules/end-of-stream": {
+            "version": "1.4.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "once": "^1.4.0"
+            }
+        },
+        "node_modules/npm/node_modules/env-paths": {
+            "version": "2.2.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/err-code": {
+            "version": "1.1.2",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/errno": {
+            "version": "0.1.7",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "prr": "~1.0.1"
+            },
+            "bin": {
+                "errno": "cli.js"
+            }
+        },
+        "node_modules/npm/node_modules/es-abstract": {
+            "version": "1.12.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/npm/node_modules/es-to-primitive": {
+            "version": "1.2.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "is-callable": "^1.1.4",
+                "is-date-object": "^1.0.1",
+                "is-symbol": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/npm/node_modules/es6-promise": {
+            "version": "4.2.8",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/es6-promisify": {
+            "version": "5.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "es6-promise": "^4.0.3"
+            }
+        },
+        "node_modules/npm/node_modules/escape-string-regexp": {
+            "version": "1.0.5",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.8.0"
+            }
+        },
+        "node_modules/npm/node_modules/execa": {
+            "version": "0.7.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/execa/node_modules/get-stream": {
+            "version": "3.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/extend": {
+            "version": "3.0.2",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/extsprintf": {
+            "version": "1.3.0",
+            "engines": [
+                "node >=0.6.0"
+            ],
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/fast-json-stable-stringify": {
+            "version": "2.0.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/figgy-pudding": {
+            "version": "3.5.1",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/find-npm-prefix": {
+            "version": "1.0.2",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/flush-write-stream": {
+            "version": "1.0.3",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "inherits": "^2.0.1",
+                "readable-stream": "^2.0.4"
+            }
+        },
+        "node_modules/npm/node_modules/flush-write-stream/node_modules/readable-stream": {
+            "version": "2.3.6",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/flush-write-stream/node_modules/string_decoder": {
+            "version": "1.1.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "safe-buffer": "~5.1.0"
+            }
+        },
+        "node_modules/npm/node_modules/forever-agent": {
+            "version": "0.6.1",
+            "inBundle": true,
+            "license": "Apache-2.0",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/npm/node_modules/form-data": {
+            "version": "2.3.2",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "asynckit": "^0.4.0",
+                "combined-stream": "1.0.6",
+                "mime-types": "^2.1.12"
+            },
+            "engines": {
+                "node": ">= 0.12"
+            }
+        },
+        "node_modules/npm/node_modules/from2": {
+            "version": "2.3.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "inherits": "^2.0.1",
+                "readable-stream": "^2.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/from2/node_modules/readable-stream": {
+            "version": "2.3.6",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/from2/node_modules/string_decoder": {
+            "version": "1.1.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "safe-buffer": "~5.1.0"
+            }
+        },
+        "node_modules/npm/node_modules/fs-minipass": {
+            "version": "1.2.7",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "minipass": "^2.6.0"
+            }
+        },
+        "node_modules/npm/node_modules/fs-minipass/node_modules/minipass": {
+            "version": "2.9.0",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "safe-buffer": "^5.1.2",
+                "yallist": "^3.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/fs-vacuum": {
+            "version": "1.2.10",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "graceful-fs": "^4.1.2",
+                "path-is-inside": "^1.0.1",
+                "rimraf": "^2.5.2"
+            }
+        },
+        "node_modules/npm/node_modules/fs-write-stream-atomic": {
+            "version": "1.0.10",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "graceful-fs": "^4.1.2",
+                "iferr": "^0.1.5",
+                "imurmurhash": "^0.1.4",
+                "readable-stream": "1 || 2"
+            }
+        },
+        "node_modules/npm/node_modules/fs-write-stream-atomic/node_modules/iferr": {
+            "version": "0.1.5",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/fs-write-stream-atomic/node_modules/readable-stream": {
+            "version": "2.3.6",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/fs-write-stream-atomic/node_modules/string_decoder": {
+            "version": "1.1.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "safe-buffer": "~5.1.0"
+            }
+        },
+        "node_modules/npm/node_modules/fs.realpath": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/function-bind": {
+            "version": "1.1.1",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/gauge": {
+            "version": "2.7.4",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/gauge/node_modules/aproba": {
+            "version": "1.2.0",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/gauge/node_modules/string-width": {
+            "version": "1.0.2",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "code-point-at": "^1.0.0",
+                "is-fullwidth-code-point": "^1.0.0",
+                "strip-ansi": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/genfun": {
+            "version": "5.0.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/gentle-fs": {
+            "version": "2.3.1",
+            "inBundle": true,
+            "license": "Artistic-2.0",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/gentle-fs/node_modules/aproba": {
+            "version": "1.2.0",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/gentle-fs/node_modules/iferr": {
+            "version": "0.1.5",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/get-caller-file": {
+            "version": "2.0.5",
+            "inBundle": true,
+            "license": "ISC",
+            "engines": {
+                "node": "6.* || 8.* || >= 10.*"
+            }
+        },
+        "node_modules/npm/node_modules/get-stream": {
+            "version": "4.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "pump": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/getpass": {
+            "version": "0.1.7",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "assert-plus": "^1.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/glob": {
+            "version": "7.1.6",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": "*"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            }
+        },
+        "node_modules/npm/node_modules/global-dirs": {
+            "version": "0.1.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "ini": "^1.3.4"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/got": {
+            "version": "6.7.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/got/node_modules/get-stream": {
+            "version": "3.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/graceful-fs": {
+            "version": "4.2.4",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/har-schema": {
+            "version": "2.0.0",
+            "inBundle": true,
+            "license": "ISC",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/har-validator": {
+            "version": "5.1.5",
+            "deprecated": "this library is no longer supported",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "ajv": "^6.12.3",
+                "har-schema": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/har-validator/node_modules/ajv": {
+            "version": "6.12.6",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "fast-deep-equal": "^3.1.1",
+                "fast-json-stable-stringify": "^2.0.0",
+                "json-schema-traverse": "^0.4.1",
+                "uri-js": "^4.2.2"
+            },
+            "funding": {
+                "type": "github",
+                "url": "https://github.com/sponsors/epoberezkin"
+            }
+        },
+        "node_modules/npm/node_modules/har-validator/node_modules/fast-deep-equal": {
+            "version": "3.1.3",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/har-validator/node_modules/json-schema-traverse": {
+            "version": "0.4.1",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/has": {
+            "version": "1.0.3",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "function-bind": "^1.1.1"
+            },
+            "engines": {
+                "node": ">= 0.4.0"
+            }
+        },
+        "node_modules/npm/node_modules/has-flag": {
+            "version": "3.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/has-symbols": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/npm/node_modules/has-unicode": {
+            "version": "2.0.1",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/hosted-git-info": {
+            "version": "2.8.9",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/http-cache-semantics": {
+            "version": "3.8.1",
+            "inBundle": true,
+            "license": "BSD-2-Clause"
+        },
+        "node_modules/npm/node_modules/http-proxy-agent": {
+            "version": "2.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "agent-base": "4",
+                "debug": "3.1.0"
+            },
+            "engines": {
+                "node": ">= 4.5.0"
+            }
+        },
+        "node_modules/npm/node_modules/http-signature": {
+            "version": "1.2.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "assert-plus": "^1.0.0",
+                "jsprim": "^1.2.2",
+                "sshpk": "^1.7.0"
+            },
+            "engines": {
+                "node": ">=0.8",
+                "npm": ">=1.3.7"
+            }
+        },
+        "node_modules/npm/node_modules/https-proxy-agent": {
+            "version": "2.2.4",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "agent-base": "^4.3.0",
+                "debug": "^3.1.0"
+            },
+            "engines": {
+                "node": ">= 4.5.0"
+            }
+        },
+        "node_modules/npm/node_modules/humanize-ms": {
+            "version": "1.2.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "ms": "^2.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/iconv-lite": {
+            "version": "0.4.23",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "safer-buffer": ">= 2.1.2 < 3"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/iferr": {
+            "version": "1.0.2",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/ignore-walk": {
+            "version": "3.0.3",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "minimatch": "^3.0.4"
+            }
+        },
+        "node_modules/npm/node_modules/import-lazy": {
+            "version": "2.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/imurmurhash": {
+            "version": "0.1.4",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.8.19"
+            }
+        },
+        "node_modules/npm/node_modules/infer-owner": {
+            "version": "1.0.4",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/inflight": {
+            "version": "1.0.6",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "once": "^1.3.0",
+                "wrappy": "1"
+            }
+        },
+        "node_modules/npm/node_modules/inherits": {
+            "version": "2.0.4",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/ini": {
+            "version": "1.3.8",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/init-package-json": {
+            "version": "1.10.3",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/ip": {
+            "version": "1.1.5",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/ip-regex": {
+            "version": "2.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/is-callable": {
+            "version": "1.1.4",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/npm/node_modules/is-ci": {
+            "version": "1.2.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "ci-info": "^1.5.0"
+            },
+            "bin": {
+                "is-ci": "bin.js"
+            }
+        },
+        "node_modules/npm/node_modules/is-ci/node_modules/ci-info": {
+            "version": "1.6.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/is-cidr": {
+            "version": "3.0.0",
+            "inBundle": true,
+            "license": "BSD-2-Clause",
+            "dependencies": {
+                "cidr-regex": "^2.0.10"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/is-date-object": {
+            "version": "1.0.1",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/npm/node_modules/is-fullwidth-code-point": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "number-is-nan": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/is-installed-globally": {
+            "version": "0.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "global-dirs": "^0.1.0",
+                "is-path-inside": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/is-npm": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/is-obj": {
+            "version": "1.0.1",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/is-path-inside": {
+            "version": "1.0.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "path-is-inside": "^1.0.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/is-redirect": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/is-regex": {
+            "version": "1.0.4",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "has": "^1.0.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/npm/node_modules/is-retry-allowed": {
+            "version": "1.2.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/is-stream": {
+            "version": "1.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/is-symbol": {
+            "version": "1.0.2",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "has-symbols": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/npm/node_modules/is-typedarray": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/isarray": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/isexe": {
+            "version": "2.0.0",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/isstream": {
+            "version": "0.1.2",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/jsbn": {
+            "version": "0.1.1",
+            "inBundle": true,
+            "license": "MIT",
+            "optional": true
+        },
+        "node_modules/npm/node_modules/json-parse-better-errors": {
+            "version": "1.0.2",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/json-schema": {
+            "version": "0.4.0",
+            "inBundle": true,
+            "license": "(AFL-2.1 OR BSD-3-Clause)"
+        },
+        "node_modules/npm/node_modules/json-stringify-safe": {
+            "version": "5.0.1",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/jsonparse": {
+            "version": "1.3.1",
+            "engines": [
+                "node >= 0.2.0"
+            ],
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/JSONStream": {
+            "version": "1.3.5",
+            "inBundle": true,
+            "license": "(MIT OR Apache-2.0)",
+            "dependencies": {
+                "jsonparse": "^1.2.0",
+                "through": ">=2.2.7 <3"
+            },
+            "bin": {
+                "JSONStream": "bin.js"
+            },
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/npm/node_modules/jsprim": {
+            "version": "1.4.2",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "assert-plus": "1.0.0",
+                "extsprintf": "1.3.0",
+                "json-schema": "0.4.0",
+                "verror": "1.10.0"
+            },
+            "engines": {
+                "node": ">=0.6.0"
+            }
+        },
+        "node_modules/npm/node_modules/latest-version": {
+            "version": "3.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "package-json": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/lazy-property": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/libcipm": {
+            "version": "4.0.8",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "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.1.0",
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/libnpm": {
+            "version": "3.0.1",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/libnpmaccess": {
+            "version": "3.0.2",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "aproba": "^2.0.0",
+                "get-stream": "^4.0.0",
+                "npm-package-arg": "^6.1.0",
+                "npm-registry-fetch": "^4.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/libnpmconfig": {
+            "version": "1.2.1",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "figgy-pudding": "^3.5.1",
+                "find-up": "^3.0.0",
+                "ini": "^1.3.5"
+            }
+        },
+        "node_modules/npm/node_modules/libnpmconfig/node_modules/find-up": {
+            "version": "3.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "locate-path": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/libnpmconfig/node_modules/locate-path": {
+            "version": "3.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "p-locate": "^3.0.0",
+                "path-exists": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/libnpmconfig/node_modules/p-limit": {
+            "version": "2.2.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "p-try": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/libnpmconfig/node_modules/p-locate": {
+            "version": "3.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "p-limit": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/libnpmconfig/node_modules/p-try": {
+            "version": "2.2.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/libnpmhook": {
+            "version": "5.0.3",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "aproba": "^2.0.0",
+                "figgy-pudding": "^3.4.1",
+                "get-stream": "^4.0.0",
+                "npm-registry-fetch": "^4.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/libnpmorg": {
+            "version": "1.0.1",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "aproba": "^2.0.0",
+                "figgy-pudding": "^3.4.1",
+                "get-stream": "^4.0.0",
+                "npm-registry-fetch": "^4.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/libnpmpublish": {
+            "version": "1.1.2",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/libnpmsearch": {
+            "version": "2.0.2",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "figgy-pudding": "^3.5.1",
+                "get-stream": "^4.0.0",
+                "npm-registry-fetch": "^4.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/libnpmteam": {
+            "version": "1.0.2",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "aproba": "^2.0.0",
+                "figgy-pudding": "^3.4.1",
+                "get-stream": "^4.0.0",
+                "npm-registry-fetch": "^4.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/libnpx": {
+            "version": "10.2.4",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "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": "^14.2.3"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/lock-verify": {
+            "version": "2.1.0",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "npm-package-arg": "^6.1.0",
+                "semver": "^5.4.1"
+            }
+        },
+        "node_modules/npm/node_modules/lockfile": {
+            "version": "1.0.4",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "signal-exit": "^3.0.2"
+            }
+        },
+        "node_modules/npm/node_modules/lodash._baseindexof": {
+            "version": "3.1.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/lodash._baseuniq": {
+            "version": "4.6.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "lodash._createset": "~4.0.0",
+                "lodash._root": "~3.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/lodash._bindcallback": {
+            "version": "3.0.1",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/lodash._cacheindexof": {
+            "version": "3.0.2",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/lodash._createcache": {
+            "version": "3.1.2",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "lodash._getnative": "^3.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/lodash._createset": {
+            "version": "4.0.3",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/lodash._getnative": {
+            "version": "3.9.1",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/lodash._root": {
+            "version": "3.0.1",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/lodash.clonedeep": {
+            "version": "4.5.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/lodash.restparam": {
+            "version": "3.6.1",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/lodash.union": {
+            "version": "4.6.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/lodash.uniq": {
+            "version": "4.5.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/lodash.without": {
+            "version": "4.4.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/lowercase-keys": {
+            "version": "1.0.1",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/lru-cache": {
+            "version": "5.1.1",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "yallist": "^3.0.2"
+            }
+        },
+        "node_modules/npm/node_modules/make-dir": {
+            "version": "1.3.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "pify": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/make-fetch-happen": {
+            "version": "5.0.2",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/meant": {
+            "version": "1.0.2",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/mime-db": {
+            "version": "1.35.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/npm/node_modules/mime-types": {
+            "version": "2.1.19",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "mime-db": "~1.35.0"
+            },
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/npm/node_modules/minimatch": {
+            "version": "3.0.4",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "brace-expansion": "^1.1.7"
+            },
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/npm/node_modules/minimist": {
+            "version": "1.2.5",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/minizlib": {
+            "version": "1.3.3",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "minipass": "^2.9.0"
+            }
+        },
+        "node_modules/npm/node_modules/minizlib/node_modules/minipass": {
+            "version": "2.9.0",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "safe-buffer": "^5.1.2",
+                "yallist": "^3.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/mississippi": {
+            "version": "3.0.0",
+            "inBundle": true,
+            "license": "BSD-2-Clause",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=4.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/mkdirp": {
+            "version": "0.5.5",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "minimist": "^1.2.5"
+            },
+            "bin": {
+                "mkdirp": "bin/cmd.js"
+            }
+        },
+        "node_modules/npm/node_modules/mkdirp/node_modules/minimist": {
+            "version": "1.2.5",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/move-concurrently": {
+            "version": "1.0.1",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/move-concurrently/node_modules/aproba": {
+            "version": "1.2.0",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/ms": {
+            "version": "2.1.1",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/mute-stream": {
+            "version": "0.0.7",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/node-fetch-npm": {
+            "version": "2.0.2",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "encoding": "^0.1.11",
+                "json-parse-better-errors": "^1.0.0",
+                "safe-buffer": "^5.1.1"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/node-gyp": {
+            "version": "5.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "env-paths": "^2.2.0",
+                "glob": "^7.1.4",
+                "graceful-fs": "^4.2.2",
+                "mkdirp": "^0.5.1",
+                "nopt": "^4.0.1",
+                "npmlog": "^4.1.2",
+                "request": "^2.88.0",
+                "rimraf": "^2.6.3",
+                "semver": "^5.7.1",
+                "tar": "^4.4.12",
+                "which": "^1.3.1"
+            },
+            "bin": {
+                "node-gyp": "bin/node-gyp.js"
+            },
+            "engines": {
+                "node": ">= 6.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/nopt": {
+            "version": "4.0.3",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "abbrev": "1",
+                "osenv": "^0.1.4"
+            },
+            "bin": {
+                "nopt": "bin/nopt.js"
+            }
+        },
+        "node_modules/npm/node_modules/normalize-package-data": {
+            "version": "2.5.0",
+            "inBundle": true,
+            "license": "BSD-2-Clause",
+            "dependencies": {
+                "hosted-git-info": "^2.1.4",
+                "resolve": "^1.10.0",
+                "semver": "2 || 3 || 4 || 5",
+                "validate-npm-package-license": "^3.0.1"
+            }
+        },
+        "node_modules/npm/node_modules/normalize-package-data/node_modules/resolve": {
+            "version": "1.10.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "path-parse": "^1.0.6"
+            }
+        },
+        "node_modules/npm/node_modules/npm-audit-report": {
+            "version": "1.3.3",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "cli-table3": "^0.5.0",
+                "console-control-strings": "^1.1.0"
+            }
+        },
+        "node_modules/npm/node_modules/npm-bundled": {
+            "version": "1.1.1",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "npm-normalize-package-bin": "^1.0.1"
+            }
+        },
+        "node_modules/npm/node_modules/npm-cache-filename": {
+            "version": "1.0.2",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/npm-install-checks": {
+            "version": "3.0.2",
+            "inBundle": true,
+            "license": "BSD-2-Clause",
+            "dependencies": {
+                "semver": "^2.3.0 || 3.x || 4 || 5"
+            }
+        },
+        "node_modules/npm/node_modules/npm-lifecycle": {
+            "version": "3.1.5",
+            "inBundle": true,
+            "license": "Artistic-2.0",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/npm-logical-tree": {
+            "version": "1.2.1",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/npm-normalize-package-bin": {
+            "version": "1.0.1",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/npm-package-arg": {
+            "version": "6.1.1",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "hosted-git-info": "^2.7.1",
+                "osenv": "^0.1.5",
+                "semver": "^5.6.0",
+                "validate-npm-package-name": "^3.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/npm-packlist": {
+            "version": "1.4.8",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "ignore-walk": "^3.0.1",
+                "npm-bundled": "^1.0.1",
+                "npm-normalize-package-bin": "^1.0.1"
+            }
+        },
+        "node_modules/npm/node_modules/npm-pick-manifest": {
+            "version": "3.0.2",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "figgy-pudding": "^3.5.1",
+                "npm-package-arg": "^6.0.0",
+                "semver": "^5.4.1"
+            }
+        },
+        "node_modules/npm/node_modules/npm-profile": {
+            "version": "4.0.4",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "aproba": "^1.1.2 || 2",
+                "figgy-pudding": "^3.4.1",
+                "npm-registry-fetch": "^4.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/npm-registry-fetch": {
+            "version": "4.0.7",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "bluebird": "^3.5.1",
+                "figgy-pudding": "^3.4.1",
+                "JSONStream": "^1.3.4",
+                "lru-cache": "^5.1.1",
+                "make-fetch-happen": "^5.0.0",
+                "npm-package-arg": "^6.1.0",
+                "safe-buffer": "^5.2.0"
+            }
+        },
+        "node_modules/npm/node_modules/npm-registry-fetch/node_modules/safe-buffer": {
+            "version": "5.2.1",
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/feross"
+                },
+                {
+                    "type": "patreon",
+                    "url": "https://www.patreon.com/feross"
+                },
+                {
+                    "type": "consulting",
+                    "url": "https://feross.org/support"
+                }
+            ],
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/npm-run-path": {
+            "version": "2.0.2",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "path-key": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/npm-user-validate": {
+            "version": "1.0.1",
+            "inBundle": true,
+            "license": "BSD-2-Clause"
+        },
+        "node_modules/npm/node_modules/npmlog": {
+            "version": "4.1.2",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "are-we-there-yet": "~1.1.2",
+                "console-control-strings": "~1.1.0",
+                "gauge": "~2.7.3",
+                "set-blocking": "~2.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/number-is-nan": {
+            "version": "1.0.1",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/oauth-sign": {
+            "version": "0.9.0",
+            "inBundle": true,
+            "license": "Apache-2.0",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/npm/node_modules/object-assign": {
+            "version": "4.1.1",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/object-keys": {
+            "version": "1.0.12",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/npm/node_modules/object.getownpropertydescriptors": {
+            "version": "2.0.3",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "define-properties": "^1.1.2",
+                "es-abstract": "^1.5.1"
+            },
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/npm/node_modules/once": {
+            "version": "1.4.0",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "wrappy": "1"
+            }
+        },
+        "node_modules/npm/node_modules/opener": {
+            "version": "1.5.2",
+            "inBundle": true,
+            "license": "(WTFPL OR MIT)",
+            "bin": {
+                "opener": "bin/opener-bin.js"
+            }
+        },
+        "node_modules/npm/node_modules/os-homedir": {
+            "version": "1.0.2",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/os-tmpdir": {
+            "version": "1.0.2",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/osenv": {
+            "version": "0.1.5",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "os-homedir": "^1.0.0",
+                "os-tmpdir": "^1.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/p-finally": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/package-json": {
+            "version": "4.0.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "got": "^6.7.1",
+                "registry-auth-token": "^3.0.1",
+                "registry-url": "^3.0.3",
+                "semver": "^5.1.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/pacote": {
+            "version": "9.5.12",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/pacote/node_modules/minipass": {
+            "version": "2.9.0",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "safe-buffer": "^5.1.2",
+                "yallist": "^3.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/parallel-transform": {
+            "version": "1.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "cyclist": "~0.2.2",
+                "inherits": "^2.0.3",
+                "readable-stream": "^2.1.5"
+            }
+        },
+        "node_modules/npm/node_modules/parallel-transform/node_modules/readable-stream": {
+            "version": "2.3.6",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/parallel-transform/node_modules/string_decoder": {
+            "version": "1.1.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "safe-buffer": "~5.1.0"
+            }
+        },
+        "node_modules/npm/node_modules/path-exists": {
+            "version": "3.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/path-is-absolute": {
+            "version": "1.0.1",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/path-is-inside": {
+            "version": "1.0.2",
+            "inBundle": true,
+            "license": "(WTFPL OR MIT)"
+        },
+        "node_modules/npm/node_modules/path-key": {
+            "version": "2.0.1",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/path-parse": {
+            "version": "1.0.7",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/performance-now": {
+            "version": "2.1.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/pify": {
+            "version": "3.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/prepend-http": {
+            "version": "1.0.4",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/process-nextick-args": {
+            "version": "2.0.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/promise-inflight": {
+            "version": "1.0.1",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/promise-retry": {
+            "version": "1.1.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "err-code": "^1.0.0",
+                "retry": "^0.10.0"
+            },
+            "engines": {
+                "node": ">=0.12"
+            }
+        },
+        "node_modules/npm/node_modules/promise-retry/node_modules/retry": {
+            "version": "0.10.1",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/npm/node_modules/promzard": {
+            "version": "0.3.0",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "read": "1"
+            }
+        },
+        "node_modules/npm/node_modules/proto-list": {
+            "version": "1.2.4",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/protoduck": {
+            "version": "5.0.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "genfun": "^5.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/prr": {
+            "version": "1.0.1",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/pseudomap": {
+            "version": "1.0.2",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/psl": {
+            "version": "1.1.29",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/pump": {
+            "version": "3.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "end-of-stream": "^1.1.0",
+                "once": "^1.3.1"
+            }
+        },
+        "node_modules/npm/node_modules/pumpify": {
+            "version": "1.5.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "duplexify": "^3.6.0",
+                "inherits": "^2.0.3",
+                "pump": "^2.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/pumpify/node_modules/pump": {
+            "version": "2.0.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "end-of-stream": "^1.1.0",
+                "once": "^1.3.1"
+            }
+        },
+        "node_modules/npm/node_modules/punycode": {
+            "version": "1.4.1",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/qrcode-terminal": {
+            "version": "0.12.0",
+            "inBundle": true,
+            "bin": {
+                "qrcode-terminal": "bin/qrcode-terminal.js"
+            }
+        },
+        "node_modules/npm/node_modules/qs": {
+            "version": "6.5.2",
+            "inBundle": true,
+            "license": "BSD-3-Clause",
+            "engines": {
+                "node": ">=0.6"
+            }
+        },
+        "node_modules/npm/node_modules/query-string": {
+            "version": "6.8.2",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "decode-uri-component": "^0.2.0",
+                "split-on-first": "^1.0.0",
+                "strict-uri-encode": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/qw": {
+            "version": "1.0.1",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/rc": {
+            "version": "1.2.8",
+            "inBundle": true,
+            "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
+            "dependencies": {
+                "deep-extend": "^0.6.0",
+                "ini": "~1.3.0",
+                "minimist": "^1.2.0",
+                "strip-json-comments": "~2.0.1"
+            },
+            "bin": {
+                "rc": "cli.js"
+            }
+        },
+        "node_modules/npm/node_modules/read": {
+            "version": "1.0.7",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "mute-stream": "~0.0.4"
+            },
+            "engines": {
+                "node": ">=0.8"
+            }
+        },
+        "node_modules/npm/node_modules/read-cmd-shim": {
+            "version": "1.0.5",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "graceful-fs": "^4.1.2"
+            }
+        },
+        "node_modules/npm/node_modules/read-installed": {
+            "version": "4.0.3",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "debuglog": "^1.0.1",
+                "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"
+            },
+            "optionalDependencies": {
+                "graceful-fs": "^4.1.2"
+            }
+        },
+        "node_modules/npm/node_modules/read-package-json": {
+            "version": "2.1.1",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "glob": "^7.1.1",
+                "json-parse-better-errors": "^1.0.1",
+                "normalize-package-data": "^2.0.0",
+                "npm-normalize-package-bin": "^1.0.0"
+            },
+            "optionalDependencies": {
+                "graceful-fs": "^4.1.2"
+            }
+        },
+        "node_modules/npm/node_modules/read-package-tree": {
+            "version": "5.3.1",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "read-package-json": "^2.0.0",
+                "readdir-scoped-modules": "^1.0.0",
+                "util-promisify": "^2.1.0"
+            }
+        },
+        "node_modules/npm/node_modules/readable-stream": {
+            "version": "3.6.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "inherits": "^2.0.3",
+                "string_decoder": "^1.1.1",
+                "util-deprecate": "^1.0.1"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/npm/node_modules/readdir-scoped-modules": {
+            "version": "1.1.0",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "debuglog": "^1.0.1",
+                "dezalgo": "^1.0.0",
+                "graceful-fs": "^4.1.2",
+                "once": "^1.3.0"
+            }
+        },
+        "node_modules/npm/node_modules/registry-auth-token": {
+            "version": "3.4.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "rc": "^1.1.6",
+                "safe-buffer": "^5.0.1"
+            }
+        },
+        "node_modules/npm/node_modules/registry-url": {
+            "version": "3.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "rc": "^1.0.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/request": {
+            "version": "2.88.0",
+            "inBundle": true,
+            "license": "Apache-2.0",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">= 4"
+            }
+        },
+        "node_modules/npm/node_modules/require-directory": {
+            "version": "2.1.1",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/require-main-filename": {
+            "version": "2.0.0",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/resolve-from": {
+            "version": "4.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/retry": {
+            "version": "0.12.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 4"
+            }
+        },
+        "node_modules/npm/node_modules/rimraf": {
+            "version": "2.7.1",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "glob": "^7.1.3"
+            },
+            "bin": {
+                "rimraf": "bin.js"
+            }
+        },
+        "node_modules/npm/node_modules/run-queue": {
+            "version": "1.0.3",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "aproba": "^1.1.1"
+            }
+        },
+        "node_modules/npm/node_modules/run-queue/node_modules/aproba": {
+            "version": "1.2.0",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/safe-buffer": {
+            "version": "5.1.2",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/safer-buffer": {
+            "version": "2.1.2",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/semver": {
+            "version": "5.7.1",
+            "inBundle": true,
+            "license": "ISC",
+            "bin": {
+                "semver": "bin/semver"
+            }
+        },
+        "node_modules/npm/node_modules/semver-diff": {
+            "version": "2.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "semver": "^5.0.3"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/set-blocking": {
+            "version": "2.0.0",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/sha": {
+            "version": "3.0.0",
+            "inBundle": true,
+            "license": "(BSD-2-Clause OR MIT)",
+            "dependencies": {
+                "graceful-fs": "^4.1.2"
+            }
+        },
+        "node_modules/npm/node_modules/shebang-command": {
+            "version": "1.2.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "shebang-regex": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/shebang-regex": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/signal-exit": {
+            "version": "3.0.2",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/slide": {
+            "version": "1.1.6",
+            "inBundle": true,
+            "license": "ISC",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/npm/node_modules/smart-buffer": {
+            "version": "4.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 6.0.0",
+                "npm": ">= 3.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/socks": {
+            "version": "2.3.3",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "ip": "1.1.5",
+                "smart-buffer": "^4.1.0"
+            },
+            "engines": {
+                "node": ">= 6.0.0",
+                "npm": ">= 3.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/socks-proxy-agent": {
+            "version": "4.0.2",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "agent-base": "~4.2.1",
+                "socks": "~2.3.2"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/npm/node_modules/socks-proxy-agent/node_modules/agent-base": {
+            "version": "4.2.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "es6-promisify": "^5.0.0"
+            },
+            "engines": {
+                "node": ">= 4.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/sorted-object": {
+            "version": "2.0.1",
+            "inBundle": true,
+            "license": "(WTFPL OR MIT)"
+        },
+        "node_modules/npm/node_modules/sorted-union-stream": {
+            "version": "2.1.3",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "from2": "^1.3.0",
+                "stream-iterate": "^1.1.0"
+            }
+        },
+        "node_modules/npm/node_modules/sorted-union-stream/node_modules/from2": {
+            "version": "1.3.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "inherits": "~2.0.1",
+                "readable-stream": "~1.1.10"
+            }
+        },
+        "node_modules/npm/node_modules/sorted-union-stream/node_modules/isarray": {
+            "version": "0.0.1",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/sorted-union-stream/node_modules/readable-stream": {
+            "version": "1.1.14",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "core-util-is": "~1.0.0",
+                "inherits": "~2.0.1",
+                "isarray": "0.0.1",
+                "string_decoder": "~0.10.x"
+            }
+        },
+        "node_modules/npm/node_modules/sorted-union-stream/node_modules/string_decoder": {
+            "version": "0.10.31",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/spdx-correct": {
+            "version": "3.0.0",
+            "inBundle": true,
+            "license": "Apache-2.0",
+            "dependencies": {
+                "spdx-expression-parse": "^3.0.0",
+                "spdx-license-ids": "^3.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/spdx-exceptions": {
+            "version": "2.1.0",
+            "inBundle": true,
+            "license": "CC-BY-3.0"
+        },
+        "node_modules/npm/node_modules/spdx-expression-parse": {
+            "version": "3.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "spdx-exceptions": "^2.1.0",
+                "spdx-license-ids": "^3.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/spdx-license-ids": {
+            "version": "3.0.5",
+            "inBundle": true,
+            "license": "CC0-1.0"
+        },
+        "node_modules/npm/node_modules/split-on-first": {
+            "version": "1.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/sshpk": {
+            "version": "1.14.2",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "asn1": "~0.2.3",
+                "assert-plus": "^1.0.0",
+                "dashdash": "^1.12.0",
+                "getpass": "^0.1.1",
+                "safer-buffer": "^2.0.2"
+            },
+            "bin": {
+                "sshpk-conv": "bin/sshpk-conv",
+                "sshpk-sign": "bin/sshpk-sign",
+                "sshpk-verify": "bin/sshpk-verify"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            },
+            "optionalDependencies": {
+                "bcrypt-pbkdf": "^1.0.0",
+                "ecc-jsbn": "~0.1.1",
+                "jsbn": "~0.1.0",
+                "tweetnacl": "~0.14.0"
+            }
+        },
+        "node_modules/npm/node_modules/ssri": {
+            "version": "6.0.2",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "figgy-pudding": "^3.5.1"
+            }
+        },
+        "node_modules/npm/node_modules/stream-each": {
+            "version": "1.2.2",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "end-of-stream": "^1.1.0",
+                "stream-shift": "^1.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/stream-iterate": {
+            "version": "1.2.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "readable-stream": "^2.1.5",
+                "stream-shift": "^1.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/stream-iterate/node_modules/readable-stream": {
+            "version": "2.3.6",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/stream-iterate/node_modules/string_decoder": {
+            "version": "1.1.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "safe-buffer": "~5.1.0"
+            }
+        },
+        "node_modules/npm/node_modules/stream-shift": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/strict-uri-encode": {
+            "version": "2.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/string_decoder": {
+            "version": "1.3.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "safe-buffer": "~5.2.0"
+            }
+        },
+        "node_modules/npm/node_modules/string_decoder/node_modules/safe-buffer": {
+            "version": "5.2.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/string-width": {
+            "version": "2.1.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "is-fullwidth-code-point": "^2.0.0",
+                "strip-ansi": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/string-width/node_modules/ansi-regex": {
+            "version": "3.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/string-width/node_modules/is-fullwidth-code-point": {
+            "version": "2.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/string-width/node_modules/strip-ansi": {
+            "version": "4.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "ansi-regex": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/stringify-package": {
+            "version": "1.0.1",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/strip-ansi": {
+            "version": "3.0.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "ansi-regex": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/strip-eof": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/strip-json-comments": {
+            "version": "2.0.1",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/supports-color": {
+            "version": "5.4.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "has-flag": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/tar": {
+            "version": "4.4.19",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "chownr": "^1.1.4",
+                "fs-minipass": "^1.2.7",
+                "minipass": "^2.9.0",
+                "minizlib": "^1.3.3",
+                "mkdirp": "^0.5.5",
+                "safe-buffer": "^5.2.1",
+                "yallist": "^3.1.1"
+            },
+            "engines": {
+                "node": ">=4.5"
+            }
+        },
+        "node_modules/npm/node_modules/tar/node_modules/minipass": {
+            "version": "2.9.0",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "safe-buffer": "^5.1.2",
+                "yallist": "^3.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/tar/node_modules/safe-buffer": {
+            "version": "5.2.1",
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/feross"
+                },
+                {
+                    "type": "patreon",
+                    "url": "https://www.patreon.com/feross"
+                },
+                {
+                    "type": "consulting",
+                    "url": "https://feross.org/support"
+                }
+            ],
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/tar/node_modules/yallist": {
+            "version": "3.1.1",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/term-size": {
+            "version": "1.2.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "execa": "^0.7.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/text-table": {
+            "version": "0.2.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/through": {
+            "version": "2.3.8",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/through2": {
+            "version": "2.0.3",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "readable-stream": "^2.1.5",
+                "xtend": "~4.0.1"
+            }
+        },
+        "node_modules/npm/node_modules/through2/node_modules/readable-stream": {
+            "version": "2.3.6",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/npm/node_modules/through2/node_modules/string_decoder": {
+            "version": "1.1.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "safe-buffer": "~5.1.0"
+            }
+        },
+        "node_modules/npm/node_modules/timed-out": {
+            "version": "4.0.1",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/tiny-relative-date": {
+            "version": "1.3.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/tough-cookie": {
+            "version": "2.4.3",
+            "inBundle": true,
+            "license": "BSD-3-Clause",
+            "dependencies": {
+                "psl": "^1.1.24",
+                "punycode": "^1.4.1"
+            },
+            "engines": {
+                "node": ">=0.8"
+            }
+        },
+        "node_modules/npm/node_modules/tunnel-agent": {
+            "version": "0.6.0",
+            "inBundle": true,
+            "license": "Apache-2.0",
+            "dependencies": {
+                "safe-buffer": "^5.0.1"
+            },
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/npm/node_modules/tweetnacl": {
+            "version": "0.14.5",
+            "inBundle": true,
+            "license": "Unlicense",
+            "optional": true
+        },
+        "node_modules/npm/node_modules/typedarray": {
+            "version": "0.0.6",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/uid-number": {
+            "version": "0.0.6",
+            "inBundle": true,
+            "license": "ISC",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/npm/node_modules/umask": {
+            "version": "1.1.0",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/unique-filename": {
+            "version": "1.1.1",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "unique-slug": "^2.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/unique-slug": {
+            "version": "2.0.0",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "imurmurhash": "^0.1.4"
+            }
+        },
+        "node_modules/npm/node_modules/unique-string": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "crypto-random-string": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/unpipe": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/npm/node_modules/unzip-response": {
+            "version": "2.0.1",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/update-notifier": {
+            "version": "2.5.0",
+            "inBundle": true,
+            "license": "BSD-2-Clause",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/uri-js": {
+            "version": "4.4.0",
+            "inBundle": true,
+            "license": "BSD-2-Clause",
+            "dependencies": {
+                "punycode": "^2.1.0"
+            }
+        },
+        "node_modules/npm/node_modules/uri-js/node_modules/punycode": {
+            "version": "2.1.1",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/url-parse-lax": {
+            "version": "1.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "prepend-http": "^1.0.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/util-deprecate": {
+            "version": "1.0.2",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/util-extend": {
+            "version": "1.0.3",
+            "inBundle": true,
+            "license": "MIT"
+        },
+        "node_modules/npm/node_modules/util-promisify": {
+            "version": "2.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "object.getownpropertydescriptors": "^2.0.3"
+            }
+        },
+        "node_modules/npm/node_modules/uuid": {
+            "version": "3.3.3",
+            "inBundle": true,
+            "license": "MIT",
+            "bin": {
+                "uuid": "bin/uuid"
+            }
+        },
+        "node_modules/npm/node_modules/validate-npm-package-license": {
+            "version": "3.0.4",
+            "inBundle": true,
+            "license": "Apache-2.0",
+            "dependencies": {
+                "spdx-correct": "^3.0.0",
+                "spdx-expression-parse": "^3.0.0"
+            }
+        },
+        "node_modules/npm/node_modules/validate-npm-package-name": {
+            "version": "3.0.0",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "builtins": "^1.0.3"
+            }
+        },
+        "node_modules/npm/node_modules/verror": {
+            "version": "1.10.0",
+            "engines": [
+                "node >=0.6.0"
+            ],
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "assert-plus": "^1.0.0",
+                "core-util-is": "1.0.2",
+                "extsprintf": "^1.2.0"
+            }
+        },
+        "node_modules/npm/node_modules/wcwidth": {
+            "version": "1.0.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "defaults": "^1.0.3"
+            }
+        },
+        "node_modules/npm/node_modules/which": {
+            "version": "1.3.1",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "isexe": "^2.0.0"
+            },
+            "bin": {
+                "which": "bin/which"
+            }
+        },
+        "node_modules/npm/node_modules/which-module": {
+            "version": "2.0.0",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/wide-align": {
+            "version": "1.1.2",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "string-width": "^1.0.2"
+            }
+        },
+        "node_modules/npm/node_modules/wide-align/node_modules/string-width": {
+            "version": "1.0.2",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "code-point-at": "^1.0.0",
+                "is-fullwidth-code-point": "^1.0.0",
+                "strip-ansi": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/npm/node_modules/widest-line": {
+            "version": "2.0.1",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "string-width": "^2.1.1"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/worker-farm": {
+            "version": "1.7.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "errno": "~0.1.7"
+            }
+        },
+        "node_modules/npm/node_modules/wrap-ansi": {
+            "version": "5.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "ansi-styles": "^3.2.0",
+                "string-width": "^3.0.0",
+                "strip-ansi": "^5.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": {
+            "version": "4.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": {
+            "version": "2.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": {
+            "version": "3.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "emoji-regex": "^7.0.1",
+                "is-fullwidth-code-point": "^2.0.0",
+                "strip-ansi": "^5.1.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": {
+            "version": "5.2.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "ansi-regex": "^4.1.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/wrappy": {
+            "version": "1.0.2",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/write-file-atomic": {
+            "version": "2.4.3",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "graceful-fs": "^4.1.11",
+                "imurmurhash": "^0.1.4",
+                "signal-exit": "^3.0.2"
+            }
+        },
+        "node_modules/npm/node_modules/xdg-basedir": {
+            "version": "3.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/xtend": {
+            "version": "4.0.1",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=0.4"
+            }
+        },
+        "node_modules/npm/node_modules/y18n": {
+            "version": "4.0.1",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/yallist": {
+            "version": "3.0.3",
+            "inBundle": true,
+            "license": "ISC"
+        },
+        "node_modules/npm/node_modules/yargs": {
+            "version": "14.2.3",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "cliui": "^5.0.0",
+                "decamelize": "^1.2.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": "^15.0.1"
+            }
+        },
+        "node_modules/npm/node_modules/yargs-parser": {
+            "version": "15.0.1",
+            "inBundle": true,
+            "license": "ISC",
+            "dependencies": {
+                "camelcase": "^5.0.0",
+                "decamelize": "^1.2.0"
+            }
+        },
+        "node_modules/npm/node_modules/yargs-parser/node_modules/camelcase": {
+            "version": "5.3.1",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/yargs/node_modules/ansi-regex": {
+            "version": "4.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/yargs/node_modules/find-up": {
+            "version": "3.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "locate-path": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/yargs/node_modules/is-fullwidth-code-point": {
+            "version": "2.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/npm/node_modules/yargs/node_modules/locate-path": {
+            "version": "3.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "p-locate": "^3.0.0",
+                "path-exists": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/yargs/node_modules/p-limit": {
+            "version": "2.3.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "p-try": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/npm/node_modules/yargs/node_modules/p-locate": {
+            "version": "3.0.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "p-limit": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/yargs/node_modules/p-try": {
+            "version": "2.2.0",
+            "inBundle": true,
+            "license": "MIT",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/yargs/node_modules/string-width": {
+            "version": "3.1.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "emoji-regex": "^7.0.1",
+                "is-fullwidth-code-point": "^2.0.0",
+                "strip-ansi": "^5.1.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npm/node_modules/yargs/node_modules/strip-ansi": {
+            "version": "5.2.0",
+            "inBundle": true,
+            "license": "MIT",
+            "dependencies": {
+                "ansi-regex": "^4.1.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/npmlog": {
+            "version": "4.1.2",
+            "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
+            "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+            "dependencies": {
+                "are-we-there-yet": "~1.1.2",
+                "console-control-strings": "~1.1.0",
+                "gauge": "~2.7.3",
+                "set-blocking": "~2.0.0"
+            }
+        },
+        "node_modules/nth-check": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
+            "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
+            "dependencies": {
+                "boolbase": "^1.0.0"
+            },
+            "funding": {
+                "url": "https://github.com/fb55/nth-check?sponsor=1"
+            }
+        },
+        "node_modules/number-is-nan": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+            "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/nwsapi": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz",
+            "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==",
+            "dev": true
+        },
+        "node_modules/oauth": {
+            "version": "0.9.15",
+            "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
+            "integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE="
+        },
+        "node_modules/oauth-sign": {
+            "version": "0.9.0",
+            "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+            "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/object-assign": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+            "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/object-copy": {
+            "version": "0.1.0",
+            "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+            "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+            "dependencies": {
+                "copy-descriptor": "^0.1.0",
+                "define-property": "^0.2.5",
+                "kind-of": "^3.0.3"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/object-copy/node_modules/define-property": {
+            "version": "0.2.5",
+            "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+            "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+            "dependencies": {
+                "is-descriptor": "^0.1.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/object-copy/node_modules/is-buffer": {
+            "version": "1.1.6",
+            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+            "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+        },
+        "node_modules/object-copy/node_modules/kind-of": {
+            "version": "3.2.2",
+            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+            "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+            "dependencies": {
+                "is-buffer": "^1.1.5"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/object-inspect": {
+            "version": "1.7.0",
+            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
+            "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/object-is": {
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
+            "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/object-keys": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+            "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/object-to-arguments": {
+            "version": "0.0.8",
+            "resolved": "https://registry.npmjs.org/object-to-arguments/-/object-to-arguments-0.0.8.tgz",
+            "integrity": "sha512-BfWfuAwuhdH1bhMG5EG90WE/eckkBhBvnke8eSEkCDXoLE9Jk5JwYGTbCx1ehGwV48HvBkn62VukPBdlMUOY9w==",
+            "dependencies": {
+                "inspect-parameters-declaration": "0.0.10",
+                "magicli": "0.0.5",
+                "split-skip": "0.0.2",
+                "stringify-parameters": "0.0.4",
+                "unpack-string": "0.0.2"
+            },
+            "bin": {
+                "object-to-arguments": "bin/cli.js"
+            }
+        },
+        "node_modules/object-to-arguments/node_modules/inspect-parameters-declaration": {
+            "version": "0.0.10",
+            "resolved": "https://registry.npmjs.org/inspect-parameters-declaration/-/inspect-parameters-declaration-0.0.10.tgz",
+            "integrity": "sha512-L8/Bvt9iDXQTZ63xY5/MAyvzz+FagR/qGh1kIXvUpsno3AAE0Z95d6QO51zrcMGaEGpwh/57idfMxTxbvRmytg==",
+            "dependencies": {
+                "magicli": "0.0.5",
+                "split-skip": "0.0.2",
+                "stringify-parameters": "0.0.4",
+                "unpack-string": "0.0.2"
+            },
+            "bin": {
+                "inspect-parameters-declaration": "bin/cli.js"
+            }
+        },
+        "node_modules/object-to-arguments/node_modules/magicli": {
+            "version": "0.0.5",
+            "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz",
+            "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=",
+            "dependencies": {
+                "commander": "^2.9.0",
+                "get-stdin": "^5.0.1",
+                "inspect-function": "^0.2.1",
+                "pipe-functions": "^1.2.0"
+            }
+        },
+        "node_modules/object-visit": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+            "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+            "dependencies": {
+                "isobject": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/object.assign": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+            "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+            "dependencies": {
+                "define-properties": "^1.1.2",
+                "function-bind": "^1.1.1",
+                "has-symbols": "^1.0.0",
+                "object-keys": "^1.0.11"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/object.getownpropertydescriptors": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz",
+            "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==",
+            "dependencies": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.0-next.1"
+            },
+            "engines": {
+                "node": ">= 0.8"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/object.pick": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+            "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+            "dependencies": {
+                "isobject": "^3.0.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/obuf": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
+            "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
+            "dev": true
+        },
+        "node_modules/on-finished": {
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+            "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+            "dependencies": {
+                "ee-first": "1.1.1"
+            },
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/on-headers": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+            "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/once": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+            "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+            "dependencies": {
+                "wrappy": "1"
+            }
+        },
+        "node_modules/onetime": {
+            "version": "5.1.2",
+            "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+            "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+            "dependencies": {
+                "mimic-fn": "^2.1.0"
+            },
+            "engines": {
+                "node": ">=6"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/opentype.js": {
+            "version": "1.3.4",
+            "resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-1.3.4.tgz",
+            "integrity": "sha512-d2JE9RP/6uagpQAVtJoF0pJJA/fgai89Cc50Yp0EJHk+eLp6QQ7gBoblsnubRULNY132I0J1QKMJ+JTbMqz4sw==",
+            "dependencies": {
+                "string.prototype.codepointat": "^0.2.1",
+                "tiny-inflate": "^1.0.3"
+            },
+            "bin": {
+                "ot": "bin/ot"
+            },
+            "engines": {
+                "node": ">= 8.0.0"
+            }
+        },
+        "node_modules/opn": {
+            "version": "5.5.0",
+            "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz",
+            "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==",
+            "dev": true,
+            "dependencies": {
+                "is-wsl": "^1.1.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/optional-require": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.0.3.tgz",
+            "integrity": "sha512-RV2Zp2MY2aeYK5G+B/Sps8lW5NHAzE5QClbFP15j+PWmP+T9PxlJXBOOLoSAdgwFvS4t0aMR4vpedMkbHfh0nA==",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/optionator": {
+            "version": "0.8.3",
+            "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+            "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+            "dev": true,
+            "dependencies": {
+                "deep-is": "~0.1.3",
+                "fast-levenshtein": "~2.0.6",
+                "levn": "~0.3.0",
+                "prelude-ls": "~1.1.2",
+                "type-check": "~0.3.2",
+                "word-wrap": "~1.2.3"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/orderedmap": {
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-1.1.5.tgz",
+            "integrity": "sha512-/fzlCGKRmfayGoI9UUXvJfc2nMZlJHW30QqEvwPvlg8tsX7jyiUSomYie6mYqx7Z9bOMGoag0H/q1PS/0PjYkg=="
+        },
+        "node_modules/original": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
+            "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
+            "dev": true,
+            "dependencies": {
+                "url-parse": "^1.4.3"
+            }
+        },
+        "node_modules/os-homedir": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+            "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/os-tmpdir": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+            "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/osenv": {
+            "version": "0.1.5",
+            "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
+            "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+            "dependencies": {
+                "os-homedir": "^1.0.0",
+                "os-tmpdir": "^1.0.0"
+            }
+        },
+        "node_modules/p-cancelable": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz",
+            "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==",
+            "engines": {
+                "node": ">=12.20"
+            }
+        },
+        "node_modules/p-debounce": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/p-debounce/-/p-debounce-1.0.0.tgz",
+            "integrity": "sha1-y38svu/YegnrqGHhErZ1J+Yh4v0=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/p-finally": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+            "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/p-limit": {
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+            "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+            "dependencies": {
+                "p-try": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/p-locate": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+            "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+            "dependencies": {
+                "p-limit": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/p-map": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
+            "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
+            "dev": true,
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/p-retry": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz",
+            "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==",
+            "dev": true,
+            "dependencies": {
+                "retry": "^0.12.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/p-try": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+            "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/package-json": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz",
+            "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=",
+            "dependencies": {
+                "got": "^6.7.1",
+                "registry-auth-token": "^3.0.1",
+                "registry-url": "^3.0.3",
+                "semver": "^5.1.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/package-json/node_modules/got": {
+            "version": "6.7.1",
+            "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
+            "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/package-json/node_modules/lowercase-keys": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
+            "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/pako": {
+            "version": "1.0.11",
+            "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+            "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+        },
+        "node_modules/parallel-transform": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz",
+            "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==",
+            "dev": true,
+            "dependencies": {
+                "cyclist": "^1.0.1",
+                "inherits": "^2.0.3",
+                "readable-stream": "^2.1.5"
+            }
+        },
+        "node_modules/parallel-transform/node_modules/readable-stream": {
+            "version": "2.3.7",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+            "dev": true,
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/param-case": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
+            "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
+            "dependencies": {
+                "dot-case": "^3.0.4",
+                "tslib": "^2.0.3"
+            }
+        },
+        "node_modules/param-case/node_modules/tslib": {
+            "version": "2.3.1",
+            "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+            "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+        },
+        "node_modules/parent-module": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+            "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+            "dependencies": {
+                "callsites": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/parse-json": {
+            "version": "5.2.0",
+            "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+            "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+            "dependencies": {
+                "@babel/code-frame": "^7.0.0",
+                "error-ex": "^1.3.1",
+                "json-parse-even-better-errors": "^2.3.0",
+                "lines-and-columns": "^1.1.6"
+            },
+            "engines": {
+                "node": ">=8"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/parseqs": {
+            "version": "0.0.6",
+            "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
+            "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
+        },
+        "node_modules/parseuri": {
+            "version": "0.0.6",
+            "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
+            "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
+        },
+        "node_modules/parseurl": {
+            "version": "1.3.3",
+            "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+            "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/pascal-case": {
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
+            "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
+            "dependencies": {
+                "no-case": "^3.0.4",
+                "tslib": "^2.0.3"
+            }
+        },
+        "node_modules/pascal-case/node_modules/tslib": {
+            "version": "2.3.1",
+            "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+            "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+        },
+        "node_modules/pascalcase": {
+            "version": "0.1.1",
+            "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+            "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/passport": {
+            "version": "0.4.1",
+            "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz",
+            "integrity": "sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==",
+            "dependencies": {
+                "passport-strategy": "1.x.x",
+                "pause": "0.0.1"
+            },
+            "engines": {
+                "node": ">= 0.4.0"
+            }
+        },
+        "node_modules/passport-google-oauth20": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz",
+            "integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==",
+            "dependencies": {
+                "passport-oauth2": "1.x.x"
+            },
+            "engines": {
+                "node": ">= 0.4.0"
+            }
+        },
+        "node_modules/passport-local": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
+            "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=",
+            "dependencies": {
+                "passport-strategy": "1.x.x"
+            },
+            "engines": {
+                "node": ">= 0.4.0"
+            }
+        },
+        "node_modules/passport-oauth2": {
+            "version": "1.5.0",
+            "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.5.0.tgz",
+            "integrity": "sha512-kqBt6vR/5VlCK8iCx1/KpY42kQ+NEHZwsSyt4Y6STiNjU+wWICG1i8ucc1FapXDGO15C5O5VZz7+7vRzrDPXXQ==",
+            "dependencies": {
+                "base64url": "3.x.x",
+                "oauth": "0.9.x",
+                "passport-strategy": "1.x.x",
+                "uid2": "0.0.x",
+                "utils-merge": "1.x.x"
+            },
+            "engines": {
+                "node": ">= 0.4.0"
+            }
+        },
+        "node_modules/passport-strategy": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
+            "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=",
+            "engines": {
+                "node": ">= 0.4.0"
+            }
+        },
+        "node_modules/path-browserify": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
+            "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="
+        },
+        "node_modules/path-dirname": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+            "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA="
+        },
+        "node_modules/path-exists": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+            "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/path-is-absolute": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+            "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/path-is-inside": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+            "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM="
+        },
+        "node_modules/path-key": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+            "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/path-parse": {
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+            "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
+        },
+        "node_modules/path-to-regexp": {
+            "version": "0.1.7",
+            "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+            "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+        },
+        "node_modules/path-type": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+            "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/pathval": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
+            "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/pause": {
+            "version": "0.0.1",
+            "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
+            "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
+        },
+        "node_modules/pdf-parse": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/pdf-parse/-/pdf-parse-1.1.1.tgz",
+            "integrity": "sha512-v6ZJ/efsBpGrGGknjtq9J/oC8tZWq0KWL5vQrk2GlzLEQPUDB1ex+13Rmidl1neNN358Jn9EHZw5y07FFtaC7A==",
+            "dependencies": {
+                "debug": "^3.1.0",
+                "node-ensure": "^0.0.0"
+            },
+            "engines": {
+                "node": ">=6.8.1"
+            }
+        },
+        "node_modules/pdfjs": {
+            "version": "2.4.7",
+            "resolved": "https://registry.npmjs.org/pdfjs/-/pdfjs-2.4.7.tgz",
+            "integrity": "sha512-qGGZiQ7cz7nDgRgNSMm0qsZ4QPlAvZr+kWwB78hZzClojtfqGbGUT/gwzf8S2nniwvLMB56boBTTIppQohTJUA==",
+            "dependencies": {
+                "@rkusa/linebreak": "^1.0.0",
+                "opentype.js": "^1.3.3",
+                "pako": "^2.0.3",
+                "readable-stream": "^3.6.0",
+                "unorm": "^1.6.0",
+                "uuid": "^8.3.1"
+            },
+            "engines": {
+                "node": ">=7"
+            }
+        },
+        "node_modules/pdfjs-dist": {
+            "version": "2.13.216",
+            "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.13.216.tgz",
+            "integrity": "sha512-qn/9a/3IHIKZarTK6ajeeFXBkG15Lg1Fx99PxU09PAU2i874X8mTcHJYyDJxu7WDfNhV6hM7bRQBZU384anoqQ==",
+            "dependencies": {
+                "web-streams-polyfill": "^3.2.0"
+            },
+            "peerDependencies": {
+                "worker-loader": "^3.0.8"
+            },
+            "peerDependenciesMeta": {
+                "worker-loader": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/pdfjs/node_modules/pako": {
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/pako/-/pako-2.0.4.tgz",
+            "integrity": "sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg=="
+        },
+        "node_modules/pdfjs/node_modules/uuid": {
+            "version": "8.3.2",
+            "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+            "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+            "bin": {
+                "uuid": "dist/bin/uuid"
+            }
+        },
+        "node_modules/pend": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+            "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
+        },
+        "node_modules/performance-now": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+            "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+        },
+        "node_modules/picocolors": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+            "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+        },
+        "node_modules/picomatch": {
+            "version": "2.3.1",
+            "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+            "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+            "dev": true,
+            "engines": {
+                "node": ">=8.6"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/jonschlinkert"
+            }
+        },
+        "node_modules/pify": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+            "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+            "dev": true,
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/pinkie": {
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+            "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/pinkie-promise": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+            "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+            "dependencies": {
+                "pinkie": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/pipe-functions": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/pipe-functions/-/pipe-functions-1.3.0.tgz",
+            "integrity": "sha512-6Rtbp7criZRwedlvWbUYxqlqJoAlMvYHo2UcRWq79xZ54vZcaNHpVBOcWkX3ErT2aUA69tv+uiv4zKJbhD/Wgg=="
+        },
+        "node_modules/pkg-dir": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
+            "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
+            "dev": true,
+            "dependencies": {
+                "find-up": "^2.1.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/pkg-dir/node_modules/find-up": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+            "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+            "dev": true,
+            "dependencies": {
+                "locate-path": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/pkg-dir/node_modules/locate-path": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+            "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+            "dev": true,
+            "dependencies": {
+                "p-locate": "^2.0.0",
+                "path-exists": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/pkg-dir/node_modules/p-limit": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+            "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+            "dev": true,
+            "dependencies": {
+                "p-try": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/pkg-dir/node_modules/p-locate": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+            "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+            "dev": true,
+            "dependencies": {
+                "p-limit": "^1.1.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/pkg-dir/node_modules/p-try": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+            "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+            "dev": true,
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/please-upgrade-node": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz",
+            "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==",
+            "dependencies": {
+                "semver-compare": "^1.0.0"
+            }
+        },
+        "node_modules/plist": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/plist/-/plist-1.2.0.tgz",
+            "integrity": "sha1-CEtQk93JJQbiWfh0uNmxr7jHlZM=",
+            "dependencies": {
+                "base64-js": "0.0.8",
+                "util-deprecate": "1.0.2",
+                "xmlbuilder": "4.0.0",
+                "xmldom": "0.1.x"
+            }
+        },
+        "node_modules/plist/node_modules/base64-js": {
+            "version": "0.0.8",
+            "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
+            "integrity": "sha1-EQHpVE9KdrG8OybUUsqW16NeeXg=",
+            "engines": {
+                "node": ">= 0.4"
+            }
+        },
+        "node_modules/pn": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
+            "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
+            "dev": true
+        },
+        "node_modules/popper.js": {
+            "version": "1.16.1-lts",
+            "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz",
+            "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA=="
+        },
+        "node_modules/portfinder": {
+            "version": "1.0.28",
+            "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
+            "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==",
+            "dev": true,
+            "dependencies": {
+                "async": "^2.6.2",
+                "debug": "^3.1.1",
+                "mkdirp": "^0.5.5"
+            },
+            "engines": {
+                "node": ">= 0.12.0"
+            }
+        },
+        "node_modules/portfinder/node_modules/mkdirp": {
+            "version": "0.5.5",
+            "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+            "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+            "dev": true,
+            "dependencies": {
+                "minimist": "^1.2.5"
+            },
+            "bin": {
+                "mkdirp": "bin/cmd.js"
+            }
+        },
+        "node_modules/posix-character-classes": {
+            "version": "0.1.1",
+            "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+            "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/postcss": {
+            "version": "7.0.27",
+            "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz",
+            "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==",
+            "dev": true,
+            "dependencies": {
+                "chalk": "^2.4.2",
+                "source-map": "^0.6.1",
+                "supports-color": "^6.1.0"
+            },
+            "engines": {
+                "node": ">=6.0.0"
+            },
+            "funding": {
+                "type": "tidelift",
+                "url": "https://tidelift.com/funding/github/npm/postcss"
+            }
+        },
+        "node_modules/postcss-modules-extract-imports": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz",
+            "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==",
+            "dev": true,
+            "dependencies": {
+                "postcss": "^7.0.5"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/postcss-modules-local-by-default": {
+            "version": "2.0.6",
+            "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz",
+            "integrity": "sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA==",
+            "dev": true,
+            "dependencies": {
+                "postcss": "^7.0.6",
+                "postcss-selector-parser": "^6.0.0",
+                "postcss-value-parser": "^3.3.1"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/postcss-modules-scope": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz",
+            "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==",
+            "dev": true,
+            "dependencies": {
+                "postcss": "^7.0.6",
+                "postcss-selector-parser": "^6.0.0"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/postcss-modules-values": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz",
+            "integrity": "sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w==",
+            "dev": true,
+            "dependencies": {
+                "icss-replace-symbols": "^1.1.0",
+                "postcss": "^7.0.6"
+            }
+        },
+        "node_modules/postcss-selector-parser": {
+            "version": "6.0.2",
+            "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
+            "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
+            "dev": true,
+            "dependencies": {
+                "cssesc": "^3.0.0",
+                "indexes-of": "^1.0.1",
+                "uniq": "^1.0.1"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/postcss-value-parser": {
+            "version": "3.3.1",
+            "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+            "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
+        },
+        "node_modules/postcss/node_modules/source-map": {
+            "version": "0.6.1",
+            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+            "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/postcss/node_modules/supports-color": {
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+            "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+            "dev": true,
+            "dependencies": {
+                "has-flag": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/prebuild-install": {
+            "version": "5.3.3",
+            "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.3.tgz",
+            "integrity": "sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g==",
+            "dependencies": {
+                "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.7.0",
+                "noop-logger": "^0.1.1",
+                "npmlog": "^4.0.1",
+                "pump": "^3.0.0",
+                "rc": "^1.2.7",
+                "simple-get": "^3.0.3",
+                "tar-fs": "^2.0.0",
+                "tunnel-agent": "^0.6.0",
+                "which-pm-runs": "^1.0.0"
+            },
+            "bin": {
+                "prebuild-install": "bin.js"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/prelude-ls": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+            "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+            "dev": true,
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/prepend-http": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
+            "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/pretty-error": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
+            "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==",
+            "dependencies": {
+                "lodash": "^4.17.20",
+                "renderkid": "^3.0.0"
+            }
+        },
+        "node_modules/probe-image-size": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/probe-image-size/-/probe-image-size-4.1.1.tgz",
+            "integrity": "sha512-42LqKZqTLxH/UvAZ2/cKhAsR4G/Y6B7i7fI2qtQu9hRBK4YjS6gqO+QRtwTjvojUx4+/+JuOMzLoFyRecT9qRw==",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/probe-image-size/node_modules/deepmerge": {
+            "version": "4.2.2",
+            "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
+            "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/process": {
+            "version": "0.11.10",
+            "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+            "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
+            "engines": {
+                "node": ">= 0.6.0"
+            }
+        },
+        "node_modules/process-nextick-args": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+            "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+        },
+        "node_modules/progress": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+            "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+            "engines": {
+                "node": ">=0.4.0"
+            }
+        },
+        "node_modules/promise": {
+            "version": "7.3.1",
+            "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
+            "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+            "dependencies": {
+                "asap": "~2.0.3"
+            }
+        },
+        "node_modules/promise-inflight": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
+            "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
+            "dev": true
+        },
+        "node_modules/prop-types": {
+            "version": "15.7.2",
+            "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
+            "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
+            "dependencies": {
+                "loose-envify": "^1.4.0",
+                "object-assign": "^4.1.1",
+                "react-is": "^16.8.1"
+            }
+        },
+        "node_modules/prosemirror-commands": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.2.1.tgz",
+            "integrity": "sha512-S/IkpXfpuLFsRynC2HQ5iYROUPiZskKS1+ClcWycGJvj4HMb/mVfeEkQrixYxgTl96EAh+RZQNWPC06GZXk5tQ==",
+            "dependencies": {
+                "prosemirror-model": "^1.0.0",
+                "prosemirror-state": "^1.0.0",
+                "prosemirror-transform": "^1.0.0"
+            }
+        },
+        "node_modules/prosemirror-dev-tools": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/prosemirror-dev-tools/-/prosemirror-dev-tools-3.1.0.tgz",
+            "integrity": "sha512-ghuxb6QD3WWl3EE8OK1O3P67OKjbTxt5mvagAUK6xd0kS7SBysSS4KE+HiNPxC8r812EEFFJ2S6asIs9i7rvkQ==",
+            "dependencies": {
+                "@babel/runtime": "^7.0.0",
+                "@emotion/css": "^11.1.3",
+                "@emotion/react": "^11.4.0",
+                "@emotion/styled": "^11.3.0",
+                "html": "^1.0.0",
+                "jsondiffpatch": "^0.4.1",
+                "nanoid": "^2.1.11",
+                "prop-types": "^15.7.2",
+                "prosemirror-model": ">=1.0.0",
+                "prosemirror-state": ">=1.0.0",
+                "react-dock": "^0.2.4",
+                "react-json-tree": "^0.11.2",
+                "unstated": "^2.1.1"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "peerDependencies": {
+                "react": ">=16.8.0",
+                "react-dom": ">=16.8.0"
+            }
+        },
+        "node_modules/prosemirror-find-replace": {
+            "version": "0.9.0",
+            "resolved": "https://registry.npmjs.org/prosemirror-find-replace/-/prosemirror-find-replace-0.9.0.tgz",
+            "integrity": "sha1-QgsENNF5xdBJD44hSNhVGpVJY4I=",
+            "peerDependencies": {
+                "prosemirror": ">= 0.7.0"
+            }
+        },
+        "node_modules/prosemirror-history": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.2.0.tgz",
+            "integrity": "sha512-B9v9xtf4fYbKxQwIr+3wtTDNLDZcmMMmGiI3TAPShnUzvo+Rmv1GiUrsQChY1meetHl7rhML2cppF3FTs7f7UQ==",
+            "dependencies": {
+                "prosemirror-state": "^1.2.2",
+                "prosemirror-transform": "^1.0.0",
+                "rope-sequence": "^1.3.0"
+            }
+        },
+        "node_modules/prosemirror-inputrules": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.1.3.tgz",
+            "integrity": "sha512-ZaHCLyBtvbyIHv0f5p6boQTIJjlD6o2NPZiEaZWT2DA+j591zS29QQEMT4lBqwcLW3qRSf7ZvoKNbf05YrsStw==",
+            "dependencies": {
+                "prosemirror-state": "^1.0.0",
+                "prosemirror-transform": "^1.0.0"
+            }
+        },
+        "node_modules/prosemirror-keymap": {
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.1.5.tgz",
+            "integrity": "sha512-8SZgPH3K+GLsHL2wKuwBD9rxhsbnVBTwpHCO4VUO5GmqUQlxd/2GtBVWTsyLq4Dp3N9nGgPd3+lZFKUDuVp+Vw==",
+            "dependencies": {
+                "prosemirror-state": "^1.0.0",
+                "w3c-keyname": "^2.2.0"
+            }
+        },
+        "node_modules/prosemirror-model": {
+            "version": "1.16.1",
+            "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.16.1.tgz",
+            "integrity": "sha512-r1/w0HDU40TtkXp0DyKBnFPYwd8FSlUSJmGCGFv4DeynfeSlyQF2FD0RQbVEMOe6P3PpUSXM6LZBV7W/YNZ4mA==",
+            "dependencies": {
+                "orderedmap": "^1.1.0"
+            }
+        },
+        "node_modules/prosemirror-schema-list": {
+            "version": "1.1.6",
+            "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.1.6.tgz",
+            "integrity": "sha512-aFGEdaCWmJzouZ8DwedmvSsL50JpRkqhQ6tcpThwJONVVmCgI36LJHtoQ4VGZbusMavaBhXXr33zyD2IVsTlkw==",
+            "dependencies": {
+                "prosemirror-model": "^1.0.0",
+                "prosemirror-transform": "^1.0.0"
+            }
+        },
+        "node_modules/prosemirror-state": {
+            "version": "1.3.4",
+            "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.3.4.tgz",
+            "integrity": "sha512-Xkkrpd1y/TQ6HKzN3agsQIGRcLckUMA9u3j207L04mt8ToRgpGeyhbVv0HI7omDORIBHjR29b7AwlATFFf2GLA==",
+            "dependencies": {
+                "prosemirror-model": "^1.0.0",
+                "prosemirror-transform": "^1.0.0"
+            }
+        },
+        "node_modules/prosemirror-transform": {
+            "version": "1.3.4",
+            "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.3.4.tgz",
+            "integrity": "sha512-gTsg3UIeaFuEY6+YmNPMgTpEkCKPedkFIUnsPpOMbclU701fEVI/e4VOXACXh3BO5rZJaBbEBwrnzB0mLp6eBA==",
+            "dependencies": {
+                "prosemirror-model": "^1.0.0"
+            }
+        },
+        "node_modules/prosemirror-view": {
+            "version": "1.23.7",
+            "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.23.7.tgz",
+            "integrity": "sha512-ugY+g/4UI2Ree1zzdvbyQWF2KpbFa7kxKMLHaEJcxiPaErnZiD5wZFqIgFinc7fY2v/QM3DLnJ++2I45ULRdrg==",
+            "dependencies": {
+                "prosemirror-model": "^1.16.0",
+                "prosemirror-state": "^1.0.0",
+                "prosemirror-transform": "^1.1.0"
+            }
+        },
+        "node_modules/proxy-addr": {
+            "version": "2.0.7",
+            "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+            "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+            "dependencies": {
+                "forwarded": "0.2.0",
+                "ipaddr.js": "1.9.1"
+            },
+            "engines": {
+                "node": ">= 0.10"
+            }
+        },
+        "node_modules/proxy-from-env": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+            "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+        },
+        "node_modules/prr": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+            "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
+            "dev": true
+        },
+        "node_modules/pseudomap": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+            "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
+        },
+        "node_modules/psl": {
+            "version": "1.8.0",
+            "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
+            "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
+        },
+        "node_modules/pstree.remy": {
+            "version": "1.1.7",
+            "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.7.tgz",
+            "integrity": "sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A=="
+        },
+        "node_modules/pug": {
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.4.tgz",
+            "integrity": "sha512-XhoaDlvi6NIzL49nu094R2NA6P37ijtgMDuWE+ofekDChvfKnzFal60bhSdiy8y2PBO6fmz3oMEIcfpBVRUdvw==",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/pug-attrs": {
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-2.0.4.tgz",
+            "integrity": "sha512-TaZ4Z2TWUPDJcV3wjU3RtUXMrd3kM4Wzjbe3EWnSsZPsJ3LDI0F3yCnf2/W7PPFF+edUFQ0HgDL1IoxSz5K8EQ==",
+            "dependencies": {
+                "constantinople": "^3.0.1",
+                "js-stringify": "^1.0.1",
+                "pug-runtime": "^2.0.5"
+            }
+        },
+        "node_modules/pug-code-gen": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-2.0.2.tgz",
+            "integrity": "sha512-kROFWv/AHx/9CRgoGJeRSm+4mLWchbgpRzTEn8XCiwwOy6Vh0gAClS8Vh5TEJ9DBjaP8wCjS3J6HKsEsYdvaCw==",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/pug-error": {
+            "version": "1.3.3",
+            "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-1.3.3.tgz",
+            "integrity": "sha512-qE3YhESP2mRAWMFJgKdtT5D7ckThRScXRwkfo+Erqga7dyJdY3ZquspprMCj/9sJ2ijm5hXFWQE/A3l4poMWiQ=="
+        },
+        "node_modules/pug-filters": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-3.1.1.tgz",
+            "integrity": "sha512-lFfjNyGEyVWC4BwX0WyvkoWLapI5xHSM3xZJFUhx4JM4XyyRdO8Aucc6pCygnqV2uSgJFaJWW3Ft1wCWSoQkQg==",
+            "dependencies": {
+                "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.1.6",
+                "uglify-js": "^2.6.1"
+            }
+        },
+        "node_modules/pug-lexer": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-4.1.0.tgz",
+            "integrity": "sha512-i55yzEBtjm0mlplW4LoANq7k3S8gDdfC6+LThGEvsK4FuobcKfDAwt6V4jKPH9RtiE3a2Akfg5UpafZ1OksaPA==",
+            "dependencies": {
+                "character-parser": "^2.1.1",
+                "is-expression": "^3.0.0",
+                "pug-error": "^1.3.3"
+            }
+        },
+        "node_modules/pug-linker": {
+            "version": "3.0.6",
+            "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-3.0.6.tgz",
+            "integrity": "sha512-bagfuHttfQOpANGy1Y6NJ+0mNb7dD2MswFG2ZKj22s8g0wVsojpRlqveEQHmgXXcfROB2RT6oqbPYr9EN2ZWzg==",
+            "dependencies": {
+                "pug-error": "^1.3.3",
+                "pug-walk": "^1.1.8"
+            }
+        },
+        "node_modules/pug-load": {
+            "version": "2.0.12",
+            "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-2.0.12.tgz",
+            "integrity": "sha512-UqpgGpyyXRYgJs/X60sE6SIf8UBsmcHYKNaOccyVLEuT6OPBIMo6xMPhoJnqtB3Q3BbO4Z3Bjz5qDsUWh4rXsg==",
+            "dependencies": {
+                "object-assign": "^4.1.0",
+                "pug-walk": "^1.1.8"
+            }
+        },
+        "node_modules/pug-parser": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-5.0.1.tgz",
+            "integrity": "sha512-nGHqK+w07p5/PsPIyzkTQfzlYfuqoiGjaoqHv1LjOv2ZLXmGX1O+4Vcvps+P4LhxZ3drYSljjq4b+Naid126wA==",
+            "dependencies": {
+                "pug-error": "^1.3.3",
+                "token-stream": "0.0.1"
+            }
+        },
+        "node_modules/pug-runtime": {
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-2.0.5.tgz",
+            "integrity": "sha512-P+rXKn9un4fQY77wtpcuFyvFaBww7/91f3jHa154qU26qFAnOe6SW1CbIDcxiG5lLK9HazYrMCCuDvNgDQNptw=="
+        },
+        "node_modules/pug-strip-comments": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-1.0.4.tgz",
+            "integrity": "sha512-i5j/9CS4yFhSxHp5iKPHwigaig/VV9g+FgReLJWWHEHbvKsbqL0oP/K5ubuLco6Wu3Kan5p7u7qk8A4oLLh6vw==",
+            "dependencies": {
+                "pug-error": "^1.3.3"
+            }
+        },
+        "node_modules/pug-walk": {
+            "version": "1.1.8",
+            "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-1.1.8.tgz",
+            "integrity": "sha512-GMu3M5nUL3fju4/egXwZO0XLi6fW/K3T3VTgFQ14GxNi8btlxgT5qZL//JwZFm/2Fa64J/PNS8AZeys3wiMkVA=="
+        },
+        "node_modules/pump": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+            "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+            "dependencies": {
+                "end-of-stream": "^1.1.0",
+                "once": "^1.3.1"
+            }
+        },
+        "node_modules/pumpify": {
+            "version": "1.5.1",
+            "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
+            "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
+            "dev": true,
+            "dependencies": {
+                "duplexify": "^3.6.0",
+                "inherits": "^2.0.3",
+                "pump": "^2.0.0"
+            }
+        },
+        "node_modules/pumpify/node_modules/pump": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
+            "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+            "dev": true,
+            "dependencies": {
+                "end-of-stream": "^1.1.0",
+                "once": "^1.3.1"
+            }
+        },
+        "node_modules/punycode": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+            "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/puppeteer": {
+            "version": "3.3.0",
+            "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-3.3.0.tgz",
+            "integrity": "sha512-23zNqRltZ1PPoK28uRefWJ/zKb5Jhnzbbwbpcna2o5+QMn17F0khq5s1bdH3vPlyj+J36pubccR8wiNA/VE0Vw==",
+            "deprecated": "Version no longer supported. Upgrade to @latest",
+            "hasInstallScript": true,
+            "dependencies": {
+                "debug": "^4.1.0",
+                "extract-zip": "^2.0.0",
+                "https-proxy-agent": "^4.0.0",
+                "mime": "^2.0.3",
+                "progress": "^2.0.1",
+                "proxy-from-env": "^1.0.0",
+                "rimraf": "^3.0.2",
+                "tar-fs": "^2.0.0",
+                "unbzip2-stream": "^1.3.3",
+                "ws": "^7.2.3"
+            },
+            "engines": {
+                "node": ">=10.18.1"
+            }
+        },
+        "node_modules/puppeteer/node_modules/agent-base": {
+            "version": "5.1.1",
+            "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz",
+            "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==",
+            "engines": {
+                "node": ">= 6.0.0"
+            }
+        },
+        "node_modules/puppeteer/node_modules/debug": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+            "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+            "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
+            "dependencies": {
+                "ms": "^2.1.1"
+            }
+        },
+        "node_modules/puppeteer/node_modules/https-proxy-agent": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz",
+            "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==",
+            "dependencies": {
+                "agent-base": "5",
+                "debug": "4"
+            },
+            "engines": {
+                "node": ">= 6.0.0"
+            }
+        },
+        "node_modules/puppeteer/node_modules/mime": {
+            "version": "2.4.6",
+            "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
+            "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==",
+            "bin": {
+                "mime": "cli.js"
+            },
+            "engines": {
+                "node": ">=4.0.0"
+            }
+        },
+        "node_modules/pure-color": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz",
+            "integrity": "sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4="
+        },
+        "node_modules/q": {
+            "version": "1.5.1",
+            "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
+            "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
+            "engines": {
+                "node": ">=0.6.0",
+                "teleport": ">=0.2.0"
+            }
+        },
+        "node_modules/qs": {
+            "version": "6.5.3",
+            "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
+            "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
+            "engines": {
+                "node": ">=0.6"
+            }
+        },
+        "node_modules/query-string": {
+            "version": "6.14.1",
+            "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz",
+            "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==",
+            "dependencies": {
+                "decode-uri-component": "^0.2.0",
+                "filter-obj": "^1.1.0",
+                "split-on-first": "^1.0.0",
+                "strict-uri-encode": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/querystring": {
+            "version": "0.2.0",
+            "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+            "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
+            "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.",
+            "dev": true,
+            "engines": {
+                "node": ">=0.4.x"
+            }
+        },
+        "node_modules/querystringify": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+            "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
+            "dev": true
+        },
+        "node_modules/quick-lru": {
+            "version": "5.1.1",
+            "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+            "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/raf-schd": {
+            "version": "4.0.3",
+            "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz",
+            "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ=="
+        },
+        "node_modules/random-bytes": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
+            "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=",
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/randombytes": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+            "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+            "dependencies": {
+                "safe-buffer": "^5.1.0"
+            }
+        },
+        "node_modules/range-parser": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+            "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/raw-body": {
+            "version": "2.4.3",
+            "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz",
+            "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==",
+            "dependencies": {
+                "bytes": "3.1.2",
+                "http-errors": "1.8.1",
+                "iconv-lite": "0.4.24",
+                "unpipe": "1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/raw-loader": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-1.0.0.tgz",
+            "integrity": "sha512-Uqy5AqELpytJTRxYT4fhltcKPj0TyaEpzJDcGz7DFJi+pQOOi3GjR/DOdxTkTsF+NzhnldIoG6TORaBlInUuqA==",
+            "dependencies": {
+                "loader-utils": "^1.1.0",
+                "schema-utils": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 6.9.0"
+            },
+            "peerDependencies": {
+                "webpack": "^4.3.0"
+            }
+        },
+        "node_modules/raw-loader/node_modules/schema-utils": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+            "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+            "dependencies": {
+                "ajv": "^6.1.0",
+                "ajv-errors": "^1.0.0",
+                "ajv-keywords": "^3.1.0"
+            },
+            "engines": {
+                "node": ">= 4"
+            }
+        },
+        "node_modules/rc": {
+            "version": "1.2.8",
+            "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+            "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+            "dependencies": {
+                "deep-extend": "^0.6.0",
+                "ini": "~1.3.0",
+                "minimist": "^1.2.0",
+                "strip-json-comments": "~2.0.1"
+            },
+            "bin": {
+                "rc": "cli.js"
+            }
+        },
+        "node_modules/rc-switch": {
+            "version": "1.9.2",
+            "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-1.9.2.tgz",
+            "integrity": "sha512-qaK7mY4FLDKy99Hq3A1tf8CcqfzKtHp9LPX8WTnZ0MzdHCTneSARb1XD7Eqeu8BactasYGsi2bF9p18Q+/5JEw==",
+            "dependencies": {
+                "classnames": "^2.2.1",
+                "prop-types": "^15.5.6",
+                "react-lifecycles-compat": "^3.0.4"
+            },
+            "peerDependencies": {
+                "react": "^16.0.0",
+                "react-dom": "^16.0.0"
+            }
+        },
+        "node_modules/react": {
+            "version": "16.14.0",
+            "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz",
+            "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==",
+            "dependencies": {
+                "loose-envify": "^1.1.0",
+                "object-assign": "^4.1.1",
+                "prop-types": "^15.6.2"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/react-audio-waveform": {
+            "version": "0.0.5",
+            "resolved": "https://registry.npmjs.org/react-audio-waveform/-/react-audio-waveform-0.0.5.tgz",
+            "integrity": "sha512-4dJwhl+LQRuywzmmjuhWT5GueT3Ai2ZhB+loVxg0sRdhplyDp96xAu10/V/+J25Cl7YqNtl2DWSxvSoI/i6l6w==",
+            "peerDependencies": {
+                "react": "^15.5.4",
+                "react-dom": "^15.4.1"
+            }
+        },
+        "node_modules/react-autosuggest": {
+            "version": "9.4.3",
+            "resolved": "https://registry.npmjs.org/react-autosuggest/-/react-autosuggest-9.4.3.tgz",
+            "integrity": "sha512-wFbp5QpgFQRfw9cwKvcgLR8theikOUkv8PFsuLYqI2PUgVlx186Cz8MYt5bLxculi+jxGGUUVt+h0esaBZZouw==",
+            "dependencies": {
+                "prop-types": "^15.5.10",
+                "react-autowhatever": "^10.1.2",
+                "shallow-equal": "^1.0.0"
+            },
+            "peerDependencies": {
+                "react": ">=0.14.7"
+            }
+        },
+        "node_modules/react-autowhatever": {
+            "version": "10.2.1",
+            "resolved": "https://registry.npmjs.org/react-autowhatever/-/react-autowhatever-10.2.1.tgz",
+            "integrity": "sha512-5gQyoETyBH6GmuW1N1J81CuoAV+Djeg66DEo03xiZOl3WOwJHBP5LisKUvCGOakjrXU4M3hcIvCIqMBYGUmqOA==",
+            "dependencies": {
+                "prop-types": "^15.5.8",
+                "react-themeable": "^1.1.0",
+                "section-iterator": "^2.0.0"
+            },
+            "peerDependencies": {
+                "react": ">=0.14.7"
+            }
+        },
+        "node_modules/react-base16-styling": {
+            "version": "0.5.3",
+            "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.5.3.tgz",
+            "integrity": "sha1-OFjyTpxN2MvT9wLz901YHKKRcmk=",
+            "dependencies": {
+                "base16": "^1.0.0",
+                "lodash.curry": "^4.0.1",
+                "lodash.flow": "^3.3.0",
+                "pure-color": "^1.2.0"
+            }
+        },
+        "node_modules/react-beautiful-dnd": {
+            "version": "13.1.0",
+            "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.0.tgz",
+            "integrity": "sha512-aGvblPZTJowOWUNiwd6tNfEpgkX5OxmpqxHKNW/4VmvZTNTbeiq7bA3bn5T+QSF2uibXB0D1DmJsb1aC/+3cUA==",
+            "dependencies": {
+                "@babel/runtime": "^7.9.2",
+                "css-box-model": "^1.2.0",
+                "memoize-one": "^5.1.1",
+                "raf-schd": "^4.0.2",
+                "react-redux": "^7.2.0",
+                "redux": "^4.0.4",
+                "use-memo-one": "^1.1.1"
+            },
+            "peerDependencies": {
+                "react": "^16.8.5 || ^17.0.0",
+                "react-dom": "^16.8.5 || ^17.0.0"
+            }
+        },
+        "node_modules/react-color": {
+            "version": "2.19.3",
+            "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz",
+            "integrity": "sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==",
+            "dependencies": {
+                "@icons/material": "^0.2.4",
+                "lodash": "^4.17.15",
+                "lodash-es": "^4.17.15",
+                "material-colors": "^1.2.1",
+                "prop-types": "^15.5.10",
+                "reactcss": "^1.2.0",
+                "tinycolor2": "^1.4.1"
+            },
+            "peerDependencies": {
+                "react": "*"
+            }
+        },
+        "node_modules/react-compound-slider": {
+            "version": "2.5.0",
+            "resolved": "https://registry.npmjs.org/react-compound-slider/-/react-compound-slider-2.5.0.tgz",
+            "integrity": "sha512-T84FtSI0bkQPmH5GaaHbL+2McOyIR6M5sqS80dqw/bHc5r2UKLYY64BWTbsL+XO0jlx7REuJJnZUBqo4eSRl7g==",
+            "dependencies": {
+                "@babel/runtime": "^7.7.7",
+                "d3-array": "^1.2.4",
+                "prop-types": "^15.7.2",
+                "warning": "^3.0.0"
+            },
+            "peerDependencies": {
+                "react": "^16.3.0"
+            }
+        },
+        "node_modules/react-datepicker": {
+            "version": "3.8.0",
+            "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-3.8.0.tgz",
+            "integrity": "sha512-iFVNEp8DJoX5yEvEiciM7sJKmLGrvE70U38KhpG13XrulNSijeHw1RZkhd/0UmuXR71dcZB/kdfjiidifstZjw==",
+            "dependencies": {
+                "classnames": "^2.2.6",
+                "date-fns": "^2.0.1",
+                "prop-types": "^15.7.2",
+                "react-onclickoutside": "^6.10.0",
+                "react-popper": "^1.3.8"
+            },
+            "peerDependencies": {
+                "react": "^16.9.0 || ^17",
+                "react-dom": "^16.9.0 || ^17"
+            }
+        },
+        "node_modules/react-dock": {
+            "version": "0.2.4",
+            "resolved": "https://registry.npmjs.org/react-dock/-/react-dock-0.2.4.tgz",
+            "integrity": "sha1-5yfcdVCztzEWY13LnA4E0Lev4Xw=",
+            "dependencies": {
+                "lodash.debounce": "^3.1.1",
+                "prop-types": "^15.5.8"
+            },
+            "peerDependencies": {
+                "babel-runtime": "^6.3.13",
+                "react": ">=0.13.0"
+            }
+        },
+        "node_modules/react-dom": {
+            "version": "16.14.0",
+            "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz",
+            "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==",
+            "dependencies": {
+                "loose-envify": "^1.1.0",
+                "object-assign": "^4.1.1",
+                "prop-types": "^15.6.2",
+                "scheduler": "^0.19.1"
+            },
+            "peerDependencies": {
+                "react": "^16.14.0"
+            }
+        },
+        "node_modules/react-dom/node_modules/scheduler": {
+            "version": "0.19.1",
+            "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
+            "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
+            "dependencies": {
+                "loose-envify": "^1.1.0",
+                "object-assign": "^4.1.1"
+            }
+        },
+        "node_modules/react-draggable": {
+            "version": "4.4.3",
+            "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.3.tgz",
+            "integrity": "sha512-jV4TE59MBuWm7gb6Ns3Q1mxX8Azffb7oTtDtBgFkxRvhDp38YAARmRplrj0+XGkhOJB5XziArX+4HUUABtyZ0w==",
+            "dependencies": {
+                "classnames": "^2.2.5",
+                "prop-types": "^15.6.0"
+            }
+        },
+        "node_modules/react-event-listener": {
+            "version": "0.6.6",
+            "resolved": "https://registry.npmjs.org/react-event-listener/-/react-event-listener-0.6.6.tgz",
+            "integrity": "sha512-+hCNqfy7o9wvO6UgjqFmBzARJS7qrNoda0VqzvOuioEpoEXKutiKuv92dSz6kP7rYLmyHPyYNLesi5t/aH1gfw==",
+            "dependencies": {
+                "@babel/runtime": "^7.2.0",
+                "prop-types": "^15.6.0",
+                "warning": "^4.0.1"
+            },
+            "peerDependencies": {
+                "react": "^16.3.0"
+            }
+        },
+        "node_modules/react-event-listener/node_modules/warning": {
+            "version": "4.0.3",
+            "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+            "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+            "dependencies": {
+                "loose-envify": "^1.0.0"
+            }
+        },
+        "node_modules/react-grid-layout": {
+            "version": "0.18.3",
+            "resolved": "https://registry.npmjs.org/react-grid-layout/-/react-grid-layout-0.18.3.tgz",
+            "integrity": "sha512-lHkrk941Tk5nTwZPa9uj6ttHBT0VehSHwEhWbINBJKvM1GRaFNOefvjcuxSyuCI5JWjVUP+Qm3ARt2470AlxMA==",
+            "dependencies": {
+                "classnames": "2.x",
+                "lodash.isequal": "^4.0.0",
+                "prop-types": "^15.0.0",
+                "react-draggable": "^4.0.0",
+                "react-resizable": "^1.9.0"
+            }
+        },
+        "node_modules/react-image-lightbox-with-rotate": {
+            "version": "5.1.1",
+            "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==",
+            "dependencies": {
+                "blueimp-load-image": "^2.19.0",
+                "prop-types": "^15.6.1",
+                "react-modal": "^3.4.4"
+            },
+            "peerDependencies": {
+                "react": "^15.5.0 || ^16.0.0",
+                "react-dom": "^15.5.0 || ^16.0.0"
+            }
+        },
+        "node_modules/react-input-autosize": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-3.0.0.tgz",
+            "integrity": "sha512-nL9uS7jEs/zu8sqwFE5MAPx6pPkNAriACQ2rGLlqmKr2sPGtN7TXTyDdQt4lbNXVx7Uzadb40x8qotIuru6Rhg==",
+            "dependencies": {
+                "prop-types": "^15.5.8"
+            },
+            "peerDependencies": {
+                "react": "^16.3.0 || ^17.0.0"
+            }
+        },
+        "node_modules/react-is": {
+            "version": "16.13.1",
+            "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+            "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+        },
+        "node_modules/react-json-tree": {
+            "version": "0.11.2",
+            "resolved": "https://registry.npmjs.org/react-json-tree/-/react-json-tree-0.11.2.tgz",
+            "integrity": "sha512-aYhUPj1y5jR3ZQ+G3N7aL8FbTyO03iLwnVvvEikLcNFqNTyabdljo9xDftZndUBFyyyL0aK3qGO9+8EilILHUw==",
+            "dependencies": {
+                "babel-runtime": "^6.6.1",
+                "prop-types": "^15.5.8",
+                "react-base16-styling": "^0.5.1"
+            },
+            "peerDependencies": {
+                "react": "^15.0.0 || ^16.0.0",
+                "react-dom": "^15.0.0 || ^16.0.0"
+            }
+        },
+        "node_modules/react-jsx-parser": {
+            "version": "1.29.0",
+            "resolved": "https://registry.npmjs.org/react-jsx-parser/-/react-jsx-parser-1.29.0.tgz",
+            "integrity": "sha512-u0svZd0UsPffRrIK0sTbox54jhEbTmg6ED9ob5FQahp1DeOpd2Rq+KiSTIFNYkZUL+WZi4Ntia/Oj5T4lDJafQ==",
+            "hasInstallScript": true,
+            "dependencies": {
+                "@types/jsdom": "^16.2.6",
+                "acorn": "^8.0.5",
+                "acorn-jsx": "^5.3.1",
+                "browserslist": "^4.14.5",
+                "core-js": "^3.8.3"
+            },
+            "optionalDependencies": {
+                "@types/react": "^17.0.1",
+                "@types/react-dom": "^17.0.0"
+            },
+            "peerDependencies": {
+                "react": ">=17",
+                "react-dom": ">=17"
+            }
+        },
+        "node_modules/react-jsx-parser/node_modules/@types/react": {
+            "version": "17.0.39",
+            "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz",
+            "integrity": "sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==",
+            "optional": true,
+            "dependencies": {
+                "@types/prop-types": "*",
+                "@types/scheduler": "*",
+                "csstype": "^3.0.2"
+            }
+        },
+        "node_modules/react-jsx-parser/node_modules/@types/react-dom": {
+            "version": "17.0.11",
+            "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.11.tgz",
+            "integrity": "sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q==",
+            "optional": true,
+            "dependencies": {
+                "@types/react": "*"
+            }
+        },
+        "node_modules/react-jsx-parser/node_modules/acorn": {
+            "version": "8.7.0",
+            "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
+            "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
+            "bin": {
+                "acorn": "bin/acorn"
+            },
+            "engines": {
+                "node": ">=0.4.0"
+            }
+        },
+        "node_modules/react-jsx-parser/node_modules/core-js": {
+            "version": "3.21.1",
+            "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.21.1.tgz",
+            "integrity": "sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig==",
+            "hasInstallScript": true,
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/core-js"
+            }
+        },
+        "node_modules/react-jsx-parser/node_modules/csstype": {
+            "version": "3.0.10",
+            "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
+            "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==",
+            "optional": true
+        },
+        "node_modules/react-lifecycles-compat": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
+            "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
+        },
+        "node_modules/react-loading": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/react-loading/-/react-loading-2.0.3.tgz",
+            "integrity": "sha512-Vdqy79zq+bpeWJqC+xjltUjuGApyoItPgL0vgVfcJHhqwU7bAMKzysfGW/ADu6i0z0JiOCRJjo+IkFNkRNbA3A==",
+            "peerDependencies": {
+                "prop-types": "^15.6.0",
+                "react": ">=0.14.0"
+            }
+        },
+        "node_modules/react-measure": {
+            "version": "2.5.2",
+            "resolved": "https://registry.npmjs.org/react-measure/-/react-measure-2.5.2.tgz",
+            "integrity": "sha512-M+rpbTLWJ3FD6FXvYV6YEGvQ5tMayQ3fGrZhRPHrE9bVlBYfDCLuDcgNttYfk8IqfOI03jz6cbpqMRTUclQnaA==",
+            "dependencies": {
+                "@babel/runtime": "^7.2.0",
+                "get-node-dimensions": "^1.2.1",
+                "prop-types": "^15.6.2",
+                "resize-observer-polyfill": "^1.5.0"
+            },
+            "peerDependencies": {
+                "react": ">0.13.0",
+                "react-dom": ">0.13.0"
+            }
+        },
+        "node_modules/react-merge-refs": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/react-merge-refs/-/react-merge-refs-1.1.0.tgz",
+            "integrity": "sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==",
+            "funding": {
+                "type": "github",
+                "url": "https://github.com/sponsors/gregberge"
+            }
+        },
+        "node_modules/react-modal": {
+            "version": "3.11.2",
+            "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.11.2.tgz",
+            "integrity": "sha512-o8gvvCOFaG1T7W6JUvsYjRjMVToLZgLIsi5kdhFIQCtHxDkA47LznX62j+l6YQkpXDbvQegsDyxe/+JJsFQN7w==",
+            "dependencies": {
+                "exenv": "^1.2.0",
+                "prop-types": "^15.5.10",
+                "react-lifecycles-compat": "^3.0.0",
+                "warning": "^4.0.3"
+            },
+            "peerDependencies": {
+                "react": "^0.14.0 || ^15.0.0 || ^16",
+                "react-dom": "^0.14.0 || ^15.0.0 || ^16"
+            }
+        },
+        "node_modules/react-modal/node_modules/warning": {
+            "version": "4.0.3",
+            "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+            "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+            "dependencies": {
+                "loose-envify": "^1.0.0"
+            }
+        },
+        "node_modules/react-onclickoutside": {
+            "version": "6.12.1",
+            "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.12.1.tgz",
+            "integrity": "sha512-a5Q7CkWznBRUWPmocCvE8b6lEYw1s6+opp/60dCunhO+G6E4tDTO2Sd2jKE+leEnnrLAE2Wj5DlDHNqj5wPv1Q==",
+            "funding": {
+                "type": "individual",
+                "url": "https://github.com/Pomax/react-onclickoutside/blob/master/FUNDING.md"
+            },
+            "peerDependencies": {
+                "react": "^15.5.x || ^16.x || ^17.x",
+                "react-dom": "^15.5.x || ^16.x || ^17.x"
+            }
+        },
+        "node_modules/react-popper": {
+            "version": "1.3.11",
+            "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.11.tgz",
+            "integrity": "sha512-VSA/bS+pSndSF2fiasHK/PTEEAyOpX60+H5EPAjoArr8JGm+oihu4UbrqcEBpQibJxBVCpYyjAX7abJ+7DoYVg==",
+            "dependencies": {
+                "@babel/runtime": "^7.1.2",
+                "@hypnosphi/create-react-context": "^0.3.1",
+                "deep-equal": "^1.1.1",
+                "popper.js": "^1.14.4",
+                "prop-types": "^15.6.1",
+                "typed-styles": "^0.0.7",
+                "warning": "^4.0.2"
+            },
+            "peerDependencies": {
+                "react": "0.14.x || ^15.0.0 || ^16.0.0 || ^17.0.0"
+            }
+        },
+        "node_modules/react-popper/node_modules/popper.js": {
+            "version": "1.16.1",
+            "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
+            "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
+            "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1",
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/popperjs"
+            }
+        },
+        "node_modules/react-popper/node_modules/warning": {
+            "version": "4.0.3",
+            "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+            "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+            "dependencies": {
+                "loose-envify": "^1.0.0"
+            }
+        },
+        "node_modules/react-reconciler": {
+            "version": "0.26.2",
+            "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.26.2.tgz",
+            "integrity": "sha512-nK6kgY28HwrMNwDnMui3dvm3rCFjZrcGiuwLc5COUipBK5hWHLOxMJhSnSomirqWwjPBJKV1QcbkI0VJr7Gl1Q==",
+            "dependencies": {
+                "loose-envify": "^1.1.0",
+                "object-assign": "^4.1.1",
+                "scheduler": "^0.20.2"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            },
+            "peerDependencies": {
+                "react": "^17.0.2"
+            }
+        },
+        "node_modules/react-redux": {
+            "version": "7.2.6",
+            "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz",
+            "integrity": "sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==",
+            "dependencies": {
+                "@babel/runtime": "^7.15.4",
+                "@types/react-redux": "^7.1.20",
+                "hoist-non-react-statics": "^3.3.2",
+                "loose-envify": "^1.4.0",
+                "prop-types": "^15.7.2",
+                "react-is": "^17.0.2"
+            },
+            "peerDependencies": {
+                "react": "^16.8.3 || ^17"
+            },
+            "peerDependenciesMeta": {
+                "react-dom": {
+                    "optional": true
+                },
+                "react-native": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/react-redux/node_modules/@babel/runtime": {
+            "version": "7.17.2",
+            "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
+            "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
+            "dependencies": {
+                "regenerator-runtime": "^0.13.4"
+            },
+            "engines": {
+                "node": ">=6.9.0"
+            }
+        },
+        "node_modules/react-redux/node_modules/react-is": {
+            "version": "17.0.2",
+            "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+            "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
+        },
+        "node_modules/react-refresh-typescript": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/react-refresh-typescript/-/react-refresh-typescript-2.0.3.tgz",
+            "integrity": "sha512-CnR+UkpCFUIxW3u+KPxc6UhSO3M3aQxd9XCvgdry4a22LFwwITnjbANFUKtPGxA/Skw3PbZJrLiZ08UQnpXprQ==",
+            "peerDependencies": {
+                "react-refresh": "0.10.x || 0.11.x",
+                "typescript": "^4"
+            }
+        },
+        "node_modules/react-resizable": {
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-1.11.1.tgz",
+            "integrity": "sha512-S70gbLaAYqjuAd49utRHibtHLrHXInh7GuOR+6OO6RO6uleQfuBnWmZjRABfqNEx3C3Z6VPLg0/0uOYFrkfu9Q==",
+            "dependencies": {
+                "prop-types": "15.x",
+                "react-draggable": "^4.0.3"
+            },
+            "peerDependencies": {
+                "react": "0.14.x || 15.x || 16.x || 17.x",
+                "react-dom": "0.14.x || 15.x || 16.x || 17.x"
+            }
+        },
+        "node_modules/react-resizable-rotatable-draggable": {
+            "version": "0.2.0",
+            "resolved": "https://registry.npmjs.org/react-resizable-rotatable-draggable/-/react-resizable-rotatable-draggable-0.2.0.tgz",
+            "integrity": "sha512-F8TPx3z7/AcmRViySbYV3LpUWXFpHlGAmKmNcYMgPlS+h1eYFazRG3xYS8Z6e48hWY1EcCny/YNrwRNUrap8CQ==",
+            "engines": {
+                "node": ">=8",
+                "npm": ">=6"
+            },
+            "peerDependencies": {
+                "prop-types": "^15",
+                "react": "^16",
+                "styled-components": "^4"
+            }
+        },
+        "node_modules/react-reveal": {
+            "version": "1.2.2",
+            "resolved": "https://registry.npmjs.org/react-reveal/-/react-reveal-1.2.2.tgz",
+            "integrity": "sha512-JCv3fAoU6Z+Lcd8U48bwzm4pMZ79qsedSXYwpwt6lJNtj/v5nKJYZZbw3yhaQPPgYePo3Y0NOCoYOq/jcsisuw==",
+            "dependencies": {
+                "prop-types": "^15.5.10"
+            },
+            "peerDependencies": {
+                "react": "^15.3.0 || ^16.0.0"
+            }
+        },
+        "node_modules/react-select": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/react-select/-/react-select-3.2.0.tgz",
+            "integrity": "sha512-B/q3TnCZXEKItO0fFN/I0tWOX3WJvi/X2wtdffmwSQVRwg5BpValScTO1vdic9AxlUgmeSzib2hAZAwIUQUZGQ==",
+            "dependencies": {
+                "@babel/runtime": "^7.4.4",
+                "@emotion/cache": "^10.0.9",
+                "@emotion/core": "^10.0.9",
+                "@emotion/css": "^10.0.9",
+                "memoize-one": "^5.0.0",
+                "prop-types": "^15.6.0",
+                "react-input-autosize": "^3.0.0",
+                "react-transition-group": "^4.3.0"
+            },
+            "peerDependencies": {
+                "react": "^16.8.0 || ^17.0.0",
+                "react-dom": "^16.8.0 || ^17.0.0"
+            }
+        },
+        "node_modules/react-select/node_modules/@emotion/css": {
+            "version": "10.0.27",
+            "resolved": "https://registry.npmjs.org/@emotion/css/-/css-10.0.27.tgz",
+            "integrity": "sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==",
+            "dependencies": {
+                "@emotion/serialize": "^0.11.15",
+                "@emotion/utils": "0.11.3",
+                "babel-plugin-emotion": "^10.0.27"
+            }
+        },
+        "node_modules/react-select/node_modules/csstype": {
+            "version": "3.0.10",
+            "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
+            "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
+        },
+        "node_modules/react-select/node_modules/dom-helpers": {
+            "version": "5.2.1",
+            "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+            "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+            "dependencies": {
+                "@babel/runtime": "^7.8.7",
+                "csstype": "^3.0.2"
+            }
+        },
+        "node_modules/react-select/node_modules/react-transition-group": {
+            "version": "4.4.2",
+            "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz",
+            "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==",
+            "dependencies": {
+                "@babel/runtime": "^7.5.5",
+                "dom-helpers": "^5.0.1",
+                "loose-envify": "^1.4.0",
+                "prop-types": "^15.6.2"
+            },
+            "peerDependencies": {
+                "react": ">=16.6.0",
+                "react-dom": ">=16.6.0"
+            }
+        },
+        "node_modules/react-table": {
+            "version": "7.7.0",
+            "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.7.0.tgz",
+            "integrity": "sha512-jBlj70iBwOTvvImsU9t01LjFjy4sXEtclBovl3mTiqjz23Reu0DKnRza4zlLtOPACx6j2/7MrQIthIK1Wi+LIA==",
+            "funding": {
+                "type": "github",
+                "url": "https://github.com/sponsors/tannerlinsley"
+            },
+            "peerDependencies": {
+                "react": "^16.8.3 || ^17.0.0-0"
+            }
+        },
+        "node_modules/react-themeable": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/react-themeable/-/react-themeable-1.1.0.tgz",
+            "integrity": "sha1-fURm3ZsrX6dQWHJ4JenxUro3mg4=",
+            "dependencies": {
+                "object-assign": "^3.0.0"
+            }
+        },
+        "node_modules/react-themeable/node_modules/object-assign": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz",
+            "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/react-three-fiber": {
+            "version": "0.0.0-deprecated",
+            "resolved": "https://registry.npmjs.org/react-three-fiber/-/react-three-fiber-0.0.0-deprecated.tgz",
+            "integrity": "sha512-EblIqTAsIpkYeM8bZtC4lcpTE0A2zCEGipFB52RgcQq/q+0oryrk7Sxt+sqhIjUu6xMNEVywV8dr74lz5yWO6A=="
+        },
+        "node_modules/react-transition-group": {
+            "version": "2.9.0",
+            "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz",
+            "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==",
+            "dependencies": {
+                "dom-helpers": "^3.4.0",
+                "loose-envify": "^1.4.0",
+                "prop-types": "^15.6.2",
+                "react-lifecycles-compat": "^3.0.4"
+            },
+            "peerDependencies": {
+                "react": ">=15.0.0",
+                "react-dom": ">=15.0.0"
+            }
+        },
+        "node_modules/react-use-measure": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz",
+            "integrity": "sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig==",
+            "dependencies": {
+                "debounce": "^1.2.1"
+            },
+            "peerDependencies": {
+                "react": ">=16.13",
+                "react-dom": ">=16.13"
+            }
+        },
+        "node_modules/reactcss": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/reactcss/-/reactcss-1.2.3.tgz",
+            "integrity": "sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==",
+            "dependencies": {
+                "lodash": "^4.0.1"
+            }
+        },
+        "node_modules/read-pkg": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
+            "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
+            "dependencies": {
+                "load-json-file": "^1.0.0",
+                "normalize-package-data": "^2.3.2",
+                "path-type": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/read-pkg-up": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
+            "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
+            "dependencies": {
+                "find-up": "^1.0.0",
+                "read-pkg": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/read-pkg-up/node_modules/find-up": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+            "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+            "dependencies": {
+                "path-exists": "^2.0.0",
+                "pinkie-promise": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/read-pkg-up/node_modules/path-exists": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+            "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+            "dependencies": {
+                "pinkie-promise": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/read-pkg/node_modules/path-type": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
+            "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
+            "dependencies": {
+                "graceful-fs": "^4.1.2",
+                "pify": "^2.0.0",
+                "pinkie-promise": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/read-pkg/node_modules/pify": {
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+            "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/readable-stream": {
+            "version": "3.6.0",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+            "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+            "dependencies": {
+                "inherits": "^2.0.3",
+                "string_decoder": "^1.1.1",
+                "util-deprecate": "^1.0.1"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/readdirp": {
+            "version": "2.2.1",
+            "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+            "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+            "dependencies": {
+                "graceful-fs": "^4.1.11",
+                "micromatch": "^3.1.10",
+                "readable-stream": "^2.0.2"
+            },
+            "engines": {
+                "node": ">=0.10"
+            }
+        },
+        "node_modules/readdirp/node_modules/readable-stream": {
+            "version": "2.3.7",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/readline": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz",
+            "integrity": "sha1-xYDXfvLPyHUrEySYBg3JeTp6wBw="
+        },
+        "node_modules/rechoir": {
+            "version": "0.6.2",
+            "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+            "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
+            "dependencies": {
+                "resolve": "^1.1.6"
+            },
+            "engines": {
+                "node": ">= 0.10"
+            }
+        },
+        "node_modules/recompose": {
+            "version": "0.26.0",
+            "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.26.0.tgz",
+            "integrity": "sha512-KwOu6ztO0mN5vy3+zDcc45lgnaUoaQse/a5yLVqtzTK13czSWnFGmXbQVmnoMgDkI5POd1EwIKSbjU1V7xdZog==",
+            "dependencies": {
+                "change-emitter": "^0.1.2",
+                "fbjs": "^0.8.1",
+                "hoist-non-react-statics": "^2.3.1",
+                "symbol-observable": "^1.0.4"
+            },
+            "peerDependencies": {
+                "react": "^0.14.0 || ^15.0.0 || ^16.0.0"
+            }
+        },
+        "node_modules/recompose/node_modules/hoist-non-react-statics": {
+            "version": "2.5.5",
+            "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
+            "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
+        },
+        "node_modules/redent": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
+            "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
+            "dependencies": {
+                "indent-string": "^2.1.0",
+                "strip-indent": "^1.0.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/reduce-flatten": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz",
+            "integrity": "sha1-JYx479FT3fk8tWEjf2EYTzaW4yc=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/redux": {
+            "version": "4.1.2",
+            "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz",
+            "integrity": "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==",
+            "dependencies": {
+                "@babel/runtime": "^7.9.2"
+            }
+        },
+        "node_modules/regenerator-runtime": {
+            "version": "0.13.5",
+            "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
+            "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
+        },
+        "node_modules/regex-not": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+            "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+            "dependencies": {
+                "extend-shallow": "^3.0.2",
+                "safe-regex": "^1.1.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/regexp-clone": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz",
+            "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw=="
+        },
+        "node_modules/regexp.prototype.flags": {
+            "version": "1.4.1",
+            "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz",
+            "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==",
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/registry-auth-token": {
+            "version": "3.4.0",
+            "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz",
+            "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==",
+            "dependencies": {
+                "rc": "^1.1.6",
+                "safe-buffer": "^5.0.1"
+            }
+        },
+        "node_modules/registry-url": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
+            "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
+            "dependencies": {
+                "rc": "^1.0.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/relateurl": {
+            "version": "0.2.7",
+            "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+            "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=",
+            "engines": {
+                "node": ">= 0.10"
+            }
+        },
+        "node_modules/remove-trailing-separator": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+            "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8="
+        },
+        "node_modules/renderkid": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
+            "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==",
+            "dependencies": {
+                "css-select": "^4.1.3",
+                "dom-converter": "^0.2.0",
+                "htmlparser2": "^6.1.0",
+                "lodash": "^4.17.21",
+                "strip-ansi": "^6.0.1"
+            }
+        },
+        "node_modules/renderkid/node_modules/ansi-regex": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/renderkid/node_modules/dom-serializer": {
+            "version": "1.3.2",
+            "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
+            "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
+            "dependencies": {
+                "domelementtype": "^2.0.1",
+                "domhandler": "^4.2.0",
+                "entities": "^2.0.0"
+            },
+            "funding": {
+                "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+            }
+        },
+        "node_modules/renderkid/node_modules/domelementtype": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
+            "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==",
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/fb55"
+                }
+            ]
+        },
+        "node_modules/renderkid/node_modules/domhandler": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz",
+            "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==",
+            "dependencies": {
+                "domelementtype": "^2.2.0"
+            },
+            "engines": {
+                "node": ">= 4"
+            },
+            "funding": {
+                "url": "https://github.com/fb55/domhandler?sponsor=1"
+            }
+        },
+        "node_modules/renderkid/node_modules/domutils": {
+            "version": "2.8.0",
+            "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+            "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+            "dependencies": {
+                "dom-serializer": "^1.0.1",
+                "domelementtype": "^2.2.0",
+                "domhandler": "^4.2.0"
+            },
+            "funding": {
+                "url": "https://github.com/fb55/domutils?sponsor=1"
+            }
+        },
+        "node_modules/renderkid/node_modules/entities": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+            "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+            "funding": {
+                "url": "https://github.com/fb55/entities?sponsor=1"
+            }
+        },
+        "node_modules/renderkid/node_modules/htmlparser2": {
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
+            "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
+            "funding": [
+                "https://github.com/fb55/htmlparser2?sponsor=1",
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/fb55"
+                }
+            ],
+            "dependencies": {
+                "domelementtype": "^2.0.1",
+                "domhandler": "^4.0.0",
+                "domutils": "^2.5.2",
+                "entities": "^2.0.0"
+            }
+        },
+        "node_modules/renderkid/node_modules/strip-ansi": {
+            "version": "6.0.1",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+            "dependencies": {
+                "ansi-regex": "^5.0.1"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/repeat-element": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
+            "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/repeat-string": {
+            "version": "1.6.1",
+            "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+            "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+            "engines": {
+                "node": ">=0.10"
+            }
+        },
+        "node_modules/repeating": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
+            "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
+            "dependencies": {
+                "is-finite": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/request": {
+            "version": "2.88.2",
+            "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+            "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+            "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
+            "dependencies": {
+                "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.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.19",
+                "oauth-sign": "~0.9.0",
+                "performance-now": "^2.1.0",
+                "qs": "~6.5.2",
+                "safe-buffer": "^5.1.2",
+                "tough-cookie": "~2.5.0",
+                "tunnel-agent": "^0.6.0",
+                "uuid": "^3.3.2"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/request-promise": {
+            "version": "4.2.6",
+            "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.6.tgz",
+            "integrity": "sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==",
+            "deprecated": "request-promise has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142",
+            "dependencies": {
+                "bluebird": "^3.5.0",
+                "request-promise-core": "1.1.4",
+                "stealthy-require": "^1.1.1",
+                "tough-cookie": "^2.3.3"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            },
+            "peerDependencies": {
+                "request": "^2.34"
+            }
+        },
+        "node_modules/request-promise-core": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz",
+            "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==",
+            "dev": true,
+            "dependencies": {
+                "lodash": "^4.17.15"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            },
+            "peerDependencies": {
+                "request": "^2.34"
+            }
+        },
+        "node_modules/request-promise-native": {
+            "version": "1.0.8",
+            "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz",
+            "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==",
+            "deprecated": "request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142",
+            "dev": true,
+            "dependencies": {
+                "request-promise-core": "1.1.3",
+                "stealthy-require": "^1.1.1",
+                "tough-cookie": "^2.3.3"
+            },
+            "engines": {
+                "node": ">=0.12.0"
+            },
+            "peerDependencies": {
+                "request": "^2.34"
+            }
+        },
+        "node_modules/request-promise-native/node_modules/tough-cookie": {
+            "version": "2.5.0",
+            "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+            "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+            "dev": true,
+            "dependencies": {
+                "psl": "^1.1.28",
+                "punycode": "^2.1.1"
+            },
+            "engines": {
+                "node": ">=0.8"
+            }
+        },
+        "node_modules/request-promise/node_modules/request-promise-core": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
+            "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
+            "dependencies": {
+                "lodash": "^4.17.19"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            },
+            "peerDependencies": {
+                "request": "^2.34"
+            }
+        },
+        "node_modules/request-promise/node_modules/tough-cookie": {
+            "version": "2.5.0",
+            "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+            "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+            "dependencies": {
+                "psl": "^1.1.28",
+                "punycode": "^2.1.1"
+            },
+            "engines": {
+                "node": ">=0.8"
+            }
+        },
+        "node_modules/request/node_modules/form-data": {
+            "version": "2.3.3",
+            "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+            "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+            "dependencies": {
+                "asynckit": "^0.4.0",
+                "combined-stream": "^1.0.6",
+                "mime-types": "^2.1.12"
+            },
+            "engines": {
+                "node": ">= 0.12"
+            }
+        },
+        "node_modules/request/node_modules/tough-cookie": {
+            "version": "2.5.0",
+            "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+            "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+            "dependencies": {
+                "psl": "^1.1.28",
+                "punycode": "^2.1.1"
+            },
+            "engines": {
+                "node": ">=0.8"
+            }
+        },
+        "node_modules/require_optional": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
+            "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==",
+            "dependencies": {
+                "resolve-from": "^2.0.0",
+                "semver": "^5.1.0"
+            }
+        },
+        "node_modules/require_optional/node_modules/resolve-from": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
+            "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/require-at": {
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz",
+            "integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/require-directory": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+            "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/require-from-string": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+            "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/require-main-filename": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+            "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
+        },
+        "node_modules/require-package-name": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/require-package-name/-/require-package-name-2.0.1.tgz",
+            "integrity": "sha1-wR6XJ2tluOKSP3Xav1+y7ww4Qbk="
+        },
+        "node_modules/requires-port": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+            "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
+            "dev": true
+        },
+        "node_modules/resize-observer-polyfill": {
+            "version": "1.5.1",
+            "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+            "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
+        },
+        "node_modules/resolve": {
+            "version": "1.17.0",
+            "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+            "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+            "dependencies": {
+                "path-parse": "^1.0.6"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/resolve-alpn": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
+            "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
+        },
+        "node_modules/resolve-cwd": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
+            "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
+            "dev": true,
+            "dependencies": {
+                "resolve-from": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/resolve-cwd/node_modules/resolve-from": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+            "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
+            "dev": true,
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/resolve-from": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+            "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/resolve-url": {
+            "version": "0.2.1",
+            "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+            "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+            "deprecated": "https://github.com/lydell/resolve-url#deprecated"
+        },
+        "node_modules/responselike": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz",
+            "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==",
+            "dependencies": {
+                "lowercase-keys": "^2.0.0"
+            }
+        },
+        "node_modules/responselike/node_modules/lowercase-keys": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+            "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/ret": {
+            "version": "0.1.15",
+            "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+            "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+            "engines": {
+                "node": ">=0.12"
+            }
+        },
+        "node_modules/retry": {
+            "version": "0.12.0",
+            "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+            "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
+            "dev": true,
+            "engines": {
+                "node": ">= 4"
+            }
+        },
+        "node_modules/reveal.js": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/reveal.js/-/reveal.js-4.3.0.tgz",
+            "integrity": "sha512-KzZxJjj1gmxVNyplY6g9MiGwtDvZJiYkMvG1Qmaita7vWT/8eoTEK+RuIPLvxDeyxOtTz56u2wrOETVO79qL4A==",
+            "engines": {
+                "node": ">=10.0.0"
+            }
+        },
+        "node_modules/right-align": {
+            "version": "0.1.3",
+            "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
+            "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
+            "dependencies": {
+                "align-text": "^0.1.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/rimraf": {
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+            "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+            "dependencies": {
+                "glob": "^7.1.3"
+            },
+            "bin": {
+                "rimraf": "bin.js"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/isaacs"
+            }
+        },
+        "node_modules/rope-sequence": {
+            "version": "1.3.2",
+            "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.2.tgz",
+            "integrity": "sha512-ku6MFrwEVSVmXLvy3dYph3LAMNS0890K7fabn+0YIRQ2T96T9F4gkFf0vf0WW0JUraNWwGRtInEpH7yO4tbQZg=="
+        },
+        "node_modules/rtcpeerconnection-shim": {
+            "version": "1.2.15",
+            "resolved": "https://registry.npmjs.org/rtcpeerconnection-shim/-/rtcpeerconnection-shim-1.2.15.tgz",
+            "integrity": "sha512-C6DxhXt7bssQ1nHb154lqeL0SXz5Dx4RczXZu2Aa/L1NJFnEVDxFwCBo3fqtuljhHIGceg5JKBV4XJ0gW5JKyw==",
+            "dependencies": {
+                "sdp": "^2.6.0"
+            },
+            "engines": {
+                "node": ">=6.0.0",
+                "npm": ">=3.10.0"
+            }
+        },
+        "node_modules/run-queue": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
+            "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
+            "dev": true,
+            "dependencies": {
+                "aproba": "^1.1.1"
+            }
+        },
+        "node_modules/safe-buffer": {
+            "version": "5.1.2",
+            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+            "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+        },
+        "node_modules/safe-regex": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+            "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+            "dependencies": {
+                "ret": "~0.1.10"
+            }
+        },
+        "node_modules/safer-buffer": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+            "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+        },
+        "node_modules/saslprep": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
+            "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
+            "optional": true,
+            "dependencies": {
+                "sparse-bitfield": "^3.0.3"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/sass-graph": {
+            "version": "2.2.5",
+            "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.5.tgz",
+            "integrity": "sha512-VFWDAHOe6mRuT4mZRd4eKE+d8Uedrk6Xnh7Sh9b4NGufQLQjOrvf/MQoOdx+0s92L89FeyUUNfU597j/3uNpag==",
+            "dependencies": {
+                "glob": "^7.0.0",
+                "lodash": "^4.0.0",
+                "scss-tokenizer": "^0.2.3",
+                "yargs": "^13.3.2"
+            },
+            "bin": {
+                "sassgraph": "bin/sassgraph"
+            }
+        },
+        "node_modules/sass-loader": {
+            "version": "7.3.1",
+            "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.3.1.tgz",
+            "integrity": "sha512-tuU7+zm0pTCynKYHpdqaPpe+MMTQ76I9TPZ7i4/5dZsigE350shQWe5EZNl5dBidM49TPET75tNqRbcsUZWeNA==",
+            "dev": true,
+            "dependencies": {
+                "clone-deep": "^4.0.1",
+                "loader-utils": "^1.0.1",
+                "neo-async": "^2.5.0",
+                "pify": "^4.0.1",
+                "semver": "^6.3.0"
+            },
+            "engines": {
+                "node": ">= 6.9.0"
+            },
+            "peerDependencies": {
+                "webpack": "^3.0.0 || ^4.0.0"
+            }
+        },
+        "node_modules/sass-loader/node_modules/semver": {
+            "version": "6.3.0",
+            "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+            "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+            "dev": true,
+            "bin": {
+                "semver": "bin/semver.js"
+            }
+        },
+        "node_modules/saxes": {
+            "version": "3.1.11",
+            "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz",
+            "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==",
+            "dev": true,
+            "dependencies": {
+                "xmlchars": "^2.1.1"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/scheduler": {
+            "version": "0.20.2",
+            "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
+            "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
+            "dependencies": {
+                "loose-envify": "^1.1.0",
+                "object-assign": "^4.1.1"
+            }
+        },
+        "node_modules/schema-utils": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+            "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+            "dev": true,
+            "dependencies": {
+                "ajv": "^6.1.0",
+                "ajv-errors": "^1.0.0",
+                "ajv-keywords": "^3.1.0"
+            },
+            "engines": {
+                "node": ">= 4"
+            }
+        },
+        "node_modules/scss-loader": {
+            "version": "0.0.1",
+            "resolved": "https://registry.npmjs.org/scss-loader/-/scss-loader-0.0.1.tgz",
+            "integrity": "sha1-6uAXueDzjBKlMtslwiC5Avs05nE=",
+            "dev": true
+        },
+        "node_modules/scss-tokenizer": {
+            "version": "0.2.3",
+            "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
+            "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=",
+            "dependencies": {
+                "js-base64": "^2.1.8",
+                "source-map": "^0.4.2"
+            }
+        },
+        "node_modules/scss-tokenizer/node_modules/source-map": {
+            "version": "0.4.4",
+            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
+            "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
+            "dependencies": {
+                "amdefine": ">=0.0.4"
+            },
+            "engines": {
+                "node": ">=0.8.0"
+            }
+        },
+        "node_modules/sdp": {
+            "version": "2.12.0",
+            "resolved": "https://registry.npmjs.org/sdp/-/sdp-2.12.0.tgz",
+            "integrity": "sha512-jhXqQAQVM+8Xj5EjJGVweuEzgtGWb3tmEEpl3CLP3cStInSbVHSg0QWOGQzNq8pSID4JkpeV2mPqlMDLrm0/Vw=="
+        },
+        "node_modules/section-iterator": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/section-iterator/-/section-iterator-2.0.0.tgz",
+            "integrity": "sha1-v0RNev7rlK1Dw5rS+yYVFifMuio="
+        },
+        "node_modules/select-hose": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
+            "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=",
+            "dev": true
+        },
+        "node_modules/selfsigned": {
+            "version": "1.10.14",
+            "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.14.tgz",
+            "integrity": "sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA==",
+            "dev": true,
+            "dependencies": {
+                "node-forge": "^0.10.0"
+            }
+        },
+        "node_modules/selfsigned/node_modules/node-forge": {
+            "version": "0.10.0",
+            "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
+            "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==",
+            "dev": true,
+            "engines": {
+                "node": ">= 6.0.0"
+            }
+        },
+        "node_modules/semver": {
+            "version": "5.7.1",
+            "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+            "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+            "bin": {
+                "semver": "bin/semver"
+            }
+        },
+        "node_modules/semver-compare": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
+            "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w="
+        },
+        "node_modules/semver-diff": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz",
+            "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=",
+            "dependencies": {
+                "semver": "^5.0.3"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/send": {
+            "version": "0.17.2",
+            "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz",
+            "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==",
+            "dependencies": {
+                "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",
+                "fresh": "0.5.2",
+                "http-errors": "1.8.1",
+                "mime": "1.6.0",
+                "ms": "2.1.3",
+                "on-finished": "~2.3.0",
+                "range-parser": "~1.2.1",
+                "statuses": "~1.5.0"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/send/node_modules/debug": {
+            "version": "2.6.9",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+            "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+            "dependencies": {
+                "ms": "2.0.0"
+            }
+        },
+        "node_modules/send/node_modules/debug/node_modules/ms": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        },
+        "node_modules/send/node_modules/ms": {
+            "version": "2.1.3",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+            "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+        },
+        "node_modules/serialize-javascript": {
+            "version": "1.9.1",
+            "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",
+            "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==",
+            "dev": true
+        },
+        "node_modules/serializr": {
+            "version": "1.5.4",
+            "resolved": "https://registry.npmjs.org/serializr/-/serializr-1.5.4.tgz",
+            "integrity": "sha512-ImLlkNfNSSv9d7Ah1j/yrPk1FHbRC1zkD7URcgx/8M1eYHGUxGf/Ig/d8ML04ifqN7QesyYKsfLYA4xjAVAR/g=="
+        },
+        "node_modules/serve-index": {
+            "version": "1.9.1",
+            "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
+            "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=",
+            "dev": true,
+            "dependencies": {
+                "accepts": "~1.3.4",
+                "batch": "0.6.1",
+                "debug": "2.6.9",
+                "escape-html": "~1.0.3",
+                "http-errors": "~1.6.2",
+                "mime-types": "~2.1.17",
+                "parseurl": "~1.3.2"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/serve-index/node_modules/debug": {
+            "version": "2.6.9",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+            "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+            "dev": true,
+            "dependencies": {
+                "ms": "2.0.0"
+            }
+        },
+        "node_modules/serve-index/node_modules/http-errors": {
+            "version": "1.6.3",
+            "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+            "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
+            "dev": true,
+            "dependencies": {
+                "depd": "~1.1.2",
+                "inherits": "2.0.3",
+                "setprototypeof": "1.1.0",
+                "statuses": ">= 1.4.0 < 2"
+            },
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/serve-index/node_modules/inherits": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+            "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+            "dev": true
+        },
+        "node_modules/serve-index/node_modules/ms": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+            "dev": true
+        },
+        "node_modules/serve-index/node_modules/setprototypeof": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+            "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
+            "dev": true
+        },
+        "node_modules/serve-static": {
+            "version": "1.14.2",
+            "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz",
+            "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==",
+            "dependencies": {
+                "encodeurl": "~1.0.2",
+                "escape-html": "~1.0.3",
+                "parseurl": "~1.3.3",
+                "send": "0.17.2"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/set-blocking": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+            "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
+        },
+        "node_modules/set-immediate-shim": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
+            "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/set-value": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+            "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+            "dependencies": {
+                "extend-shallow": "^2.0.1",
+                "is-extendable": "^0.1.1",
+                "is-plain-object": "^2.0.3",
+                "split-string": "^3.0.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/set-value/node_modules/extend-shallow": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+            "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+            "dependencies": {
+                "is-extendable": "^0.1.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/set-value/node_modules/is-extendable": {
+            "version": "0.1.1",
+            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+            "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/setimmediate": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+            "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
+        },
+        "node_modules/setprototypeof": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+            "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
+        },
+        "node_modules/shallow-clone": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
+            "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
+            "dependencies": {
+                "kind-of": "^6.0.2"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/shallow-equal": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz",
+            "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA=="
+        },
+        "node_modules/sharp": {
+            "version": "0.23.4",
+            "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.23.4.tgz",
+            "integrity": "sha512-fJMagt6cT0UDy9XCsgyLi0eiwWWhQRxbwGmqQT6sY8Av4s0SVsT/deg8fobBQCTDU5iXRgz0rAeXoE2LBZ8g+Q==",
+            "hasInstallScript": true,
+            "dependencies": {
+                "color": "^3.1.2",
+                "detect-libc": "^1.0.3",
+                "nan": "^2.14.0",
+                "npmlog": "^4.1.2",
+                "prebuild-install": "^5.3.3",
+                "semver": "^6.3.0",
+                "simple-get": "^3.1.0",
+                "tar": "^5.0.5",
+                "tunnel-agent": "^0.6.0"
+            },
+            "engines": {
+                "node": ">=8.5.0"
+            },
+            "funding": {
+                "url": "https://opencollective.com/libvips"
+            }
+        },
+        "node_modules/sharp/node_modules/fs-minipass": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+            "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+            "dependencies": {
+                "minipass": "^3.0.0"
+            },
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/sharp/node_modules/minipass": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.1.tgz",
+            "integrity": "sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w==",
+            "dependencies": {
+                "yallist": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/sharp/node_modules/minizlib": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.0.tgz",
+            "integrity": "sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA==",
+            "dependencies": {
+                "minipass": "^3.0.0",
+                "yallist": "^4.0.0"
+            },
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/sharp/node_modules/semver": {
+            "version": "6.3.0",
+            "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+            "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+            "bin": {
+                "semver": "bin/semver.js"
+            }
+        },
+        "node_modules/sharp/node_modules/tar": {
+            "version": "5.0.5",
+            "resolved": "https://registry.npmjs.org/tar/-/tar-5.0.5.tgz",
+            "integrity": "sha512-MNIgJddrV2TkuwChwcSNds/5E9VijOiw7kAc1y5hTNJoLDSuIyid2QtLYiCYNnICebpuvjhPQZsXwUL0O3l7OQ==",
+            "dependencies": {
+                "chownr": "^1.1.3",
+                "fs-minipass": "^2.0.0",
+                "minipass": "^3.0.0",
+                "minizlib": "^2.1.0",
+                "mkdirp": "^0.5.0",
+                "yallist": "^4.0.0"
+            },
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/sharp/node_modules/yallist": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+            "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+        },
+        "node_modules/shebang-command": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+            "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+            "dependencies": {
+                "shebang-regex": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/shebang-regex": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+            "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/shelljs": {
+            "version": "0.8.5",
+            "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz",
+            "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
+            "dependencies": {
+                "glob": "^7.0.0",
+                "interpret": "^1.0.0",
+                "rechoir": "^0.6.2"
+            },
+            "bin": {
+                "shjs": "bin/shjs"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/side-channel": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+            "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+            "dependencies": {
+                "call-bind": "^1.0.0",
+                "get-intrinsic": "^1.0.2",
+                "object-inspect": "^1.9.0"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/side-channel/node_modules/object-inspect": {
+            "version": "1.12.0",
+            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
+            "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==",
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/sift": {
+            "version": "13.5.2",
+            "resolved": "https://registry.npmjs.org/sift/-/sift-13.5.2.tgz",
+            "integrity": "sha512-+gxdEOMA2J+AI+fVsCqeNn7Tgx3M9ZN9jdi95939l1IJ8cZsqS8sqpJyOkic2SJk+1+98Uwryt/gL6XDaV+UZA=="
+        },
+        "node_modules/signal-exit": {
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
+            "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
+        },
+        "node_modules/simple-assign": {
+            "version": "0.1.0",
+            "resolved": "https://registry.npmjs.org/simple-assign/-/simple-assign-0.1.0.tgz",
+            "integrity": "sha1-F/0wZqXz13OPUDIbsPFMooHMS6o="
+        },
+        "node_modules/simple-concat": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
+            "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
+        },
+        "node_modules/simple-get": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
+            "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
+            "dependencies": {
+                "decompress-response": "^4.2.0",
+                "once": "^1.3.1",
+                "simple-concat": "^1.0.0"
+            }
+        },
+        "node_modules/simple-swizzle": {
+            "version": "0.2.2",
+            "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+            "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
+            "dependencies": {
+                "is-arrayish": "^0.3.1"
+            }
+        },
+        "node_modules/simple-swizzle/node_modules/is-arrayish": {
+            "version": "0.3.2",
+            "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+            "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
+        },
+        "node_modules/skmeans": {
+            "version": "0.9.7",
+            "resolved": "https://registry.npmjs.org/skmeans/-/skmeans-0.9.7.tgz",
+            "integrity": "sha512-hNj1/oZ7ygsfmPZ7ZfN5MUBRoGg1gtpnImuJBgLO0ljQ67DtJuiQaiYdS4lUA6s0KCwnPhGivtC/WRwIZLkHyg=="
+        },
+        "node_modules/slash": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
+            "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
+            "dev": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/sliced": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz",
+            "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E="
+        },
+        "node_modules/snapdragon": {
+            "version": "0.8.2",
+            "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+            "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/snapdragon-node": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+            "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+            "dependencies": {
+                "define-property": "^1.0.0",
+                "isobject": "^3.0.0",
+                "snapdragon-util": "^3.0.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/snapdragon-node/node_modules/define-property": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+            "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+            "dependencies": {
+                "is-descriptor": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/snapdragon-node/node_modules/is-accessor-descriptor": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+            "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+            "dependencies": {
+                "kind-of": "^6.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/snapdragon-node/node_modules/is-data-descriptor": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+            "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+            "dependencies": {
+                "kind-of": "^6.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/snapdragon-node/node_modules/is-descriptor": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+            "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+            "dependencies": {
+                "is-accessor-descriptor": "^1.0.0",
+                "is-data-descriptor": "^1.0.0",
+                "kind-of": "^6.0.2"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/snapdragon-util": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+            "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+            "dependencies": {
+                "kind-of": "^3.2.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/snapdragon-util/node_modules/is-buffer": {
+            "version": "1.1.6",
+            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+            "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+        },
+        "node_modules/snapdragon-util/node_modules/kind-of": {
+            "version": "3.2.2",
+            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+            "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+            "dependencies": {
+                "is-buffer": "^1.1.5"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/snapdragon/node_modules/debug": {
+            "version": "2.6.9",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+            "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+            "dependencies": {
+                "ms": "2.0.0"
+            }
+        },
+        "node_modules/snapdragon/node_modules/define-property": {
+            "version": "0.2.5",
+            "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+            "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+            "dependencies": {
+                "is-descriptor": "^0.1.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/snapdragon/node_modules/extend-shallow": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+            "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+            "dependencies": {
+                "is-extendable": "^0.1.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/snapdragon/node_modules/is-extendable": {
+            "version": "0.1.1",
+            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+            "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/snapdragon/node_modules/ms": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        },
+        "node_modules/socket.io": {
+            "version": "2.4.1",
+            "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.4.1.tgz",
+            "integrity": "sha512-Si18v0mMXGAqLqCVpTxBa8MGqriHGQh8ccEOhmsmNS3thNCGBwO8WGrwMibANsWtQQ5NStdZwHqZR3naJVFc3w==",
+            "dependencies": {
+                "debug": "~4.1.0",
+                "engine.io": "~3.5.0",
+                "has-binary2": "~1.0.2",
+                "socket.io-adapter": "~1.1.0",
+                "socket.io-client": "2.4.0",
+                "socket.io-parser": "~3.4.0"
+            }
+        },
+        "node_modules/socket.io-adapter": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz",
+            "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g=="
+        },
+        "node_modules/socket.io-client": {
+            "version": "2.4.0",
+            "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.4.0.tgz",
+            "integrity": "sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ==",
+            "dependencies": {
+                "backo2": "1.0.2",
+                "component-bind": "1.0.0",
+                "component-emitter": "~1.3.0",
+                "debug": "~3.1.0",
+                "engine.io-client": "~3.5.0",
+                "has-binary2": "~1.0.2",
+                "indexof": "0.0.1",
+                "parseqs": "0.0.6",
+                "parseuri": "0.0.6",
+                "socket.io-parser": "~3.3.0",
+                "to-array": "0.1.4"
+            }
+        },
+        "node_modules/socket.io-client/node_modules/debug": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+            "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+            "dependencies": {
+                "ms": "2.0.0"
+            }
+        },
+        "node_modules/socket.io-client/node_modules/isarray": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+            "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+        },
+        "node_modules/socket.io-client/node_modules/ms": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        },
+        "node_modules/socket.io-client/node_modules/socket.io-parser": {
+            "version": "3.3.2",
+            "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.2.tgz",
+            "integrity": "sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg==",
+            "dependencies": {
+                "component-emitter": "~1.3.0",
+                "debug": "~3.1.0",
+                "isarray": "2.0.1"
+            }
+        },
+        "node_modules/socket.io-parser": {
+            "version": "4.1.2",
+            "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.2.tgz",
+            "integrity": "sha512-j3kk71QLJuyQ/hh5F/L2t1goqzdTL0gvDzuhTuNSwihfuFUrcSji0qFZmJJPtG6Rmug153eOPsUizeirf1IIog==",
+            "dev": true,
+            "dependencies": {
+                "@socket.io/component-emitter": "~3.0.0",
+                "debug": "~4.3.1"
+            },
+            "engines": {
+                "node": ">=10.0.0"
+            }
+        },
+        "node_modules/socket.io-parser/node_modules/debug": {
+            "version": "4.3.3",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
+            "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
+            "dev": true,
+            "dependencies": {
+                "ms": "2.1.2"
+            },
+            "engines": {
+                "node": ">=6.0"
+            },
+            "peerDependenciesMeta": {
+                "supports-color": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/socket.io-parser/node_modules/ms": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+            "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+            "dev": true
+        },
+        "node_modules/socket.io/node_modules/component-emitter": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+            "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
+        },
+        "node_modules/socket.io/node_modules/debug": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+            "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+            "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
+            "dependencies": {
+                "ms": "^2.1.1"
+            }
+        },
+        "node_modules/socket.io/node_modules/isarray": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+            "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+        },
+        "node_modules/socket.io/node_modules/socket.io-parser": {
+            "version": "3.4.1",
+            "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz",
+            "integrity": "sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A==",
+            "dependencies": {
+                "component-emitter": "1.2.1",
+                "debug": "~4.1.0",
+                "isarray": "2.0.1"
+            }
+        },
+        "node_modules/sockjs": {
+            "version": "0.3.24",
+            "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
+            "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==",
+            "dev": true,
+            "dependencies": {
+                "faye-websocket": "^0.11.3",
+                "uuid": "^8.3.2",
+                "websocket-driver": "^0.7.4"
+            }
+        },
+        "node_modules/sockjs-client": {
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.0.tgz",
+            "integrity": "sha512-qVHJlyfdHFht3eBFZdKEXKTlb7I4IV41xnVNo8yUKA1UHcPJwgW2SvTq9LhnjjCywSkSK7c/e4nghU0GOoMCRQ==",
+            "dev": true,
+            "dependencies": {
+                "debug": "^3.2.7",
+                "eventsource": "^1.1.0",
+                "faye-websocket": "^0.11.4",
+                "inherits": "^2.0.4",
+                "url-parse": "^1.5.10"
+            },
+            "engines": {
+                "node": ">=12"
+            },
+            "funding": {
+                "url": "https://tidelift.com/funding/github/npm/sockjs-client"
+            }
+        },
+        "node_modules/sockjs-client/node_modules/debug": {
+            "version": "3.2.7",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+            "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+            "dev": true,
+            "dependencies": {
+                "ms": "^2.1.1"
+            }
+        },
+        "node_modules/sockjs/node_modules/uuid": {
+            "version": "8.3.2",
+            "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+            "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+            "dev": true,
+            "bin": {
+                "uuid": "dist/bin/uuid"
+            }
+        },
+        "node_modules/solr-node": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/solr-node/-/solr-node-1.2.1.tgz",
+            "integrity": "sha512-DN3+FSBgpJEgGTNddzS8tNb+ILSn5MLcsWf15G9rGxi/sROHbpcevdRSVx6s5/nz56c/5AnBTBZWak7IXWX97A==",
+            "dependencies": {
+                "@log4js-node/log4js-api": "^1.0.2",
+                "node-fetch": "^2.3.0",
+                "underscore": "^1.8.3"
+            },
+            "engines": {
+                "node": ">= 0.12.0"
+            }
+        },
+        "node_modules/solr-node/node_modules/node-fetch": {
+            "version": "2.6.0",
+            "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
+            "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==",
+            "engines": {
+                "node": "4.x || >=6.0.0"
+            }
+        },
+        "node_modules/source-map": {
+            "version": "0.5.7",
+            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+            "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/source-map-resolve": {
+            "version": "0.5.3",
+            "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
+            "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
+            "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/source-map-support": {
+            "version": "0.5.19",
+            "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+            "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+            "dev": true,
+            "dependencies": {
+                "buffer-from": "^1.0.0",
+                "source-map": "^0.6.0"
+            }
+        },
+        "node_modules/source-map-support/node_modules/source-map": {
+            "version": "0.6.1",
+            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+            "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/source-map-url": {
+            "version": "0.4.0",
+            "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
+            "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
+            "deprecated": "See https://github.com/lydell/source-map-url#deprecated"
+        },
+        "node_modules/sparse-bitfield": {
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
+            "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
+            "optional": true,
+            "dependencies": {
+                "memory-pager": "^1.0.2"
+            }
+        },
+        "node_modules/spdx-correct": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
+            "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
+            "dependencies": {
+                "spdx-expression-parse": "^3.0.0",
+                "spdx-license-ids": "^3.0.0"
+            }
+        },
+        "node_modules/spdx-exceptions": {
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+            "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A=="
+        },
+        "node_modules/spdx-expression-parse": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
+            "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+            "dependencies": {
+                "spdx-exceptions": "^2.1.0",
+                "spdx-license-ids": "^3.0.0"
+            }
+        },
+        "node_modules/spdx-license-ids": {
+            "version": "3.0.5",
+            "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
+            "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q=="
+        },
+        "node_modules/spdy": {
+            "version": "4.0.2",
+            "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz",
+            "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==",
+            "dev": true,
+            "dependencies": {
+                "debug": "^4.1.0",
+                "handle-thing": "^2.0.0",
+                "http-deceiver": "^1.2.7",
+                "select-hose": "^2.0.0",
+                "spdy-transport": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=6.0.0"
+            }
+        },
+        "node_modules/spdy-transport": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz",
+            "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==",
+            "dev": true,
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/spdy-transport/node_modules/debug": {
+            "version": "4.3.3",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
+            "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
+            "dev": true,
+            "dependencies": {
+                "ms": "2.1.2"
+            },
+            "engines": {
+                "node": ">=6.0"
+            },
+            "peerDependenciesMeta": {
+                "supports-color": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/spdy-transport/node_modules/ms": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+            "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+            "dev": true
+        },
+        "node_modules/spdy/node_modules/debug": {
+            "version": "4.3.3",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
+            "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
+            "dev": true,
+            "dependencies": {
+                "ms": "2.1.2"
+            },
+            "engines": {
+                "node": ">=6.0"
+            },
+            "peerDependenciesMeta": {
+                "supports-color": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/spdy/node_modules/ms": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+            "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+            "dev": true
+        },
+        "node_modules/split-on-first": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
+            "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/split-skip": {
+            "version": "0.0.2",
+            "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.2.tgz",
+            "integrity": "sha1-2J2Iu9L3Pka1FYqjcKVhIk6A1GE="
+        },
+        "node_modules/split-string": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+            "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+            "dependencies": {
+                "extend-shallow": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/sprintf-js": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+            "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
+        },
+        "node_modules/sshpk": {
+            "version": "1.17.0",
+            "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
+            "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==",
+            "dependencies": {
+                "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"
+            },
+            "bin": {
+                "sshpk-conv": "bin/sshpk-conv",
+                "sshpk-sign": "bin/sshpk-sign",
+                "sshpk-verify": "bin/sshpk-verify"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/ssri": {
+            "version": "5.3.0",
+            "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz",
+            "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==",
+            "dev": true,
+            "dependencies": {
+                "safe-buffer": "^5.1.1"
+            }
+        },
+        "node_modules/standard-error": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/standard-error/-/standard-error-1.1.0.tgz",
+            "integrity": "sha1-I+UWj6HAggGJ5YEnAaeQWFENDTQ="
+        },
+        "node_modules/standard-http-error": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/standard-http-error/-/standard-http-error-2.0.1.tgz",
+            "integrity": "sha1-+K6RcuPO+cs40ucIShkl9Xp8NL0=",
+            "dependencies": {
+                "standard-error": ">= 1.1.0 < 2"
+            }
+        },
+        "node_modules/static-extend": {
+            "version": "0.1.2",
+            "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+            "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+            "dependencies": {
+                "define-property": "^0.2.5",
+                "object-copy": "^0.1.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/static-extend/node_modules/define-property": {
+            "version": "0.2.5",
+            "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+            "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+            "dependencies": {
+                "is-descriptor": "^0.1.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/statuses": {
+            "version": "1.5.0",
+            "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+            "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/stdout-stream": {
+            "version": "1.4.1",
+            "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz",
+            "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==",
+            "dependencies": {
+                "readable-stream": "^2.0.1"
+            }
+        },
+        "node_modules/stdout-stream/node_modules/readable-stream": {
+            "version": "2.3.7",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/stealthy-require": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
+            "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/stream-browserify": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz",
+            "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==",
+            "dependencies": {
+                "inherits": "~2.0.4",
+                "readable-stream": "^3.5.0"
+            }
+        },
+        "node_modules/stream-each": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz",
+            "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==",
+            "dev": true,
+            "dependencies": {
+                "end-of-stream": "^1.1.0",
+                "stream-shift": "^1.0.0"
+            }
+        },
+        "node_modules/stream-parser": {
+            "version": "0.3.1",
+            "resolved": "https://registry.npmjs.org/stream-parser/-/stream-parser-0.3.1.tgz",
+            "integrity": "sha1-FhhUhpRCACGhGC/wrxkRwSl2F3M=",
+            "dependencies": {
+                "debug": "2"
+            }
+        },
+        "node_modules/stream-parser/node_modules/debug": {
+            "version": "2.6.9",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+            "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+            "dependencies": {
+                "ms": "2.0.0"
+            }
+        },
+        "node_modules/stream-parser/node_modules/ms": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        },
+        "node_modules/stream-shift": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
+            "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
+            "dev": true
+        },
+        "node_modules/strict-uri-encode": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
+            "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/string_decoder": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+            "dependencies": {
+                "safe-buffer": "~5.1.0"
+            }
+        },
+        "node_modules/string-width": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+            "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+            "dependencies": {
+                "is-fullwidth-code-point": "^2.0.0",
+                "strip-ansi": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/string.prototype.codepointat": {
+            "version": "0.2.1",
+            "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz",
+            "integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg=="
+        },
+        "node_modules/string.prototype.trimend": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
+            "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
+            "dependencies": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.5"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/string.prototype.trimleft": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz",
+            "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==",
+            "dependencies": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.5",
+                "string.prototype.trimstart": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/string.prototype.trimright": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz",
+            "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==",
+            "dependencies": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.5",
+                "string.prototype.trimend": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/string.prototype.trimstart": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
+            "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
+            "dependencies": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.5"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/stringify-parameters": {
+            "version": "0.0.4",
+            "resolved": "https://registry.npmjs.org/stringify-parameters/-/stringify-parameters-0.0.4.tgz",
+            "integrity": "sha512-H3L90ERn5UPtkpO8eugnKcLgpIVlvTyUTrcLGm607AV5JDH6z0GymtNLr3gjGlP6I6NB/mxNX9QpY6jEQGLPdQ==",
+            "dependencies": {
+                "magicli": "0.0.5",
+                "unpack-string": "0.0.2"
+            },
+            "bin": {
+                "stringify-parameters": "bin/cli.js"
+            }
+        },
+        "node_modules/stringify-parameters/node_modules/magicli": {
+            "version": "0.0.5",
+            "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz",
+            "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=",
+            "dependencies": {
+                "commander": "^2.9.0",
+                "get-stdin": "^5.0.1",
+                "inspect-function": "^0.2.1",
+                "pipe-functions": "^1.2.0"
+            }
+        },
+        "node_modules/strip-ansi": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+            "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+            "dependencies": {
+                "ansi-regex": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/strip-bom": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+            "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+            "dependencies": {
+                "is-utf8": "^0.2.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/strip-eof": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+            "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/strip-final-newline": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+            "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/strip-indent": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
+            "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
+            "dependencies": {
+                "get-stdin": "^4.0.1"
+            },
+            "bin": {
+                "strip-indent": "cli.js"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/strip-indent/node_modules/get-stdin": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
+            "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/strip-json-comments": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+            "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/style-loader": {
+            "version": "0.23.1",
+            "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz",
+            "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==",
+            "dev": true,
+            "dependencies": {
+                "loader-utils": "^1.1.0",
+                "schema-utils": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.12.0"
+            }
+        },
+        "node_modules/style-loader/node_modules/schema-utils": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+            "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+            "dev": true,
+            "dependencies": {
+                "ajv": "^6.1.0",
+                "ajv-errors": "^1.0.0",
+                "ajv-keywords": "^3.1.0"
+            },
+            "engines": {
+                "node": ">= 4"
+            }
+        },
+        "node_modules/styled-components": {
+            "version": "4.4.1",
+            "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-4.4.1.tgz",
+            "integrity": "sha512-RNqj14kYzw++6Sr38n7197xG33ipEOktGElty4I70IKzQF1jzaD1U4xQ+Ny/i03UUhHlC5NWEO+d8olRCDji6g==",
+            "hasInstallScript": true,
+            "dependencies": {
+                "@babel/helper-module-imports": "^7.0.0",
+                "@babel/traverse": "^7.0.0",
+                "@emotion/is-prop-valid": "^0.8.1",
+                "@emotion/unitless": "^0.7.0",
+                "babel-plugin-styled-components": ">= 1",
+                "css-to-react-native": "^2.2.2",
+                "memoize-one": "^5.0.0",
+                "merge-anything": "^2.2.4",
+                "prop-types": "^15.5.4",
+                "react-is": "^16.6.0",
+                "stylis": "^3.5.0",
+                "stylis-rule-sheet": "^0.0.10",
+                "supports-color": "^5.5.0"
+            },
+            "peerDependencies": {
+                "react": ">= 16.3.0",
+                "react-dom": ">= 16.3.0"
+            }
+        },
+        "node_modules/stylis": {
+            "version": "3.5.4",
+            "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz",
+            "integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q=="
+        },
+        "node_modules/stylis-rule-sheet": {
+            "version": "0.0.10",
+            "resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz",
+            "integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw==",
+            "peerDependencies": {
+                "stylis": "^3.5.0"
+            }
+        },
+        "node_modules/supercluster": {
+            "version": "7.1.4",
+            "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.4.tgz",
+            "integrity": "sha512-GhKkRM1jMR6WUwGPw05fs66pOFWhf59lXq+Q3J3SxPvhNcmgOtLRV6aVQPMRsmXdpaeFJGivt+t7QXUPL3ff4g==",
+            "dependencies": {
+                "kdbush": "^3.0.0"
+            }
+        },
+        "node_modules/supports-color": {
+            "version": "5.5.0",
+            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+            "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+            "dependencies": {
+                "has-flag": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/symbol-observable": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
+            "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/symbol-tree": {
+            "version": "3.2.4",
+            "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+            "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+            "dev": true
+        },
+        "node_modules/table-layout": {
+            "version": "0.4.5",
+            "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz",
+            "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==",
+            "dependencies": {
+                "array-back": "^2.0.0",
+                "deep-extend": "~0.6.0",
+                "lodash.padend": "^4.6.1",
+                "typical": "^2.6.1",
+                "wordwrapjs": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=4.0.0"
+            }
+        },
+        "node_modules/tapable": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+            "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+            "dev": true,
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/tar-fs": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz",
+            "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==",
+            "dependencies": {
+                "chownr": "^1.1.1",
+                "mkdirp-classic": "^0.5.2",
+                "pump": "^3.0.0",
+                "tar-stream": "^2.0.0"
+            }
+        },
+        "node_modules/tar-stream": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz",
+            "integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==",
+            "dependencies": {
+                "bl": "^4.0.1",
+                "end-of-stream": "^1.4.1",
+                "fs-constants": "^1.0.0",
+                "inherits": "^2.0.3",
+                "readable-stream": "^3.1.1"
+            }
+        },
+        "node_modules/temp-dir": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz",
+            "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/tempy": {
+            "version": "0.2.1",
+            "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.2.1.tgz",
+            "integrity": "sha512-LB83o9bfZGrntdqPuRdanIVCPReam9SOZKW0fOy5I9X3A854GGWi0tjCqoXEk84XIEYBc/x9Hq3EFop/H5wJaw==",
+            "dependencies": {
+                "temp-dir": "^1.0.0",
+                "unique-string": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/term-size": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz",
+            "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=",
+            "dependencies": {
+                "execa": "^0.7.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/terser": {
+            "version": "5.12.0",
+            "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.0.tgz",
+            "integrity": "sha512-R3AUhNBGWiFc77HXag+1fXpAxTAFRQTJemlJKjAgD9r8xXTpjNKqIXwHM/o7Rh+O0kUJtS3WQVdBeMKFk5sw9A==",
+            "dependencies": {
+                "acorn": "^8.5.0",
+                "commander": "^2.20.0",
+                "source-map": "~0.7.2",
+                "source-map-support": "~0.5.20"
+            },
+            "bin": {
+                "terser": "bin/terser"
+            },
+            "engines": {
+                "node": ">=10"
+            }
+        },
+        "node_modules/terser-webpack-plugin": {
+            "version": "5.3.1",
+            "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz",
+            "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==",
+            "dependencies": {
+                "jest-worker": "^27.4.5",
+                "schema-utils": "^3.1.1",
+                "serialize-javascript": "^6.0.0",
+                "source-map": "^0.6.1",
+                "terser": "^5.7.2"
+            },
+            "engines": {
+                "node": ">= 10.13.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/webpack"
+            },
+            "peerDependencies": {
+                "webpack": "^5.1.0"
+            },
+            "peerDependenciesMeta": {
+                "@swc/core": {
+                    "optional": true
+                },
+                "esbuild": {
+                    "optional": true
+                },
+                "uglify-js": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/terser-webpack-plugin/node_modules/ajv": {
+            "version": "6.12.6",
+            "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+            "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+            "dependencies": {
+                "fast-deep-equal": "^3.1.1",
+                "fast-json-stable-stringify": "^2.0.0",
+                "json-schema-traverse": "^0.4.1",
+                "uri-js": "^4.2.2"
+            },
+            "funding": {
+                "type": "github",
+                "url": "https://github.com/sponsors/epoberezkin"
+            }
+        },
+        "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": {
+            "version": "3.5.2",
+            "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+            "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+            "peerDependencies": {
+                "ajv": "^6.9.1"
+            }
+        },
+        "node_modules/terser-webpack-plugin/node_modules/schema-utils": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+            "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+            "dependencies": {
+                "@types/json-schema": "^7.0.8",
+                "ajv": "^6.12.5",
+                "ajv-keywords": "^3.5.2"
+            },
+            "engines": {
+                "node": ">= 10.13.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/webpack"
+            }
+        },
+        "node_modules/terser-webpack-plugin/node_modules/serialize-javascript": {
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+            "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+            "dependencies": {
+                "randombytes": "^2.1.0"
+            }
+        },
+        "node_modules/terser-webpack-plugin/node_modules/source-map": {
+            "version": "0.6.1",
+            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+            "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/terser/node_modules/acorn": {
+            "version": "8.7.0",
+            "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
+            "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
+            "bin": {
+                "acorn": "bin/acorn"
+            },
+            "engines": {
+                "node": ">=0.4.0"
+            }
+        },
+        "node_modules/terser/node_modules/source-map": {
+            "version": "0.7.3",
+            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+            "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/terser/node_modules/source-map-support": {
+            "version": "0.5.21",
+            "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+            "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+            "dependencies": {
+                "buffer-from": "^1.0.0",
+                "source-map": "^0.6.0"
+            }
+        },
+        "node_modules/terser/node_modules/source-map-support/node_modules/source-map": {
+            "version": "0.6.1",
+            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+            "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/textarea-caret": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/textarea-caret/-/textarea-caret-3.1.0.tgz",
+            "integrity": "sha512-cXAvzO9pP5CGa6NKx0WYHl+8CHKZs8byMkt3PCJBCmq2a34YA9pO1NrQET5pzeqnBjBdToF5No4rrmkDUgQC2Q=="
+        },
+        "node_modules/three": {
+            "version": "0.127.0",
+            "resolved": "https://registry.npmjs.org/three/-/three-0.127.0.tgz",
+            "integrity": "sha512-wtgrn+mhYUbobxT7QN3GPdu3SRpSBQvwY6uOzLChWS7QE//f7paDU/+wlzbg+ngeIvBBqjBHSRuywTh8A99Jng=="
+        },
+        "node_modules/through": {
+            "version": "2.3.8",
+            "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+            "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
+        },
+        "node_modules/through2": {
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+            "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+            "dev": true,
+            "dependencies": {
+                "readable-stream": "~2.3.6",
+                "xtend": "~4.0.1"
+            }
+        },
+        "node_modules/through2/node_modules/readable-stream": {
+            "version": "2.3.7",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+            "dev": true,
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/thunky": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
+            "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
+            "dev": true
+        },
+        "node_modules/timed-out": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
+            "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/tiny-inflate": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
+            "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="
+        },
+        "node_modules/tiny-invariant": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz",
+            "integrity": "sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg=="
+        },
+        "node_modules/tiny-warning": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
+            "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
+        },
+        "node_modules/tinycolor2": {
+            "version": "1.4.2",
+            "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.2.tgz",
+            "integrity": "sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA==",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/to-array": {
+            "version": "0.1.4",
+            "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
+            "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
+        },
+        "node_modules/to-fast-properties": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+            "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/to-object-path": {
+            "version": "0.3.0",
+            "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+            "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+            "dependencies": {
+                "kind-of": "^3.0.2"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/to-object-path/node_modules/is-buffer": {
+            "version": "1.1.6",
+            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+            "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+        },
+        "node_modules/to-object-path/node_modules/kind-of": {
+            "version": "3.2.2",
+            "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+            "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+            "dependencies": {
+                "is-buffer": "^1.1.5"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/to-regex": {
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+            "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+            "dependencies": {
+                "define-property": "^2.0.2",
+                "extend-shallow": "^3.0.2",
+                "regex-not": "^1.0.2",
+                "safe-regex": "^1.1.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/to-regex-range": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+            "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+            "dependencies": {
+                "is-number": "^3.0.0",
+                "repeat-string": "^1.6.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/toidentifier": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+            "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+            "engines": {
+                "node": ">=0.6"
+            }
+        },
+        "node_modules/token-stream": {
+            "version": "0.0.1",
+            "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz",
+            "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo="
+        },
+        "node_modules/touch": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
+            "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
+            "dependencies": {
+                "nopt": "~1.0.10"
+            },
+            "bin": {
+                "nodetouch": "bin/nodetouch.js"
+            }
+        },
+        "node_modules/touch/node_modules/nopt": {
+            "version": "1.0.10",
+            "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+            "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
+            "dependencies": {
+                "abbrev": "1"
+            },
+            "bin": {
+                "nopt": "bin/nopt.js"
+            },
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/tough-cookie": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
+            "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==",
+            "dependencies": {
+                "psl": "^1.1.33",
+                "punycode": "^2.1.1",
+                "universalify": "^0.1.2"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/tr46": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
+            "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+            "dev": true,
+            "dependencies": {
+                "punycode": "^2.1.0"
+            }
+        },
+        "node_modules/translate-google-api": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/translate-google-api/-/translate-google-api-1.0.4.tgz",
+            "integrity": "sha512-KVXmo4+64/H1vIbnzf2zNiJ2JLeEB3jrEnNRP2EFNAGNqna/5bmw/Cps3pCHu0n3BzTOoWh9u6wFvrRYdzQ6Iw==",
+            "dependencies": {
+                "axios": "^0.20.0"
+            }
+        },
+        "node_modules/translate-google-api/node_modules/axios": {
+            "version": "0.20.0",
+            "resolved": "https://registry.npmjs.org/axios/-/axios-0.20.0.tgz",
+            "integrity": "sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA==",
+            "deprecated": "Critical security vulnerability fixed in v0.21.1. For more information, see https://github.com/axios/axios/pull/3410",
+            "dependencies": {
+                "follow-redirects": "^1.10.0"
+            }
+        },
+        "node_modules/traverse-chain": {
+            "version": "0.1.0",
+            "resolved": "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz",
+            "integrity": "sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE="
+        },
+        "node_modules/tree-kill": {
+            "version": "1.2.2",
+            "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+            "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+            "dev": true,
+            "bin": {
+                "tree-kill": "cli.js"
+            }
+        },
+        "node_modules/trim-newlines": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
+            "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/true-case-path": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz",
+            "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==",
+            "dependencies": {
+                "glob": "^7.1.2"
+            }
+        },
+        "node_modules/tryit": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz",
+            "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics="
+        },
+        "node_modules/ts-loader": {
+            "version": "5.4.5",
+            "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.4.5.tgz",
+            "integrity": "sha512-XYsjfnRQCBum9AMRZpk2rTYSVpdZBpZK+kDh0TeT3kxmQNBDVIeUjdPjY5RZry4eIAb8XHc4gYSUiUWPYvzSRw==",
+            "dev": true,
+            "dependencies": {
+                "chalk": "^2.3.0",
+                "enhanced-resolve": "^4.0.0",
+                "loader-utils": "^1.0.2",
+                "micromatch": "^3.1.4",
+                "semver": "^5.0.1"
+            },
+            "engines": {
+                "node": ">=6.11.5"
+            },
+            "peerDependencies": {
+                "typescript": "*"
+            }
+        },
+        "node_modules/ts-node": {
+            "version": "7.0.1",
+            "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz",
+            "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==",
+            "dev": true,
+            "dependencies": {
+                "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"
+            },
+            "bin": {
+                "ts-node": "dist/bin.js"
+            },
+            "engines": {
+                "node": ">=4.2.0"
+            }
+        },
+        "node_modules/ts-node-dev": {
+            "version": "1.1.8",
+            "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.8.tgz",
+            "integrity": "sha512-Q/m3vEwzYwLZKmV6/0VlFxcZzVV/xcgOt+Tx/VjaaRHyiBcFlV0541yrT09QjzzCxlDZ34OzKjrFAynlmtflEg==",
+            "dev": true,
+            "dependencies": {
+                "chokidar": "^3.5.1",
+                "dynamic-dedupe": "^0.3.0",
+                "minimist": "^1.2.5",
+                "mkdirp": "^1.0.4",
+                "resolve": "^1.0.0",
+                "rimraf": "^2.6.1",
+                "source-map-support": "^0.5.12",
+                "tree-kill": "^1.2.2",
+                "ts-node": "^9.0.0",
+                "tsconfig": "^7.0.0"
+            },
+            "bin": {
+                "ts-node-dev": "lib/bin.js",
+                "tsnd": "lib/bin.js"
+            },
+            "engines": {
+                "node": ">=0.8.0"
+            },
+            "peerDependencies": {
+                "node-notifier": "*",
+                "typescript": "*"
+            },
+            "peerDependenciesMeta": {
+                "node-notifier": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/ts-node-dev/node_modules/anymatch": {
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+            "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+            "dev": true,
+            "dependencies": {
+                "normalize-path": "^3.0.0",
+                "picomatch": "^2.0.4"
+            },
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/ts-node-dev/node_modules/binary-extensions": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+            "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+            "dev": true,
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/ts-node-dev/node_modules/braces": {
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+            "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+            "dev": true,
+            "dependencies": {
+                "fill-range": "^7.0.1"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/ts-node-dev/node_modules/chokidar": {
+            "version": "3.5.3",
+            "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+            "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "individual",
+                    "url": "https://paulmillr.com/funding/"
+                }
+            ],
+            "dependencies": {
+                "anymatch": "~3.1.2",
+                "braces": "~3.0.2",
+                "glob-parent": "~5.1.2",
+                "is-binary-path": "~2.1.0",
+                "is-glob": "~4.0.1",
+                "normalize-path": "~3.0.0",
+                "readdirp": "~3.6.0"
+            },
+            "engines": {
+                "node": ">= 8.10.0"
+            },
+            "optionalDependencies": {
+                "fsevents": "~2.3.2"
+            }
+        },
+        "node_modules/ts-node-dev/node_modules/diff": {
+            "version": "4.0.2",
+            "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+            "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.3.1"
+            }
+        },
+        "node_modules/ts-node-dev/node_modules/fill-range": {
+            "version": "7.0.1",
+            "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+            "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+            "dev": true,
+            "dependencies": {
+                "to-regex-range": "^5.0.1"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/ts-node-dev/node_modules/glob-parent": {
+            "version": "5.1.2",
+            "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+            "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+            "dev": true,
+            "dependencies": {
+                "is-glob": "^4.0.1"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/ts-node-dev/node_modules/is-binary-path": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+            "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+            "dev": true,
+            "dependencies": {
+                "binary-extensions": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/ts-node-dev/node_modules/is-number": {
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+            "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.12.0"
+            }
+        },
+        "node_modules/ts-node-dev/node_modules/mkdirp": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+            "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+            "dev": true,
+            "bin": {
+                "mkdirp": "bin/cmd.js"
+            },
+            "engines": {
+                "node": ">=10"
+            }
+        },
+        "node_modules/ts-node-dev/node_modules/readdirp": {
+            "version": "3.6.0",
+            "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+            "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+            "dev": true,
+            "dependencies": {
+                "picomatch": "^2.2.1"
+            },
+            "engines": {
+                "node": ">=8.10.0"
+            }
+        },
+        "node_modules/ts-node-dev/node_modules/rimraf": {
+            "version": "2.7.1",
+            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+            "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+            "dev": true,
+            "dependencies": {
+                "glob": "^7.1.3"
+            },
+            "bin": {
+                "rimraf": "bin.js"
+            }
+        },
+        "node_modules/ts-node-dev/node_modules/to-regex-range": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+            "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+            "dev": true,
+            "dependencies": {
+                "is-number": "^7.0.0"
+            },
+            "engines": {
+                "node": ">=8.0"
+            }
+        },
+        "node_modules/ts-node-dev/node_modules/ts-node": {
+            "version": "9.1.1",
+            "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz",
+            "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==",
+            "dev": true,
+            "dependencies": {
+                "arg": "^4.1.0",
+                "create-require": "^1.1.0",
+                "diff": "^4.0.1",
+                "make-error": "^1.1.1",
+                "source-map-support": "^0.5.17",
+                "yn": "3.1.1"
+            },
+            "bin": {
+                "ts-node": "dist/bin.js",
+                "ts-node-script": "dist/bin-script.js",
+                "ts-node-transpile-only": "dist/bin-transpile.js",
+                "ts-script": "dist/bin-script-deprecated.js"
+            },
+            "engines": {
+                "node": ">=10.0.0"
+            },
+            "peerDependencies": {
+                "typescript": ">=2.7"
+            }
+        },
+        "node_modules/ts-node-dev/node_modules/yn": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+            "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+            "dev": true,
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/ts-node/node_modules/arrify": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+            "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
+            "dev": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/tsconfig": {
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz",
+            "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==",
+            "dev": true,
+            "dependencies": {
+                "@types/strip-bom": "^3.0.0",
+                "@types/strip-json-comments": "0.0.30",
+                "strip-bom": "^3.0.0",
+                "strip-json-comments": "^2.0.0"
+            }
+        },
+        "node_modules/tsconfig/node_modules/strip-bom": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+            "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+            "dev": true,
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/tslib": {
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
+            "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==",
+            "dev": true
+        },
+        "node_modules/tslint": {
+            "version": "5.20.1",
+            "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz",
+            "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==",
+            "dev": true,
+            "dependencies": {
+                "@babel/code-frame": "^7.0.0",
+                "builtin-modules": "^1.1.1",
+                "chalk": "^2.3.0",
+                "commander": "^2.12.1",
+                "diff": "^4.0.1",
+                "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"
+            },
+            "bin": {
+                "tslint": "bin/tslint"
+            },
+            "engines": {
+                "node": ">=4.8.0"
+            },
+            "peerDependencies": {
+                "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev"
+            }
+        },
+        "node_modules/tslint-loader": {
+            "version": "3.6.0",
+            "resolved": "https://registry.npmjs.org/tslint-loader/-/tslint-loader-3.6.0.tgz",
+            "integrity": "sha512-Me9Qf/87BOfCY8uJJw+J7VMF4U8WiMXKLhKKKugMydF0xMhMOt9wo2mjYTNhwbF9H7SHh8PAIwRG8roisTNekQ==",
+            "deprecated": "TSLint is deprecated. As such migrate from tslint-loader to eslint-loader.",
+            "dev": true,
+            "dependencies": {
+                "loader-utils": "^1.0.2",
+                "mkdirp": "^0.5.1",
+                "object-assign": "^4.1.1",
+                "rimraf": "^2.4.4",
+                "semver": "^5.3.0"
+            },
+            "peerDependencies": {
+                "tslint": ">=4.0.0"
+            }
+        },
+        "node_modules/tslint-loader/node_modules/rimraf": {
+            "version": "2.7.1",
+            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+            "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+            "dev": true,
+            "dependencies": {
+                "glob": "^7.1.3"
+            },
+            "bin": {
+                "rimraf": "bin.js"
+            }
+        },
+        "node_modules/tslint/node_modules/diff": {
+            "version": "4.0.2",
+            "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+            "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.3.1"
+            }
+        },
+        "node_modules/tsscmp": {
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
+            "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==",
+            "engines": {
+                "node": ">=0.6.x"
+            }
+        },
+        "node_modules/tsutils": {
+            "version": "2.29.0",
+            "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
+            "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
+            "dev": true,
+            "dependencies": {
+                "tslib": "^1.8.1"
+            },
+            "peerDependencies": {
+                "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev"
+            }
+        },
+        "node_modules/tunnel-agent": {
+            "version": "0.6.0",
+            "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+            "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+            "dependencies": {
+                "safe-buffer": "^5.0.1"
+            },
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/tweetnacl": {
+            "version": "0.14.5",
+            "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+            "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
+        },
+        "node_modules/type": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+            "dev": true
+        },
+        "node_modules/type-check": {
+            "version": "0.3.2",
+            "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+            "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+            "dev": true,
+            "dependencies": {
+                "prelude-ls": "~1.1.2"
+            },
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/type-detect": {
+            "version": "4.0.8",
+            "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+            "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/type-is": {
+            "version": "1.6.18",
+            "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+            "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+            "dependencies": {
+                "media-typer": "0.3.0",
+                "mime-types": "~2.1.24"
+            },
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/typed-styles": {
+            "version": "0.0.7",
+            "resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz",
+            "integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q=="
+        },
+        "node_modules/typedarray": {
+            "version": "0.0.6",
+            "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+            "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
+        },
+        "node_modules/typescript": {
+            "version": "4.6.2",
+            "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz",
+            "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==",
+            "bin": {
+                "tsc": "bin/tsc",
+                "tsserver": "bin/tsserver"
+            },
+            "engines": {
+                "node": ">=4.2.0"
+            }
+        },
+        "node_modules/typescript-collections": {
+            "version": "1.3.3",
+            "resolved": "https://registry.npmjs.org/typescript-collections/-/typescript-collections-1.3.3.tgz",
+            "integrity": "sha512-7sI4e/bZijOzyURng88oOFZCISQPTHozfE2sUu5AviFYk5QV7fYGb6YiDl+vKjF/pICA354JImBImL9XJWUvdQ=="
+        },
+        "node_modules/typescript-language-server": {
+            "version": "0.4.0",
+            "resolved": "https://registry.npmjs.org/typescript-language-server/-/typescript-language-server-0.4.0.tgz",
+            "integrity": "sha512-K8jNOmDFn+QfrCh8ujby2pGDs5rpjYZQn+zvQnf42rxG4IHbfw5CHoMvbGkWPK/J5Gw8/l5K3i03kVZC2IBElg==",
+            "dependencies": {
+                "command-exists": "1.2.6",
+                "commander": "^2.11.0",
+                "fs-extra": "^7.0.0",
+                "p-debounce": "^1.0.0",
+                "tempy": "^0.2.1",
+                "vscode-languageserver": "^5.3.0-next",
+                "vscode-uri": "^1.0.5"
+            },
+            "bin": {
+                "typescript-language-server": "lib/cli.js"
+            }
+        },
+        "node_modules/typescript-language-server/node_modules/fs-extra": {
+            "version": "7.0.1",
+            "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
+            "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
+            "dependencies": {
+                "graceful-fs": "^4.1.2",
+                "jsonfile": "^4.0.0",
+                "universalify": "^0.1.0"
+            },
+            "engines": {
+                "node": ">=6 <7 || >=8"
+            }
+        },
+        "node_modules/typescript-language-server/node_modules/jsonfile": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+            "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+            "optionalDependencies": {
+                "graceful-fs": "^4.1.6"
+            }
+        },
+        "node_modules/typical": {
+            "version": "2.6.1",
+            "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz",
+            "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0="
+        },
+        "node_modules/ua-parser-js": {
+            "version": "0.7.21",
+            "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz",
+            "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==",
+            "engines": {
+                "node": "*"
+            }
+        },
+        "node_modules/uglify-js": {
+            "version": "2.8.29",
+            "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
+            "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
+            "dependencies": {
+                "source-map": "~0.5.1",
+                "yargs": "~3.10.0"
+            },
+            "bin": {
+                "uglifyjs": "bin/uglifyjs"
+            },
+            "engines": {
+                "node": ">=0.8.0"
+            },
+            "optionalDependencies": {
+                "uglify-to-browserify": "~1.0.0"
+            }
+        },
+        "node_modules/uglify-js/node_modules/camelcase": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
+            "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/uglify-js/node_modules/cliui": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
+            "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
+            "dependencies": {
+                "center-align": "^0.1.1",
+                "right-align": "^0.1.1",
+                "wordwrap": "0.0.2"
+            }
+        },
+        "node_modules/uglify-js/node_modules/yargs": {
+            "version": "3.10.0",
+            "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
+            "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
+            "dependencies": {
+                "camelcase": "^1.0.2",
+                "cliui": "^2.1.0",
+                "decamelize": "^1.0.0",
+                "window-size": "0.1.0"
+            }
+        },
+        "node_modules/uglify-to-browserify": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
+            "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
+            "optional": true
+        },
+        "node_modules/uid-safe": {
+            "version": "2.1.5",
+            "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
+            "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
+            "dependencies": {
+                "random-bytes": "~1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/uid2": {
+            "version": "0.0.3",
+            "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz",
+            "integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I="
+        },
+        "node_modules/unbox-primitive": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
+            "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==",
+            "dependencies": {
+                "function-bind": "^1.1.1",
+                "has-bigints": "^1.0.1",
+                "has-symbols": "^1.0.2",
+                "which-boxed-primitive": "^1.0.2"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/unbox-primitive/node_modules/has-symbols": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+            "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/unbzip2-stream": {
+            "version": "1.4.3",
+            "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz",
+            "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==",
+            "dependencies": {
+                "buffer": "^5.2.1",
+                "through": "^2.3.8"
+            }
+        },
+        "node_modules/undefsafe": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz",
+            "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==",
+            "dependencies": {
+                "debug": "^2.2.0"
+            }
+        },
+        "node_modules/undefsafe/node_modules/debug": {
+            "version": "2.6.9",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+            "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+            "dependencies": {
+                "ms": "2.0.0"
+            }
+        },
+        "node_modules/undefsafe/node_modules/ms": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        },
+        "node_modules/underscore": {
+            "version": "1.10.2",
+            "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz",
+            "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg=="
+        },
+        "node_modules/unicode-trie": {
+            "version": "0.3.1",
+            "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-0.3.1.tgz",
+            "integrity": "sha1-1nHd3YkQGgi6w3tqUWEBBgIFIIU=",
+            "dependencies": {
+                "pako": "^0.2.5",
+                "tiny-inflate": "^1.0.0"
+            }
+        },
+        "node_modules/unicode-trie/node_modules/pako": {
+            "version": "0.2.9",
+            "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
+            "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU="
+        },
+        "node_modules/union-value": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+            "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+            "dependencies": {
+                "arr-union": "^3.1.0",
+                "get-value": "^2.0.6",
+                "is-extendable": "^0.1.1",
+                "set-value": "^2.0.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/union-value/node_modules/is-extendable": {
+            "version": "0.1.1",
+            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+            "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/uniq": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
+            "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
+            "dev": true
+        },
+        "node_modules/unique-filename": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
+            "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
+            "dev": true,
+            "dependencies": {
+                "unique-slug": "^2.0.0"
+            }
+        },
+        "node_modules/unique-slug": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz",
+            "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==",
+            "dev": true,
+            "dependencies": {
+                "imurmurhash": "^0.1.4"
+            }
+        },
+        "node_modules/unique-string": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz",
+            "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=",
+            "dependencies": {
+                "crypto-random-string": "^1.0.0"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/universalify": {
+            "version": "0.1.2",
+            "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+            "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+            "engines": {
+                "node": ">= 4.0.0"
+            }
+        },
+        "node_modules/unorm": {
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz",
+            "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==",
+            "engines": {
+                "node": ">= 0.4.0"
+            }
+        },
+        "node_modules/unpack-string": {
+            "version": "0.0.2",
+            "resolved": "https://registry.npmjs.org/unpack-string/-/unpack-string-0.0.2.tgz",
+            "integrity": "sha1-MC7PCCOLATm9Q0pNf9Z83zPKJ10="
+        },
+        "node_modules/unpipe": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+            "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/unset-value": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+            "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+            "dependencies": {
+                "has-value": "^0.3.1",
+                "isobject": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/unset-value/node_modules/has-value": {
+            "version": "0.3.1",
+            "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+            "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+            "dependencies": {
+                "get-value": "^2.0.3",
+                "has-values": "^0.1.4",
+                "isobject": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/unset-value/node_modules/has-value/node_modules/isobject": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+            "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+            "dependencies": {
+                "isarray": "1.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/unset-value/node_modules/has-values": {
+            "version": "0.1.4",
+            "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+            "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/unstated": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/unstated/-/unstated-2.1.1.tgz",
+            "integrity": "sha512-fORlTWMZxq7NuMJDxyIrrYIZKN7wEWYQ9SiaJfIRcSpsowr6Ph/JIfK2tgtXLW614JfPG/t5q9eEIhXRCf55xg==",
+            "dependencies": {
+                "create-react-context": "^0.1.5"
+            },
+            "peerDependencies": {
+                "react": "^15.0.0 || ^16.0.0"
+            }
+        },
+        "node_modules/unstated/node_modules/create-react-context": {
+            "version": "0.1.6",
+            "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.1.6.tgz",
+            "integrity": "sha512-eCnYYEUEc5i32LHwpE/W7NlddOB9oHwsPaWtWzYtflNkkwa3IfindIcoXdVWs12zCbwaMCavKNu84EXogVIWHw==",
+            "peerDependencies": {
+                "prop-types": "^15.0.0",
+                "react": "^14.0.0 || ^15.0.0 || ^16.0.0"
+            }
+        },
+        "node_modules/unzip-response": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz",
+            "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/upath": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
+            "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
+            "engines": {
+                "node": ">=4",
+                "yarn": "*"
+            }
+        },
+        "node_modules/update-notifier": {
+            "version": "2.5.0",
+            "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz",
+            "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==",
+            "dependencies": {
+                "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"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/uri-js": {
+            "version": "4.2.2",
+            "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+            "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+            "dependencies": {
+                "punycode": "^2.1.0"
+            }
+        },
+        "node_modules/urix": {
+            "version": "0.1.0",
+            "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+            "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+            "deprecated": "Please see https://github.com/lydell/urix#deprecated"
+        },
+        "node_modules/url": {
+            "version": "0.11.0",
+            "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+            "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+            "dev": true,
+            "dependencies": {
+                "punycode": "1.3.2",
+                "querystring": "0.2.0"
+            }
+        },
+        "node_modules/url-loader": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.2.tgz",
+            "integrity": "sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==",
+            "dependencies": {
+                "loader-utils": "^1.1.0",
+                "mime": "^2.0.3",
+                "schema-utils": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 6.9.0"
+            },
+            "peerDependencies": {
+                "webpack": "^3.0.0 || ^4.0.0"
+            }
+        },
+        "node_modules/url-loader/node_modules/mime": {
+            "version": "2.4.4",
+            "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
+            "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==",
+            "bin": {
+                "mime": "cli.js"
+            },
+            "engines": {
+                "node": ">=4.0.0"
+            }
+        },
+        "node_modules/url-loader/node_modules/schema-utils": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+            "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+            "dependencies": {
+                "ajv": "^6.1.0",
+                "ajv-errors": "^1.0.0",
+                "ajv-keywords": "^3.1.0"
+            },
+            "engines": {
+                "node": ">= 4"
+            }
+        },
+        "node_modules/url-parse": {
+            "version": "1.5.10",
+            "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+            "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+            "dev": true,
+            "dependencies": {
+                "querystringify": "^2.1.1",
+                "requires-port": "^1.0.0"
+            }
+        },
+        "node_modules/url-parse-lax": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz",
+            "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=",
+            "dependencies": {
+                "prepend-http": "^1.0.1"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/url-template": {
+            "version": "2.0.8",
+            "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz",
+            "integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE="
+        },
+        "node_modules/url/node_modules/punycode": {
+            "version": "1.3.2",
+            "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+            "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
+            "dev": true
+        },
+        "node_modules/use": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+            "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/use-asset": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/use-asset/-/use-asset-1.0.4.tgz",
+            "integrity": "sha512-7/hqDrWa0iMnCoET9W1T07EmD4Eg/Wmoj/X8TGBc++ECRK4m5yTsjP4O6s0yagbxfqIOuUkIxe2/sA+VR2GxZA==",
+            "dependencies": {
+                "fast-deep-equal": "^3.1.3"
+            },
+            "peerDependencies": {
+                "react": ">=17.0"
+            }
+        },
+        "node_modules/use-asset/node_modules/fast-deep-equal": {
+            "version": "3.1.3",
+            "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+            "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+        },
+        "node_modules/use-memo-one": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.2.tgz",
+            "integrity": "sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==",
+            "peerDependencies": {
+                "react": "^16.8.0 || ^17.0.0"
+            }
+        },
+        "node_modules/util": {
+            "version": "0.12.4",
+            "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz",
+            "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==",
+            "dependencies": {
+                "inherits": "^2.0.3",
+                "is-arguments": "^1.0.4",
+                "is-generator-function": "^1.0.7",
+                "is-typed-array": "^1.1.3",
+                "safe-buffer": "^5.1.2",
+                "which-typed-array": "^1.1.2"
+            }
+        },
+        "node_modules/util-deprecate": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+            "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+        },
+        "node_modules/utila": {
+            "version": "0.4.0",
+            "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
+            "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw="
+        },
+        "node_modules/utility-types": {
+            "version": "3.10.0",
+            "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz",
+            "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==",
+            "engines": {
+                "node": ">= 4"
+            }
+        },
+        "node_modules/utils-merge": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+            "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
+            "engines": {
+                "node": ">= 0.4.0"
+            }
+        },
+        "node_modules/uuid": {
+            "version": "3.4.0",
+            "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+            "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+            "deprecated": "Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.",
+            "bin": {
+                "uuid": "bin/uuid"
+            }
+        },
+        "node_modules/valid-url": {
+            "version": "1.0.9",
+            "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz",
+            "integrity": "sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA="
+        },
+        "node_modules/validate-npm-package-license": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+            "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+            "dependencies": {
+                "spdx-correct": "^3.0.0",
+                "spdx-expression-parse": "^3.0.0"
+            }
+        },
+        "node_modules/validator": {
+            "version": "10.11.0",
+            "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz",
+            "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==",
+            "engines": {
+                "node": ">= 0.10"
+            }
+        },
+        "node_modules/vary": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+            "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
+            "engines": {
+                "node": ">= 0.8"
+            }
+        },
+        "node_modules/verror": {
+            "version": "1.10.0",
+            "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+            "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+            "engines": [
+                "node >=0.6.0"
+            ],
+            "dependencies": {
+                "assert-plus": "^1.0.0",
+                "core-util-is": "1.0.2",
+                "extsprintf": "^1.2.0"
+            }
+        },
+        "node_modules/void-elements": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
+            "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=",
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/vscode-jsonrpc": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz",
+            "integrity": "sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==",
+            "engines": {
+                "node": ">=8.0.0 || >=10.0.0"
+            }
+        },
+        "node_modules/vscode-languageserver": {
+            "version": "5.3.0-next.10",
+            "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-5.3.0-next.10.tgz",
+            "integrity": "sha512-QL7Fe1FT6PdLtVzwJeZ78pTic4eZbzLRy7yAQgPb9xalqqgZESR0+yDZPwJrM3E7PzOmwHBceYcJR54eQZ7Kng==",
+            "dependencies": {
+                "vscode-languageserver-protocol": "^3.15.0-next.8",
+                "vscode-textbuffer": "^1.0.0"
+            },
+            "bin": {
+                "installServerIntoExtension": "bin/installServerIntoExtension"
+            }
+        },
+        "node_modules/vscode-languageserver-protocol": {
+            "version": "3.15.3",
+            "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz",
+            "integrity": "sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==",
+            "dependencies": {
+                "vscode-jsonrpc": "^5.0.1",
+                "vscode-languageserver-types": "3.15.1"
+            }
+        },
+        "node_modules/vscode-languageserver-types": {
+            "version": "3.15.1",
+            "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz",
+            "integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ=="
+        },
+        "node_modules/vscode-textbuffer": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/vscode-textbuffer/-/vscode-textbuffer-1.0.0.tgz",
+            "integrity": "sha512-zPaHo4urgpwsm+PrJWfNakolRpryNja18SUip/qIIsfhuEqEIPEXMxHOlFPjvDC4JgTaimkncNW7UMXRJTY6ow==",
+            "deprecated": "This package has been renamed to @vscode/textbuffer, please update to the new name"
+        },
+        "node_modules/vscode-uri": {
+            "version": "1.0.8",
+            "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.8.tgz",
+            "integrity": "sha512-obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ=="
+        },
+        "node_modules/vue-template-compiler": {
+            "version": "2.6.11",
+            "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz",
+            "integrity": "sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA==",
+            "dependencies": {
+                "de-indent": "^1.0.2",
+                "he": "^1.1.0"
+            }
+        },
+        "node_modules/w3c-hr-time": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
+            "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==",
+            "dev": true,
+            "dependencies": {
+                "browser-process-hrtime": "^1.0.0"
+            }
+        },
+        "node_modules/w3c-keyname": {
+            "version": "2.2.4",
+            "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.4.tgz",
+            "integrity": "sha512-tOhfEwEzFLJzf6d1ZPkYfGj+FWhIpBux9ppoP3rlclw3Z0BZv3N7b7030Z1kYth+6rDuAsXUFr+d0VE6Ed1ikw=="
+        },
+        "node_modules/w3c-xmlserializer": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz",
+            "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==",
+            "dev": true,
+            "dependencies": {
+                "domexception": "^1.0.1",
+                "webidl-conversions": "^4.0.2",
+                "xml-name-validator": "^3.0.0"
+            }
+        },
+        "node_modules/walkdir": {
+            "version": "0.4.1",
+            "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz",
+            "integrity": "sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==",
+            "engines": {
+                "node": ">=6.0.0"
+            }
+        },
+        "node_modules/warning": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
+            "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
+            "dependencies": {
+                "loose-envify": "^1.0.0"
+            }
+        },
+        "node_modules/watchpack": {
+            "version": "2.3.1",
+            "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz",
+            "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==",
+            "dependencies": {
+                "glob-to-regexp": "^0.4.1",
+                "graceful-fs": "^4.1.2"
+            },
+            "engines": {
+                "node": ">=10.13.0"
+            }
+        },
+        "node_modules/wbuf": {
+            "version": "1.7.3",
+            "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz",
+            "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==",
+            "dev": true,
+            "dependencies": {
+                "minimalistic-assert": "^1.0.0"
+            }
+        },
+        "node_modules/web-request": {
+            "version": "1.0.7",
+            "resolved": "https://registry.npmjs.org/web-request/-/web-request-1.0.7.tgz",
+            "integrity": "sha1-twxCs81FV3noLbaIYlOySR8r1Wk=",
+            "dependencies": {
+                "request": "^2.69.0"
+            }
+        },
+        "node_modules/web-streams-polyfill": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz",
+            "integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==",
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/webidl-conversions": {
+            "version": "4.0.2",
+            "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+            "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
+            "dev": true
+        },
+        "node_modules/webpack": {
+            "version": "5.69.1",
+            "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.69.1.tgz",
+            "integrity": "sha512-+VyvOSJXZMT2V5vLzOnDuMz5GxEqLk7hKWQ56YxPW/PQRUuKimPqmEIJOx8jHYeyo65pKbapbW464mvsKbaj4A==",
+            "dependencies": {
+                "@types/eslint-scope": "^3.7.3",
+                "@types/estree": "^0.0.51",
+                "@webassemblyjs/ast": "1.11.1",
+                "@webassemblyjs/wasm-edit": "1.11.1",
+                "@webassemblyjs/wasm-parser": "1.11.1",
+                "acorn": "^8.4.1",
+                "acorn-import-assertions": "^1.7.6",
+                "browserslist": "^4.14.5",
+                "chrome-trace-event": "^1.0.2",
+                "enhanced-resolve": "^5.8.3",
+                "es-module-lexer": "^0.9.0",
+                "eslint-scope": "5.1.1",
+                "events": "^3.2.0",
+                "glob-to-regexp": "^0.4.1",
+                "graceful-fs": "^4.2.9",
+                "json-parse-better-errors": "^1.0.2",
+                "loader-runner": "^4.2.0",
+                "mime-types": "^2.1.27",
+                "neo-async": "^2.6.2",
+                "schema-utils": "^3.1.0",
+                "tapable": "^2.1.1",
+                "terser-webpack-plugin": "^5.1.3",
+                "watchpack": "^2.3.1",
+                "webpack-sources": "^3.2.3"
+            },
+            "bin": {
+                "webpack": "bin/webpack.js"
+            },
+            "engines": {
+                "node": ">=10.13.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/webpack"
+            },
+            "peerDependenciesMeta": {
+                "webpack-cli": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/webpack-cli": {
+            "version": "4.9.2",
+            "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.2.tgz",
+            "integrity": "sha512-m3/AACnBBzK/kMTcxWHcZFPrw/eQuY4Df1TxvIWfWM2x7mRqBQCqKEd96oCUa9jkapLBaFfRce33eGDb4Pr7YQ==",
+            "dependencies": {
+                "@discoveryjs/json-ext": "^0.5.0",
+                "@webpack-cli/configtest": "^1.1.1",
+                "@webpack-cli/info": "^1.4.1",
+                "@webpack-cli/serve": "^1.6.1",
+                "colorette": "^2.0.14",
+                "commander": "^7.0.0",
+                "execa": "^5.0.0",
+                "fastest-levenshtein": "^1.0.12",
+                "import-local": "^3.0.2",
+                "interpret": "^2.2.0",
+                "rechoir": "^0.7.0",
+                "webpack-merge": "^5.7.3"
+            },
+            "bin": {
+                "webpack-cli": "bin/cli.js"
+            },
+            "engines": {
+                "node": ">=10.13.0"
+            },
+            "peerDependencies": {
+                "webpack": "4.x.x || 5.x.x"
+            },
+            "peerDependenciesMeta": {
+                "@webpack-cli/generators": {
+                    "optional": true
+                },
+                "@webpack-cli/migrate": {
+                    "optional": true
+                },
+                "webpack-bundle-analyzer": {
+                    "optional": true
+                },
+                "webpack-dev-server": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/webpack-cli/node_modules/commander": {
+            "version": "7.2.0",
+            "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+            "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+            "engines": {
+                "node": ">= 10"
+            }
+        },
+        "node_modules/webpack-cli/node_modules/cross-spawn": {
+            "version": "7.0.3",
+            "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+            "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+            "dependencies": {
+                "path-key": "^3.1.0",
+                "shebang-command": "^2.0.0",
+                "which": "^2.0.1"
+            },
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/webpack-cli/node_modules/execa": {
+            "version": "5.1.1",
+            "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+            "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+            "dependencies": {
+                "cross-spawn": "^7.0.3",
+                "get-stream": "^6.0.0",
+                "human-signals": "^2.1.0",
+                "is-stream": "^2.0.0",
+                "merge-stream": "^2.0.0",
+                "npm-run-path": "^4.0.1",
+                "onetime": "^5.1.2",
+                "signal-exit": "^3.0.3",
+                "strip-final-newline": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sindresorhus/execa?sponsor=1"
+            }
+        },
+        "node_modules/webpack-cli/node_modules/find-up": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+            "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+            "dependencies": {
+                "locate-path": "^5.0.0",
+                "path-exists": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/webpack-cli/node_modules/get-stream": {
+            "version": "6.0.1",
+            "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+            "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+            "engines": {
+                "node": ">=10"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/webpack-cli/node_modules/import-local": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
+            "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==",
+            "dependencies": {
+                "pkg-dir": "^4.2.0",
+                "resolve-cwd": "^3.0.0"
+            },
+            "bin": {
+                "import-local-fixture": "fixtures/cli.js"
+            },
+            "engines": {
+                "node": ">=8"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/webpack-cli/node_modules/interpret": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
+            "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==",
+            "engines": {
+                "node": ">= 0.10"
+            }
+        },
+        "node_modules/webpack-cli/node_modules/is-stream": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+            "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+            "engines": {
+                "node": ">=8"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/sindresorhus"
+            }
+        },
+        "node_modules/webpack-cli/node_modules/locate-path": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+            "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+            "dependencies": {
+                "p-locate": "^4.1.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/webpack-cli/node_modules/npm-run-path": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+            "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+            "dependencies": {
+                "path-key": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/webpack-cli/node_modules/p-locate": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+            "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+            "dependencies": {
+                "p-limit": "^2.2.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/webpack-cli/node_modules/path-exists": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+            "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/webpack-cli/node_modules/path-key": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+            "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/webpack-cli/node_modules/pkg-dir": {
+            "version": "4.2.0",
+            "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+            "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+            "dependencies": {
+                "find-up": "^4.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/webpack-cli/node_modules/rechoir": {
+            "version": "0.7.1",
+            "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
+            "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==",
+            "dependencies": {
+                "resolve": "^1.9.0"
+            },
+            "engines": {
+                "node": ">= 0.10"
+            }
+        },
+        "node_modules/webpack-cli/node_modules/resolve-cwd": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
+            "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
+            "dependencies": {
+                "resolve-from": "^5.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/webpack-cli/node_modules/resolve-from": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+            "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/webpack-cli/node_modules/shebang-command": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+            "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+            "dependencies": {
+                "shebang-regex": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/webpack-cli/node_modules/shebang-regex": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+            "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/webpack-cli/node_modules/which": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+            "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+            "dependencies": {
+                "isexe": "^2.0.0"
+            },
+            "bin": {
+                "node-which": "bin/node-which"
+            },
+            "engines": {
+                "node": ">= 8"
+            }
+        },
+        "node_modules/webpack-dev-middleware": {
+            "version": "5.3.1",
+            "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz",
+            "integrity": "sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==",
+            "dependencies": {
+                "colorette": "^2.0.10",
+                "memfs": "^3.4.1",
+                "mime-types": "^2.1.31",
+                "range-parser": "^1.2.1",
+                "schema-utils": "^4.0.0"
+            },
+            "engines": {
+                "node": ">= 12.13.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/webpack"
+            },
+            "peerDependencies": {
+                "webpack": "^4.0.0 || ^5.0.0"
+            }
+        },
+        "node_modules/webpack-dev-middleware/node_modules/ajv": {
+            "version": "8.10.0",
+            "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz",
+            "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==",
+            "dependencies": {
+                "fast-deep-equal": "^3.1.1",
+                "json-schema-traverse": "^1.0.0",
+                "require-from-string": "^2.0.2",
+                "uri-js": "^4.2.2"
+            },
+            "funding": {
+                "type": "github",
+                "url": "https://github.com/sponsors/epoberezkin"
+            }
+        },
+        "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": {
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+            "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+            "dependencies": {
+                "fast-deep-equal": "^3.1.3"
+            },
+            "peerDependencies": {
+                "ajv": "^8.8.2"
+            }
+        },
+        "node_modules/webpack-dev-middleware/node_modules/ajv-keywords/node_modules/fast-deep-equal": {
+            "version": "3.1.3",
+            "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+            "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+        },
+        "node_modules/webpack-dev-middleware/node_modules/json-schema-traverse": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+            "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+        },
+        "node_modules/webpack-dev-middleware/node_modules/mime-db": {
+            "version": "1.51.0",
+            "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
+            "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==",
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/webpack-dev-middleware/node_modules/mime-types": {
+            "version": "2.1.34",
+            "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
+            "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
+            "dependencies": {
+                "mime-db": "1.51.0"
+            },
+            "engines": {
+                "node": ">= 0.6"
+            }
+        },
+        "node_modules/webpack-dev-middleware/node_modules/schema-utils": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
+            "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==",
+            "dependencies": {
+                "@types/json-schema": "^7.0.9",
+                "ajv": "^8.8.0",
+                "ajv-formats": "^2.1.1",
+                "ajv-keywords": "^5.0.0"
+            },
+            "engines": {
+                "node": ">= 12.13.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/webpack"
+            }
+        },
+        "node_modules/webpack-dev-server": {
+            "version": "3.11.3",
+            "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.3.tgz",
+            "integrity": "sha512-3x31rjbEQWKMNzacUZRE6wXvUFuGpH7vr0lIEbYpMAG9BOxi0928QU1BBswOAP3kg3H1O4hiS+sq4YyAn6ANnA==",
+            "dev": true,
+            "dependencies": {
+                "ansi-html-community": "0.0.8",
+                "bonjour": "^3.5.0",
+                "chokidar": "^2.1.8",
+                "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.3.1",
+                "http-proxy-middleware": "0.19.1",
+                "import-local": "^2.0.0",
+                "internal-ip": "^4.3.0",
+                "ip": "^1.1.5",
+                "is-absolute-url": "^3.0.3",
+                "killable": "^1.0.1",
+                "loglevel": "^1.6.8",
+                "opn": "^5.5.0",
+                "p-retry": "^3.0.1",
+                "portfinder": "^1.0.26",
+                "schema-utils": "^1.0.0",
+                "selfsigned": "^1.10.8",
+                "semver": "^6.3.0",
+                "serve-index": "^1.9.1",
+                "sockjs": "^0.3.21",
+                "sockjs-client": "^1.5.0",
+                "spdy": "^4.0.2",
+                "strip-ansi": "^3.0.1",
+                "supports-color": "^6.1.0",
+                "url": "^0.11.0",
+                "webpack-dev-middleware": "^3.7.2",
+                "webpack-log": "^2.0.0",
+                "ws": "^6.2.1",
+                "yargs": "^13.3.2"
+            },
+            "bin": {
+                "webpack-dev-server": "bin/webpack-dev-server.js"
+            },
+            "engines": {
+                "node": ">= 6.11.5"
+            },
+            "peerDependencies": {
+                "webpack": "^4.0.0 || ^5.0.0"
+            },
+            "peerDependenciesMeta": {
+                "webpack-cli": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/webpack-dev-server/node_modules/ansi-regex": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+            "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+            "dev": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/webpack-dev-server/node_modules/debug": {
+            "version": "4.3.3",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
+            "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
+            "dev": true,
+            "dependencies": {
+                "ms": "2.1.2"
+            },
+            "engines": {
+                "node": ">=6.0"
+            },
+            "peerDependenciesMeta": {
+                "supports-color": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/webpack-dev-server/node_modules/memory-fs": {
+            "version": "0.4.1",
+            "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
+            "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
+            "dev": true,
+            "dependencies": {
+                "errno": "^0.1.3",
+                "readable-stream": "^2.0.1"
+            }
+        },
+        "node_modules/webpack-dev-server/node_modules/mime": {
+            "version": "2.6.0",
+            "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+            "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+            "dev": true,
+            "bin": {
+                "mime": "cli.js"
+            },
+            "engines": {
+                "node": ">=4.0.0"
+            }
+        },
+        "node_modules/webpack-dev-server/node_modules/ms": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+            "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+            "dev": true
+        },
+        "node_modules/webpack-dev-server/node_modules/readable-stream": {
+            "version": "2.3.7",
+            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+            "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+            "dev": true,
+            "dependencies": {
+                "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"
+            }
+        },
+        "node_modules/webpack-dev-server/node_modules/semver": {
+            "version": "6.3.0",
+            "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+            "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+            "dev": true,
+            "bin": {
+                "semver": "bin/semver.js"
+            }
+        },
+        "node_modules/webpack-dev-server/node_modules/strip-ansi": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+            "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+            "dev": true,
+            "dependencies": {
+                "ansi-regex": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/webpack-dev-server/node_modules/supports-color": {
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+            "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+            "dev": true,
+            "dependencies": {
+                "has-flag": "^3.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/webpack-dev-server/node_modules/webpack-dev-middleware": {
+            "version": "3.7.3",
+            "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz",
+            "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==",
+            "dev": true,
+            "dependencies": {
+                "memory-fs": "^0.4.1",
+                "mime": "^2.4.4",
+                "mkdirp": "^0.5.1",
+                "range-parser": "^1.2.1",
+                "webpack-log": "^2.0.0"
+            },
+            "engines": {
+                "node": ">= 6"
+            },
+            "peerDependencies": {
+                "webpack": "^4.0.0 || ^5.0.0"
+            }
+        },
+        "node_modules/webpack-dev-server/node_modules/webpack-log": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz",
+            "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==",
+            "dev": true,
+            "dependencies": {
+                "ansi-colors": "^3.0.0",
+                "uuid": "^3.3.2"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/webpack-dev-server/node_modules/ws": {
+            "version": "6.2.2",
+            "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
+            "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
+            "dev": true,
+            "dependencies": {
+                "async-limiter": "~1.0.0"
+            }
+        },
+        "node_modules/webpack-hot-middleware": {
+            "version": "2.25.1",
+            "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.25.1.tgz",
+            "integrity": "sha512-Koh0KyU/RPYwel/khxbsDz9ibDivmUbrRuKSSQvW42KSDdO4w23WI3SkHpSUKHE76LrFnnM/L7JCrpBwu8AXYw==",
+            "dev": true,
+            "dependencies": {
+                "ansi-html-community": "0.0.8",
+                "html-entities": "^2.1.0",
+                "querystring": "^0.2.0",
+                "strip-ansi": "^6.0.0"
+            }
+        },
+        "node_modules/webpack-hot-middleware/node_modules/ansi-regex": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+            "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+            "dev": true,
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/webpack-hot-middleware/node_modules/html-entities": {
+            "version": "2.3.2",
+            "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz",
+            "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==",
+            "dev": true
+        },
+        "node_modules/webpack-hot-middleware/node_modules/strip-ansi": {
+            "version": "6.0.1",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+            "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+            "dev": true,
+            "dependencies": {
+                "ansi-regex": "^5.0.1"
+            },
+            "engines": {
+                "node": ">=8"
+            }
+        },
+        "node_modules/webpack-log": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-1.2.0.tgz",
+            "integrity": "sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA==",
+            "dev": true,
+            "dependencies": {
+                "chalk": "^2.1.0",
+                "log-symbols": "^2.1.0",
+                "loglevelnext": "^1.0.1",
+                "uuid": "^3.1.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/webpack-merge": {
+            "version": "5.8.0",
+            "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz",
+            "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==",
+            "dependencies": {
+                "clone-deep": "^4.0.1",
+                "wildcard": "^2.0.0"
+            },
+            "engines": {
+                "node": ">=10.0.0"
+            }
+        },
+        "node_modules/webpack-sources": {
+            "version": "3.2.3",
+            "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
+            "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
+            "engines": {
+                "node": ">=10.13.0"
+            }
+        },
+        "node_modules/webpack/node_modules/acorn": {
+            "version": "8.7.0",
+            "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
+            "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
+            "bin": {
+                "acorn": "bin/acorn"
+            },
+            "engines": {
+                "node": ">=0.4.0"
+            }
+        },
+        "node_modules/webpack/node_modules/ajv": {
+            "version": "6.12.6",
+            "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+            "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+            "dependencies": {
+                "fast-deep-equal": "^3.1.1",
+                "fast-json-stable-stringify": "^2.0.0",
+                "json-schema-traverse": "^0.4.1",
+                "uri-js": "^4.2.2"
+            },
+            "funding": {
+                "type": "github",
+                "url": "https://github.com/sponsors/epoberezkin"
+            }
+        },
+        "node_modules/webpack/node_modules/ajv-keywords": {
+            "version": "3.5.2",
+            "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+            "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+            "peerDependencies": {
+                "ajv": "^6.9.1"
+            }
+        },
+        "node_modules/webpack/node_modules/enhanced-resolve": {
+            "version": "5.9.1",
+            "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.1.tgz",
+            "integrity": "sha512-jdyZMwCQ5Oj4c5+BTnkxPgDZO/BJzh/ADDmKebayyzNwjVX1AFCeGkOfxNx0mHi2+8BKC5VxUYiw3TIvoT7vhw==",
+            "dependencies": {
+                "graceful-fs": "^4.2.4",
+                "tapable": "^2.2.0"
+            },
+            "engines": {
+                "node": ">=10.13.0"
+            }
+        },
+        "node_modules/webpack/node_modules/graceful-fs": {
+            "version": "4.2.9",
+            "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
+            "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ=="
+        },
+        "node_modules/webpack/node_modules/neo-async": {
+            "version": "2.6.2",
+            "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+            "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
+        },
+        "node_modules/webpack/node_modules/schema-utils": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+            "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+            "dependencies": {
+                "@types/json-schema": "^7.0.8",
+                "ajv": "^6.12.5",
+                "ajv-keywords": "^3.5.2"
+            },
+            "engines": {
+                "node": ">= 10.13.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/webpack"
+            }
+        },
+        "node_modules/webpack/node_modules/tapable": {
+            "version": "2.2.1",
+            "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+            "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/webrtc-adapter": {
+            "version": "7.7.1",
+            "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-7.7.1.tgz",
+            "integrity": "sha512-TbrbBmiQBL9n0/5bvDdORc6ZfRY/Z7JnEj+EYOD1ghseZdpJ+nF2yx14k3LgQKc7JZnG7HAcL+zHnY25So9d7A==",
+            "dependencies": {
+                "rtcpeerconnection-shim": "^1.2.15",
+                "sdp": "^2.12.0"
+            },
+            "engines": {
+                "node": ">=6.0.0",
+                "npm": ">=3.10.0"
+            }
+        },
+        "node_modules/websocket-driver": {
+            "version": "0.7.4",
+            "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
+            "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
+            "dev": true,
+            "dependencies": {
+                "http-parser-js": ">=0.5.1",
+                "safe-buffer": ">=5.1.0",
+                "websocket-extensions": ">=0.1.1"
+            },
+            "engines": {
+                "node": ">=0.8.0"
+            }
+        },
+        "node_modules/websocket-extensions": {
+            "version": "0.1.4",
+            "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+            "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.8.0"
+            }
+        },
+        "node_modules/whatwg-encoding": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz",
+            "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==",
+            "dev": true,
+            "dependencies": {
+                "iconv-lite": "0.4.24"
+            }
+        },
+        "node_modules/whatwg-fetch": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
+            "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q=="
+        },
+        "node_modules/whatwg-mimetype": {
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
+            "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==",
+            "dev": true
+        },
+        "node_modules/whatwg-url": {
+            "version": "7.1.0",
+            "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
+            "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
+            "dev": true,
+            "dependencies": {
+                "lodash.sortby": "^4.7.0",
+                "tr46": "^1.0.1",
+                "webidl-conversions": "^4.0.2"
+            }
+        },
+        "node_modules/which": {
+            "version": "1.3.1",
+            "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+            "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+            "dependencies": {
+                "isexe": "^2.0.0"
+            },
+            "bin": {
+                "which": "bin/which"
+            }
+        },
+        "node_modules/which-boxed-primitive": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+            "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+            "dependencies": {
+                "is-bigint": "^1.0.1",
+                "is-boolean-object": "^1.1.0",
+                "is-number-object": "^1.0.4",
+                "is-string": "^1.0.5",
+                "is-symbol": "^1.0.3"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/which-module": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+            "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
+        },
+        "node_modules/which-pm-runs": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
+            "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
+        },
+        "node_modules/which-typed-array": {
+            "version": "1.1.7",
+            "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz",
+            "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==",
+            "dependencies": {
+                "available-typed-arrays": "^1.0.5",
+                "call-bind": "^1.0.2",
+                "es-abstract": "^1.18.5",
+                "foreach": "^2.0.5",
+                "has-tostringtag": "^1.0.0",
+                "is-typed-array": "^1.1.7"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/which-typed-array/node_modules/es-abstract": {
+            "version": "1.19.1",
+            "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz",
+            "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==",
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "es-to-primitive": "^1.2.1",
+                "function-bind": "^1.1.1",
+                "get-intrinsic": "^1.1.1",
+                "get-symbol-description": "^1.0.0",
+                "has": "^1.0.3",
+                "has-symbols": "^1.0.2",
+                "internal-slot": "^1.0.3",
+                "is-callable": "^1.2.4",
+                "is-negative-zero": "^2.0.1",
+                "is-regex": "^1.1.4",
+                "is-shared-array-buffer": "^1.0.1",
+                "is-string": "^1.0.7",
+                "is-weakref": "^1.0.1",
+                "object-inspect": "^1.11.0",
+                "object-keys": "^1.1.1",
+                "object.assign": "^4.1.2",
+                "string.prototype.trimend": "^1.0.4",
+                "string.prototype.trimstart": "^1.0.4",
+                "unbox-primitive": "^1.0.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/which-typed-array/node_modules/has-symbols": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+            "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/which-typed-array/node_modules/is-callable": {
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz",
+            "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==",
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/which-typed-array/node_modules/is-regex": {
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+            "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "has-tostringtag": "^1.0.0"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/which-typed-array/node_modules/object-inspect": {
+            "version": "1.12.0",
+            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
+            "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==",
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/which-typed-array/node_modules/object.assign": {
+            "version": "4.1.2",
+            "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+            "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+            "dependencies": {
+                "call-bind": "^1.0.0",
+                "define-properties": "^1.1.3",
+                "has-symbols": "^1.0.1",
+                "object-keys": "^1.1.1"
+            },
+            "engines": {
+                "node": ">= 0.4"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/which-typed-array/node_modules/string.prototype.trimend": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
+            "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/which-typed-array/node_modules/string.prototype.trimstart": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
+            "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
+            "dependencies": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/ljharb"
+            }
+        },
+        "node_modules/wide-align": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+            "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+            "dependencies": {
+                "string-width": "^1.0.2 || 2"
+            }
+        },
+        "node_modules/widest-line": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz",
+            "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==",
+            "dependencies": {
+                "string-width": "^2.1.1"
+            },
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/wikijs": {
+            "version": "6.3.3",
+            "resolved": "https://registry.npmjs.org/wikijs/-/wikijs-6.3.3.tgz",
+            "integrity": "sha512-pYVaUuJyTd7VO2aNxbdN341zgio+QuGdc6HC3jyQqakyaLJdOD8Shj+bs7lnZnhv/wml2u+C+OME9YUkck06Cg==",
+            "dependencies": {
+                "cross-fetch": "^3.0.2",
+                "hyntax": "^1.1.9",
+                "infobox-parser": "3.6.2"
+            },
+            "engines": {
+                "node": ">=0.10.4"
+            },
+            "funding": {
+                "type": "individual",
+                "url": "https://www.buymeacoffee.com/2tmRKi9"
+            }
+        },
+        "node_modules/wildcard": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
+            "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw=="
+        },
+        "node_modules/window-size": {
+            "version": "0.1.0",
+            "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
+            "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=",
+            "engines": {
+                "node": ">= 0.8.0"
+            }
+        },
+        "node_modules/with": {
+            "version": "5.1.1",
+            "resolved": "https://registry.npmjs.org/with/-/with-5.1.1.tgz",
+            "integrity": "sha1-+k2qktrzLE6pTtRTyB8EaGtXXf4=",
+            "dependencies": {
+                "acorn": "^3.1.0",
+                "acorn-globals": "^3.0.0"
+            }
+        },
+        "node_modules/word-wrap": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+            "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
+        "node_modules/words-to-numbers": {
+            "version": "1.5.1",
+            "resolved": "https://registry.npmjs.org/words-to-numbers/-/words-to-numbers-1.5.1.tgz",
+            "integrity": "sha512-uvz7zSCKmmA7o5f5zp4Z5l24RQhy6HSNu10URhNxQWv1I82RsFaZX3qD07RLFUMJsCV38oAuaca13AvhO+9yGw==",
+            "dependencies": {
+                "babel-runtime": "6.x.x",
+                "clj-fuzzy": "^0.3.2",
+                "its-set": "^1.1.5"
+            }
+        },
+        "node_modules/wordwrap": {
+            "version": "0.0.2",
+            "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
+            "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
+            "engines": {
+                "node": ">=0.4.0"
+            }
+        },
+        "node_modules/wordwrapjs": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz",
+            "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==",
+            "dependencies": {
+                "reduce-flatten": "^1.0.1",
+                "typical": "^2.6.1"
+            },
+            "engines": {
+                "node": ">=4.0.0"
+            }
+        },
+        "node_modules/worker-rpc": {
+            "version": "0.1.1",
+            "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz",
+            "integrity": "sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg==",
+            "dev": true,
+            "dependencies": {
+                "microevent.ts": "~0.1.1"
+            }
+        },
+        "node_modules/wrap-ansi": {
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+            "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+            "dependencies": {
+                "ansi-styles": "^3.2.0",
+                "string-width": "^3.0.0",
+                "strip-ansi": "^5.0.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/wrap-ansi/node_modules/ansi-regex": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+            "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/wrap-ansi/node_modules/string-width": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+            "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+            "dependencies": {
+                "emoji-regex": "^7.0.1",
+                "is-fullwidth-code-point": "^2.0.0",
+                "strip-ansi": "^5.1.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/wrap-ansi/node_modules/strip-ansi": {
+            "version": "5.2.0",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+            "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+            "dependencies": {
+                "ansi-regex": "^4.1.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/wrappy": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+            "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+        },
+        "node_modules/write-file-atomic": {
+            "version": "2.4.3",
+            "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz",
+            "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==",
+            "dependencies": {
+                "graceful-fs": "^4.1.11",
+                "imurmurhash": "^0.1.4",
+                "signal-exit": "^3.0.2"
+            }
+        },
+        "node_modules/ws": {
+            "version": "7.2.5",
+            "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz",
+            "integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA==",
+            "engines": {
+                "node": ">=8.3.0"
+            },
+            "peerDependencies": {
+                "bufferutil": "^4.0.1",
+                "utf-8-validate": "^5.0.2"
+            },
+            "peerDependenciesMeta": {
+                "bufferutil": {
+                    "optional": true
+                },
+                "utf-8-validate": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/xdg-basedir": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz",
+            "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=",
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/xml-name-validator": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
+            "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
+            "dev": true
+        },
+        "node_modules/xmlbuilder": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.0.0.tgz",
+            "integrity": "sha1-mLj2UcowqmJANvEn0RzGbce5B6M=",
+            "dependencies": {
+                "lodash": "^3.5.0"
+            },
+            "engines": {
+                "node": ">=0.8.0"
+            }
+        },
+        "node_modules/xmlbuilder/node_modules/lodash": {
+            "version": "3.10.1",
+            "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
+            "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y="
+        },
+        "node_modules/xmlchars": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+            "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+            "dev": true
+        },
+        "node_modules/xmldom": {
+            "version": "0.1.31",
+            "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.31.tgz",
+            "integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==",
+            "deprecated": "Deprecated due to CVE-2021-21366 resolved in 0.5.0",
+            "engines": {
+                "node": ">=0.1"
+            }
+        },
+        "node_modules/xmlhttprequest-ssl": {
+            "version": "1.6.3",
+            "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz",
+            "integrity": "sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q==",
+            "engines": {
+                "node": ">=0.4.0"
+            }
+        },
+        "node_modules/xoauth2": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/xoauth2/-/xoauth2-1.2.0.tgz",
+            "integrity": "sha1-8u76wRRyyXHqO8RuVU60sSMhRuU="
+        },
+        "node_modules/xregexp": {
+            "version": "4.4.1",
+            "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.4.1.tgz",
+            "integrity": "sha512-2u9HwfadaJaY9zHtRRnH6BY6CQVNQKkYm3oLtC9gJXXzfsbACg5X5e4EZZGVAH+YIfa+QA9lsFQTTe3HURF3ag==",
+            "dependencies": {
+                "@babel/runtime-corejs3": "^7.12.1"
+            }
+        },
+        "node_modules/xtend": {
+            "version": "4.0.2",
+            "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+            "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.4"
+            }
+        },
+        "node_modules/y18n": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+            "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
+        },
+        "node_modules/yallist": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+            "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
+        },
+        "node_modules/yaml": {
+            "version": "1.10.2",
+            "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+            "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/yargs": {
+            "version": "13.3.2",
+            "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+            "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+            "dependencies": {
+                "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.2"
+            }
+        },
+        "node_modules/yargs-parser": {
+            "version": "13.1.2",
+            "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+            "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+            "dependencies": {
+                "camelcase": "^5.0.0",
+                "decamelize": "^1.2.0"
+            }
+        },
+        "node_modules/yargs-unparser": {
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz",
+            "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==",
+            "dependencies": {
+                "flat": "^4.1.0",
+                "lodash": "^4.17.15",
+                "yargs": "^13.3.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/yargs/node_modules/ansi-regex": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+            "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/yargs/node_modules/string-width": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+            "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+            "dependencies": {
+                "emoji-regex": "^7.0.1",
+                "is-fullwidth-code-point": "^2.0.0",
+                "strip-ansi": "^5.1.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/yargs/node_modules/strip-ansi": {
+            "version": "5.2.0",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+            "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+            "dependencies": {
+                "ansi-regex": "^4.1.0"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/yauzl": {
+            "version": "2.10.0",
+            "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+            "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
+            "dependencies": {
+                "buffer-crc32": "~0.2.3",
+                "fd-slicer": "~1.1.0"
+            }
+        },
+        "node_modules/yeast": {
+            "version": "0.1.2",
+            "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
+            "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
+        },
+        "node_modules/yn": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz",
+            "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=",
+            "dev": true,
+            "engines": {
+                "node": ">=4"
+            }
+        },
+        "node_modules/zip-stream": {
+            "version": "2.1.3",
+            "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-2.1.3.tgz",
+            "integrity": "sha512-EkXc2JGcKhO5N5aZ7TmuNo45budRaFGHOmz24wtJR7znbNqDPmdZtUauKX6et8KAVseAMBOyWJqEpXcHTBsh7Q==",
+            "dependencies": {
+                "archiver-utils": "^2.1.0",
+                "compress-commons": "^2.1.1",
+                "readable-stream": "^3.4.0"
+            },
+            "engines": {
+                "node": ">= 6"
+            }
+        },
+        "node_modules/zustand": {
+            "version": "3.7.1",
+            "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.1.tgz",
+            "integrity": "sha512-wHBCZlKj+bg03/hP+Tzv24YhnqqP8MCeN9ECPDXoF01062SIbnfl3j9O0znkDw1lNTY0a8WN3F///a0UhhaEqg==",
+            "engines": {
+                "node": ">=12.7.0"
+            },
+            "peerDependencies": {
+                "react": ">=16.8"
+            },
+            "peerDependenciesMeta": {
+                "react": {
+                    "optional": true
+                }
+            }
+        }
+    },
     "dependencies": {
         "@babel/code-frame": {
             "version": "7.8.3",
@@ -108,6 +25769,11 @@
                 }
             }
         },
+        "@babel/helper-plugin-utils": {
+            "version": "7.16.7",
+            "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz",
+            "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA=="
+        },
         "@babel/helper-split-export-declaration": {
             "version": "7.8.3",
             "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz",
@@ -136,6 +25802,14 @@
             "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.6.tgz",
             "integrity": "sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q=="
         },
+        "@babel/plugin-syntax-jsx": {
+            "version": "7.16.7",
+            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz",
+            "integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==",
+            "requires": {
+                "@babel/helper-plugin-utils": "^7.16.7"
+            }
+        },
         "@babel/runtime": {
             "version": "7.9.2",
             "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
@@ -145,11 +25819,11 @@
             }
         },
         "@babel/runtime-corejs3": {
-            "version": "7.9.2",
-            "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.9.2.tgz",
-            "integrity": "sha512-HHxmgxbIzOfFlZ+tdeRKtaxWOMUoCG5Mu3wKeUmOxjYrwb3AAHgnmtCUbPPK11/raIWLIBK250t8E2BPO0p7jA==",
+            "version": "7.17.2",
+            "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.2.tgz",
+            "integrity": "sha512-NcKtr2epxfIrNM4VOmPKO46TvDMCBhgi2CrSHaEarrz+Plk2K5r9QemmOFTGpZaoKnWoGH5MO+CzeRsih/Fcgg==",
             "requires": {
-                "core-js-pure": "^3.0.0",
+                "core-js-pure": "^3.20.2",
                 "regenerator-runtime": "^0.13.4"
             }
         },
@@ -209,6 +25883,99 @@
                 "to-fast-properties": "^2.0.0"
             }
         },
+        "@discoveryjs/json-ext": {
+            "version": "0.5.6",
+            "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz",
+            "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA=="
+        },
+        "@emotion/babel-plugin": {
+            "version": "11.7.2",
+            "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.7.2.tgz",
+            "integrity": "sha512-6mGSCWi9UzXut/ZAN6lGFu33wGR3SJisNl3c0tvlmb8XChH1b2SUvxvnOh7hvLpqyRdHHU9AiazV3Cwbk5SXKQ==",
+            "requires": {
+                "@babel/helper-module-imports": "^7.12.13",
+                "@babel/plugin-syntax-jsx": "^7.12.13",
+                "@babel/runtime": "^7.13.10",
+                "@emotion/hash": "^0.8.0",
+                "@emotion/memoize": "^0.7.5",
+                "@emotion/serialize": "^1.0.2",
+                "babel-plugin-macros": "^2.6.1",
+                "convert-source-map": "^1.5.0",
+                "escape-string-regexp": "^4.0.0",
+                "find-root": "^1.1.0",
+                "source-map": "^0.5.7",
+                "stylis": "4.0.13"
+            },
+            "dependencies": {
+                "@babel/helper-module-imports": {
+                    "version": "7.16.7",
+                    "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz",
+                    "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==",
+                    "requires": {
+                        "@babel/types": "^7.16.7"
+                    }
+                },
+                "@babel/helper-validator-identifier": {
+                    "version": "7.16.7",
+                    "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz",
+                    "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw=="
+                },
+                "@babel/runtime": {
+                    "version": "7.17.2",
+                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
+                    "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
+                    "requires": {
+                        "regenerator-runtime": "^0.13.4"
+                    }
+                },
+                "@babel/types": {
+                    "version": "7.17.0",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz",
+                    "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==",
+                    "requires": {
+                        "@babel/helper-validator-identifier": "^7.16.7",
+                        "to-fast-properties": "^2.0.0"
+                    }
+                },
+                "@emotion/memoize": {
+                    "version": "0.7.5",
+                    "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz",
+                    "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ=="
+                },
+                "@emotion/serialize": {
+                    "version": "1.0.2",
+                    "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz",
+                    "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==",
+                    "requires": {
+                        "@emotion/hash": "^0.8.0",
+                        "@emotion/memoize": "^0.7.4",
+                        "@emotion/unitless": "^0.7.5",
+                        "@emotion/utils": "^1.0.0",
+                        "csstype": "^3.0.2"
+                    }
+                },
+                "@emotion/utils": {
+                    "version": "1.1.0",
+                    "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz",
+                    "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
+                },
+                "csstype": {
+                    "version": "3.0.10",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
+                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
+                },
+                "escape-string-regexp": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+                    "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
+                },
+                "stylis": {
+                    "version": "4.0.13",
+                    "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
+                    "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
+                }
+            }
+        },
         "@emotion/cache": {
             "version": "10.0.29",
             "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz",
@@ -221,9 +25988,9 @@
             }
         },
         "@emotion/core": {
-            "version": "10.0.28",
-            "resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.0.28.tgz",
-            "integrity": "sha512-pH8UueKYO5jgg0Iq+AmCLxBsvuGtvlmiDCOuv8fGNYn3cowFpLN98L8zO56U0H1PjDIyAlXymgL3Wu7u7v6hbA==",
+            "version": "10.3.1",
+            "resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.3.1.tgz",
+            "integrity": "sha512-447aUEjPIm0MnE6QYIaFz9VQOHSXf4Iu6EWOIqq11EAPqinkSZmfymPTmlOE3QjLv846lH4JVZBUOtwGbuQoww==",
             "requires": {
                 "@babel/runtime": "^7.5.5",
                 "@emotion/cache": "^10.0.27",
@@ -231,16 +25998,76 @@
                 "@emotion/serialize": "^0.11.15",
                 "@emotion/sheet": "0.9.4",
                 "@emotion/utils": "0.11.3"
+            },
+            "dependencies": {
+                "@emotion/css": {
+                    "version": "10.0.27",
+                    "resolved": "https://registry.npmjs.org/@emotion/css/-/css-10.0.27.tgz",
+                    "integrity": "sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==",
+                    "requires": {
+                        "@emotion/serialize": "^0.11.15",
+                        "@emotion/utils": "0.11.3",
+                        "babel-plugin-emotion": "^10.0.27"
+                    }
+                }
             }
         },
         "@emotion/css": {
-            "version": "10.0.27",
-            "resolved": "https://registry.npmjs.org/@emotion/css/-/css-10.0.27.tgz",
-            "integrity": "sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==",
-            "requires": {
-                "@emotion/serialize": "^0.11.15",
-                "@emotion/utils": "0.11.3",
-                "babel-plugin-emotion": "^10.0.27"
+            "version": "11.7.1",
+            "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.7.1.tgz",
+            "integrity": "sha512-RUUgPlMZunlc7SE5A6Hg+VWRzb2cU6O9xlV78KCFgcnl25s7Qz/20oQg71iKudpLqk7xj0vhbJlwcJJMT0BOZg==",
+            "requires": {
+                "@emotion/babel-plugin": "^11.7.1",
+                "@emotion/cache": "^11.7.1",
+                "@emotion/serialize": "^1.0.0",
+                "@emotion/sheet": "^1.0.3",
+                "@emotion/utils": "^1.0.0"
+            },
+            "dependencies": {
+                "@emotion/cache": {
+                    "version": "11.7.1",
+                    "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz",
+                    "integrity": "sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==",
+                    "requires": {
+                        "@emotion/memoize": "^0.7.4",
+                        "@emotion/sheet": "^1.1.0",
+                        "@emotion/utils": "^1.0.0",
+                        "@emotion/weak-memoize": "^0.2.5",
+                        "stylis": "4.0.13"
+                    }
+                },
+                "@emotion/serialize": {
+                    "version": "1.0.2",
+                    "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz",
+                    "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==",
+                    "requires": {
+                        "@emotion/hash": "^0.8.0",
+                        "@emotion/memoize": "^0.7.4",
+                        "@emotion/unitless": "^0.7.5",
+                        "@emotion/utils": "^1.0.0",
+                        "csstype": "^3.0.2"
+                    }
+                },
+                "@emotion/sheet": {
+                    "version": "1.1.0",
+                    "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz",
+                    "integrity": "sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g=="
+                },
+                "@emotion/utils": {
+                    "version": "1.1.0",
+                    "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz",
+                    "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
+                },
+                "csstype": {
+                    "version": "3.0.10",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
+                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
+                },
+                "stylis": {
+                    "version": "4.0.13",
+                    "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
+                    "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
+                }
             }
         },
         "@emotion/hash": {
@@ -261,6 +26088,75 @@
             "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz",
             "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw=="
         },
+        "@emotion/react": {
+            "version": "11.8.1",
+            "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.8.1.tgz",
+            "integrity": "sha512-XGaie4nRxmtP1BZYBXqC5JGqMYF2KRKKI7vjqNvQxyRpekVAZhb6QqrElmZCAYXH1L90lAelADSVZC4PFsrJ8Q==",
+            "requires": {
+                "@babel/runtime": "^7.13.10",
+                "@emotion/babel-plugin": "^11.7.1",
+                "@emotion/cache": "^11.7.1",
+                "@emotion/serialize": "^1.0.2",
+                "@emotion/sheet": "^1.1.0",
+                "@emotion/utils": "^1.1.0",
+                "@emotion/weak-memoize": "^0.2.5",
+                "hoist-non-react-statics": "^3.3.1"
+            },
+            "dependencies": {
+                "@babel/runtime": {
+                    "version": "7.17.2",
+                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
+                    "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
+                    "requires": {
+                        "regenerator-runtime": "^0.13.4"
+                    }
+                },
+                "@emotion/cache": {
+                    "version": "11.7.1",
+                    "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz",
+                    "integrity": "sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==",
+                    "requires": {
+                        "@emotion/memoize": "^0.7.4",
+                        "@emotion/sheet": "^1.1.0",
+                        "@emotion/utils": "^1.0.0",
+                        "@emotion/weak-memoize": "^0.2.5",
+                        "stylis": "4.0.13"
+                    }
+                },
+                "@emotion/serialize": {
+                    "version": "1.0.2",
+                    "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz",
+                    "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==",
+                    "requires": {
+                        "@emotion/hash": "^0.8.0",
+                        "@emotion/memoize": "^0.7.4",
+                        "@emotion/unitless": "^0.7.5",
+                        "@emotion/utils": "^1.0.0",
+                        "csstype": "^3.0.2"
+                    }
+                },
+                "@emotion/sheet": {
+                    "version": "1.1.0",
+                    "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz",
+                    "integrity": "sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g=="
+                },
+                "@emotion/utils": {
+                    "version": "1.1.0",
+                    "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz",
+                    "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
+                },
+                "csstype": {
+                    "version": "3.0.10",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
+                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
+                },
+                "stylis": {
+                    "version": "4.0.13",
+                    "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
+                    "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
+                }
+            }
+        },
         "@emotion/serialize": {
             "version": "0.11.16",
             "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.16.tgz",
@@ -279,23 +26175,55 @@
             "integrity": "sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA=="
         },
         "@emotion/styled": {
-            "version": "10.0.27",
-            "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-10.0.27.tgz",
-            "integrity": "sha512-iK/8Sh7+NLJzyp9a5+vIQIXTYxfT4yB/OJbjzQanB2RZpvmzBQOHZWhpAMZWYEKRNNbsD6WfBw5sVWkb6WzS/Q==",
-            "requires": {
-                "@emotion/styled-base": "^10.0.27",
-                "babel-plugin-emotion": "^10.0.27"
-            }
-        },
-        "@emotion/styled-base": {
-            "version": "10.0.31",
-            "resolved": "https://registry.npmjs.org/@emotion/styled-base/-/styled-base-10.0.31.tgz",
-            "integrity": "sha512-wTOE1NcXmqMWlyrtwdkqg87Mu6Rj1MaukEoEmEkHirO5IoHDJ8LgCQL4MjJODgxWxXibGR3opGp1p7YvkNEdXQ==",
+            "version": "11.8.1",
+            "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.8.1.tgz",
+            "integrity": "sha512-OghEVAYBZMpEquHZwuelXcRjRJQOVayvbmNR0zr174NHdmMgrNkLC6TljKC5h9lZLkN5WGrdUcrKlOJ4phhoTQ==",
             "requires": {
-                "@babel/runtime": "^7.5.5",
-                "@emotion/is-prop-valid": "0.8.8",
-                "@emotion/serialize": "^0.11.15",
-                "@emotion/utils": "0.11.3"
+                "@babel/runtime": "^7.13.10",
+                "@emotion/babel-plugin": "^11.7.1",
+                "@emotion/is-prop-valid": "^1.1.2",
+                "@emotion/serialize": "^1.0.2",
+                "@emotion/utils": "^1.1.0"
+            },
+            "dependencies": {
+                "@babel/runtime": {
+                    "version": "7.17.2",
+                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
+                    "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
+                    "requires": {
+                        "regenerator-runtime": "^0.13.4"
+                    }
+                },
+                "@emotion/is-prop-valid": {
+                    "version": "1.1.2",
+                    "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.2.tgz",
+                    "integrity": "sha512-3QnhqeL+WW88YjYbQL5gUIkthuMw7a0NGbZ7wfFVk2kg/CK5w8w5FFa0RzWjyY1+sujN0NWbtSHH6OJmWHtJpQ==",
+                    "requires": {
+                        "@emotion/memoize": "^0.7.4"
+                    }
+                },
+                "@emotion/serialize": {
+                    "version": "1.0.2",
+                    "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz",
+                    "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==",
+                    "requires": {
+                        "@emotion/hash": "^0.8.0",
+                        "@emotion/memoize": "^0.7.4",
+                        "@emotion/unitless": "^0.7.5",
+                        "@emotion/utils": "^1.0.0",
+                        "csstype": "^3.0.2"
+                    }
+                },
+                "@emotion/utils": {
+                    "version": "1.1.0",
+                    "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz",
+                    "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
+                },
+                "csstype": {
+                    "version": "3.0.10",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
+                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
+                }
             }
         },
         "@emotion/stylis": {
@@ -319,69 +26247,81 @@
             "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA=="
         },
         "@fortawesome/fontawesome-common-types": {
-            "version": "0.2.29",
-            "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.29.tgz",
-            "integrity": "sha512-cY+QfDTbZ7XVxzx7jxbC98Oxr/zc7R2QpTLqTxqlfyXDrAJjzi/xUIqAUsygELB62JIrbsWxtSRhayKFkGI7MA=="
+            "version": "0.3.0",
+            "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.3.0.tgz",
+            "integrity": "sha512-CA3MAZBTxVsF6SkfkHXDerkhcQs0QPofy43eFdbWJJkZiq3SfiaH1msOkac59rQaqto5EqWnASboY1dBuKen5w=="
         },
         "@fortawesome/fontawesome-svg-core": {
-            "version": "1.2.35",
-            "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.35.tgz",
-            "integrity": "sha512-uLEXifXIL7hnh2sNZQrIJWNol7cTVIzwI+4qcBIq9QWaZqUblm0IDrtSqbNg+3SQf8SMGHkiSigD++rHmCHjBg==",
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.3.0.tgz",
+            "integrity": "sha512-UIL6crBWhjTNQcONt96ExjUnKt1D68foe3xjEensLDclqQ6YagwCRYVQdrp/hW0ALRp/5Fv/VKw+MqTUWYYvPg==",
             "requires": {
-                "@fortawesome/fontawesome-common-types": "^0.2.35"
-            },
-            "dependencies": {
-                "@fortawesome/fontawesome-common-types": {
-                    "version": "0.2.35",
-                    "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.35.tgz",
-                    "integrity": "sha512-IHUfxSEDS9dDGqYwIW7wTN6tn/O8E0n5PcAHz9cAaBoZw6UpG20IG/YM3NNLaGPwPqgjBAFjIURzqoQs3rrtuw=="
-                }
+                "@fortawesome/fontawesome-common-types": "^0.3.0"
             }
         },
         "@fortawesome/free-brands-svg-icons": {
-            "version": "5.14.0",
-            "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.14.0.tgz",
-            "integrity": "sha512-WsqPFTvJFI7MYkcy0jeFE2zY+blC4OrnB9MJOcn1NxRXT/sSfEEhrI7CwzIkiYajLiVDBKWeErYOvpsMeodmCQ==",
+            "version": "5.15.4",
+            "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.15.4.tgz",
+            "integrity": "sha512-f1witbwycL9cTENJegcmcZRYyawAFbm8+c6IirLmwbbpqz46wyjbQYLuxOc7weXFXfB7QR8/Vd2u5R3q6JYD9g==",
             "requires": {
-                "@fortawesome/fontawesome-common-types": "^0.2.30"
+                "@fortawesome/fontawesome-common-types": "^0.2.36"
             },
             "dependencies": {
                 "@fortawesome/fontawesome-common-types": {
-                    "version": "0.2.30",
-                    "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.30.tgz",
-                    "integrity": "sha512-TsRwpTuKwFNiPhk1UfKgw7zNPeV5RhNp2Uw3pws+9gDAkPGKrtjR1y2lI3SYn7+YzyfuNknflpBA1LRKjt7hMg=="
+                    "version": "0.2.36",
+                    "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz",
+                    "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg=="
                 }
             }
         },
         "@fortawesome/free-regular-svg-icons": {
-            "version": "5.13.1",
-            "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.13.1.tgz",
-            "integrity": "sha512-sSeaqqmv2ovA5LKcrbh3VnEDZHVhaxijWKm4R0AdT0eG21pgxNsJbStD8lW9z6bgSuWXRNHhbhOmARuRCLS8tw==",
+            "version": "5.15.4",
+            "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.4.tgz",
+            "integrity": "sha512-9VNNnU3CXHy9XednJ3wzQp6SwNwT3XaM26oS4Rp391GsxVYA+0oDR2J194YCIWf7jNRCYKjUCOduxdceLrx+xw==",
             "requires": {
-                "@fortawesome/fontawesome-common-types": "^0.2.29"
+                "@fortawesome/fontawesome-common-types": "^0.2.36"
+            },
+            "dependencies": {
+                "@fortawesome/fontawesome-common-types": {
+                    "version": "0.2.36",
+                    "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz",
+                    "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg=="
+                }
             }
         },
         "@fortawesome/free-solid-svg-icons": {
-            "version": "5.15.3",
-            "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.3.tgz",
-            "integrity": "sha512-XPeeu1IlGYqz4VWGRAT5ukNMd4VHUEEJ7ysZ7pSSgaEtNvSo+FLurybGJVmiqkQdK50OkSja2bfZXOeyMGRD8Q==",
+            "version": "5.15.4",
+            "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz",
+            "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==",
             "requires": {
-                "@fortawesome/fontawesome-common-types": "^0.2.35"
+                "@fortawesome/fontawesome-common-types": "^0.2.36"
             },
             "dependencies": {
                 "@fortawesome/fontawesome-common-types": {
-                    "version": "0.2.35",
-                    "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.35.tgz",
-                    "integrity": "sha512-IHUfxSEDS9dDGqYwIW7wTN6tn/O8E0n5PcAHz9cAaBoZw6UpG20IG/YM3NNLaGPwPqgjBAFjIURzqoQs3rrtuw=="
+                    "version": "0.2.36",
+                    "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz",
+                    "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg=="
                 }
             }
         },
         "@fortawesome/react-fontawesome": {
-            "version": "0.1.14",
-            "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.14.tgz",
-            "integrity": "sha512-4wqNb0gRLVaBm/h+lGe8UfPPivcbuJ6ecI4hIgW0LjI7kzpYB9FkN0L9apbVzg+lsBdcTf0AlBtODjcSX5mmKA==",
+            "version": "0.1.17",
+            "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.17.tgz",
+            "integrity": "sha512-dX43Z5IvMaW7fwzU8farosYjKNGfRb2HB/DgjVBHeJZ/NSnuuaujPPx0YOdcAq+n3mqn70tyCde2HM1mqbhiuw==",
             "requires": {
-                "prop-types": "^15.7.2"
+                "prop-types": "^15.8.1"
+            },
+            "dependencies": {
+                "prop-types": {
+                    "version": "15.8.1",
+                    "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+                    "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+                    "requires": {
+                        "loose-envify": "^1.4.0",
+                        "object-assign": "^4.1.1",
+                        "react-is": "^16.13.1"
+                    }
+                }
             }
         },
         "@googlemaps/js-api-loader": {
@@ -418,11 +26358,11 @@
             }
         },
         "@hig/flyout": {
-            "version": "1.2.1",
-            "resolved": "https://registry.npmjs.org/@hig/flyout/-/flyout-1.2.1.tgz",
-            "integrity": "sha512-oNybmhStyMmEC07DPsjMZg4Dgm65X7Jcp2nDFR9zZghVof/1C3WJtJvdouVgqx7BJw2IedVj8CUGoPuoll/0IQ==",
+            "version": "1.3.1",
+            "resolved": "https://registry.npmjs.org/@hig/flyout/-/flyout-1.3.1.tgz",
+            "integrity": "sha512-dj69PnHtvXQ+IbbFv167ObHCuSVVv5ycMdFIOPMY+SCqSCssYiu1PkEI6eQxOePAb3MYhF4u7oi3Vy2XmxfkgA==",
             "requires": {
-                "@hig/utils": "^0.4.0",
+                "@hig/utils": "^0.4.1",
                 "emotion": "^10.0.0",
                 "prop-types": "^15.7.1",
                 "react-transition-group": "^2.3.1"
@@ -438,69 +26378,295 @@
             }
         },
         "@hig/theme-data": {
-            "version": "2.16.1",
-            "resolved": "https://registry.npmjs.org/@hig/theme-data/-/theme-data-2.16.1.tgz",
-            "integrity": "sha512-3nBnzRqYyc6ejoCgA+zBjQGqe6LAXlcemTs0257E5bWu/JyZe8YyOLN+psq2zyjF+FUk35p38wWNPyaOcPnhvw==",
+            "version": "2.23.1",
+            "resolved": "https://registry.npmjs.org/@hig/theme-data/-/theme-data-2.23.1.tgz",
+            "integrity": "sha512-Yca1iCIs8U1dpkLW1VNiKOJW9fCJdb70f1dc61+R+HO1QJ8jMBu5eOpmQE1NKotJmcazhsXpo6xC11yV1crE4w==",
             "requires": {
                 "tinycolor2": "^1.4.1"
             }
         },
         "@hig/utils": {
-            "version": "0.4.0",
-            "resolved": "https://registry.npmjs.org/@hig/utils/-/utils-0.4.0.tgz",
-            "integrity": "sha512-EQnMGZKdPh9UJaBUKLKXp92sSoCo+PTpgrGNd8q+71uRFdD0udMu/+yeVekTEtNOJcCk1gnKfyg1rRvIbTcpRw==",
+            "version": "0.4.1",
+            "resolved": "https://registry.npmjs.org/@hig/utils/-/utils-0.4.1.tgz",
+            "integrity": "sha512-yx8HF5dLNo/WwwF5dR9P1pZu0tKhCuItrqbGbg52HpLUsptODBs/1ATT76cBaAJ7dSBCeNOKKiE4WKSrsA8bdQ==",
             "requires": {
                 "emotion": "^10.0.0",
                 "lodash.memoize": "^4.1.2"
             }
         },
+        "@hypnosphi/create-react-context": {
+            "version": "0.3.1",
+            "resolved": "https://registry.npmjs.org/@hypnosphi/create-react-context/-/create-react-context-0.3.1.tgz",
+            "integrity": "sha512-V1klUed202XahrWJLLOT3EXNeCpFHCcJntdFGI15ntCwau+jfT386w7OFTMaCqOgXUH1fa0w/I1oZs+i/Rfr0A==",
+            "requires": {
+                "gud": "^1.0.0",
+                "warning": "^4.0.3"
+            },
+            "dependencies": {
+                "warning": {
+                    "version": "4.0.3",
+                    "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+                    "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+                    "requires": {
+                        "loose-envify": "^1.0.0"
+                    }
+                }
+            }
+        },
         "@icons/material": {
             "version": "0.2.4",
             "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz",
-            "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw=="
+            "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==",
+            "requires": {}
         },
         "@log4js-node/log4js-api": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/@log4js-node/log4js-api/-/log4js-api-1.0.2.tgz",
             "integrity": "sha512-6SJfx949YEWooh/CUPpJ+F491y4BYJmknz4hUN1+RHvKoUEynKbRmhnwbk/VLmh4OthLLDNCyWXfbh4DG1cTXA=="
         },
+        "@mapbox/node-pre-gyp": {
+            "version": "1.0.8",
+            "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz",
+            "integrity": "sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg==",
+            "requires": {
+                "detect-libc": "^1.0.3",
+                "https-proxy-agent": "^5.0.0",
+                "make-dir": "^3.1.0",
+                "node-fetch": "^2.6.5",
+                "nopt": "^5.0.0",
+                "npmlog": "^5.0.1",
+                "rimraf": "^3.0.2",
+                "semver": "^7.3.5",
+                "tar": "^6.1.11"
+            },
+            "dependencies": {
+                "ansi-regex": {
+                    "version": "5.0.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                    "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+                },
+                "are-we-there-yet": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
+                    "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
+                    "requires": {
+                        "delegates": "^1.0.0",
+                        "readable-stream": "^3.6.0"
+                    }
+                },
+                "chownr": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+                    "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
+                },
+                "emoji-regex": {
+                    "version": "8.0.0",
+                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+                    "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+                },
+                "fs-minipass": {
+                    "version": "2.1.0",
+                    "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+                    "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+                    "requires": {
+                        "minipass": "^3.0.0"
+                    }
+                },
+                "gauge": {
+                    "version": "3.0.2",
+                    "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
+                    "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
+                    "requires": {
+                        "aproba": "^1.0.3 || ^2.0.0",
+                        "color-support": "^1.1.2",
+                        "console-control-strings": "^1.0.0",
+                        "has-unicode": "^2.0.1",
+                        "object-assign": "^4.1.1",
+                        "signal-exit": "^3.0.0",
+                        "string-width": "^4.2.3",
+                        "strip-ansi": "^6.0.1",
+                        "wide-align": "^1.1.2"
+                    }
+                },
+                "is-fullwidth-code-point": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+                    "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+                },
+                "lru-cache": {
+                    "version": "6.0.0",
+                    "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+                    "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+                    "requires": {
+                        "yallist": "^4.0.0"
+                    }
+                },
+                "make-dir": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+                    "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+                    "requires": {
+                        "semver": "^6.0.0"
+                    },
+                    "dependencies": {
+                        "semver": {
+                            "version": "6.3.0",
+                            "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+                            "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
+                        }
+                    }
+                },
+                "minipass": {
+                    "version": "3.1.6",
+                    "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz",
+                    "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==",
+                    "requires": {
+                        "yallist": "^4.0.0"
+                    }
+                },
+                "minizlib": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+                    "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+                    "requires": {
+                        "minipass": "^3.0.0",
+                        "yallist": "^4.0.0"
+                    }
+                },
+                "mkdirp": {
+                    "version": "1.0.4",
+                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+                    "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
+                },
+                "node-fetch": {
+                    "version": "2.6.7",
+                    "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+                    "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+                    "requires": {
+                        "whatwg-url": "^5.0.0"
+                    }
+                },
+                "nopt": {
+                    "version": "5.0.0",
+                    "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
+                    "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
+                    "requires": {
+                        "abbrev": "1"
+                    }
+                },
+                "npmlog": {
+                    "version": "5.0.1",
+                    "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
+                    "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
+                    "requires": {
+                        "are-we-there-yet": "^2.0.0",
+                        "console-control-strings": "^1.1.0",
+                        "gauge": "^3.0.0",
+                        "set-blocking": "^2.0.0"
+                    }
+                },
+                "semver": {
+                    "version": "7.3.5",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+                    "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+                    "requires": {
+                        "lru-cache": "^6.0.0"
+                    }
+                },
+                "string-width": {
+                    "version": "4.2.3",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+                    "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+                    "requires": {
+                        "emoji-regex": "^8.0.0",
+                        "is-fullwidth-code-point": "^3.0.0",
+                        "strip-ansi": "^6.0.1"
+                    }
+                },
+                "strip-ansi": {
+                    "version": "6.0.1",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                    "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                    "requires": {
+                        "ansi-regex": "^5.0.1"
+                    }
+                },
+                "tar": {
+                    "version": "6.1.11",
+                    "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
+                    "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
+                    "requires": {
+                        "chownr": "^2.0.0",
+                        "fs-minipass": "^2.0.0",
+                        "minipass": "^3.0.0",
+                        "minizlib": "^2.1.1",
+                        "mkdirp": "^1.0.3",
+                        "yallist": "^4.0.0"
+                    }
+                },
+                "tr46": {
+                    "version": "0.0.3",
+                    "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+                    "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
+                },
+                "webidl-conversions": {
+                    "version": "3.0.1",
+                    "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+                    "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
+                },
+                "whatwg-url": {
+                    "version": "5.0.0",
+                    "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+                    "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
+                    "requires": {
+                        "tr46": "~0.0.3",
+                        "webidl-conversions": "^3.0.0"
+                    }
+                },
+                "yallist": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+                    "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+                }
+            }
+        },
         "@material-ui/core": {
-            "version": "4.11.0",
-            "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.0.tgz",
-            "integrity": "sha512-bYo9uIub8wGhZySHqLQ833zi4ZML+XCBE1XwJ8EuUVSpTWWG57Pm+YugQToJNFsEyiKFhPh8DPD0bgupz8n01g==",
+            "version": "4.12.3",
+            "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.3.tgz",
+            "integrity": "sha512-sdpgI/PL56QVsEJldwEe4FFaFTLUqN+rd7sSZiRCdx2E/C7z5yK0y/khAWVBH24tXwto7I1hCzNWfJGZIYJKnw==",
             "requires": {
                 "@babel/runtime": "^7.4.4",
-                "@material-ui/styles": "^4.10.0",
-                "@material-ui/system": "^4.9.14",
-                "@material-ui/types": "^5.1.0",
-                "@material-ui/utils": "^4.10.2",
+                "@material-ui/styles": "^4.11.4",
+                "@material-ui/system": "^4.12.1",
+                "@material-ui/types": "5.1.0",
+                "@material-ui/utils": "^4.11.2",
                 "@types/react-transition-group": "^4.2.0",
                 "clsx": "^1.0.4",
                 "hoist-non-react-statics": "^3.3.2",
                 "popper.js": "1.16.1-lts",
                 "prop-types": "^15.7.2",
-                "react-is": "^16.8.0",
+                "react-is": "^16.8.0 || ^17.0.0",
                 "react-transition-group": "^4.4.0"
             },
             "dependencies": {
+                "csstype": {
+                    "version": "3.0.10",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
+                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
+                },
                 "dom-helpers": {
-                    "version": "5.1.4",
-                    "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.4.tgz",
-                    "integrity": "sha512-TjMyeVUvNEnOnhzs6uAn9Ya47GmMo3qq7m+Lr/3ON0Rs5kHvb8I+SQYjLUSYn7qhEm0QjW0yrBkvz9yOrwwz1A==",
+                    "version": "5.2.1",
+                    "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+                    "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
                     "requires": {
                         "@babel/runtime": "^7.8.7",
-                        "csstype": "^2.6.7"
+                        "csstype": "^3.0.2"
                     }
                 },
-                "popper.js": {
-                    "version": "1.16.1-lts",
-                    "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz",
-                    "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA=="
-                },
                 "react-transition-group": {
-                    "version": "4.4.1",
-                    "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz",
-                    "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==",
+                    "version": "4.4.2",
+                    "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz",
+                    "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==",
                     "requires": {
                         "@babel/runtime": "^7.5.5",
                         "dom-helpers": "^5.0.1",
@@ -511,35 +26677,35 @@
             }
         },
         "@material-ui/styles": {
-            "version": "4.10.0",
-            "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.10.0.tgz",
-            "integrity": "sha512-XPwiVTpd3rlnbfrgtEJ1eJJdFCXZkHxy8TrdieaTvwxNYj42VnnCyFzxYeNW9Lhj4V1oD8YtQ6S5Gie7bZDf7Q==",
+            "version": "4.11.4",
+            "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.4.tgz",
+            "integrity": "sha512-KNTIZcnj/zprG5LW0Sao7zw+yG3O35pviHzejMdcSGCdWbiO8qzRgOYL8JAxAsWBKOKYwVZxXtHWaB5T2Kvxew==",
             "requires": {
                 "@babel/runtime": "^7.4.4",
                 "@emotion/hash": "^0.8.0",
-                "@material-ui/types": "^5.1.0",
-                "@material-ui/utils": "^4.9.6",
+                "@material-ui/types": "5.1.0",
+                "@material-ui/utils": "^4.11.2",
                 "clsx": "^1.0.4",
                 "csstype": "^2.5.2",
                 "hoist-non-react-statics": "^3.3.2",
-                "jss": "^10.0.3",
-                "jss-plugin-camel-case": "^10.0.3",
-                "jss-plugin-default-unit": "^10.0.3",
-                "jss-plugin-global": "^10.0.3",
-                "jss-plugin-nested": "^10.0.3",
-                "jss-plugin-props-sort": "^10.0.3",
-                "jss-plugin-rule-value-function": "^10.0.3",
-                "jss-plugin-vendor-prefixer": "^10.0.3",
+                "jss": "^10.5.1",
+                "jss-plugin-camel-case": "^10.5.1",
+                "jss-plugin-default-unit": "^10.5.1",
+                "jss-plugin-global": "^10.5.1",
+                "jss-plugin-nested": "^10.5.1",
+                "jss-plugin-props-sort": "^10.5.1",
+                "jss-plugin-rule-value-function": "^10.5.1",
+                "jss-plugin-vendor-prefixer": "^10.5.1",
                 "prop-types": "^15.7.2"
             }
         },
         "@material-ui/system": {
-            "version": "4.9.14",
-            "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.9.14.tgz",
-            "integrity": "sha512-oQbaqfSnNlEkXEziDcJDDIy8pbvwUmZXWNqlmIwDqr/ZdCK8FuV3f4nxikUh7hvClKV2gnQ9djh5CZFTHkZj3w==",
+            "version": "4.12.1",
+            "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.1.tgz",
+            "integrity": "sha512-lUdzs4q9kEXZGhbN7BptyiS1rLNHe6kG9o8Y307HCvF4sQxbCgpL2qi+gUk+yI8a2DNk48gISEQxoxpgph0xIw==",
             "requires": {
                 "@babel/runtime": "^7.4.4",
-                "@material-ui/utils": "^4.9.6",
+                "@material-ui/utils": "^4.11.2",
                 "csstype": "^2.5.2",
                 "prop-types": "^15.7.2"
             }
@@ -547,16 +26713,17 @@
         "@material-ui/types": {
             "version": "5.1.0",
             "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz",
-            "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A=="
+            "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==",
+            "requires": {}
         },
         "@material-ui/utils": {
-            "version": "4.10.2",
-            "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.10.2.tgz",
-            "integrity": "sha512-eg29v74P7W5r6a4tWWDAAfZldXIzfyO1am2fIsC39hdUUHm/33k6pGOKPbgDjg/U/4ifmgAePy/1OjkKN6rFRw==",
+            "version": "4.11.2",
+            "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.2.tgz",
+            "integrity": "sha512-Uul8w38u+PICe2Fg2pDKCaIG7kOyhowZ9vjiC1FsVwPABTW8vPPKfF6OvxRq3IiBaI1faOJmgdvMG7rMJARBhA==",
             "requires": {
                 "@babel/runtime": "^7.4.4",
                 "prop-types": "^15.7.2",
-                "react-is": "^16.8.0"
+                "react-is": "^16.8.0 || ^17.0.0"
             }
         },
         "@react-google-maps/api": {
@@ -583,9 +26750,9 @@
             "integrity": "sha512-EjeoCM+U/6W9qoGWqurcDbCubqTEDTFET7Jd3XcLHqTLvFAE473x2YbQg98mJXKNgPKcPTNEcTiYYGiIz9tEjA=="
         },
         "@react-three/fiber": {
-            "version": "6.0.16",
-            "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-6.0.16.tgz",
-            "integrity": "sha512-oKMYNJOd5t+V4o18aRMhaRhcDQ1NeeCDy52yJvXpu7zrsR04YZtXD4RacW0FDH0KnwkOLDst95LCCphm3Thk/A==",
+            "version": "6.2.3",
+            "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-6.2.3.tgz",
+            "integrity": "sha512-XuL3683iVcwjm3enour57k4OjkA1MQztqpRL2oQh30v7ygf1vJg95gKo8cqqgPJctc2pZAMUW1eRpr+Xqjwqrg==",
             "requires": {
                 "@babel/runtime": "^7.13.10",
                 "react-merge-refs": "^1.1.0",
@@ -596,25 +26763,16 @@
                 "scheduler": "^0.20.2",
                 "use-asset": "^1.0.4",
                 "utility-types": "^3.10.0",
-                "zustand": "^3.3.3"
+                "zustand": "^3.5.1"
             },
             "dependencies": {
                 "@babel/runtime": {
-                    "version": "7.13.17",
-                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.17.tgz",
-                    "integrity": "sha512-NCdgJEelPTSh+FEFylhnP1ylq848l1z9t9N0j1Lfbcw0+KXGjsTvUmkxy+voLLXB5SOKMbLLx4jxYliGrYQseA==",
+                    "version": "7.17.2",
+                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
+                    "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
                     "requires": {
                         "regenerator-runtime": "^0.13.4"
                     }
-                },
-                "scheduler": {
-                    "version": "0.20.2",
-                    "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
-                    "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
-                    "requires": {
-                        "loose-envify": "^1.1.0",
-                        "object-assign": "^4.1.1"
-                    }
                 }
             }
         },
@@ -626,6 +26784,25 @@
                 "unicode-trie": "^0.3.0"
             }
         },
+        "@sindresorhus/is": {
+            "version": "4.6.0",
+            "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+            "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="
+        },
+        "@socket.io/component-emitter": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz",
+            "integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==",
+            "dev": true
+        },
+        "@szmarczak/http-timer": {
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz",
+            "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==",
+            "requires": {
+                "defer-to-connect": "^2.0.1"
+            }
+        },
         "@turf/clone": {
             "version": "6.5.0",
             "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-6.5.0.tgz",
@@ -689,9 +26866,9 @@
             }
         },
         "@types/adm-zip": {
-            "version": "0.4.33",
-            "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.4.33.tgz",
-            "integrity": "sha512-WM0DCWFLjXtddl0fu0+iN2ZF+qz8RF9RddG5OSy/S90AQz01Fu8lHn/3oTIZDxvG8gVcnBLAHMHOdBLbV6m6Mw==",
+            "version": "0.4.34",
+            "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.4.34.tgz",
+            "integrity": "sha512-8ToYLLAYhkRfcmmljrKi22gT2pqu7hGMDtORP1emwIEGmgUTZOsaDjzWFzW5N2frcFRz/50CWt4zA1CxJ73pmQ==",
             "dev": true,
             "requires": {
                 "@types/node": "*"
@@ -703,25 +26880,19 @@
             "integrity": "sha512-ACymFQ5qgSrZNR1Fqjk7Wv9gH6dFgejn2gpLkkceWxTKzivRJsshX4xhBVALgvF79gUdXiMCRIdusN728XpeGA==",
             "dev": true
         },
-        "@types/anymatch": {
-            "version": "1.3.1",
-            "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz",
-            "integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==",
-            "dev": true
-        },
         "@types/archiver": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-3.1.0.tgz",
-            "integrity": "sha512-nTvHwgWONL+iXG+9CX+gnQ/tTOV+qucAjwpXqeUn4OCRMxP42T29FFP/7XaOo0EqqO3TlENhObeZEe7RUJAriw==",
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-3.1.1.tgz",
+            "integrity": "sha512-TzVZ9204sH1TuFylfr1cw/AA/3/VldAAXswEwKLXUOzA9mDg+m6gHF9EaqKNlozcjc6knX5m1KAqJzksPLSEfw==",
             "dev": true,
             "requires": {
                 "@types/glob": "*"
             }
         },
         "@types/assert": {
-            "version": "1.5.4",
-            "resolved": "https://registry.npmjs.org/@types/assert/-/assert-1.5.4.tgz",
-            "integrity": "sha512-CaFVW21Ulu0J9sUaEWJjwmhkDkeoxa4fniVSERzZC13sU9v8NNM2lMlkfZZv60j47D+qDt0Lyo8skVP3CTXUdA=="
+            "version": "1.5.6",
+            "resolved": "https://registry.npmjs.org/@types/assert/-/assert-1.5.6.tgz",
+            "integrity": "sha512-Y7gDJiIqb9qKUHfBQYOWGngUpLORtirAVPuj/CWJrU2C6ZM4/y3XLwuwfGMF8s7QzW746LQZx23m0+1FSgjfug=="
         },
         "@types/async": {
             "version": "2.4.2",
@@ -754,27 +26925,38 @@
             "integrity": "sha512-ElU16s8E6Pr6magp8ihwH1O8pbUJASbMND/qgUc9RsLmP3lMLHiDMRXdjtaObwW5GPtOVYOsXDUIhTIluT+yaw=="
         },
         "@types/bluebird": {
-            "version": "3.5.32",
-            "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.32.tgz",
-            "integrity": "sha512-dIOxFfI0C+jz89g6lQ+TqhGgPQ0MxSnh/E4xuC0blhFtyW269+mPG5QeLgbdwst/LvdP8o1y0o/Gz5EHXLec/g==",
+            "version": "3.5.36",
+            "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.36.tgz",
+            "integrity": "sha512-HBNx4lhkxN7bx6P0++W8E289foSu8kO8GCk2unhuVggO+cE7rh9DhZUyPhUxNRG9m+5B5BTKxZQ5ZP92x/mx9Q==",
             "dev": true
         },
         "@types/body-parser": {
-            "version": "1.19.0",
-            "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
-            "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==",
+            "version": "1.19.2",
+            "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
+            "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
+            "dev": true,
             "requires": {
                 "@types/connect": "*",
                 "@types/node": "*"
             }
         },
         "@types/bson": {
-            "version": "4.0.2",
-            "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.2.tgz",
-            "integrity": "sha512-+uWmsejEHfmSjyyM/LkrP0orfE2m5Mx9Xel4tXNeqi1ldK5XMQcDsFkBmLDtuyKUbxj2jGDo0H240fbCRJZo7Q==",
-            "dev": true,
+            "version": "4.2.0",
+            "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.2.0.tgz",
+            "integrity": "sha512-ELCPqAdroMdcuxqwMgUpifQyRoTpyYCNr1V9xKyF40VsBobsj+BbWNRvwGchMgBPGqkw655ypkjj2MEF5ywVwg==",
             "requires": {
-                "@types/node": "*"
+                "bson": "*"
+            }
+        },
+        "@types/cacheable-request": {
+            "version": "6.0.2",
+            "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz",
+            "integrity": "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==",
+            "requires": {
+                "@types/http-cache-semantics": "*",
+                "@types/keyv": "*",
+                "@types/node": "*",
+                "@types/responselike": "*"
             }
         },
         "@types/caseless": {
@@ -784,24 +26966,24 @@
             "dev": true
         },
         "@types/chai": {
-            "version": "4.2.11",
-            "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.11.tgz",
-            "integrity": "sha512-t7uW6eFafjO+qJ3BIV2gGUyZs27egcNRkUdalkud+Qa3+kg//f129iuOFivHDXQ+vnU3fDXuwgv0cqMCbcE8sw==",
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz",
+            "integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==",
             "dev": true
         },
         "@types/color": {
-            "version": "3.0.1",
-            "resolved": "https://registry.npmjs.org/@types/color/-/color-3.0.1.tgz",
-            "integrity": "sha512-oeUWVaAwI+xINDUx+3F2vJkl/vVB03VChFF/Gl3iQCdbcakjuoJyMOba+3BXRtnBhxZ7uBYqQBi9EpLnvSoztA==",
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/@types/color/-/color-3.0.3.tgz",
+            "integrity": "sha512-X//qzJ3d3Zj82J9sC/C18ZY5f43utPbAJ6PhYt/M7uG6etcF6MRpKdN880KBy43B0BMzSfeT96MzrsNjFI3GbA==",
             "dev": true,
             "requires": {
                 "@types/color-convert": "*"
             }
         },
         "@types/color-convert": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/@types/color-convert/-/color-convert-1.9.0.tgz",
-            "integrity": "sha512-OKGEfULrvSL2VRbkl/gnjjgbbF7ycIlpSsX7Nkab4MOWi5XxmgBYvuiQ7lcCFY5cPDz7MUNaKgxte2VRmtr4Fg==",
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/@types/color-convert/-/color-convert-2.0.0.tgz",
+            "integrity": "sha512-m7GG7IKKGuJUXvkZ1qqG3ChccdIM/qBBo913z+Xft0nKCX4hAU/IxKwZBU4cpRZ7GS5kV4vOblUkILtSShCPXQ==",
             "dev": true,
             "requires": {
                 "@types/color-name": "*"
@@ -816,6 +26998,7 @@
             "version": "3.4.33",
             "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
             "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
+            "dev": true,
             "requires": {
                 "@types/node": "*"
             }
@@ -839,9 +27022,9 @@
             }
         },
         "@types/cookie-session": {
-            "version": "2.0.41",
-            "resolved": "https://registry.npmjs.org/@types/cookie-session/-/cookie-session-2.0.41.tgz",
-            "integrity": "sha512-Ytd7zWY3WvC7TP9rKVjlnYHus8Hq+lJuioHOtKJ7dXg2Nt+O+9UpelygYy5Eb67QQN92HPO5qEG0vnmAlqRLLw==",
+            "version": "2.0.44",
+            "resolved": "https://registry.npmjs.org/@types/cookie-session/-/cookie-session-2.0.44.tgz",
+            "integrity": "sha512-3DheOZ41pql6raSIkqEPphJdhA2dX2bkS+s2Qacv8YMKkoCbAIEXbsDil7351ARzMqvfyDUGNeHGiRZveIzhqQ==",
             "dev": true,
             "requires": {
                 "@types/express": "*",
@@ -849,43 +27032,45 @@
             }
         },
         "@types/cors": {
-            "version": "2.8.8",
-            "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.8.tgz",
-            "integrity": "sha512-fO3gf3DxU2Trcbr75O7obVndW/X5k8rJNZkLXlQWStTHhP71PkRqjwPIEI0yMnJdg9R9OasjU+Bsr+Hr1xy/0w==",
-            "requires": {
-                "@types/express": "*"
-            }
+            "version": "2.8.12",
+            "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
+            "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw=="
         },
         "@types/d3-axis": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-2.0.0.tgz",
-            "integrity": "sha512-gUdlEwGBLl3tXGiBnBNmNzph9W3bCfa4tBgWZD60Z1eDQKTY4zyCAcZ3LksignGfKawYatmDYcBdjJ5h/54sqA==",
+            "version": "2.1.3",
+            "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-2.1.3.tgz",
+            "integrity": "sha512-QjXjwZ0xzyrW2ndkmkb09ErgWDEYtbLBKGui73QLMFm3woqWpxptfD5Y7vqQdybMcu7WEbjZ5q+w2w5+uh2IjA==",
             "requires": {
-                "@types/d3-selection": "*"
+                "@types/d3-selection": "^2"
             }
         },
         "@types/d3-color": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-2.0.1.tgz",
-            "integrity": "sha512-u7LTCL7RnaavFSmob2rIAJLNwu50i6gFwY9cHFr80BrQURYQBRkJ+Yv47nA3Fm7FeRhdWTiVTeqvSeOuMAOzBQ=="
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-2.0.3.tgz",
+            "integrity": "sha512-+0EtEjBfKEDtH9Rk3u3kLOUXM5F+iZK+WvASPb0MhIZl8J8NUvGeZRwKCXl+P3HkYx5TdU4YtcibpqHkSR9n7w=="
         },
         "@types/d3-scale": {
-            "version": "3.2.2",
-            "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-3.2.2.tgz",
-            "integrity": "sha512-qpQe8G02tzUwt9sdWX1h8A/W0Q1+N48wMnYXVOkrzeLUkCfvzJYV9Ee3aORCS4dN4ONRLFmMvaXdziQ29XGLjQ==",
+            "version": "3.3.2",
+            "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-3.3.2.tgz",
+            "integrity": "sha512-gGqr7x1ost9px3FvIfUMi5XA/F/yAf4UkUDtdQhpH92XCT0Oa7zkkRzY61gPVJq+DxpHn/btouw5ohWkbBsCzQ==",
             "requires": {
-                "@types/d3-time": "*"
+                "@types/d3-time": "^2"
             }
         },
         "@types/d3-selection": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-2.0.0.tgz",
-            "integrity": "sha512-EF0lWZ4tg7oDFg4YQFlbOU3936e3a9UmoQ2IXlBy1+cv2c2Pv7knhKUzGlH5Hq2sF/KeDTH1amiRPey2rrLMQA=="
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-2.0.1.tgz",
+            "integrity": "sha512-3mhtPnGE+c71rl/T5HMy+ykg7migAZ4T6gzU0HxpgBFKcasBrSnwRbYV1/UZR6o5fkpySxhWxAhd7yhjj8jL7g=="
         },
         "@types/d3-time": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.0.0.tgz",
-            "integrity": "sha512-Abz8bTzy8UWDeYs9pCa3D37i29EWDjNTjemdk0ei1ApYVNqulYlGUKip/jLOpogkPSsPz/GvZCYiC7MFlEk0iQ=="
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.1.1.tgz",
+            "integrity": "sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg=="
+        },
+        "@types/dom-speech-recognition": {
+            "version": "0.0.1",
+            "resolved": "https://registry.npmjs.org/@types/dom-speech-recognition/-/dom-speech-recognition-0.0.1.tgz",
+            "integrity": "sha512-udCxb8DvjcDKfk1WTBzDsxFbLgYxmQGKrE/ricoMqHRNjSlSUCcamVTA5lIQqzY10mY5qCY0QDwBfFEwhfoDPw=="
         },
         "@types/dotenv": {
             "version": "6.1.1",
@@ -897,36 +27082,59 @@
             }
         },
         "@types/engine.io": {
-            "version": "3.1.4",
-            "resolved": "https://registry.npmjs.org/@types/engine.io/-/engine.io-3.1.4.tgz",
-            "integrity": "sha512-98rXVukLD6/ozrQ2O80NAlWDGA4INg+tqsEReWJldqyi2fulC9V7Use/n28SWgROXKm6003ycWV4gZHoF8GA6w==",
+            "version": "3.1.7",
+            "resolved": "https://registry.npmjs.org/@types/engine.io/-/engine.io-3.1.7.tgz",
+            "integrity": "sha512-qNjVXcrp+1sS8YpRUa714r0pgzOwESdW5UjHL7D/2ZFdBX0BXUXtg1LUrp+ylvqbvMcMWUy73YpRoxPN2VoKAQ==",
             "dev": true,
             "requires": {
                 "@types/node": "*"
             }
         },
+        "@types/eslint": {
+            "version": "8.4.1",
+            "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz",
+            "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==",
+            "requires": {
+                "@types/estree": "*",
+                "@types/json-schema": "*"
+            }
+        },
+        "@types/eslint-scope": {
+            "version": "3.7.3",
+            "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz",
+            "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==",
+            "requires": {
+                "@types/eslint": "*",
+                "@types/estree": "*"
+            }
+        },
+        "@types/estree": {
+            "version": "0.0.51",
+            "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz",
+            "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ=="
+        },
         "@types/events": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
-            "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==",
-            "dev": true
+            "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g=="
         },
         "@types/exif": {
-            "version": "0.6.1",
-            "resolved": "https://registry.npmjs.org/@types/exif/-/exif-0.6.1.tgz",
-            "integrity": "sha512-hw4RQiuu+L//CpZ1vL3EopGY3L6mGvPFK2CpWCmvlwUAquvosWfCU3ZFq9jsZ2rmIun71XJm2I2KPx7tT1VuxA==",
+            "version": "0.6.3",
+            "resolved": "https://registry.npmjs.org/@types/exif/-/exif-0.6.3.tgz",
+            "integrity": "sha512-OMthUry1d2/gpXL2H3RGaj7KWRw0EWdxI0OANeUWbYMotBxljFuAl3eydCgDaBYTyD+d4Q3atdVcmimwS6KvGg==",
             "dev": true,
             "requires": {
                 "@types/node": "*"
             }
         },
         "@types/express": {
-            "version": "4.17.6",
-            "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.6.tgz",
-            "integrity": "sha512-n/mr9tZI83kd4azlPG5y997C/M4DNABK9yErhFM6hKdym4kkmd9j0vtsJyjFIwfRBxtrxZtAfGZCNRIBMFLK5w==",
+            "version": "4.17.13",
+            "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz",
+            "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==",
+            "dev": true,
             "requires": {
                 "@types/body-parser": "*",
-                "@types/express-serve-static-core": "*",
+                "@types/express-serve-static-core": "^4.17.18",
                 "@types/qs": "*",
                 "@types/serve-static": "*"
             }
@@ -942,22 +27150,23 @@
             }
         },
         "@types/express-serve-static-core": {
-            "version": "4.17.5",
-            "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.5.tgz",
-            "integrity": "sha512-578YH5Lt88AKoADy0b2jQGwJtrBxezXtVe/MBqWXKZpqx91SnC0pVkVCcxcytz3lWW+cHBYDi3Ysh0WXc+rAYw==",
+            "version": "4.17.28",
+            "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz",
+            "integrity": "sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==",
+            "dev": true,
             "requires": {
                 "@types/node": "*",
+                "@types/qs": "*",
                 "@types/range-parser": "*"
             }
         },
         "@types/express-session": {
-            "version": "1.17.0",
-            "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.0.tgz",
-            "integrity": "sha512-OQEHeBFE1UhChVIBhRh9qElHUvTp4BzKKHxMDkGHT7WuYk5eL93hPG7D8YAIkoBSbhNEY0RjreF15zn+U0eLjA==",
+            "version": "1.17.4",
+            "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.4.tgz",
+            "integrity": "sha512-7cNlSI8+oOBUHTfPXMwDxF/Lchx5aJ3ho7+p9jJZYVg9dVDJFh3qdMXmJtRsysnvS+C6x46k9DRYmrmCkE+MVg==",
             "dev": true,
             "requires": {
-                "@types/express": "*",
-                "@types/node": "*"
+                "@types/express": "*"
             }
         },
         "@types/express-validator": {
@@ -970,38 +27179,41 @@
             }
         },
         "@types/file-saver": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.1.tgz",
-            "integrity": "sha512-g1QUuhYVVAamfCifK7oB7G3aIl4BbOyzDOqVyUfEr4tfBKrXfeH+M+Tg7HKCXSrbzxYdhyCP7z9WbKo0R2hBCw==",
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.5.tgz",
+            "integrity": "sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==",
             "dev": true
         },
         "@types/formidable": {
             "version": "1.0.31",
             "resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-1.0.31.tgz",
             "integrity": "sha512-dIhM5t8lRP0oWe2HF8MuPvdd1TpPTjhDMAqemcq6oIZQCBQTovhBAdTQ5L5veJB4pdQChadmHuxtB0YzqvfU3Q==",
-            "dev": true,
             "requires": {
                 "@types/events": "*",
                 "@types/node": "*"
             }
         },
+        "@types/geojson": {
+            "version": "7946.0.8",
+            "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.8.tgz",
+            "integrity": "sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA=="
+        },
         "@types/glob": {
-            "version": "7.1.1",
-            "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz",
-            "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==",
+            "version": "7.2.0",
+            "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
+            "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
             "dev": true,
             "requires": {
-                "@types/events": "*",
                 "@types/minimatch": "*",
                 "@types/node": "*"
             }
         },
         "@types/google-maps": {
-            "version": "3.2.2",
-            "resolved": "https://registry.npmjs.org/@types/google-maps/-/google-maps-3.2.2.tgz",
-            "integrity": "sha512-/XPVfS28VzUdE/HlmBRoe5ii1nNMyWujyRfRY08bD/JgmPlWSiY8enB2dqTe9mlc3kULq7LfFa1wcupM+lQfqA==",
+            "version": "3.2.3",
+            "resolved": "https://registry.npmjs.org/@types/google-maps/-/google-maps-3.2.3.tgz",
+            "integrity": "sha512-+GR/7sMvo2bENS26H4t2QqoCqE4FE5qhsG8aaDM9mJAlsorkLyaFdTQra8VkmS1RNE09fx9c1kac+GKG0RtA2A==",
             "requires": {
-                "@types/googlemaps": "*"
+                "@types/google.maps": "*"
             }
         },
         "@types/google-maps-react": {
@@ -1018,59 +27230,88 @@
             "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.46.1.tgz",
             "integrity": "sha512-GAa5ZWYgXG50yLXybb7A824esGm/L0LKHS7qD0qkP0IA/Qp5r922P9tmYcbCkGEf3Zgf7Ukbp7l08/IGIJuQwQ=="
         },
-        "@types/googlemaps": {
-            "version": "3.39.8",
-            "resolved": "https://registry.npmjs.org/@types/googlemaps/-/googlemaps-3.39.8.tgz",
-            "integrity": "sha512-z03u79t1v8QIktoUXypWD06Fzl499/hA162hurA+eCDlWXxFynuU+hMZIaferILF5Gzr4PMX1ShHszT666sUHQ=="
+        "@types/hoist-non-react-statics": {
+            "version": "3.3.1",
+            "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
+            "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
+            "requires": {
+                "@types/react": "*",
+                "hoist-non-react-statics": "^3.3.0"
+            }
+        },
+        "@types/html-minifier-terser": {
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+            "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg=="
+        },
+        "@types/http-cache-semantics": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz",
+            "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="
         },
         "@types/jquery": {
-            "version": "3.5.0",
-            "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.0.tgz",
-            "integrity": "sha512-C7qQUjpMWDUNYQRTXsP5nbYYwCwwgy84yPgoTT7fPN69NH92wLeCtFaMsWeolJD1AF/6uQw3pYt62rzv83sMmw==",
+            "version": "3.5.14",
+            "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.14.tgz",
+            "integrity": "sha512-X1gtMRMbziVQkErhTQmSe2jFwwENA/Zr+PprCkF63vFq+Yt5PZ4AlKqgmeNlwgn7dhsXEK888eIW2520EpC+xg==",
             "dev": true,
             "requires": {
                 "@types/sizzle": "*"
             }
         },
+        "@types/jsdom": {
+            "version": "16.2.14",
+            "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-16.2.14.tgz",
+            "integrity": "sha512-6BAy1xXEmMuHeAJ4Fv4yXKwBDTGTOseExKE3OaHiNycdHdZw59KfYzrt0DkDluvwmik1HRt6QS7bImxUmpSy+w==",
+            "requires": {
+                "@types/node": "*",
+                "@types/parse5": "*",
+                "@types/tough-cookie": "*"
+            }
+        },
+        "@types/json-schema": {
+            "version": "7.0.9",
+            "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
+            "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ=="
+        },
         "@types/keygrip": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz",
             "integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==",
             "dev": true
         },
-        "@types/libxmljs": {
-            "version": "0.18.6",
-            "resolved": "https://registry.npmjs.org/@types/libxmljs/-/libxmljs-0.18.6.tgz",
-            "integrity": "sha512-xVUs71CwL5wYYfx5oH344DYWdoE2hVWlnRxlXFYyA8BcueN+Ey/h4FyhzEikbIJSXBKyPpJKhGu5c3NOx15nww==",
-            "dev": true,
+        "@types/keyv": {
+            "version": "3.1.3",
+            "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz",
+            "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==",
             "requires": {
                 "@types/node": "*"
             }
         },
-        "@types/lodash": {
-            "version": "4.14.157",
-            "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.157.tgz",
-            "integrity": "sha512-Ft5BNFmv2pHDgxV5JDsndOWTRJ+56zte0ZpYLowp03tW+K+t8u8YMOzAnpuqPgzX6WO1XpDIUm7u04M8vdDiVQ==",
-            "dev": true
-        },
-        "@types/memory-fs": {
-            "version": "0.3.3",
-            "resolved": "https://registry.npmjs.org/@types/memory-fs/-/memory-fs-0.3.3.tgz",
-            "integrity": "sha512-rLEYzl1xODshz+Lm+YX8NYws8Xw7/qcYbQInMkotl96VpLZmUvoCfYYGxfajMSiugANV02QO5Fc+R98KKeE4gQ==",
+        "@types/libxmljs": {
+            "version": "0.18.7",
+            "resolved": "https://registry.npmjs.org/@types/libxmljs/-/libxmljs-0.18.7.tgz",
+            "integrity": "sha512-g88L5tS5UJKfzmPZbvGckcULvPHzguy24lFFTXUVm2UfezQBC5adHmA15RlTx0y0+s/+TyrCV1oPvxn+KqfH8A==",
             "dev": true,
             "requires": {
                 "@types/node": "*"
             }
         },
+        "@types/lodash": {
+            "version": "4.14.179",
+            "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.179.tgz",
+            "integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w==",
+            "dev": true
+        },
         "@types/mime": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz",
-            "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw=="
+            "version": "1.3.2",
+            "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
+            "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
+            "dev": true
         },
         "@types/minimatch": {
-            "version": "3.0.3",
-            "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
-            "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==",
+            "version": "3.0.5",
+            "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz",
+            "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==",
             "dev": true
         },
         "@types/mobile-detect": {
@@ -1089,29 +27330,27 @@
             "dev": true
         },
         "@types/mongodb": {
-            "version": "3.5.25",
-            "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.5.25.tgz",
-            "integrity": "sha512-2H/Owt+pHCl9YmBOYnXc3VdnxejJEjVdH+QCWL5ZAfPehEn3evygKBX3/vKRv7aTwfNbUd0E5vjJdQklH/9a6w==",
-            "dev": true,
+            "version": "3.6.20",
+            "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz",
+            "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==",
             "requires": {
                 "@types/bson": "*",
                 "@types/node": "*"
             }
         },
         "@types/mongoose": {
-            "version": "5.7.28",
-            "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.7.28.tgz",
-            "integrity": "sha512-ll74S2QhC34MSSSk30NHlU8ILESIA2l4mDfAfDse/VM20tT9MRV9WGKWK3HglKRmdL77iMUYMcgHdKJebabtzA==",
+            "version": "5.11.97",
+            "resolved": "https://registry.npmjs.org/@types/mongoose/-/mongoose-5.11.97.tgz",
+            "integrity": "sha512-cqwOVYT3qXyLiGw7ueU2kX9noE8DPGRY6z8eUxudhXY8NZ7DMKYAxyZkLSevGfhCX3dO/AoX5/SO9lAzfjon0Q==",
             "dev": true,
             "requires": {
-                "@types/mongodb": "*",
-                "@types/node": "*"
+                "mongoose": "*"
             }
         },
         "@types/node": {
-            "version": "10.17.26",
-            "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.26.tgz",
-            "integrity": "sha512-myMwkO2Cr82kirHY8uknNRHEVtn0wV3DTQfkrjx17jmkstDRZ24gNUdl8AHXVyVclTYI/bNjgTPTAWvWLqXqkw=="
+            "version": "10.17.60",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
+            "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="
         },
         "@types/nodemailer": {
             "version": "4.6.8",
@@ -1142,19 +27381,24 @@
             "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
             "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
         },
+        "@types/parse5": {
+            "version": "6.0.3",
+            "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz",
+            "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g=="
+        },
         "@types/passport": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.3.tgz",
-            "integrity": "sha512-nyztuxtDPQv9utCzU0qW7Gl8BY2Dn8BKlYAFFyxKipFxjaVd96celbkLCV/tRqqBUZ+JB8If3UfgV8347DTo3Q==",
+            "version": "1.0.7",
+            "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.7.tgz",
+            "integrity": "sha512-JtswU8N3kxBYgo+n9of7C97YQBT+AYPP2aBfNGTzABqPAZnK/WOAaKfh3XesUYMZRrXFuoPc2Hv0/G/nQFveHw==",
             "dev": true,
             "requires": {
                 "@types/express": "*"
             }
         },
         "@types/passport-google-oauth20": {
-            "version": "2.0.3",
-            "resolved": "https://registry.npmjs.org/@types/passport-google-oauth20/-/passport-google-oauth20-2.0.3.tgz",
-            "integrity": "sha512-6EUEGzEg4acwowvgR/yVZIj8S2Kkwc6JmlY2/wnM1wJHNz20o7s1TIGrxnah8ymLgJasYDpy95P3TMMqlmetPw==",
+            "version": "2.0.11",
+            "resolved": "https://registry.npmjs.org/@types/passport-google-oauth20/-/passport-google-oauth20-2.0.11.tgz",
+            "integrity": "sha512-9XMT1GfwhZL7UQEiCepLef55RNPHkbrCtsU7rsWPTEOsmu5qVIW8nSemtB4p+P24CuOhA+IKkv8LsPThYghGww==",
             "dev": true,
             "requires": {
                 "@types/express": "*",
@@ -1163,9 +27407,9 @@
             }
         },
         "@types/passport-local": {
-            "version": "1.0.33",
-            "resolved": "https://registry.npmjs.org/@types/passport-local/-/passport-local-1.0.33.tgz",
-            "integrity": "sha512-+rn6ZIxje0jZ2+DAiWFI8vGG7ZFKB0hXx2cUdMmudSWsigSq6ES7Emso46r4HJk0qCgrZVfI8sJiM7HIYf4SbA==",
+            "version": "1.0.34",
+            "resolved": "https://registry.npmjs.org/@types/passport-local/-/passport-local-1.0.34.tgz",
+            "integrity": "sha512-PSc07UdYx+jhadySxxIYWuv6sAnY5e+gesn/5lkPKfBeGuIYn9OPR+AAEDq73VRUh6NBTpvE/iPE62rzZUslog==",
             "dev": true,
             "requires": {
                 "@types/express": "*",
@@ -1174,9 +27418,9 @@
             }
         },
         "@types/passport-oauth2": {
-            "version": "1.4.9",
-            "resolved": "https://registry.npmjs.org/@types/passport-oauth2/-/passport-oauth2-1.4.9.tgz",
-            "integrity": "sha512-QP0q+NVQOaIu2r0e10QWkiUA0Ya5mOBHRJN0UrI+LolMLOP1/VN4EVIpJ3xVwFo+xqNFRoFvFwJhBvKnk7kpUA==",
+            "version": "1.4.11",
+            "resolved": "https://registry.npmjs.org/@types/passport-oauth2/-/passport-oauth2-1.4.11.tgz",
+            "integrity": "sha512-KUNwmGhe/3xPbjkzkPwwcPmyFwfyiSgtV1qOrPBLaU4i4q9GSCdAOyCbkFG0gUxAyEmYwqo9OAF/rjPjJ6ImdA==",
             "dev": true,
             "requires": {
                 "@types/express": "*",
@@ -1195,20 +27439,23 @@
             }
         },
         "@types/pdfjs-dist": {
-            "version": "2.1.4",
-            "resolved": "https://registry.npmjs.org/@types/pdfjs-dist/-/pdfjs-dist-2.1.4.tgz",
-            "integrity": "sha512-KnEc8W2IcntVL/SKfRWy2lcJax07xEaO7ghcT0/hc2Lp5UOcmaXAi887RKOLhP8BLKsVGJ7uYH0mYo1uSvbu2A==",
-            "dev": true
+            "version": "2.10.378",
+            "resolved": "https://registry.npmjs.org/@types/pdfjs-dist/-/pdfjs-dist-2.10.378.tgz",
+            "integrity": "sha512-TRdIPqdsvKmPla44kVy4jv5Nt5vjMfVjbIEke1CRULIrwKNRC4lIiZvNYDJvbUMNCFPNIUcOKhXTyMJrX18IMA==",
+            "dev": true,
+            "requires": {
+                "pdfjs-dist": "*"
+            }
         },
         "@types/prop-types": {
-            "version": "15.7.3",
-            "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
-            "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw=="
+            "version": "15.7.4",
+            "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
+            "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ=="
         },
         "@types/prosemirror-commands": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/@types/prosemirror-commands/-/prosemirror-commands-1.0.1.tgz",
-            "integrity": "sha512-GeE12m8VT9N1JrzoY//946IX8ZyQOLNmvryJ+BNQs/HvhmXW9EWOcWUE6OBRtxK7Y8SrzSOwx4XmqSgVmK3tGQ==",
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-commands/-/prosemirror-commands-1.0.4.tgz",
+            "integrity": "sha512-utDNYB3EXLjAfYIcRWJe6pn3kcQ5kG4RijbT/0Y/TFOm6yhvYS/D9eJVnijdg9LDjykapcezchxGRqFD5LcyaQ==",
             "dev": true,
             "requires": {
                 "@types/prosemirror-model": "*",
@@ -1227,9 +27474,9 @@
             }
         },
         "@types/prosemirror-history": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/@types/prosemirror-history/-/prosemirror-history-1.0.1.tgz",
-            "integrity": "sha512-BYyPJlWDo3VEnWS5X2DCHXrrAKEjdbCe1DUjGL6R/8hmwMFe3iMJGYdBkOXU1FfkTpw7Z+PlwY/pMyeelVydmg==",
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-history/-/prosemirror-history-1.0.3.tgz",
+            "integrity": "sha512-5TloMDRavgLjOAKXp1Li8u0xcsspzbT1Cm9F2pwHOkgvQOz1jWQb2VIXO7RVNsFjLBZdIXlyfSLivro3DuMWXg==",
             "dev": true,
             "requires": {
                 "@types/prosemirror-model": "*",
@@ -1237,9 +27484,9 @@
             }
         },
         "@types/prosemirror-inputrules": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/@types/prosemirror-inputrules/-/prosemirror-inputrules-1.0.2.tgz",
-            "integrity": "sha512-bKFneQUPnkZmzCJ1uoitpKH6PFW0hc4q55NsC7mFUCvX0eZl0GRKxyfV47jkJbsbyUQoO/QFv0WwLDz2bo15sA==",
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-inputrules/-/prosemirror-inputrules-1.0.4.tgz",
+            "integrity": "sha512-lJIMpOjO47SYozQybUkpV6QmfuQt7GZKHtVrvS+mR5UekA8NMC5HRIVMyaIauJLWhKU6oaNjpVaXdw41kh165g==",
             "dev": true,
             "requires": {
                 "@types/prosemirror-model": "*",
@@ -1247,20 +27494,21 @@
             }
         },
         "@types/prosemirror-keymap": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/@types/prosemirror-keymap/-/prosemirror-keymap-1.0.2.tgz",
-            "integrity": "sha512-94aDnpdrdI5S2UM7MQIjauFeo0MI5S7uU+SMT01m5NXT5CZ/NLHhzN0d1V5zKxe7Nh7fVxuZ7l9DTu5nSMN9sw==",
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-keymap/-/prosemirror-keymap-1.0.4.tgz",
+            "integrity": "sha512-ycevwkqUh+jEQtPwqO7sWGcm+Sybmhu8MpBsM8DlO3+YTKnXbKA6SDz/+q14q1wK3UA8lHJyfR+v+GPxfUSemg==",
             "dev": true,
             "requires": {
+                "@types/prosemirror-commands": "*",
                 "@types/prosemirror-model": "*",
                 "@types/prosemirror-state": "*",
                 "@types/prosemirror-view": "*"
             }
         },
         "@types/prosemirror-menu": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/@types/prosemirror-menu/-/prosemirror-menu-1.0.2.tgz",
-            "integrity": "sha512-u79CwEQR271k/kjdvRXyPGG7Rn+vPKbJfgSO7vOR/zEYcvSz7lLhBg1OZiSKqfSuhnIAbQh53qLylmB8PTw+WA==",
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-menu/-/prosemirror-menu-1.0.6.tgz",
+            "integrity": "sha512-teKfCPnnbYeB8DJiUIof4N0f8kyWbQP3BagEBRP6r+7/o0fiqHsnrex8GXuCSNNUqlpSpDQkJy52aq4jvzmDpg==",
             "dev": true,
             "requires": {
                 "@types/prosemirror-model": "*",
@@ -1269,18 +27517,18 @@
             }
         },
         "@types/prosemirror-model": {
-            "version": "1.7.2",
-            "resolved": "https://registry.npmjs.org/@types/prosemirror-model/-/prosemirror-model-1.7.2.tgz",
-            "integrity": "sha512-2l+yXvidg3AUHN07mO4Jd8Q84fo6ksFsy7LHUurLYrZ74uTahBp2fzcO49AKZMzww2EulXJ40Kl/OFaQ/7A1fw==",
+            "version": "1.16.1",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-model/-/prosemirror-model-1.16.1.tgz",
+            "integrity": "sha512-SrrCe2cHlYrQ9o55e2i/c3wt1yRajTTpRLvzfmB+2DWjWEbBLTByVWyjrdpKtQTxAaTeU2aeDGo1iuwl/jF27w==",
             "dev": true,
             "requires": {
                 "@types/orderedmap": "*"
             }
         },
         "@types/prosemirror-schema-list": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/@types/prosemirror-schema-list/-/prosemirror-schema-list-1.0.1.tgz",
-            "integrity": "sha512-+iUYq+pj2wVHSThj0MjNDzkkGwq8aDQ6j0UJK8a0cNCL8v44Ftcx1noGPtBIEUJgitH960VnfBNoTWfQoQZfRA==",
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-schema-list/-/prosemirror-schema-list-1.0.3.tgz",
+            "integrity": "sha512-uWybOf+M2Ea7rlbs0yLsS4YJYNGXYtn4N+w8HCw3Vvfl6wBAROzlMt0gV/D/VW/7J/LlAjwMezuGe8xi24HzXA==",
             "dev": true,
             "requires": {
                 "@types/orderedmap": "*",
@@ -1289,9 +27537,9 @@
             }
         },
         "@types/prosemirror-state": {
-            "version": "1.2.4",
-            "resolved": "https://registry.npmjs.org/@types/prosemirror-state/-/prosemirror-state-1.2.4.tgz",
-            "integrity": "sha512-Gch4THfZ9QNsRQ7myibU8cG99F3b8/3Gto083ZuutNG72E0VmS8yfQzA5ahbndr5GUIbmKyOD5LqKTBvx/M0qw==",
+            "version": "1.2.8",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-state/-/prosemirror-state-1.2.8.tgz",
+            "integrity": "sha512-mq9uyQWcpu8jeamO6Callrdvf/e1H/aRLR2kZWSpZrPHctEsxWHBbluD/wqVjXBRIOoMHLf6ZvOkrkmGLoCHVA==",
             "dev": true,
             "requires": {
                 "@types/prosemirror-model": "*",
@@ -1300,18 +27548,18 @@
             }
         },
         "@types/prosemirror-transform": {
-            "version": "1.1.1",
-            "resolved": "https://registry.npmjs.org/@types/prosemirror-transform/-/prosemirror-transform-1.1.1.tgz",
-            "integrity": "sha512-yYCYSoiRH+Wcbl8GJc0PFCzeyMzNQ1vL2xrHHSXZuNcIlH75VoiKrZFeZ6BS9cl8mYXjZrlmdBe8YOxYvyKM6A==",
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-transform/-/prosemirror-transform-1.1.5.tgz",
+            "integrity": "sha512-Wr2HXaEF4JPklWpC17RTxE6PxyU54Taqk5FMhK1ojgcN93J+GpkYW8s0mD3rl7KfTmlhVwZPCHE9o0cYf2Go5A==",
             "dev": true,
             "requires": {
                 "@types/prosemirror-model": "*"
             }
         },
         "@types/prosemirror-view": {
-            "version": "1.11.4",
-            "resolved": "https://registry.npmjs.org/@types/prosemirror-view/-/prosemirror-view-1.11.4.tgz",
-            "integrity": "sha512-Hh8v2tpCEMaIesQuw7Y7Pz6imoC1T/bR5OlNGVtp944PZvctXiBvFRkQIb0YvZpt7vVkFzeq2kmR+7mnUfvWiw==",
+            "version": "1.23.1",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-view/-/prosemirror-view-1.23.1.tgz",
+            "integrity": "sha512-6e1B2oKUnhmZPUrsVvYjDqeVjE6jGezygjtoHsAK4ZENAxHzHqy5NT4jUvdPTWjCYeH0t2Y7pSfRPNrPIyQX4A==",
             "dev": true,
             "requires": {
                 "@types/prosemirror-model": "*",
@@ -1320,31 +27568,41 @@
             }
         },
         "@types/qs": {
-            "version": "6.9.1",
-            "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.1.tgz",
-            "integrity": "sha512-lhbQXx9HKZAPgBkISrBcmAcMpZsmpe/Cd/hY7LGZS5OfkySUBItnPZHgQPssWYUET8elF+yCFBbP1Q0RZPTdaw=="
+            "version": "6.9.7",
+            "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+            "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
+            "dev": true
         },
         "@types/range-parser": {
-            "version": "1.2.3",
-            "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
-            "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA=="
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
+            "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==",
+            "dev": true
         },
         "@types/rc-switch": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/@types/rc-switch/-/rc-switch-1.9.0.tgz",
-            "integrity": "sha512-L3ZQ2/1GAaiKuQTMO/2sRdhnNNVtnnvdE0ZOL3/+v54j5LguOvYqTSOHPk9nLPi5va3pdRN1MTce3JkJvGZ65A==",
+            "version": "1.9.2",
+            "resolved": "https://registry.npmjs.org/@types/rc-switch/-/rc-switch-1.9.2.tgz",
+            "integrity": "sha512-+2e+KP23qqJ4RMb63aeb866cPPZv8g7rCu2NYJlwyxRveH3Gm/OT25Zj3BfRLViCrxwGhpwKq49ugiUBZkr9AQ==",
             "dev": true,
             "requires": {
                 "@types/react": "*"
             }
         },
         "@types/react": {
-            "version": "16.9.41",
-            "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.41.tgz",
-            "integrity": "sha512-6cFei7F7L4wwuM+IND/Q2cV1koQUvJ8iSV+Gwn0c3kvABZ691g7sp3hfEQHOUBJtccl1gPi+EyNjMIl9nGA0ug==",
+            "version": "16.14.23",
+            "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.23.tgz",
+            "integrity": "sha512-WngBZLuSkP4IAgPi0HOsGCHo6dn3CcuLQnCfC17VbA7YBgipZiZoTOhObwl/93DsFW0Y2a/ZXeonpW4DxirEJg==",
             "requires": {
                 "@types/prop-types": "*",
-                "csstype": "^2.2.0"
+                "@types/scheduler": "*",
+                "csstype": "^3.0.2"
+            },
+            "dependencies": {
+                "csstype": {
+                    "version": "3.0.10",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
+                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
+                }
             }
         },
         "@types/react-autosuggest": {
@@ -1357,9 +27615,9 @@
             }
         },
         "@types/react-color": {
-            "version": "2.17.4",
-            "resolved": "https://registry.npmjs.org/@types/react-color/-/react-color-2.17.4.tgz",
-            "integrity": "sha512-pAO3+7uHoESg5QMqjnGjw9F7sALjEZsaU41yGiUZbmHiJMoSXH1UklFJ1bZkwhYskaJgiY+AS6wirl17yBh5GA==",
+            "version": "2.17.6",
+            "resolved": "https://registry.npmjs.org/@types/react-color/-/react-color-2.17.6.tgz",
+            "integrity": "sha512-5CEKnrpvgZz8v5UYbpLgBeK+V6K7KdszksSDK6mNjL/8wrsqQfIyKB45CQOAEYtRgn+tWqTeqbUtvFWjXDpURQ==",
             "dev": true,
             "requires": {
                 "@types/react": "*",
@@ -1367,55 +27625,74 @@
             }
         },
         "@types/react-datepicker": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-3.0.2.tgz",
-            "integrity": "sha512-xW04NZRF+9ZnzOD3XrlIzBEKgUsN6LVgZJJsXH8NIUlVjyPh+sdtLPfVoDp+GQzGq1M0TuMLNZsv0sJ3N9XwDA==",
+            "version": "3.1.8",
+            "resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-3.1.8.tgz",
+            "integrity": "sha512-RFEg7++xhosMq02i2lsuaUPEbZGn66U3dxtvw9LU/ZRqLkBGr9Ft2LTz6vbeYYVtaBdOr0NcQatOLnlfUaS8kw==",
             "dev": true,
             "requires": {
                 "@types/react": "*",
                 "date-fns": "^2.0.1",
                 "popper.js": "^1.14.1"
+            },
+            "dependencies": {
+                "popper.js": {
+                    "version": "1.16.1",
+                    "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
+                    "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
+                    "dev": true
+                }
             }
         },
         "@types/react-dom": {
-            "version": "16.9.8",
-            "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.8.tgz",
-            "integrity": "sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA==",
+            "version": "16.9.14",
+            "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.14.tgz",
+            "integrity": "sha512-FIX2AVmPTGP30OUJ+0vadeIFJJ07Mh1m+U0rxfgyW34p3rTlXI+nlenvAxNn4BP36YyI9IJ/+UJ7Wu22N1pI7A==",
             "dev": true,
             "requires": {
-                "@types/react": "*"
+                "@types/react": "^16"
             }
         },
         "@types/react-grid-layout": {
-            "version": "0.17.1",
-            "resolved": "https://registry.npmjs.org/@types/react-grid-layout/-/react-grid-layout-0.17.1.tgz",
-            "integrity": "sha512-1ssQjX3X2A89jx94jECJ0Ze2EHFRYlBHjRh2pnlwjJj1WaEijXUNvwKnUzKwgNFnyZ91Pzqu9Z3V7Atzi9ge7A==",
+            "version": "0.17.2",
+            "resolved": "https://registry.npmjs.org/@types/react-grid-layout/-/react-grid-layout-0.17.2.tgz",
+            "integrity": "sha512-iLKQ5rI9KyPGs/BlWmeBfoikRnoOmTuddK8V5mOCgJ46e2JaecZuPhuIX1kV0mkQSk6X3XoEZALVW0XQbz9g5Q==",
             "dev": true,
             "requires": {
                 "@types/react": "*"
             }
         },
         "@types/react-measure": {
-            "version": "2.0.6",
-            "resolved": "https://registry.npmjs.org/@types/react-measure/-/react-measure-2.0.6.tgz",
-            "integrity": "sha512-FxAwgDVKvxm4SPXu24x9cwzsty8x33UueazHcpxM1UWZlGJI57yIHM2djE3xUJhYVxuzNzi4E8UL3kmCkdh+4A==",
+            "version": "2.0.8",
+            "resolved": "https://registry.npmjs.org/@types/react-measure/-/react-measure-2.0.8.tgz",
+            "integrity": "sha512-Pu4/hQ/1AKVN6efoawtcM+l376WYOI8e1fiM6ir4pdLkHilDCkJLjUGvAm0mWKJ0GE6hzu55yCrcJ/xNyEdFwA==",
             "dev": true,
             "requires": {
                 "@types/react": "*"
             }
         },
         "@types/react-reconciler": {
-            "version": "0.26.1",
-            "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.1.tgz",
-            "integrity": "sha512-jeizEH5o/k6tv42RYNbaulR9KvoSM4RAUq1Q2SXd2HZ7dqgTxN9OIf+GU/sErT7CohB/mUxy9hSjaRLiCPGF5w==",
+            "version": "0.26.4",
+            "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.4.tgz",
+            "integrity": "sha512-bdx4aIBkQRDAnzc23JBFeZmVpmfLJHfHikmQukEt9qs4bQtq9f+PDbNwhR9u74FkIUyIDz1I1qJ8OF6RwadKpw==",
             "requires": {
                 "@types/react": "*"
             }
         },
+        "@types/react-redux": {
+            "version": "7.1.22",
+            "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.22.tgz",
+            "integrity": "sha512-GxIA1kM7ClU73I6wg9IRTVwSO9GS+SAKZKe0Enj+82HMU6aoESFU2HNAdNi3+J53IaOHPiUfT3kSG4L828joDQ==",
+            "requires": {
+                "@types/hoist-non-react-statics": "^3.3.0",
+                "@types/react": "*",
+                "hoist-non-react-statics": "^3.3.0",
+                "redux": "^4.0.0"
+            }
+        },
         "@types/react-select": {
-            "version": "3.0.13",
-            "resolved": "https://registry.npmjs.org/@types/react-select/-/react-select-3.0.13.tgz",
-            "integrity": "sha512-JxmSArGgzAOtb37+Jz2+3av8rVmp/3s3DGwlcP+g59/a3owkiuuU4/Jajd+qA32beDPHy4gJR2kkxagPY3j9kg==",
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/@types/react-select/-/react-select-3.1.2.tgz",
+            "integrity": "sha512-ygvR/2FL87R2OLObEWFootYzkvm67LRA+URYEAcBuvKk7IXmdsnIwSGm60cVXGaqkJQHozb2Cy1t94tCYb6rJA==",
             "dev": true,
             "requires": {
                 "@types/react": "*",
@@ -1424,34 +27701,35 @@
             }
         },
         "@types/react-table": {
-            "version": "6.8.7",
-            "resolved": "https://registry.npmjs.org/@types/react-table/-/react-table-6.8.7.tgz",
-            "integrity": "sha512-1U0xl47jk0BzE+HNHgxZYSLvtybSvnlLhOpW9Mfqf9iuRm/fGqgRab3TKivPCY6Tl7WPFM2hWEJ1GnsuSFc9AQ==",
+            "version": "6.8.9",
+            "resolved": "https://registry.npmjs.org/@types/react-table/-/react-table-6.8.9.tgz",
+            "integrity": "sha512-fVQXjy/EYDbgraScgjDONA291McKqGrw0R0NeK639fx2bS4T19TnXMjg3FjOPlkI3qYTQtFTPADlRYysaQIMpA==",
+            "dev": true,
             "requires": {
                 "@types/react": "*"
             }
         },
         "@types/react-transition-group": {
-            "version": "4.4.0",
-            "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz",
-            "integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==",
+            "version": "4.4.4",
+            "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz",
+            "integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==",
             "requires": {
                 "@types/react": "*"
             }
         },
         "@types/reactcss": {
-            "version": "1.2.3",
-            "resolved": "https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.3.tgz",
-            "integrity": "sha512-d2gQQ0IL6hXLnoRfVYZukQNWHuVsE75DzFTLPUuyyEhJS8G2VvlE+qfQQ91SJjaMqlURRCNIsX7Jcsw6cEuJlA==",
+            "version": "1.2.6",
+            "resolved": "https://registry.npmjs.org/@types/reactcss/-/reactcss-1.2.6.tgz",
+            "integrity": "sha512-qaIzpCuXNWomGR1Xq8SCFTtF4v8V27Y6f+b9+bzHiv087MylI/nTCqqdChNeWS7tslgROmYB7yeiruWX7WnqNg==",
             "dev": true,
             "requires": {
                 "@types/react": "*"
             }
         },
         "@types/request": {
-            "version": "2.48.5",
-            "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.5.tgz",
-            "integrity": "sha512-/LO7xRVnL3DxJ1WkPGDQrp4VTV1reX9RkC85mJ+Qzykj2Bdw+mG15aAfDahc76HtknjzE16SX/Yddn6MxVbmGQ==",
+            "version": "2.48.8",
+            "resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.8.tgz",
+            "integrity": "sha512-whjk1EDJPcAR2kYHRbFl/lKeeKYTi05A15K9bnLInCVroNDCtXce57xKdI0/rQaA3K+6q0eFyUBPmqfSndUZdQ==",
             "dev": true,
             "requires": {
                 "@types/caseless": "*",
@@ -1461,37 +27739,51 @@
             }
         },
         "@types/request-promise": {
-            "version": "4.1.46",
-            "resolved": "https://registry.npmjs.org/@types/request-promise/-/request-promise-4.1.46.tgz",
-            "integrity": "sha512-3Thpj2Va5m0ji3spaCk8YKrjkZyZc6RqUVOphA0n/Xet66AW/AiOAs5vfXhQIL5NmkaO7Jnun7Nl9NEjJ2zBaw==",
+            "version": "4.1.48",
+            "resolved": "https://registry.npmjs.org/@types/request-promise/-/request-promise-4.1.48.tgz",
+            "integrity": "sha512-sLsfxfwP5G3E3U64QXxKwA6ctsxZ7uKyl4I28pMj3JvV+ztWECRns73GL71KMOOJME5u1A5Vs5dkBqyiR1Zcnw==",
             "dev": true,
             "requires": {
                 "@types/bluebird": "*",
                 "@types/request": "*"
             }
         },
+        "@types/responselike": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
+            "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
+            "requires": {
+                "@types/node": "*"
+            }
+        },
         "@types/reveal": {
             "version": "3.3.33",
             "resolved": "https://registry.npmjs.org/@types/reveal/-/reveal-3.3.33.tgz",
             "integrity": "sha512-lKbezA9Oa5LfdSRwFDc/FHEGH4+FjiXh/a/PCSZAmN+KCeQJL/3ClOdAQwOxt3zdHc8XyioT+cNvIOletwRI7A=="
         },
         "@types/rimraf": {
-            "version": "2.0.4",
-            "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-2.0.4.tgz",
-            "integrity": "sha512-8gBudvllD2A/c0CcEX/BivIDorHFt5UI5m46TsNj8DjWCCTTZT74kEe4g+QsY7P/B9WdO98d82zZgXO/RQzu2Q==",
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-2.0.5.tgz",
+            "integrity": "sha512-YyP+VfeaqAyFmXoTh3HChxOQMyjByRMsHU7kc5KOJkSlXudhMhQIALbYV7rHh/l8d2lX3VUQzprrcAgWdRuU8g==",
             "dev": true,
             "requires": {
                 "@types/glob": "*",
                 "@types/node": "*"
             }
         },
+        "@types/scheduler": {
+            "version": "0.16.2",
+            "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
+            "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
+        },
         "@types/serve-static": {
-            "version": "1.13.3",
-            "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz",
-            "integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==",
+            "version": "1.13.10",
+            "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz",
+            "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==",
+            "dev": true,
             "requires": {
-                "@types/express-serve-static-core": "*",
-                "@types/mime": "*"
+                "@types/mime": "^1",
+                "@types/node": "*"
             }
         },
         "@types/sharp": {
@@ -1504,9 +27796,9 @@
             }
         },
         "@types/shelljs": {
-            "version": "0.8.8",
-            "resolved": "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.8.8.tgz",
-            "integrity": "sha512-lD3LWdg6j8r0VRBFahJVaxoW0SIcswxKaFUrmKl33RJVeeoNYQAz4uqCJ5Z6v4oIBOsC5GozX+I5SorIKiTcQA==",
+            "version": "0.8.11",
+            "resolved": "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.8.11.tgz",
+            "integrity": "sha512-x9yaMvEh5BEaZKeVQC4vp3l+QoFj3BXcd4aYfuKSzIIyihjdVARAadYy3SMNIz0WCCdS2vB9JL/U6GQk5PaxQw==",
             "dev": true,
             "requires": {
                 "@types/glob": "*",
@@ -1514,27 +27806,37 @@
             }
         },
         "@types/sizzle": {
-            "version": "2.3.2",
-            "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz",
-            "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==",
+            "version": "2.3.3",
+            "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz",
+            "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==",
             "dev": true
         },
         "@types/socket.io": {
-            "version": "2.1.8",
-            "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.8.tgz",
-            "integrity": "sha512-NIQfh9WwJuJKlgmby4NgwMpoBOmNPCDgaRNPiLYZBtkbHkszK/9R52B5yGkd5a34rbVdAADuo8FhOS/5AZDemw==",
+            "version": "2.1.13",
+            "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.13.tgz",
+            "integrity": "sha512-JRgH3nCgsWel4OPANkhH8TelpXvacAJ9VeryjuqCDiaVDMpLysd6sbt0dr6Z15pqH3p2YpOT3T1C5vQ+O/7uyg==",
             "dev": true,
             "requires": {
                 "@types/engine.io": "*",
-                "@types/node": "*"
+                "@types/node": "*",
+                "@types/socket.io-parser": "*"
             }
         },
         "@types/socket.io-client": {
-            "version": "1.4.33",
-            "resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.33.tgz",
-            "integrity": "sha512-m4LnxkljsI9fMsjwpW5QhRpMixo2BeeLpFmg0AE+sS4H1pzAd/cs/ftTiL60FLZgfFa8PFRPx5KsHu8O0bADKQ==",
+            "version": "1.4.36",
+            "resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.36.tgz",
+            "integrity": "sha512-ZJWjtFBeBy1kRSYpVbeGYTElf6BqPQUkXDlHHD4k/42byCN5Rh027f4yARHCink9sKAkbtGZXEAmR0ZCnc2/Ag==",
             "dev": true
         },
+        "@types/socket.io-parser": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/@types/socket.io-parser/-/socket.io-parser-3.0.0.tgz",
+            "integrity": "sha512-Ry/rbTE6HQNL9eu3LpL1Ocup5VexXu1bSSGlSho/IR5LuRc8YvxwSNJ3JxqTltVJEATLbZkMQETSbxfKNgp4Ew==",
+            "dev": true,
+            "requires": {
+                "socket.io-parser": "*"
+            }
+        },
         "@types/source-list-map": {
             "version": "0.1.2",
             "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
@@ -1553,10 +27855,18 @@
             "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==",
             "dev": true
         },
+        "@types/supercluster": {
+            "version": "7.1.0",
+            "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.0.tgz",
+            "integrity": "sha512-6JapQ2GmEkH66r23BK49I+u6zczVDGTtiJEVvKDYZVSm/vepWaJuTq6BXzJ6I4agG5s8vA1KM7m/gXWDg03O4Q==",
+            "requires": {
+                "@types/geojson": "*"
+            }
+        },
         "@types/tapable": {
-            "version": "1.0.7",
-            "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.7.tgz",
-            "integrity": "sha512-0VBprVqfgFD7Ehb2vd8Lh9TG3jP98gvr8rgehQqzztZNI7o8zS8Ad4jyZneKELphpuE212D8J70LnSNQSyO6bQ==",
+            "version": "1.0.8",
+            "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.8.tgz",
+            "integrity": "sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==",
             "dev": true
         },
         "@types/three": {
@@ -1565,10 +27875,9 @@
             "integrity": "sha512-6JqTgijtfXcTJik8NtiNxr2L90ex6ElM00qilOGeUcrEsJLOdzLJSIkXHUYS+KPAYQYtRJQKD6XaXds3HjS+gg=="
         },
         "@types/tough-cookie": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz",
-            "integrity": "sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==",
-            "dev": true
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz",
+            "integrity": "sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg=="
         },
         "@types/typescript": {
             "version": "2.0.0",
@@ -1580,9 +27889,9 @@
             }
         },
         "@types/uglify-js": {
-            "version": "3.13.0",
-            "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.0.tgz",
-            "integrity": "sha512-EGkrJD5Uy+Pg0NUR8uA4bJ5WMfljyad0G+784vLCNUkD+QwOJXUbBYExXfVGf7YtyzdQp3L/XMYcliB987kL5Q==",
+            "version": "3.13.1",
+            "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.1.tgz",
+            "integrity": "sha512-O3MmRAk6ZuAKa9CHgg0Pr0+lUOqoMLpc9AS4R8ano2auvsg7IE8syF3Xh/NPr26TWklxYcqoEEFdzLLs1fV9PQ==",
             "dev": true,
             "requires": {
                 "source-map": "^0.6.1"
@@ -1597,9 +27906,9 @@
             }
         },
         "@types/uuid": {
-            "version": "3.4.9",
-            "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.9.tgz",
-            "integrity": "sha512-XDwyIlt/47l2kWLTzw/mtrpLdB+GPSskR2n/PIcPn+VYhVO77rGhRncIR5GPU0KRzXuqkDO+J5qqrG0Y8P6jzQ==",
+            "version": "3.4.10",
+            "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.10.tgz",
+            "integrity": "sha512-BgeaZuElf7DEYZhWYDTc/XcLZXdVgFkVSTa13BqKvbnmUrxr3TJFKofUxCtDO9UQOdhnV+HPOESdHiHKZOJV1A==",
             "dev": true
         },
         "@types/valid-url": {
@@ -1608,20 +27917,35 @@
             "integrity": "sha512-+33x29mg+ecU88ODdWpqaie2upIuRkhujVLA7TuJjM823cNMbeggfI6NhxewaRaRF8dy+g33e4uIg/m5Mb3xDQ==",
             "dev": true
         },
+        "@types/web": {
+            "version": "0.0.53",
+            "resolved": "https://registry.npmjs.org/@types/web/-/web-0.0.53.tgz",
+            "integrity": "sha512-ULJJgHevSiiD/QaSWcpAfWHXHEaeXUXGGJ82XVderV4TAWdI22NFyjWcRfzJwjEIdUi0gvfVRlTFDeLTZpvF4Q=="
+        },
         "@types/webpack": {
-            "version": "4.41.27",
-            "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.27.tgz",
-            "integrity": "sha512-wK/oi5gcHi72VMTbOaQ70VcDxSQ1uX8S2tukBK9ARuGXrYM/+u4ou73roc7trXDNmCxCoerE8zruQqX/wuHszA==",
+            "version": "4.41.32",
+            "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.32.tgz",
+            "integrity": "sha512-cb+0ioil/7oz5//7tZUSwbrSAN/NWHrQylz5cW8G0dWTcF/g+/dSdMlKVZspBYuMAN1+WnwHrkxiRrLcwd0Heg==",
             "dev": true,
             "requires": {
-                "@types/anymatch": "*",
                 "@types/node": "*",
                 "@types/tapable": "^1",
                 "@types/uglify-js": "*",
                 "@types/webpack-sources": "*",
+                "anymatch": "^3.0.0",
                 "source-map": "^0.6.0"
             },
             "dependencies": {
+                "anymatch": {
+                    "version": "3.1.2",
+                    "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+                    "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+                    "dev": true,
+                    "requires": {
+                        "normalize-path": "^3.0.0",
+                        "picomatch": "^2.0.4"
+                    }
+                },
                 "source-map": {
                     "version": "0.6.1",
                     "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -1630,32 +27954,29 @@
                 }
             }
         },
-        "@types/webpack-dev-middleware": {
-            "version": "2.0.7",
-            "resolved": "https://registry.npmjs.org/@types/webpack-dev-middleware/-/webpack-dev-middleware-2.0.7.tgz",
-            "integrity": "sha512-pArv7YnqpbSCOBWiRlR1KgqVorlzkIiwto66hI8Gdgp5ATQAC+Ug4QVz69ZI1dP+D5Rs8QWnbT81tE4tcMuTag==",
-            "dev": true,
-            "requires": {
-                "@types/connect": "*",
-                "@types/memory-fs": "*",
-                "@types/webpack": "^4",
-                "loglevel": "^1.6.2"
-            }
-        },
         "@types/webpack-hot-middleware": {
-            "version": "2.25.4",
-            "resolved": "https://registry.npmjs.org/@types/webpack-hot-middleware/-/webpack-hot-middleware-2.25.4.tgz",
-            "integrity": "sha512-6tQb9EBKIANZYUVLQYWiWfDFVe7FhXSj4bB2EF5QB7VtYWL3HDR+y/zqjZPAnCorv0spLqVMRqjRK8AmhfocMw==",
+            "version": "2.25.6",
+            "resolved": "https://registry.npmjs.org/@types/webpack-hot-middleware/-/webpack-hot-middleware-2.25.6.tgz",
+            "integrity": "sha512-1Q9ClNvZR30HIsEAHYQL3bXJK1K7IsrqjGMTfIureFjphsGOZ3TkbeoCupbCmi26pSLjVTPHp+pFrJNpOkBSVg==",
             "dev": true,
             "requires": {
                 "@types/connect": "*",
-                "@types/webpack": "^4"
+                "tapable": "^2.2.0",
+                "webpack": "^5"
+            },
+            "dependencies": {
+                "tapable": {
+                    "version": "2.2.1",
+                    "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+                    "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+                    "dev": true
+                }
             }
         },
         "@types/webpack-sources": {
-            "version": "2.1.0",
-            "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-2.1.0.tgz",
-            "integrity": "sha512-LXn/oYIpBeucgP1EIJbKQ2/4ZmpvRl+dlrFdX7+94SKRUV3Evy3FsfMZY318vGhkWUS5MPhtOM3w1/hCOAOXcg==",
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-3.2.0.tgz",
+            "integrity": "sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg==",
             "dev": true,
             "requires": {
                 "@types/node": "*",
@@ -1672,18 +27993,21 @@
             }
         },
         "@types/webscopeio__react-textarea-autocomplete": {
-            "version": "4.6.1",
-            "resolved": "https://registry.npmjs.org/@types/webscopeio__react-textarea-autocomplete/-/webscopeio__react-textarea-autocomplete-4.6.1.tgz",
-            "integrity": "sha512-rdiDMsTbyFJRbC2BYKIgiAFG/SFkaDGYQYfzo3U2T+EjMCE0JZ8IHZ9nZrJ2Tm83Blrj66cnaEc0H3iwwRqQMA==",
+            "version": "4.7.2",
+            "resolved": "https://registry.npmjs.org/@types/webscopeio__react-textarea-autocomplete/-/webscopeio__react-textarea-autocomplete-4.7.2.tgz",
+            "integrity": "sha512-e1DZGD+eH19BnllTWCGXAdrMa2kI53wEMuhn/d+wUmnu8//ZI6BiuK/EPdw07fI4+tlyo5qdPZdXdpkoXHJVOw==",
             "requires": {
                 "@types/react": "*"
             }
         },
         "@types/xregexp": {
-            "version": "4.3.0",
-            "resolved": "https://registry.npmjs.org/@types/xregexp/-/xregexp-4.3.0.tgz",
-            "integrity": "sha512-3gJTS9gt27pS7U9q5IVqo4YvKSlkf2ck8ish6etuDj6LIRxkL/2Y8RMUtK/QzvE1Yv2zwWV5yemI2BS0GGGFnA==",
-            "dev": true
+            "version": "4.4.0",
+            "resolved": "https://registry.npmjs.org/@types/xregexp/-/xregexp-4.4.0.tgz",
+            "integrity": "sha512-RJJHNci1sRRq8nZjWxzCbQdLhJVq+JcDHpsdzoTtFAR9qdsMhAWqKQ1NHsNcenKDtLsOwCBe/kfSKM82yXtocg==",
+            "dev": true,
+            "requires": {
+                "xregexp": "*"
+            }
         },
         "@types/yauzl": {
             "version": "2.9.1",
@@ -1701,184 +28025,160 @@
             "dev": true
         },
         "@webassemblyjs/ast": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
-            "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==",
-            "dev": true,
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
+            "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==",
             "requires": {
-                "@webassemblyjs/helper-module-context": "1.9.0",
-                "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
-                "@webassemblyjs/wast-parser": "1.9.0"
+                "@webassemblyjs/helper-numbers": "1.11.1",
+                "@webassemblyjs/helper-wasm-bytecode": "1.11.1"
             }
         },
         "@webassemblyjs/floating-point-hex-parser": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz",
-            "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==",
-            "dev": true
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz",
+            "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ=="
         },
         "@webassemblyjs/helper-api-error": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz",
-            "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==",
-            "dev": true
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz",
+            "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg=="
         },
         "@webassemblyjs/helper-buffer": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz",
-            "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==",
-            "dev": true
-        },
-        "@webassemblyjs/helper-code-frame": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz",
-            "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==",
-            "dev": true,
-            "requires": {
-                "@webassemblyjs/wast-printer": "1.9.0"
-            }
-        },
-        "@webassemblyjs/helper-fsm": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz",
-            "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==",
-            "dev": true
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz",
+            "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA=="
         },
-        "@webassemblyjs/helper-module-context": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz",
-            "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==",
-            "dev": true,
+        "@webassemblyjs/helper-numbers": {
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz",
+            "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==",
             "requires": {
-                "@webassemblyjs/ast": "1.9.0"
+                "@webassemblyjs/floating-point-hex-parser": "1.11.1",
+                "@webassemblyjs/helper-api-error": "1.11.1",
+                "@xtuc/long": "4.2.2"
             }
         },
         "@webassemblyjs/helper-wasm-bytecode": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz",
-            "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==",
-            "dev": true
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz",
+            "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q=="
         },
         "@webassemblyjs/helper-wasm-section": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz",
-            "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==",
-            "dev": true,
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz",
+            "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==",
             "requires": {
-                "@webassemblyjs/ast": "1.9.0",
-                "@webassemblyjs/helper-buffer": "1.9.0",
-                "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
-                "@webassemblyjs/wasm-gen": "1.9.0"
+                "@webassemblyjs/ast": "1.11.1",
+                "@webassemblyjs/helper-buffer": "1.11.1",
+                "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+                "@webassemblyjs/wasm-gen": "1.11.1"
             }
         },
         "@webassemblyjs/ieee754": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz",
-            "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==",
-            "dev": true,
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz",
+            "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==",
             "requires": {
                 "@xtuc/ieee754": "^1.2.0"
             }
         },
         "@webassemblyjs/leb128": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz",
-            "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==",
-            "dev": true,
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz",
+            "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==",
             "requires": {
                 "@xtuc/long": "4.2.2"
             }
         },
         "@webassemblyjs/utf8": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz",
-            "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==",
-            "dev": true
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz",
+            "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ=="
         },
         "@webassemblyjs/wasm-edit": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz",
-            "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==",
-            "dev": true,
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz",
+            "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==",
             "requires": {
-                "@webassemblyjs/ast": "1.9.0",
-                "@webassemblyjs/helper-buffer": "1.9.0",
-                "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
-                "@webassemblyjs/helper-wasm-section": "1.9.0",
-                "@webassemblyjs/wasm-gen": "1.9.0",
-                "@webassemblyjs/wasm-opt": "1.9.0",
-                "@webassemblyjs/wasm-parser": "1.9.0",
-                "@webassemblyjs/wast-printer": "1.9.0"
+                "@webassemblyjs/ast": "1.11.1",
+                "@webassemblyjs/helper-buffer": "1.11.1",
+                "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+                "@webassemblyjs/helper-wasm-section": "1.11.1",
+                "@webassemblyjs/wasm-gen": "1.11.1",
+                "@webassemblyjs/wasm-opt": "1.11.1",
+                "@webassemblyjs/wasm-parser": "1.11.1",
+                "@webassemblyjs/wast-printer": "1.11.1"
             }
         },
         "@webassemblyjs/wasm-gen": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz",
-            "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==",
-            "dev": true,
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz",
+            "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==",
             "requires": {
-                "@webassemblyjs/ast": "1.9.0",
-                "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
-                "@webassemblyjs/ieee754": "1.9.0",
-                "@webassemblyjs/leb128": "1.9.0",
-                "@webassemblyjs/utf8": "1.9.0"
+                "@webassemblyjs/ast": "1.11.1",
+                "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+                "@webassemblyjs/ieee754": "1.11.1",
+                "@webassemblyjs/leb128": "1.11.1",
+                "@webassemblyjs/utf8": "1.11.1"
             }
         },
         "@webassemblyjs/wasm-opt": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz",
-            "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==",
-            "dev": true,
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz",
+            "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==",
             "requires": {
-                "@webassemblyjs/ast": "1.9.0",
-                "@webassemblyjs/helper-buffer": "1.9.0",
-                "@webassemblyjs/wasm-gen": "1.9.0",
-                "@webassemblyjs/wasm-parser": "1.9.0"
+                "@webassemblyjs/ast": "1.11.1",
+                "@webassemblyjs/helper-buffer": "1.11.1",
+                "@webassemblyjs/wasm-gen": "1.11.1",
+                "@webassemblyjs/wasm-parser": "1.11.1"
             }
         },
         "@webassemblyjs/wasm-parser": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz",
-            "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==",
-            "dev": true,
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz",
+            "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==",
             "requires": {
-                "@webassemblyjs/ast": "1.9.0",
-                "@webassemblyjs/helper-api-error": "1.9.0",
-                "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
-                "@webassemblyjs/ieee754": "1.9.0",
-                "@webassemblyjs/leb128": "1.9.0",
-                "@webassemblyjs/utf8": "1.9.0"
+                "@webassemblyjs/ast": "1.11.1",
+                "@webassemblyjs/helper-api-error": "1.11.1",
+                "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+                "@webassemblyjs/ieee754": "1.11.1",
+                "@webassemblyjs/leb128": "1.11.1",
+                "@webassemblyjs/utf8": "1.11.1"
             }
         },
-        "@webassemblyjs/wast-parser": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz",
-            "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==",
-            "dev": true,
+        "@webassemblyjs/wast-printer": {
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz",
+            "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==",
             "requires": {
-                "@webassemblyjs/ast": "1.9.0",
-                "@webassemblyjs/floating-point-hex-parser": "1.9.0",
-                "@webassemblyjs/helper-api-error": "1.9.0",
-                "@webassemblyjs/helper-code-frame": "1.9.0",
-                "@webassemblyjs/helper-fsm": "1.9.0",
+                "@webassemblyjs/ast": "1.11.1",
                 "@xtuc/long": "4.2.2"
             }
         },
-        "@webassemblyjs/wast-printer": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz",
-            "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==",
-            "dev": true,
+        "@webpack-cli/configtest": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.1.tgz",
+            "integrity": "sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg==",
+            "requires": {}
+        },
+        "@webpack-cli/info": {
+            "version": "1.4.1",
+            "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.1.tgz",
+            "integrity": "sha512-PKVGmazEq3oAo46Q63tpMr4HipI3OPfP7LiNOEJg963RMgT0rqheag28NCML0o3GIzA3DmxP1ZIAv9oTX1CUIA==",
             "requires": {
-                "@webassemblyjs/ast": "1.9.0",
-                "@webassemblyjs/wast-parser": "1.9.0",
-                "@xtuc/long": "4.2.2"
+                "envinfo": "^7.7.3"
             }
         },
+        "@webpack-cli/serve": {
+            "version": "1.6.1",
+            "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.1.tgz",
+            "integrity": "sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw==",
+            "requires": {}
+        },
         "@webscopeio/react-textarea-autocomplete": {
-            "version": "4.7.0",
-            "resolved": "https://registry.npmjs.org/@webscopeio/react-textarea-autocomplete/-/react-textarea-autocomplete-4.7.0.tgz",
-            "integrity": "sha512-uzH7PUlXQk6jCTh1eGZdXjvjxveZpznhQywX5I5ojsXgre+XSzlK66chdDdfi+lQABC3N3TzfSq6Hlxa8hlrLA==",
+            "version": "4.9.1",
+            "resolved": "https://registry.npmjs.org/@webscopeio/react-textarea-autocomplete/-/react-textarea-autocomplete-4.9.1.tgz",
+            "integrity": "sha512-g5cnRRXQQBGH5Km/0eSq8xFlnHZeG2WVRr+uu/3PKbHcgvNUIHKEy2tjOA9m4MK8KFzbFtfnj2MqdPf9R+5/Cw==",
             "requires": {
                 "custom-event": "^1.0.1",
                 "textarea-caret": "3.0.2"
@@ -1894,14 +28194,12 @@
         "@xtuc/ieee754": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
-            "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
-            "dev": true
+            "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA=="
         },
         "@xtuc/long": {
             "version": "4.2.2",
             "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
-            "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
-            "dev": true
+            "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
         },
         "abab": {
             "version": "2.0.3",
@@ -1923,12 +28221,27 @@
             }
         },
         "accepts": {
-            "version": "1.3.7",
-            "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
-            "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
+            "version": "1.3.8",
+            "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+            "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
             "requires": {
-                "mime-types": "~2.1.24",
-                "negotiator": "0.6.2"
+                "mime-types": "~2.1.34",
+                "negotiator": "0.6.3"
+            },
+            "dependencies": {
+                "mime-db": {
+                    "version": "1.51.0",
+                    "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
+                    "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g=="
+                },
+                "mime-types": {
+                    "version": "2.1.34",
+                    "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
+                    "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
+                    "requires": {
+                        "mime-db": "1.51.0"
+                    }
+                }
             }
         },
         "acorn": {
@@ -1951,10 +28264,17 @@
                 }
             }
         },
+        "acorn-import-assertions": {
+            "version": "1.8.0",
+            "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
+            "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==",
+            "requires": {}
+        },
         "acorn-jsx": {
-            "version": "5.2.0",
-            "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz",
-            "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ=="
+            "version": "5.3.2",
+            "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+            "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+            "requires": {}
         },
         "acorn-walk": {
             "version": "6.2.0",
@@ -2004,12 +28324,40 @@
         "ajv-errors": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
-            "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ=="
+            "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
+            "requires": {}
+        },
+        "ajv-formats": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
+            "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+            "requires": {
+                "ajv": "^8.0.0"
+            },
+            "dependencies": {
+                "ajv": {
+                    "version": "8.10.0",
+                    "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz",
+                    "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==",
+                    "requires": {
+                        "fast-deep-equal": "^3.1.1",
+                        "json-schema-traverse": "^1.0.0",
+                        "require-from-string": "^2.0.2",
+                        "uri-js": "^4.2.2"
+                    }
+                },
+                "json-schema-traverse": {
+                    "version": "1.0.0",
+                    "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+                    "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+                }
+            }
         },
         "ajv-keywords": {
             "version": "3.4.1",
             "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz",
-            "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ=="
+            "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
+            "requires": {}
         },
         "align-text": {
             "version": "0.1.4",
@@ -2069,10 +28417,10 @@
                 }
             }
         },
-        "ansi-html": {
-            "version": "0.0.7",
-            "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
-            "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=",
+        "ansi-html-community": {
+            "version": "0.0.8",
+            "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz",
+            "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==",
             "dev": true
         },
         "ansi-regex": {
@@ -2348,60 +28696,13 @@
             "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
         },
         "asn1": {
-            "version": "0.2.4",
-            "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
-            "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+            "version": "0.2.6",
+            "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
+            "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
             "requires": {
                 "safer-buffer": "~2.1.0"
             }
         },
-        "asn1.js": {
-            "version": "5.4.1",
-            "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
-            "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
-            "dev": true,
-            "requires": {
-                "bn.js": "^4.0.0",
-                "inherits": "^2.0.1",
-                "minimalistic-assert": "^1.0.0",
-                "safer-buffer": "^2.1.0"
-            },
-            "dependencies": {
-                "bn.js": {
-                    "version": "4.12.0",
-                    "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
-                    "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
-                    "dev": true
-                }
-            }
-        },
-        "assert": {
-            "version": "1.5.0",
-            "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz",
-            "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==",
-            "dev": true,
-            "requires": {
-                "object-assign": "^4.1.1",
-                "util": "0.10.3"
-            },
-            "dependencies": {
-                "inherits": {
-                    "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
-                    "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
-                    "dev": true
-                },
-                "util": {
-                    "version": "0.10.3",
-                    "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
-                    "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
-                    "dev": true,
-                    "requires": {
-                        "inherits": "2.0.1"
-                    }
-                }
-            }
-        },
         "assert-plus": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
@@ -2438,7 +28739,8 @@
         "async-limiter": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
-            "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
+            "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
+            "dev": true
         },
         "asynckit": {
             "version": "0.4.0",
@@ -2450,6 +28752,11 @@
             "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
             "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
         },
+        "available-typed-arrays": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
+            "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw=="
+        },
         "awesome-typescript-loader": {
             "version": "5.2.1",
             "resolved": "https://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-5.2.1.tgz",
@@ -2472,9 +28779,9 @@
             "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
         },
         "aws4": {
-            "version": "1.9.1",
-            "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
-            "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
+            "version": "1.11.0",
+            "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
+            "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA=="
         },
         "axios": {
             "version": "0.19.2",
@@ -2507,6 +28814,11 @@
                 }
             }
         },
+        "babel": {
+            "version": "6.23.0",
+            "resolved": "https://registry.npmjs.org/babel/-/babel-6.23.0.tgz",
+            "integrity": "sha1-0NHn2APpdHZb7qMjLU4VPA77kPQ="
+        },
         "babel-code-frame": {
             "version": "6.26.0",
             "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
@@ -2567,9 +28879,9 @@
             }
         },
         "babel-plugin-emotion": {
-            "version": "10.0.33",
-            "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.0.33.tgz",
-            "integrity": "sha512-bxZbTTGz0AJQDHm8k6Rf3RQJ8tX2scsfsRyKVgAbiUPUNIRtlK+7JxP+TAd1kRLABFxe0CFm2VdK4ePkoA9FxQ==",
+            "version": "10.2.2",
+            "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.2.2.tgz",
+            "integrity": "sha512-SMSkGoqTbTyUTDeuVuPIWifPdUGkTk1Kf9BWRiXIOIcuyMfsdp2EjeiiFvOzX8NOBvEh/ypKYvUh2rkgAJMCLA==",
             "requires": {
                 "@babel/helper-module-imports": "^7.0.0",
                 "@emotion/hash": "0.8.0",
@@ -2619,9 +28931,9 @@
             },
             "dependencies": {
                 "core-js": {
-                    "version": "2.6.11",
-                    "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
-                    "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg=="
+                    "version": "2.6.12",
+                    "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
+                    "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
                 },
                 "regenerator-runtime": {
                     "version": "0.11.1",
@@ -2718,10 +29030,15 @@
             "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz",
             "integrity": "sha1-4pf2DX7BAUp6lxo568ipjAtoHnA="
         },
+        "Base64": {
+            "version": "0.2.1",
+            "resolved": "https://registry.npmjs.org/Base64/-/Base64-0.2.1.tgz",
+            "integrity": "sha1-ujpCMHCOGGcFBl5mur3Uw1z2ACg="
+        },
         "base64-arraybuffer": {
-            "version": "0.1.5",
-            "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
-            "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
+            "version": "0.1.4",
+            "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
+            "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
         },
         "base64-js": {
             "version": "1.3.1",
@@ -2757,14 +29074,6 @@
                 "tweetnacl": "^0.14.3"
             }
         },
-        "better-assert": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
-            "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
-            "requires": {
-                "callsite": "1.0.0"
-            }
-        },
         "bezier-curve": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/bezier-curve/-/bezier-curve-1.0.0.tgz",
@@ -2790,11 +29099,6 @@
             "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
             "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw=="
         },
-        "bindings": {
-            "version": "1.3.1",
-            "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.1.tgz",
-            "integrity": "sha512-i47mqjF9UbjxJhxGf+pZ6kSxrnI3wBLlnGI2ArWJ4r0VrvDS7ZYXkprq/pLaBWYq4GM0r4zdHY+NNRqEMU7uew=="
-        },
         "bl": {
             "version": "4.0.2",
             "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz",
@@ -2828,27 +29132,21 @@
             "resolved": "https://registry.npmjs.org/blueimp-load-image/-/blueimp-load-image-2.31.0.tgz",
             "integrity": "sha512-6sTGh1OiUmuH8ftAYvUzALivoOmcnahinGmjZFI4puZVowXoKTn/bXtth7N1skW5AlezEOfjgFH4lNXHeNRQog=="
         },
-        "bn.js": {
-            "version": "5.2.0",
-            "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz",
-            "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==",
-            "dev": true
-        },
         "body-parser": {
-            "version": "1.19.0",
-            "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
-            "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
+            "version": "1.19.2",
+            "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz",
+            "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==",
             "requires": {
-                "bytes": "3.1.0",
+                "bytes": "3.1.2",
                 "content-type": "~1.0.4",
                 "debug": "2.6.9",
                 "depd": "~1.1.2",
-                "http-errors": "1.7.2",
+                "http-errors": "1.8.1",
                 "iconv-lite": "0.4.24",
                 "on-finished": "~2.3.0",
-                "qs": "6.7.0",
-                "raw-body": "2.4.0",
-                "type-is": "~1.6.17"
+                "qs": "6.9.7",
+                "raw-body": "2.4.3",
+                "type-is": "~1.6.18"
             },
             "dependencies": {
                 "debug": {
@@ -2865,9 +29163,9 @@
                     "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                 },
                 "qs": {
-                    "version": "6.7.0",
-                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
-                    "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
+                    "version": "6.9.7",
+                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz",
+                    "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw=="
                 }
             }
         },
@@ -2899,9 +29197,10 @@
             "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
         },
         "bootstrap": {
-            "version": "4.5.0",
-            "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.5.0.tgz",
-            "integrity": "sha512-Z93QoXvodoVslA+PWNdk23Hze4RBYIkpb5h8I2HY2Tu2h7A0LpAgLcyrhrSUyo2/Oxm2l1fRZPs1e5hnxnliXA=="
+            "version": "4.6.1",
+            "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.1.tgz",
+            "integrity": "sha512-0dj+VgI9Ecom+rvvpNZ4MUZJz8dcX7WCX+eTID9+/8HgOkv3dsRzi8BGeZJCQU6flWQVYxwTQnEZFrmJSEO7og==",
+            "requires": {}
         },
         "bowser": {
             "version": "1.9.4",
@@ -2970,11 +29269,10 @@
                 }
             }
         },
-        "brorand": {
-            "version": "1.1.0",
-            "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
-            "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
-            "dev": true
+        "browser-assert": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/browser-assert/-/browser-assert-1.2.1.tgz",
+            "integrity": "sha1-mqpaKox0aFwq4Fv+Ru/WBvBowgA="
         },
         "browser-process-hrtime": {
             "version": "1.0.0",
@@ -2987,92 +29285,26 @@
             "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
             "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw=="
         },
-        "browserify-aes": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
-            "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
-            "dev": true,
-            "requires": {
-                "buffer-xor": "^1.0.3",
-                "cipher-base": "^1.0.0",
-                "create-hash": "^1.1.0",
-                "evp_bytestokey": "^1.0.3",
-                "inherits": "^2.0.1",
-                "safe-buffer": "^5.0.1"
-            }
-        },
-        "browserify-cipher": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
-            "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
-            "dev": true,
-            "requires": {
-                "browserify-aes": "^1.0.4",
-                "browserify-des": "^1.0.0",
-                "evp_bytestokey": "^1.0.0"
-            }
-        },
-        "browserify-des": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
-            "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
-            "dev": true,
-            "requires": {
-                "cipher-base": "^1.0.1",
-                "des.js": "^1.0.0",
-                "inherits": "^2.0.1",
-                "safe-buffer": "^5.1.2"
-            }
-        },
-        "browserify-rsa": {
-            "version": "4.1.0",
-            "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz",
-            "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==",
-            "dev": true,
-            "requires": {
-                "bn.js": "^5.0.0",
-                "randombytes": "^2.0.1"
-            }
-        },
-        "browserify-sign": {
-            "version": "4.2.1",
-            "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz",
-            "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==",
-            "dev": true,
+        "browserslist": {
+            "version": "4.19.3",
+            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.3.tgz",
+            "integrity": "sha512-XK3X4xtKJ+Txj8G5c30B4gsm71s69lqXlkYui4s6EkKxuv49qjYlY6oVd+IFJ73d4YymtM3+djvvt/R/iJwwDg==",
             "requires": {
-                "bn.js": "^5.1.1",
-                "browserify-rsa": "^4.0.1",
-                "create-hash": "^1.2.0",
-                "create-hmac": "^1.1.7",
-                "elliptic": "^6.5.3",
-                "inherits": "^2.0.4",
-                "parse-asn1": "^5.1.5",
-                "readable-stream": "^3.6.0",
-                "safe-buffer": "^5.2.0"
-            },
-            "dependencies": {
-                "safe-buffer": {
-                    "version": "5.2.1",
-                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
-                    "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
-                    "dev": true
-                }
+                "caniuse-lite": "^1.0.30001312",
+                "electron-to-chromium": "^1.4.71",
+                "escalade": "^3.1.1",
+                "node-releases": "^2.0.2",
+                "picocolors": "^1.0.0"
             }
         },
-        "browserify-zlib": {
-            "version": "0.2.0",
-            "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
-            "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
-            "dev": true,
+        "bson": {
+            "version": "4.6.1",
+            "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.1.tgz",
+            "integrity": "sha512-I1LQ7Hz5zgwR4QquilLNZwbhPw0Apx7i7X9kGMBTsqPdml/03Q9NBtD9nt/19ahjlphktQImrnderxqpzeVDjw==",
             "requires": {
-                "pako": "~1.0.5"
+                "buffer": "^5.6.0"
             }
         },
-        "bson": {
-            "version": "1.0.9",
-            "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.9.tgz",
-            "integrity": "sha512-IQX9/h7WdMBIW/q/++tGd+emQr0XMdeZ6icnT/74Xk9fnabWn+gZgpE+9V+gujL3hhJOoNrnDVY7tWdzc7NUTg=="
-        },
         "buffer": {
             "version": "5.6.0",
             "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
@@ -3108,12 +29340,6 @@
             "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz",
             "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E="
         },
-        "buffer-xor": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
-            "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
-            "dev": true
-        },
         "built-in-math-eval": {
             "version": "0.3.0",
             "resolved": "https://registry.npmjs.org/built-in-math-eval/-/built-in-math-eval-0.3.0.tgz",
@@ -3128,16 +29354,10 @@
             "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
             "dev": true
         },
-        "builtin-status-codes": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
-            "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
-            "dev": true
-        },
         "bytes": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
-            "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+            "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="
         },
         "cacache": {
             "version": "10.0.4",
@@ -3203,6 +29423,49 @@
                 "unset-value": "^1.0.0"
             }
         },
+        "cacheable-lookup": {
+            "version": "6.0.4",
+            "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-6.0.4.tgz",
+            "integrity": "sha512-mbcDEZCkv2CZF4G01kr8eBd/5agkt9oCqz75tJMSIsquvRZ2sL6Hi5zGVKi/0OSC9oO1GHfJ2AV0ZIOY9vye0A=="
+        },
+        "cacheable-request": {
+            "version": "7.0.2",
+            "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz",
+            "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==",
+            "requires": {
+                "clone-response": "^1.0.2",
+                "get-stream": "^5.1.0",
+                "http-cache-semantics": "^4.0.0",
+                "keyv": "^4.0.0",
+                "lowercase-keys": "^2.0.0",
+                "normalize-url": "^6.0.1",
+                "responselike": "^2.0.0"
+            },
+            "dependencies": {
+                "get-stream": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+                    "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+                    "requires": {
+                        "pump": "^3.0.0"
+                    }
+                },
+                "lowercase-keys": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+                    "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="
+                }
+            }
+        },
+        "call-bind": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+            "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+            "requires": {
+                "function-bind": "^1.1.1",
+                "get-intrinsic": "^1.0.2"
+            }
+        },
         "caller-callsite": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
@@ -3226,16 +29489,27 @@
                 "caller-callsite": "^2.0.0"
             }
         },
-        "callsite": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
-            "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
-        },
         "callsites": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
             "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
         },
+        "camel-case": {
+            "version": "4.1.2",
+            "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
+            "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
+            "requires": {
+                "pascal-case": "^3.1.2",
+                "tslib": "^2.0.3"
+            },
+            "dependencies": {
+                "tslib": {
+                    "version": "2.3.1",
+                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+                    "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+                }
+            }
+        },
         "camelcase": {
             "version": "5.3.1",
             "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
@@ -3262,40 +29536,25 @@
             "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz",
             "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
         },
+        "caniuse-lite": {
+            "version": "1.0.30001312",
+            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz",
+            "integrity": "sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ=="
+        },
         "canvas": {
-            "version": "2.6.1",
-            "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.6.1.tgz",
-            "integrity": "sha512-S98rKsPcuhfTcYbtF53UIJhcbgIAK533d1kJKMwsMwAIFgfd58MOyxRud3kktlzWiEkFliaJtvyZCBtud/XVEA==",
+            "version": "2.9.0",
+            "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.9.0.tgz",
+            "integrity": "sha512-0l93g7uxp7rMyr7H+XRQ28A3ud0dKIUTIEkUe1Dxh4rjUYN7B93+SjC3r1PDKA18xcQN87OFGgUnyw7LSgNLSQ==",
             "requires": {
-                "nan": "^2.14.0",
-                "node-pre-gyp": "^0.11.0",
+                "@mapbox/node-pre-gyp": "^1.0.0",
+                "nan": "^2.15.0",
                 "simple-get": "^3.0.3"
             },
             "dependencies": {
-                "node-pre-gyp": {
-                    "version": "0.11.0",
-                    "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.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"
-                    }
-                },
-                "rimraf": {
-                    "version": "2.7.1",
-                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
-                    "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
-                    "requires": {
-                        "glob": "^7.1.3"
-                    }
+                "nan": {
+                    "version": "2.15.0",
+                    "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
+                    "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ=="
                 }
             }
         },
@@ -3319,15 +29578,16 @@
             }
         },
         "chai": {
-            "version": "4.2.0",
-            "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
-            "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==",
+            "version": "4.3.6",
+            "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz",
+            "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==",
             "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",
+                "loupe": "^2.3.1",
+                "pathval": "^1.1.1",
                 "type-detect": "^4.0.5"
             }
         },
@@ -3364,30 +29624,6 @@
             "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
             "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII="
         },
-        "cheerio": {
-            "version": "1.0.0-rc.3",
-            "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.1",
-                "htmlparser2": "^3.9.1",
-                "lodash": "^4.15.0",
-                "parse5": "^3.0.1"
-            },
-            "dependencies": {
-                "dom-serializer": {
-                    "version": "0.1.1",
-                    "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
-                    "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==",
-                    "requires": {
-                        "domelementtype": "^1.3.0",
-                        "entities": "^1.1.1"
-                    }
-                }
-            }
-        },
         "child_process": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz",
@@ -3835,22 +30071,22 @@
                             "bundled": true,
                             "optional": true
                         },
-                        "string-width": {
-                            "version": "1.0.2",
+                        "string_decoder": {
+                            "version": "1.1.1",
                             "bundled": true,
                             "optional": true,
                             "requires": {
-                                "code-point-at": "^1.0.0",
-                                "is-fullwidth-code-point": "^1.0.0",
-                                "strip-ansi": "^3.0.0"
+                                "safe-buffer": "~5.1.0"
                             }
                         },
-                        "string_decoder": {
-                            "version": "1.1.1",
+                        "string-width": {
+                            "version": "1.0.2",
                             "bundled": true,
                             "optional": true,
                             "requires": {
-                                "safe-buffer": "~5.1.0"
+                                "code-point-at": "^1.0.0",
+                                "is-fullwidth-code-point": "^1.0.0",
+                                "strip-ansi": "^3.0.0"
                             }
                         },
                         "strip-ansi": {
@@ -3924,24 +30160,13 @@
         "chrome-trace-event": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
-            "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
-            "dev": true
+            "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg=="
         },
         "ci-info": {
             "version": "1.6.0",
             "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz",
             "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A=="
         },
-        "cipher-base": {
-            "version": "1.0.4",
-            "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
-            "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
-            "dev": true,
-            "requires": {
-                "inherits": "^2.0.1",
-                "safe-buffer": "^5.0.1"
-            }
-        },
         "clamp": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz",
@@ -4072,13 +30297,27 @@
             "version": "4.0.1",
             "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
             "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
-            "dev": true,
             "requires": {
                 "is-plain-object": "^2.0.4",
                 "kind-of": "^6.0.2",
                 "shallow-clone": "^3.0.0"
             }
         },
+        "clone-response": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
+            "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
+            "requires": {
+                "mimic-response": "^1.0.0"
+            },
+            "dependencies": {
+                "mimic-response": {
+                    "version": "1.0.1",
+                    "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
+                    "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
+                }
+            }
+        },
         "clsx": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
@@ -4099,12 +30338,12 @@
             }
         },
         "color": {
-            "version": "3.1.2",
-            "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz",
-            "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==",
+            "version": "3.2.1",
+            "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
+            "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
             "requires": {
-                "color-convert": "^1.9.1",
-                "color-string": "^1.5.2"
+                "color-convert": "^1.9.3",
+                "color-string": "^1.6.0"
             }
         },
         "color-convert": {
@@ -4121,14 +30360,24 @@
             "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
         },
         "color-string": {
-            "version": "1.5.3",
-            "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
-            "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
+            "version": "1.9.0",
+            "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz",
+            "integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==",
             "requires": {
                 "color-name": "^1.0.0",
                 "simple-swizzle": "^0.2.2"
             }
         },
+        "color-support": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+            "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="
+        },
+        "colorette": {
+            "version": "2.0.16",
+            "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz",
+            "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g=="
+        },
         "colors": {
             "version": "1.4.0",
             "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
@@ -4361,12 +30610,6 @@
                 }
             }
         },
-        "console-browserify": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
-            "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==",
-            "dev": true
-        },
         "console-control-strings": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
@@ -4383,18 +30626,19 @@
                 "babylon": "^6.18.0"
             }
         },
-        "constants-browserify": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
-            "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
-            "dev": true
-        },
         "content-disposition": {
-            "version": "0.5.3",
-            "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
-            "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
+            "version": "0.5.4",
+            "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+            "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
             "requires": {
-                "safe-buffer": "5.1.2"
+                "safe-buffer": "5.2.1"
+            },
+            "dependencies": {
+                "safe-buffer": {
+                    "version": "5.2.1",
+                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+                    "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+                }
             }
         },
         "content-type": {
@@ -4403,42 +30647,50 @@
             "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
         },
         "convert-source-map": {
-            "version": "1.7.0",
-            "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
-            "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
+            "version": "1.8.0",
+            "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz",
+            "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==",
             "requires": {
                 "safe-buffer": "~5.1.1"
             }
         },
         "cookie": {
-            "version": "0.4.0",
-            "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
-            "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
+            "version": "0.4.1",
+            "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
+            "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
         },
         "cookie-parser": {
-            "version": "1.4.5",
-            "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.5.tgz",
-            "integrity": "sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==",
+            "version": "1.4.6",
+            "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
+            "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
             "requires": {
-                "cookie": "0.4.0",
+                "cookie": "0.4.1",
                 "cookie-signature": "1.0.6"
             }
         },
         "cookie-session": {
-            "version": "2.0.0-rc.1",
-            "resolved": "https://registry.npmjs.org/cookie-session/-/cookie-session-2.0.0-rc.1.tgz",
-            "integrity": "sha512-zg80EsLe7S1J4y0XxV7SZ8Fbi90ZZoampuX2bfYDOvJfc//98sSlZC41YDzTTjtVbeU1VlVdBbldXOOyi5xzEw==",
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/cookie-session/-/cookie-session-2.0.0.tgz",
+            "integrity": "sha512-hKvgoThbw00zQOleSlUr2qpvuNweoqBtxrmx0UFosx6AGi9lYtLoA+RbsvknrEX8Pr6MDbdWAb2j6SnMn+lPsg==",
             "requires": {
                 "cookies": "0.8.0",
-                "debug": "3.2.6",
+                "debug": "3.2.7",
                 "on-headers": "~1.0.2",
-                "safe-buffer": "5.2.0"
+                "safe-buffer": "5.2.1"
             },
             "dependencies": {
+                "debug": {
+                    "version": "3.2.7",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+                    "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+                    "requires": {
+                        "ms": "^2.1.1"
+                    }
+                },
                 "safe-buffer": {
-                    "version": "5.2.0",
-                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
-                    "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
+                    "version": "5.2.1",
+                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+                    "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
                 }
             }
         },
@@ -4532,9 +30784,9 @@
             "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
         },
         "core-js-pure": {
-            "version": "3.6.5",
-            "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz",
-            "integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA=="
+            "version": "3.21.1",
+            "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz",
+            "integrity": "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ=="
         },
         "core-util-is": {
             "version": "1.0.2",
@@ -4579,24 +30831,6 @@
                 "readable-stream": "^3.4.0"
             }
         },
-        "create-ecdh": {
-            "version": "4.0.4",
-            "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz",
-            "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==",
-            "dev": true,
-            "requires": {
-                "bn.js": "^4.1.0",
-                "elliptic": "^6.5.3"
-            },
-            "dependencies": {
-                "bn.js": {
-                    "version": "4.12.0",
-                    "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
-                    "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
-                    "dev": true
-                }
-            }
-        },
         "create-emotion": {
             "version": "10.0.27",
             "resolved": "https://registry.npmjs.org/create-emotion/-/create-emotion-10.0.27.tgz",
@@ -4616,33 +30850,6 @@
                 "capture-stack-trace": "^1.0.0"
             }
         },
-        "create-hash": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
-            "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
-            "dev": true,
-            "requires": {
-                "cipher-base": "^1.0.1",
-                "inherits": "^2.0.1",
-                "md5.js": "^1.3.4",
-                "ripemd160": "^2.0.1",
-                "sha.js": "^2.4.0"
-            }
-        },
-        "create-hmac": {
-            "version": "1.1.7",
-            "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
-            "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
-            "dev": true,
-            "requires": {
-                "cipher-base": "^1.0.3",
-                "create-hash": "^1.1.0",
-                "inherits": "^2.0.1",
-                "ripemd160": "^2.0.0",
-                "safe-buffer": "^5.0.1",
-                "sha.js": "^2.4.8"
-            }
-        },
         "create-react-context": {
             "version": "0.2.3",
             "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.2.3.tgz",
@@ -4652,6 +30859,12 @@
                 "gud": "^1.0.0"
             }
         },
+        "create-require": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+            "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+            "dev": true
+        },
         "cross-env": {
             "version": "5.2.1",
             "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.1.tgz",
@@ -4677,18 +30890,39 @@
             }
         },
         "cross-fetch": {
-            "version": "3.0.4",
-            "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.4.tgz",
-            "integrity": "sha512-MSHgpjQqgbT/94D4CyADeNoYh52zMkCX4pcJvPP5WqPsLFMKjr2TCMg381ox5qI0ii2dPwaLx/00477knXqXVw==",
+            "version": "3.1.5",
+            "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
+            "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
             "requires": {
-                "node-fetch": "2.6.0",
-                "whatwg-fetch": "3.0.0"
+                "node-fetch": "2.6.7"
             },
             "dependencies": {
                 "node-fetch": {
-                    "version": "2.6.0",
-                    "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
-                    "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
+                    "version": "2.6.7",
+                    "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+                    "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+                    "requires": {
+                        "whatwg-url": "^5.0.0"
+                    }
+                },
+                "tr46": {
+                    "version": "0.0.3",
+                    "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+                    "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
+                },
+                "webidl-conversions": {
+                    "version": "3.0.1",
+                    "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+                    "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
+                },
+                "whatwg-url": {
+                    "version": "5.0.0",
+                    "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+                    "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
+                    "requires": {
+                        "tr46": "~0.0.3",
+                        "webidl-conversions": "^3.0.0"
+                    }
                 }
             }
         },
@@ -4717,25 +30951,6 @@
                 }
             }
         },
-        "crypto-browserify": {
-            "version": "3.12.0",
-            "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
-            "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
-            "dev": true,
-            "requires": {
-                "browserify-cipher": "^1.0.0",
-                "browserify-sign": "^4.0.0",
-                "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": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
@@ -4796,24 +31011,54 @@
             }
         },
         "css-select": {
-            "version": "1.2.0",
-            "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",
-                "domutils": "1.5.1",
-                "nth-check": "~1.0.1"
+            "version": "4.2.1",
+            "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz",
+            "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==",
+            "requires": {
+                "boolbase": "^1.0.0",
+                "css-what": "^5.1.0",
+                "domhandler": "^4.3.0",
+                "domutils": "^2.8.0",
+                "nth-check": "^2.0.1"
             },
             "dependencies": {
+                "dom-serializer": {
+                    "version": "1.3.2",
+                    "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
+                    "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
+                    "requires": {
+                        "domelementtype": "^2.0.1",
+                        "domhandler": "^4.2.0",
+                        "entities": "^2.0.0"
+                    }
+                },
+                "domelementtype": {
+                    "version": "2.2.0",
+                    "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
+                    "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
+                },
+                "domhandler": {
+                    "version": "4.3.0",
+                    "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz",
+                    "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==",
+                    "requires": {
+                        "domelementtype": "^2.2.0"
+                    }
+                },
                 "domutils": {
-                    "version": "1.5.1",
-                    "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
-                    "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
+                    "version": "2.8.0",
+                    "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+                    "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
                     "requires": {
-                        "dom-serializer": "0",
-                        "domelementtype": "1"
+                        "dom-serializer": "^1.0.1",
+                        "domelementtype": "^2.2.0",
+                        "domhandler": "^4.2.0"
                     }
+                },
+                "entities": {
+                    "version": "2.2.0",
+                    "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+                    "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
                 }
             }
         },
@@ -4837,9 +31082,9 @@
             }
         },
         "css-what": {
-            "version": "2.1.3",
-            "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
-            "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg=="
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
+            "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw=="
         },
         "cssesc": {
             "version": "3.0.0",
@@ -4871,9 +31116,9 @@
             }
         },
         "csstype": {
-            "version": "2.6.10",
-            "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.10.tgz",
-            "integrity": "sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w=="
+            "version": "2.6.19",
+            "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.19.tgz",
+            "integrity": "sha512-ZVxXaNy28/k3kJg0Fou5MiYpp88j7H9hLZp8PDC3jV0WFjfH5E9xHb56L0W59cPbKbcHXeP4qyT8PrHp8t6LcQ=="
         },
         "currently-unhandled": {
             "version": "0.4.1",
@@ -4894,25 +31139,20 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
-        "d": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
-            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
-            "dev": true,
-            "requires": {
-                "es5-ext": "^0.10.50",
-                "type": "^1.0.1"
-            }
-        },
+        "D": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/D/-/D-1.0.0.tgz",
+            "integrity": "sha512-nQvrCBu7K2pSSEtIM0EEF03FVjcczCXInMt3moLNFbjlWx6bZrX72uT6/1uAXDbnzGUAx9gTyDiQ+vrFi663oA=="
+        },
         "d3-array": {
             "version": "1.2.4",
             "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
             "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
         },
         "d3-axis": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-2.0.0.tgz",
-            "integrity": "sha512-9nzB0uePtb+u9+dWir+HTuEAKJOEUYJoEwbJPsZ1B4K3iZUgzJcSENQ05Nj7S4CIfbZZ8/jQGoUzGKFznBhiiQ=="
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-2.1.0.tgz",
+            "integrity": "sha512-z/G2TQMyuf0X3qP+Mh+2PimoJD41VOCjViJzT0BHeL/+JQAofkiWZbWxlwFGb1N8EN+Cl/CW+MUKbVzr1689Cw=="
         },
         "d3-color": {
             "version": "2.0.0",
@@ -4957,21 +31197,21 @@
             "integrity": "sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA=="
         },
         "d3-scale": {
-            "version": "3.2.3",
-            "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.2.3.tgz",
-            "integrity": "sha512-8E37oWEmEzj57bHcnjPVOBS3n4jqakOeuv1EDdQSiSrYnMCBdMd3nc4HtKk7uia8DUHcY/CGuJ42xxgtEYrX0g==",
+            "version": "3.3.0",
+            "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz",
+            "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==",
             "requires": {
                 "d3-array": "^2.3.0",
                 "d3-format": "1 - 2",
                 "d3-interpolate": "1.2.0 - 2",
-                "d3-time": "1 - 2",
+                "d3-time": "^2.1.1",
                 "d3-time-format": "2 - 3"
             },
             "dependencies": {
                 "d3-array": {
-                    "version": "2.11.0",
-                    "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.11.0.tgz",
-                    "integrity": "sha512-26clcwmHQEdsLv34oNKq5Ia9tQ26Y/4HqS3dQzF42QBUqymZJ+9PORcN1G52bt37NsL2ABoX4lvyYZc+A9Y0zw==",
+                    "version": "2.12.1",
+                    "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
+                    "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
                     "requires": {
                         "internmap": "^1.0.0"
                     }
@@ -4984,17 +31224,30 @@
             "integrity": "sha512-XoGGqhLUN/W14NmaqcO/bb1nqjDAw5WtSYb2X8wiuQWvSZUsUVYsOSkOybUrNvcBjaywBdYPy03eXHMXjk9nZA=="
         },
         "d3-shape": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-2.0.0.tgz",
-            "integrity": "sha512-djpGlA779ua+rImicYyyjnOjeubyhql1Jyn1HK0bTyawuH76UQRWXd+pftr67H6Fa8hSwetkgb/0id3agKWykw==",
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-2.1.0.tgz",
+            "integrity": "sha512-PnjUqfM2PpskbSLTJvAzp2Wv4CZsnAgTfcVRTwW03QR3MkXF8Uo7B1y/lWkAsmbKwuecto++4NlsYcvYpXpTHA==",
             "requires": {
                 "d3-path": "1 - 2"
             }
         },
         "d3-time": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.0.0.tgz",
-            "integrity": "sha512-2mvhstTFcMvwStWd9Tj3e6CEqtOivtD8AUiHT8ido/xmzrI9ijrUUihZ6nHuf/vsScRBonagOdj0Vv+SEL5G3Q=="
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz",
+            "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==",
+            "requires": {
+                "d3-array": "2"
+            },
+            "dependencies": {
+                "d3-array": {
+                    "version": "2.12.1",
+                    "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
+                    "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
+                    "requires": {
+                        "internmap": "^1.0.0"
+                    }
+                }
+            }
         },
         "d3-time-format": {
             "version": "3.0.0",
@@ -5053,27 +31306,9 @@
             }
         },
         "date-fns": {
-            "version": "2.14.0",
-            "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.14.0.tgz",
-            "integrity": "sha512-1zD+68jhFgDIM0rF05rcwYO8cExdNqxjq4xP1QKM60Q45mnO6zaMWB4tOzrIr4M4GSLntsKeE4c9Bdl2jhL/yw=="
-        },
-        "dateformat": {
-            "version": "1.0.12",
-            "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz",
-            "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=",
-            "dev": true,
-            "requires": {
-                "get-stdin": "^4.0.1",
-                "meow": "^3.3.0"
-            },
-            "dependencies": {
-                "get-stdin": {
-                    "version": "4.0.1",
-                    "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
-                    "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
-                    "dev": true
-                }
-            }
+            "version": "2.28.0",
+            "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz",
+            "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw=="
         },
         "de-indent": {
             "version": "1.0.2",
@@ -5197,6 +31432,11 @@
                 }
             }
         },
+        "defer-to-connect": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
+            "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="
+        },
         "define-properties": {
             "version": "1.1.3",
             "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
@@ -5300,9 +31540,9 @@
             "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
         },
         "denque": {
-            "version": "1.4.1",
-            "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz",
-            "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ=="
+            "version": "1.5.1",
+            "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz",
+            "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw=="
         },
         "density-clustering": {
             "version": "1.3.0",
@@ -5524,36 +31764,20 @@
             "resolved": "https://registry.npmjs.org/deps-regex/-/deps-regex-0.1.4.tgz",
             "integrity": "sha1-UYZnt2kUYKXn4KNBvnbrfOgJAYQ="
         },
-        "des.js": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
-            "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==",
-            "dev": true,
-            "requires": {
-                "inherits": "^2.0.1",
-                "minimalistic-assert": "^1.0.0"
-            }
-        },
         "destroy": {
             "version": "1.0.4",
             "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
             "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
         },
-        "detect-file": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
-            "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=",
-            "dev": true
-        },
         "detect-libc": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
             "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
         },
         "detect-node": {
-            "version": "2.0.4",
-            "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz",
-            "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==",
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
+            "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
             "dev": true
         },
         "diff": {
@@ -5566,25 +31790,6 @@
             "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz",
             "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw=="
         },
-        "diffie-hellman": {
-            "version": "5.0.3",
-            "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
-            "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
-            "dev": true,
-            "requires": {
-                "bn.js": "^4.1.0",
-                "miller-rabin": "^4.0.0",
-                "randombytes": "^2.0.0"
-            },
-            "dependencies": {
-                "bn.js": {
-                    "version": "4.12.0",
-                    "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
-                    "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
-                    "dev": true
-                }
-            }
-        },
         "dir-glob": {
             "version": "2.2.2",
             "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz",
@@ -5618,9 +31823,9 @@
             "dev": true
         },
         "dns-packet": {
-            "version": "1.3.1",
-            "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz",
-            "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==",
+            "version": "1.3.4",
+            "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.4.tgz",
+            "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==",
             "dev": true,
             "requires": {
                 "ip": "^1.1.0",
@@ -5641,6 +31846,14 @@
             "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz",
             "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk="
         },
+        "dom-converter": {
+            "version": "0.2.0",
+            "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
+            "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==",
+            "requires": {
+                "utila": "~0.4"
+            }
+        },
         "dom-helpers": {
             "version": "3.4.0",
             "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
@@ -5670,12 +31883,6 @@
                 }
             }
         },
-        "domain-browser": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
-            "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
-            "dev": true
-        },
         "domelementtype": {
             "version": "1.3.1",
             "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
@@ -5707,6 +31914,22 @@
                 "domelementtype": "1"
             }
         },
+        "dot-case": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
+            "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+            "requires": {
+                "no-case": "^3.0.4",
+                "tslib": "^2.0.3"
+            },
+            "dependencies": {
+                "tslib": {
+                    "version": "2.3.1",
+                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+                    "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+                }
+            }
+        },
         "dot-prop": {
             "version": "4.2.0",
             "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
@@ -5791,28 +32014,10 @@
             "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
             "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
         },
-        "elliptic": {
-            "version": "6.5.4",
-            "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
-            "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
-            "dev": true,
-            "requires": {
-                "bn.js": "^4.11.9",
-                "brorand": "^1.1.0",
-                "hash.js": "^1.0.0",
-                "hmac-drbg": "^1.0.1",
-                "inherits": "^2.0.4",
-                "minimalistic-assert": "^1.0.1",
-                "minimalistic-crypto-utils": "^1.0.1"
-            },
-            "dependencies": {
-                "bn.js": {
-                    "version": "4.12.0",
-                    "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
-                    "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
-                    "dev": true
-                }
-            }
+        "electron-to-chromium": {
+            "version": "1.4.75",
+            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.75.tgz",
+            "integrity": "sha512-LxgUNeu3BVU7sXaKjUDD9xivocQLxFtq6wgERrutdY/yIOps3ODOZExK1jg8DTEg4U8TUCb5MLGeWFOYuxjF3Q=="
         },
         "emoji-regex": {
             "version": "7.0.3",
@@ -5855,23 +32060,18 @@
             }
         },
         "engine.io": {
-            "version": "3.4.1",
-            "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.1.tgz",
-            "integrity": "sha512-8MfIfF1/IIfxuc2gv5K+XlFZczw/BpTvqBdl0E2fBLkYQp4miv4LuDTVtYt4yMyaIFLEr4vtaSgV4mjvll8Crw==",
+            "version": "3.5.0",
+            "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.5.0.tgz",
+            "integrity": "sha512-21HlvPUKaitDGE4GXNtQ7PLP0Sz4aWLddMPw2VTyFz1FVZqu/kZsJUO8WNpKuE/OCL7nkfRaOui2ZCJloGznGA==",
             "requires": {
                 "accepts": "~1.3.4",
                 "base64id": "2.0.0",
-                "cookie": "0.3.1",
+                "cookie": "~0.4.1",
                 "debug": "~4.1.0",
                 "engine.io-parser": "~2.2.0",
-                "ws": "^7.1.2"
+                "ws": "~7.4.2"
             },
             "dependencies": {
-                "cookie": {
-                    "version": "0.3.1",
-                    "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
-                    "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
-                },
                 "debug": {
                     "version": "4.1.1",
                     "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
@@ -5879,58 +32079,62 @@
                     "requires": {
                         "ms": "^2.1.1"
                     }
+                },
+                "ws": {
+                    "version": "7.4.6",
+                    "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
+                    "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
+                    "requires": {}
                 }
             }
         },
         "engine.io-client": {
-            "version": "3.4.1",
-            "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.1.tgz",
-            "integrity": "sha512-RJNmA+A9Js+8Aoq815xpGAsgWH1VoSYM//2VgIiu9lNOaHFfLpTjH4tOzktBpjIs5lvOfiNY1dwf+NuU6D38Mw==",
+            "version": "3.5.2",
+            "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.2.tgz",
+            "integrity": "sha512-QEqIp+gJ/kMHeUun7f5Vv3bteRHppHH/FMBQX/esFj/fuYfjyUKWGMo3VCvIP/V8bE9KcjHmRZrhIz2Z9oNsDA==",
             "requires": {
-                "component-emitter": "1.2.1",
+                "component-emitter": "~1.3.0",
                 "component-inherit": "0.0.3",
-                "debug": "~4.1.0",
+                "debug": "~3.1.0",
                 "engine.io-parser": "~2.2.0",
                 "has-cors": "1.1.0",
                 "indexof": "0.0.1",
-                "parseqs": "0.0.5",
-                "parseuri": "0.0.5",
-                "ws": "~6.1.0",
-                "xmlhttprequest-ssl": "~1.5.4",
+                "parseqs": "0.0.6",
+                "parseuri": "0.0.6",
+                "ws": "~7.4.2",
+                "xmlhttprequest-ssl": "~1.6.2",
                 "yeast": "0.1.2"
             },
             "dependencies": {
-                "component-emitter": {
-                    "version": "1.2.1",
-                    "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
-                    "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
-                },
                 "debug": {
-                    "version": "4.1.1",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
-                    "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+                    "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
                     "requires": {
-                        "ms": "^2.1.1"
+                        "ms": "2.0.0"
                     }
                 },
+                "ms": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                },
                 "ws": {
-                    "version": "6.1.4",
-                    "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
-                    "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
-                    "requires": {
-                        "async-limiter": "~1.0.0"
-                    }
+                    "version": "7.4.6",
+                    "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
+                    "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
+                    "requires": {}
                 }
             }
         },
         "engine.io-parser": {
-            "version": "2.2.0",
-            "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz",
-            "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==",
+            "version": "2.2.1",
+            "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz",
+            "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==",
             "requires": {
                 "after": "0.8.2",
                 "arraybuffer.slice": "~0.0.7",
-                "base64-arraybuffer": "0.1.5",
+                "base64-arraybuffer": "0.1.4",
                 "blob": "0.0.5",
                 "has-binary2": "~1.0.2"
             }
@@ -5951,9 +32155,14 @@
             "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
             "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w=="
         },
+        "envinfo": {
+            "version": "7.8.1",
+            "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz",
+            "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw=="
+        },
         "equation-editor-react": {
-            "version": "github:bobzel/equation-editor-react#75915e852b4b36a6a4cd3e1cbc80598da6b65227",
-            "from": "github:bobzel/equation-editor-react#useLocally",
+            "version": "git+ssh://git@github.com/bobzel/equation-editor-react.git#75915e852b4b36a6a4cd3e1cbc80598da6b65227",
+            "from": "equation-editor-react@github:bobzel/equation-editor-react#useLocally",
             "requires": {
                 "jquery": "^3.4.1",
                 "mathquill": "^0.10.1-a"
@@ -5994,6 +32203,11 @@
                 "string.prototype.trimright": "^2.1.1"
             }
         },
+        "es-module-lexer": {
+            "version": "0.9.3",
+            "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
+            "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ=="
+        },
         "es-to-primitive": {
             "version": "1.2.1",
             "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
@@ -6005,14 +32219,14 @@
             }
         },
         "es5-ext": {
-            "version": "0.10.53",
-            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
-            "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
+            "version": "0.10.59",
+            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.59.tgz",
+            "integrity": "sha512-cOgyhW0tIJyQY1Kfw6Kr0viu9ZlUctVchRMZ7R0HiH3dxTSp5zJDLecwxUqPUrGKMsgBI1wd1FL+d9Jxfi4cLw==",
             "dev": true,
             "requires": {
-                "es6-iterator": "~2.0.3",
-                "es6-symbol": "~3.1.3",
-                "next-tick": "~1.0.0"
+                "es6-iterator": "^2.0.3",
+                "es6-symbol": "^3.1.3",
+                "next-tick": "^1.1.0"
             }
         },
         "es6-iterator": {
@@ -6024,6 +32238,18 @@
                 "d": "1",
                 "es5-ext": "^0.10.35",
                 "es6-symbol": "^3.1.1"
+            },
+            "dependencies": {
+                "d": {
+                    "version": "1.0.1",
+                    "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+                    "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+                    "dev": true,
+                    "requires": {
+                        "es5-ext": "^0.10.50",
+                        "type": "^1.0.1"
+                    }
+                }
             }
         },
         "es6-promise": {
@@ -6039,8 +32265,25 @@
             "requires": {
                 "d": "^1.0.1",
                 "ext": "^1.1.2"
+            },
+            "dependencies": {
+                "d": {
+                    "version": "1.0.1",
+                    "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+                    "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+                    "dev": true,
+                    "requires": {
+                        "es5-ext": "^0.10.50",
+                        "type": "^1.0.1"
+                    }
+                }
             }
         },
+        "escalade": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+            "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
+        },
         "escape-html": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -6074,12 +32317,11 @@
             }
         },
         "eslint-scope": {
-            "version": "4.0.3",
-            "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
-            "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
-            "dev": true,
+            "version": "5.1.1",
+            "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+            "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
             "requires": {
-                "esrecurse": "^4.1.0",
+                "esrecurse": "^4.3.0",
                 "estraverse": "^4.1.1"
             }
         },
@@ -6092,24 +32334,21 @@
             "version": "4.3.0",
             "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
             "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
-            "dev": true,
             "requires": {
                 "estraverse": "^5.2.0"
             },
             "dependencies": {
                 "estraverse": {
-                    "version": "5.2.0",
-                    "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
-                    "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
-                    "dev": true
+                    "version": "5.3.0",
+                    "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+                    "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="
                 }
             }
         },
         "estraverse": {
             "version": "4.3.0",
             "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
-            "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
-            "dev": true
+            "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="
         },
         "esutils": {
             "version": "2.0.3",
@@ -6127,36 +32366,25 @@
             "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
         },
         "eventemitter3": {
-            "version": "4.0.4",
-            "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz",
-            "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==",
+            "version": "4.0.7",
+            "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+            "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
             "dev": true
         },
         "events": {
             "version": "3.3.0",
             "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
-            "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
-            "dev": true
+            "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
         },
         "eventsource": {
-            "version": "1.0.7",
-            "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz",
-            "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==",
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz",
+            "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==",
             "dev": true,
             "requires": {
                 "original": "^1.0.0"
             }
         },
-        "evp_bytestokey": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
-            "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
-            "dev": true,
-            "requires": {
-                "md5.js": "^1.3.4",
-                "safe-buffer": "^5.1.1"
-            }
-        },
         "execa": {
             "version": "0.7.0",
             "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
@@ -6294,26 +32522,17 @@
             "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
             "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
         },
-        "expand-tilde": {
-            "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
-            "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=",
-            "dev": true,
-            "requires": {
-                "homedir-polyfill": "^1.0.1"
-            }
-        },
         "express": {
-            "version": "4.17.1",
-            "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
-            "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
+            "version": "4.17.3",
+            "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz",
+            "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==",
             "requires": {
-                "accepts": "~1.3.7",
+                "accepts": "~1.3.8",
                 "array-flatten": "1.1.1",
-                "body-parser": "1.19.0",
-                "content-disposition": "0.5.3",
+                "body-parser": "1.19.2",
+                "content-disposition": "0.5.4",
                 "content-type": "~1.0.4",
-                "cookie": "0.4.0",
+                "cookie": "0.4.2",
                 "cookie-signature": "1.0.6",
                 "debug": "2.6.9",
                 "depd": "~1.1.2",
@@ -6327,19 +32546,24 @@
                 "on-finished": "~2.3.0",
                 "parseurl": "~1.3.3",
                 "path-to-regexp": "0.1.7",
-                "proxy-addr": "~2.0.5",
-                "qs": "6.7.0",
+                "proxy-addr": "~2.0.7",
+                "qs": "6.9.7",
                 "range-parser": "~1.2.1",
-                "safe-buffer": "5.1.2",
-                "send": "0.17.1",
-                "serve-static": "1.14.1",
-                "setprototypeof": "1.1.1",
+                "safe-buffer": "5.2.1",
+                "send": "0.17.2",
+                "serve-static": "1.14.2",
+                "setprototypeof": "1.2.0",
                 "statuses": "~1.5.0",
                 "type-is": "~1.6.18",
                 "utils-merge": "1.0.1",
                 "vary": "~1.1.2"
             },
             "dependencies": {
+                "cookie": {
+                    "version": "0.4.2",
+                    "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+                    "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA=="
+                },
                 "debug": {
                     "version": "2.6.9",
                     "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -6354,9 +32578,14 @@
                     "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                 },
                 "qs": {
-                    "version": "6.7.0",
-                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
-                    "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
+                    "version": "6.9.7",
+                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz",
+                    "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw=="
+                },
+                "safe-buffer": {
+                    "version": "5.2.1",
+                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+                    "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
                 }
             }
         },
@@ -6369,17 +32598,17 @@
             }
         },
         "express-session": {
-            "version": "1.17.1",
-            "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.1.tgz",
-            "integrity": "sha512-UbHwgqjxQZJiWRTMyhvWGvjBQduGCSBDhhZXYenziMFjxst5rMV+aJZ6hKPHZnPyHGsrqRICxtX8jtEbm/z36Q==",
+            "version": "1.17.2",
+            "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz",
+            "integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==",
             "requires": {
-                "cookie": "0.4.0",
+                "cookie": "0.4.1",
                 "cookie-signature": "1.0.6",
                 "debug": "2.6.9",
                 "depd": "~2.0.0",
                 "on-headers": "~1.0.2",
                 "parseurl": "~1.3.3",
-                "safe-buffer": "5.2.0",
+                "safe-buffer": "5.2.1",
                 "uid-safe": "~2.1.5"
             },
             "dependencies": {
@@ -6402,9 +32631,9 @@
                     "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                 },
                 "safe-buffer": {
-                    "version": "5.2.0",
-                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
-                    "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
+                    "version": "5.2.1",
+                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+                    "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
                 }
             }
         },
@@ -6572,10 +32801,15 @@
             "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.2.tgz",
             "integrity": "sha512-5rQdinSsycpzvAoHga2EDn+LRX1d5xLFsuNG0Kg61JrAT/tASXcLL0nf/33v+sAxlQcfYmWbTURa1mmAf55jGw=="
         },
+        "fastest-levenshtein": {
+            "version": "1.0.12",
+            "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz",
+            "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow=="
+        },
         "faye-websocket": {
-            "version": "0.10.0",
-            "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
-            "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=",
+            "version": "0.11.4",
+            "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
+            "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
             "dev": true,
             "requires": {
                 "websocket-driver": ">=0.5.1"
@@ -6603,12 +32837,6 @@
                 "pend": "~1.2.0"
             }
         },
-        "figgy-pudding": {
-            "version": "3.5.2",
-            "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
-            "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==",
-            "dev": true
-        },
         "file-loader": {
             "version": "3.0.1",
             "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz",
@@ -6633,9 +32861,9 @@
             }
         },
         "file-saver": {
-            "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.2.tgz",
-            "integrity": "sha512-Wz3c3XQ5xroCxd1G8b7yL0Ehkf0TC9oYC6buPFkNnU9EnaPlifeAFCyCh+iewXTyFRcg0a6j3J7FmJsIhlhBdw=="
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
+            "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
         },
         "file-uri-to-path": {
             "version": "1.0.0",
@@ -6669,6 +32897,11 @@
                 }
             }
         },
+        "filter-obj": {
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
+            "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs="
+        },
         "finalhandler": {
             "version": "1.1.2",
             "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
@@ -6744,18 +32977,6 @@
                 "locate-path": "^3.0.0"
             }
         },
-        "findup-sync": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz",
-            "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==",
-            "dev": true,
-            "requires": {
-                "detect-file": "^1.0.0",
-                "is-glob": "^4.0.0",
-                "micromatch": "^3.0.4",
-                "resolve-dir": "^1.0.1"
-            }
-        },
         "fit-curve": {
             "version": "0.1.7",
             "resolved": "https://registry.npmjs.org/fit-curve/-/fit-curve-0.1.7.tgz",
@@ -6772,7 +32993,8 @@
         "flexlayout-react": {
             "version": "0.3.11",
             "resolved": "https://registry.npmjs.org/flexlayout-react/-/flexlayout-react-0.3.11.tgz",
-            "integrity": "sha512-V+rEfyYJBqNk9oBgotPoXg8rmom4/ji9Mvr6f7T8sIJs83RuyK1D3oOrya2ydZIUCP2T6BIvj7rFTmRRkzpU8w=="
+            "integrity": "sha512-V+rEfyYJBqNk9oBgotPoXg8rmom4/ji9Mvr6f7T8sIJs83RuyK1D3oOrya2ydZIUCP2T6BIvj7rFTmRRkzpU8w==",
+            "requires": {}
         },
         "fluent-ffmpeg": {
             "version": "2.1.2",
@@ -6836,6 +33058,11 @@
             "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
             "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA="
         },
+        "foreach": {
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
+            "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k="
+        },
         "forever-agent": {
             "version": "0.6.1",
             "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@@ -6868,15 +33095,20 @@
                 "mime-types": "^2.1.12"
             }
         },
+        "form-data-encoder": {
+            "version": "1.7.1",
+            "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz",
+            "integrity": "sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg=="
+        },
         "formidable": {
-            "version": "1.2.2",
-            "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz",
-            "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q=="
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz",
+            "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg=="
         },
         "forwarded": {
-            "version": "0.1.2",
-            "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
-            "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
+            "version": "0.2.0",
+            "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+            "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
         },
         "fragment-cache": {
             "version": "0.2.1",
@@ -6923,35 +33155,10 @@
             "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
             "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
         },
-        "fs-extra": {
-            "version": "0.26.7",
-            "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz",
-            "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=",
-            "requires": {
-                "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": {
-                    "version": "2.7.1",
-                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
-                    "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
-                    "requires": {
-                        "glob": "^7.1.3"
-                    }
-                }
-            }
-        },
-        "fs-minipass": {
-            "version": "1.2.7",
-            "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
-            "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
-            "requires": {
-                "minipass": "^2.6.0"
-            }
+        "fs-monkey": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz",
+            "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q=="
         },
         "fs-write-stream-atomic": {
             "version": "1.0.10",
@@ -6988,9 +33195,9 @@
             "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
         },
         "fsevents": {
-            "version": "2.1.3",
-            "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
-            "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+            "version": "2.3.2",
+            "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+            "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
             "dev": true,
             "optional": true
         },
@@ -7021,9 +33228,9 @@
             "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
         },
         "function-plot": {
-            "version": "1.22.7",
-            "resolved": "https://registry.npmjs.org/function-plot/-/function-plot-1.22.7.tgz",
-            "integrity": "sha512-rB6FeVqvgNECmt5PhIvFFEOyEjM9AWLIpMkj9Nzbzq9f81Irgn3ZrXAuv5+qnuzM99jPL7ZM4kK3+ImiKXcSHA==",
+            "version": "1.22.8",
+            "resolved": "https://registry.npmjs.org/function-plot/-/function-plot-1.22.8.tgz",
+            "integrity": "sha512-VGqY3Lxu+LoC2ZvdesEiK8n0jMThjEI1gAGHOcWcRED5ip0Kt018swygtKl+lmBlbvmXDXOhYW6KOPa0dbky2w==",
             "requires": {
                 "built-in-math-eval": "^0.3.0",
                 "clamp": "^1.0.1",
@@ -7137,6 +33344,16 @@
             "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
             "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE="
         },
+        "get-intrinsic": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+            "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+            "requires": {
+                "function-bind": "^1.1.1",
+                "has": "^1.0.3",
+                "has-symbols": "^1.0.1"
+            }
+        },
         "get-node-dimensions": {
             "version": "1.2.1",
             "resolved": "https://registry.npmjs.org/get-node-dimensions/-/get-node-dimensions-1.2.1.tgz",
@@ -7157,6 +33374,15 @@
             "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
             "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
         },
+        "get-symbol-description": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
+            "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
+            "requires": {
+                "call-bind": "^1.0.2",
+                "get-intrinsic": "^1.1.1"
+            }
+        },
         "get-value": {
             "version": "2.0.6",
             "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
@@ -7197,57 +33423,27 @@
                 "path-dirname": "^1.0.0"
             },
             "dependencies": {
-                "is-glob": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
-                    "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
-                    "requires": {
-                        "is-extglob": "^2.1.0"
-                    }
-                }
-            }
-        },
-        "global-dirs": {
-            "version": "0.1.1",
-            "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
-            "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
-            "requires": {
-                "ini": "^1.3.4"
-            }
-        },
-        "global-modules": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
-            "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==",
-            "dev": true,
-            "requires": {
-                "global-prefix": "^3.0.0"
-            },
-            "dependencies": {
-                "global-prefix": {
-                    "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz",
-                    "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==",
-                    "dev": true,
+                "is-glob": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+                    "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
                     "requires": {
-                        "ini": "^1.3.5",
-                        "kind-of": "^6.0.2",
-                        "which": "^1.3.1"
+                        "is-extglob": "^2.1.0"
                     }
                 }
             }
         },
-        "global-prefix": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
-            "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
-            "dev": true,
+        "glob-to-regexp": {
+            "version": "0.4.1",
+            "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+            "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
+        },
+        "global-dirs": {
+            "version": "0.1.1",
+            "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
+            "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
             "requires": {
-                "expand-tilde": "^2.0.2",
-                "homedir-polyfill": "^1.0.1",
-                "ini": "^1.3.4",
-                "is-windows": "^1.0.1",
-                "which": "^1.2.14"
+                "ini": "^1.3.4"
             }
         },
         "globals": {
@@ -7313,7 +33509,8 @@
         "google-maps-react": {
             "version": "2.0.6",
             "resolved": "https://registry.npmjs.org/google-maps-react/-/google-maps-react-2.0.6.tgz",
-            "integrity": "sha512-M8Eo9WndfQEfxcmm6yRq03qdJgw1x6rQmJ9DN+a+xPQ3K7yNDGkVDbinrf4/8vcox7nELbeopbm4bpefKewWfQ=="
+            "integrity": "sha512-M8Eo9WndfQEfxcmm6yRq03qdJgw1x6rQmJ9DN+a+xPQ3K7yNDGkVDbinrf4/8vcox7nELbeopbm4bpefKewWfQ==",
+            "requires": {}
         },
         "google-p12-pem": {
             "version": "2.0.4",
@@ -7363,21 +33560,43 @@
             }
         },
         "got": {
-            "version": "6.7.1",
-            "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
-            "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
-            "requires": {
-                "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"
+            "version": "12.0.1",
+            "resolved": "https://registry.npmjs.org/got/-/got-12.0.1.tgz",
+            "integrity": "sha512-1Zhoh+lDej3t7Ks1BP/Jufn+rNqdiHQgUOcTxHzg2Dao1LQfp5S4Iq0T3iBxN4Zdo7QqCJL+WJUNzDX6rCP2Ew==",
+            "requires": {
+                "@sindresorhus/is": "^4.2.0",
+                "@szmarczak/http-timer": "^5.0.1",
+                "@types/cacheable-request": "^6.0.2",
+                "@types/responselike": "^1.0.0",
+                "cacheable-lookup": "^6.0.4",
+                "cacheable-request": "^7.0.2",
+                "decompress-response": "^6.0.0",
+                "form-data-encoder": "1.7.1",
+                "get-stream": "^6.0.1",
+                "http2-wrapper": "^2.1.9",
+                "lowercase-keys": "^3.0.0",
+                "p-cancelable": "^3.0.0",
+                "responselike": "^2.0.0"
+            },
+            "dependencies": {
+                "decompress-response": {
+                    "version": "6.0.0",
+                    "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+                    "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+                    "requires": {
+                        "mimic-response": "^3.1.0"
+                    }
+                },
+                "get-stream": {
+                    "version": "6.0.1",
+                    "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+                    "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="
+                },
+                "mimic-response": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+                    "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
+                }
             }
         },
         "graceful-fs": {
@@ -7390,12 +33609,6 @@
             "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
             "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA=="
         },
-        "growly": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
-            "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
-            "dev": true
-        },
         "gtoken": {
             "version": "3.0.2",
             "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-3.0.2.tgz",
@@ -7431,12 +33644,25 @@
             "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
         },
         "har-validator": {
-            "version": "5.1.3",
-            "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
-            "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
+            "version": "5.1.5",
+            "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+            "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
             "requires": {
-                "ajv": "^6.5.5",
+                "ajv": "^6.12.3",
                 "har-schema": "^2.0.0"
+            },
+            "dependencies": {
+                "ajv": {
+                    "version": "6.12.6",
+                    "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+                    "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+                    "requires": {
+                        "fast-deep-equal": "^3.1.1",
+                        "fast-json-stable-stringify": "^2.0.0",
+                        "json-schema-traverse": "^0.4.1",
+                        "uri-js": "^4.2.2"
+                    }
+                }
             }
         },
         "has": {
@@ -7462,6 +33688,11 @@
                 }
             }
         },
+        "has-bigints": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
+            "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA=="
+        },
         "has-binary2": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
@@ -7492,6 +33723,21 @@
             "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
             "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
         },
+        "has-tostringtag": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
+            "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+            "requires": {
+                "has-symbols": "^1.0.2"
+            },
+            "dependencies": {
+                "has-symbols": {
+                    "version": "1.0.2",
+                    "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
+                    "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw=="
+                }
+            }
+        },
         "has-unicode": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
@@ -7531,51 +33777,11 @@
                 }
             }
         },
-        "hash-base": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
-            "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
-            "dev": true,
-            "requires": {
-                "inherits": "^2.0.4",
-                "readable-stream": "^3.6.0",
-                "safe-buffer": "^5.2.0"
-            },
-            "dependencies": {
-                "safe-buffer": {
-                    "version": "5.2.1",
-                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
-                    "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
-                    "dev": true
-                }
-            }
-        },
-        "hash.js": {
-            "version": "1.1.7",
-            "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
-            "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
-            "dev": true,
-            "requires": {
-                "inherits": "^2.0.3",
-                "minimalistic-assert": "^1.0.1"
-            }
-        },
         "he": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
             "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
         },
-        "hmac-drbg": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
-            "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
-            "dev": true,
-            "requires": {
-                "hash.js": "^1.0.3",
-                "minimalistic-assert": "^1.0.0",
-                "minimalistic-crypto-utils": "^1.0.1"
-            }
-        },
         "hoist-non-react-statics": {
             "version": "3.3.2",
             "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
@@ -7584,24 +33790,15 @@
                 "react-is": "^16.7.0"
             }
         },
-        "homedir-polyfill": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
-            "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
-            "dev": true,
-            "requires": {
-                "parse-passwd": "^1.0.0"
-            }
-        },
         "hosted-git-info": {
             "version": "2.8.8",
             "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
             "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg=="
         },
         "howler": {
-            "version": "2.2.0",
-            "resolved": "https://registry.npmjs.org/howler/-/howler-2.2.0.tgz",
-            "integrity": "sha512-sGPkrAQy7jh5mNDbkRNG0F82R2HFDYNsQXBcX4smXQT0y0F4UMsa/+jXaGwWvcrajWr2tDB7JUkH7G5qSnuIyQ=="
+            "version": "2.2.3",
+            "resolved": "https://registry.npmjs.org/howler/-/howler-2.2.3.tgz",
+            "integrity": "sha512-QM0FFkw0LRX1PR8pNzJVAY25JhIWvbKMBFM4gqk+QdV+kPXOhleWGCB6AiAF/goGjIHK2e/nIElplvjQwhr0jg=="
         },
         "hpack.js": {
             "version": "2.1.6",
@@ -7650,15 +33847,49 @@
             }
         },
         "html-entities": {
-            "version": "1.3.1",
-            "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz",
-            "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==",
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz",
+            "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==",
             "dev": true
         },
+        "html-minifier-terser": {
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+            "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==",
+            "requires": {
+                "camel-case": "^4.1.2",
+                "clean-css": "^5.2.2",
+                "commander": "^8.3.0",
+                "he": "^1.2.0",
+                "param-case": "^3.0.4",
+                "relateurl": "^0.2.7",
+                "terser": "^5.10.0"
+            },
+            "dependencies": {
+                "clean-css": {
+                    "version": "5.2.4",
+                    "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.2.4.tgz",
+                    "integrity": "sha512-nKseG8wCzEuji/4yrgM/5cthL9oTDc5UOQyFMvW/Q53oP6gLH690o1NbuTh6Y18nujr7BxlsFuS7gXLnLzKJGg==",
+                    "requires": {
+                        "source-map": "~0.6.0"
+                    }
+                },
+                "commander": {
+                    "version": "8.3.0",
+                    "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+                    "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="
+                },
+                "source-map": {
+                    "version": "0.6.1",
+                    "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+                    "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+                }
+            }
+        },
         "html-to-image": {
-            "version": "0.1.1",
-            "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-0.1.1.tgz",
-            "integrity": "sha512-UAjpXmokENeOyzfLwL0+zQ502lXyg6pkzVUmRjtljOH9dR/YdEYQhWrQ/O14hmH5/1L7jv1aOupU4Zi3Y8+iow=="
+            "version": "0.1.3",
+            "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-0.1.3.tgz",
+            "integrity": "sha512-8JTGEAAdJGL/nlp3wb/WI8fLMx2dHKOFZMdsvdon23D45ZdtsXDeRm39Wddf04ludQe3OPmvjMJ9nPjI/7hPlg=="
         },
         "html-to-text": {
             "version": "5.1.1",
@@ -7671,6 +33902,25 @@
                 "minimist": "^1.2.0"
             }
         },
+        "html-webpack-plugin": {
+            "version": "5.5.0",
+            "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz",
+            "integrity": "sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==",
+            "requires": {
+                "@types/html-minifier-terser": "^6.0.0",
+                "html-minifier-terser": "^6.0.2",
+                "lodash": "^4.17.21",
+                "pretty-error": "^4.0.0",
+                "tapable": "^2.0.0"
+            },
+            "dependencies": {
+                "tapable": {
+                    "version": "2.2.1",
+                    "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+                    "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="
+                }
+            }
+        },
         "htmlparser2": {
             "version": "3.10.1",
             "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
@@ -7684,6 +33934,20 @@
                 "readable-stream": "^3.1.1"
             }
         },
+        "http-browserify": {
+            "version": "1.7.0",
+            "resolved": "https://registry.npmjs.org/http-browserify/-/http-browserify-1.7.0.tgz",
+            "integrity": "sha1-M3la3nLfiKz7/TZ3PO/tp2RzWyA=",
+            "requires": {
+                "Base64": "~0.2.0",
+                "inherits": "~2.0.1"
+            }
+        },
+        "http-cache-semantics": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
+            "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
+        },
         "http-deceiver": {
             "version": "1.2.7",
             "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
@@ -7691,24 +33955,23 @@
             "dev": true
         },
         "http-errors": {
-            "version": "1.7.2",
-            "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
-            "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
+            "version": "1.8.1",
+            "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
+            "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
             "requires": {
                 "depd": "~1.1.2",
-                "inherits": "2.0.3",
-                "setprototypeof": "1.1.1",
+                "inherits": "2.0.4",
+                "setprototypeof": "1.2.0",
                 "statuses": ">= 1.5.0 < 2",
-                "toidentifier": "1.0.0"
-            },
-            "dependencies": {
-                "inherits": {
-                    "version": "2.0.3",
-                    "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-                    "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
-                }
+                "toidentifier": "1.0.1"
             }
         },
+        "http-parser-js": {
+            "version": "0.5.5",
+            "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.5.tgz",
+            "integrity": "sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA==",
+            "dev": true
+        },
         "http-proxy": {
             "version": "1.18.1",
             "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
@@ -7742,11 +34005,24 @@
                 "sshpk": "^1.7.0"
             }
         },
+        "http2-wrapper": {
+            "version": "2.1.10",
+            "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.1.10.tgz",
+            "integrity": "sha512-QHgsdYkieKp+6JbXP25P+tepqiHYd+FVnDwXpxi/BlUcoIB0nsmTOymTNvETuTO+pDuwcSklPE72VR3DqV+Haw==",
+            "requires": {
+                "quick-lru": "^5.1.1",
+                "resolve-alpn": "^1.2.0"
+            }
+        },
+        "https": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz",
+            "integrity": "sha1-PDfHrhqO65ZpBKKtHpdaGUt+06Q="
+        },
         "https-browserify": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
-            "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
-            "dev": true
+            "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM="
         },
         "https-proxy-agent": {
             "version": "5.0.0",
@@ -7767,15 +34043,25 @@
                 }
             }
         },
+        "human-signals": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+            "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="
+        },
+        "hyntax": {
+            "version": "1.1.9",
+            "resolved": "https://registry.npmjs.org/hyntax/-/hyntax-1.1.9.tgz",
+            "integrity": "sha512-xjxyDLbVDdLgjPnl4NM+Iu6il3UPmk6PNCBXruQKeuKDc/HtaZx1hk1AtMgw3vsn9YnLZRfoBpPxYMXcoT5KAA=="
+        },
         "hyphenate-style-name": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz",
             "integrity": "sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ=="
         },
         "i": {
-            "version": "0.3.6",
-            "resolved": "https://registry.npmjs.org/i/-/i-0.3.6.tgz",
-            "integrity": "sha1-2WyScyB28HJxG2sQ/X1PZa2O4j0="
+            "version": "0.3.7",
+            "resolved": "https://registry.npmjs.org/i/-/i-0.3.7.tgz",
+            "integrity": "sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q=="
         },
         "iconv-lite": {
             "version": "0.4.24",
@@ -7822,14 +34108,6 @@
             "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
             "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk="
         },
-        "ignore-walk": {
-            "version": "3.0.3",
-            "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz",
-            "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
-            "requires": {
-                "minimatch": "^3.0.4"
-            }
-        },
         "image-data-uri": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/image-data-uri/-/image-data-uri-2.0.1.tgz",
@@ -7839,6 +34117,36 @@
                 "magicli": "0.0.8",
                 "mime-types": "^2.1.18",
                 "request": "^2.88.0"
+            },
+            "dependencies": {
+                "fs-extra": {
+                    "version": "0.26.7",
+                    "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz",
+                    "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=",
+                    "requires": {
+                        "graceful-fs": "^4.1.2",
+                        "jsonfile": "^2.1.0",
+                        "klaw": "^1.0.0",
+                        "path-is-absolute": "^1.0.0",
+                        "rimraf": "^2.2.8"
+                    }
+                },
+                "jsonfile": {
+                    "version": "2.4.0",
+                    "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
+                    "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
+                    "requires": {
+                        "graceful-fs": "^4.1.6"
+                    }
+                },
+                "rimraf": {
+                    "version": "2.7.1",
+                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+                    "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+                    "requires": {
+                        "glob": "^7.1.3"
+                    }
+                }
             }
         },
         "image-size": {
@@ -7851,14 +34159,14 @@
             "resolved": "https://registry.npmjs.org/image-size-stream/-/image-size-stream-1.1.0.tgz",
             "integrity": "sha1-Ivou2mbG31AQh0bacUkmSy0l+Gs=",
             "requires": {
-                "image-size": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1",
+                "image-size": "git+https://github.com/netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1",
                 "readable-stream": "^1.0.33",
                 "tryit": "^1.0.1"
             },
             "dependencies": {
                 "image-size": {
-                    "version": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1",
-                    "from": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1"
+                    "version": "git+ssh://git@github.com/netroy/image-size.git#da2c863807a3e9602617bdd357b0de3ab4a064c1",
+                    "from": "image-size@git+https://github.com/netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1"
                 },
                 "isarray": {
                     "version": "0.0.1",
@@ -7889,9 +34197,9 @@
             "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
         },
         "import-fresh": {
-            "version": "3.2.1",
-            "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
-            "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
+            "version": "3.3.0",
+            "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+            "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
             "requires": {
                 "parent-module": "^1.0.0",
                 "resolve-from": "^4.0.0"
@@ -7952,12 +34260,6 @@
             "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
             "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
         },
-        "infer-owner": {
-            "version": "1.0.4",
-            "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
-            "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
-            "dev": true
-        },
         "inflight": {
             "version": "1.0.6",
             "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -7968,9 +34270,9 @@
             }
         },
         "infobox-parser": {
-            "version": "3.3.1",
-            "resolved": "https://registry.npmjs.org/infobox-parser/-/infobox-parser-3.3.1.tgz",
-            "integrity": "sha512-Aj1uF/taawGhet8cazhXz2uEDFMOqH8hnuw720wvi7Zw6bJWmA45Ta2FI9xMG5wvvo4CB6GR9S1/RUFtC6EtAg==",
+            "version": "3.6.2",
+            "resolved": "https://registry.npmjs.org/infobox-parser/-/infobox-parser-3.6.2.tgz",
+            "integrity": "sha512-lasdwvbtjCtDDO6mArAs/ueFEnBJRyo2UbZPAkd5rEG5NVJ3XFCOvbMwNTT/rJlFv1+ORw8D3UvZV4brpgATCg==",
             "requires": {
                 "camelcase": "^4.1.0"
             },
@@ -8125,15 +34427,25 @@
                 "ipaddr.js": "^1.9.0"
             }
         },
+        "internal-slot": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz",
+            "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==",
+            "requires": {
+                "get-intrinsic": "^1.1.0",
+                "has": "^1.0.3",
+                "side-channel": "^1.0.4"
+            }
+        },
         "internmap": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.0.tgz",
-            "integrity": "sha512-SdoDWwNOTE2n4JWUsLn4KXZGuZPjPF9yyOGc8bnfWnBQh7BD/l80rzSznKc/r4Y0aQ7z3RTk9X+tV4tHBpu+dA=="
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz",
+            "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="
         },
         "interpret": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
-            "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw=="
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
+            "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA=="
         },
         "interval-arithmetic": {
             "version": "1.0.6",
@@ -8210,15 +34522,27 @@
             }
         },
         "is-arguments": {
-            "version": "1.0.4",
-            "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
-            "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA=="
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
+            "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
+            "requires": {
+                "call-bind": "^1.0.2",
+                "has-tostringtag": "^1.0.0"
+            }
         },
         "is-arrayish": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
             "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
         },
+        "is-bigint": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
+            "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
+            "requires": {
+                "has-bigints": "^1.0.1"
+            }
+        },
         "is-binary-path": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
@@ -8227,6 +34551,15 @@
                 "binary-extensions": "^1.0.0"
             }
         },
+        "is-boolean-object": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
+            "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
+            "requires": {
+                "call-bind": "^1.0.2",
+                "has-tostringtag": "^1.0.0"
+            }
+        },
         "is-buffer": {
             "version": "2.0.4",
             "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
@@ -8334,6 +34667,14 @@
             "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
             "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
         },
+        "is-generator-function": {
+            "version": "1.0.10",
+            "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
+            "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==",
+            "requires": {
+                "has-tostringtag": "^1.0.0"
+            }
+        },
         "is-glob": {
             "version": "4.0.1",
             "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
@@ -8356,6 +34697,11 @@
                 "is-path-inside": "^1.0.0"
             }
         },
+        "is-negative-zero": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
+            "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA=="
+        },
         "is-npm": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz",
@@ -8384,6 +34730,14 @@
                 }
             }
         },
+        "is-number-object": {
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz",
+            "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==",
+            "requires": {
+                "has-tostringtag": "^1.0.0"
+            }
+        },
         "is-obj": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
@@ -8462,11 +34816,24 @@
                 "max-safe-integer": "^1.0.0"
             }
         },
+        "is-shared-array-buffer": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz",
+            "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA=="
+        },
         "is-stream": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
             "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
         },
+        "is-string": {
+            "version": "1.0.7",
+            "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
+            "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
+            "requires": {
+                "has-tostringtag": "^1.0.0"
+            }
+        },
         "is-symbol": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
@@ -8475,6 +34842,100 @@
                 "has-symbols": "^1.0.1"
             }
         },
+        "is-typed-array": {
+            "version": "1.1.8",
+            "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz",
+            "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==",
+            "requires": {
+                "available-typed-arrays": "^1.0.5",
+                "call-bind": "^1.0.2",
+                "es-abstract": "^1.18.5",
+                "foreach": "^2.0.5",
+                "has-tostringtag": "^1.0.0"
+            },
+            "dependencies": {
+                "es-abstract": {
+                    "version": "1.19.1",
+                    "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz",
+                    "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==",
+                    "requires": {
+                        "call-bind": "^1.0.2",
+                        "es-to-primitive": "^1.2.1",
+                        "function-bind": "^1.1.1",
+                        "get-intrinsic": "^1.1.1",
+                        "get-symbol-description": "^1.0.0",
+                        "has": "^1.0.3",
+                        "has-symbols": "^1.0.2",
+                        "internal-slot": "^1.0.3",
+                        "is-callable": "^1.2.4",
+                        "is-negative-zero": "^2.0.1",
+                        "is-regex": "^1.1.4",
+                        "is-shared-array-buffer": "^1.0.1",
+                        "is-string": "^1.0.7",
+                        "is-weakref": "^1.0.1",
+                        "object-inspect": "^1.11.0",
+                        "object-keys": "^1.1.1",
+                        "object.assign": "^4.1.2",
+                        "string.prototype.trimend": "^1.0.4",
+                        "string.prototype.trimstart": "^1.0.4",
+                        "unbox-primitive": "^1.0.1"
+                    }
+                },
+                "has-symbols": {
+                    "version": "1.0.3",
+                    "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+                    "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
+                },
+                "is-callable": {
+                    "version": "1.2.4",
+                    "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz",
+                    "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w=="
+                },
+                "is-regex": {
+                    "version": "1.1.4",
+                    "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+                    "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+                    "requires": {
+                        "call-bind": "^1.0.2",
+                        "has-tostringtag": "^1.0.0"
+                    }
+                },
+                "object-inspect": {
+                    "version": "1.12.0",
+                    "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
+                    "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g=="
+                },
+                "object.assign": {
+                    "version": "4.1.2",
+                    "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+                    "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+                    "requires": {
+                        "call-bind": "^1.0.0",
+                        "define-properties": "^1.1.3",
+                        "has-symbols": "^1.0.1",
+                        "object-keys": "^1.1.1"
+                    }
+                },
+                "string.prototype.trimend": {
+                    "version": "1.0.4",
+                    "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
+                    "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
+                    "requires": {
+                        "call-bind": "^1.0.2",
+                        "define-properties": "^1.1.3"
+                    }
+                },
+                "string.prototype.trimstart": {
+                    "version": "1.0.4",
+                    "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
+                    "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
+                    "requires": {
+                        "call-bind": "^1.0.2",
+                        "define-properties": "^1.1.3"
+                    }
+                }
+            }
+        },
         "is-typedarray": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
@@ -8485,6 +34946,14 @@
             "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
             "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI="
         },
+        "is-weakref": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
+            "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
+            "requires": {
+                "call-bind": "^1.0.2"
+            }
+        },
         "is-what": {
             "version": "3.10.0",
             "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.10.0.tgz",
@@ -8539,6 +35008,31 @@
                 "lodash.get": "^4.4.2"
             }
         },
+        "jest-worker": {
+            "version": "27.5.1",
+            "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+            "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+            "requires": {
+                "@types/node": "*",
+                "merge-stream": "^2.0.0",
+                "supports-color": "^8.0.0"
+            },
+            "dependencies": {
+                "has-flag": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+                    "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
+                },
+                "supports-color": {
+                    "version": "8.1.1",
+                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+                    "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+                    "requires": {
+                        "has-flag": "^4.0.0"
+                    }
+                }
+            }
+        },
         "jquery": {
             "version": "3.5.1",
             "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz",
@@ -8668,15 +35162,25 @@
                 "bignumber.js": "^7.0.0"
             }
         },
+        "json-buffer": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+            "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
+        },
         "json-parse-better-errors": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
             "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
         },
+        "json-parse-even-better-errors": {
+            "version": "2.3.1",
+            "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+            "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
+        },
         "json-schema": {
-            "version": "0.2.3",
-            "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
-            "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
+            "version": "0.4.0",
+            "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+            "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="
         },
         "json-schema-traverse": {
             "version": "0.4.1",
@@ -8688,12 +35192,6 @@
             "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
             "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
         },
-        "json3": {
-            "version": "3.3.3",
-            "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz",
-            "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==",
-            "dev": true
-        },
         "jsondiffpatch": {
             "version": "0.4.1",
             "resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.4.1.tgz",
@@ -8703,106 +35201,105 @@
                 "diff-match-patch": "^1.0.0"
             }
         },
-        "jsonfile": {
-            "version": "2.4.0",
-            "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
-            "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
-            "requires": {
-                "graceful-fs": "^4.1.6"
-            }
-        },
         "jsonschema": {
-            "version": "1.2.6",
-            "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.2.6.tgz",
-            "integrity": "sha512-SqhURKZG07JyKKeo/ir24QnS4/BV7a6gQy93bUSe4lUdNp0QNpIz2c9elWJQ9dpc5cQYY6cvCzgRwy0MQCLyqA=="
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.0.tgz",
+            "integrity": "sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw=="
         },
         "jsprim": {
-            "version": "1.4.1",
-            "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
-            "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+            "version": "1.4.2",
+            "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
+            "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
             "requires": {
                 "assert-plus": "1.0.0",
                 "extsprintf": "1.3.0",
-                "json-schema": "0.2.3",
+                "json-schema": "0.4.0",
                 "verror": "1.10.0"
             }
         },
         "jss": {
-            "version": "10.3.0",
-            "resolved": "https://registry.npmjs.org/jss/-/jss-10.3.0.tgz",
-            "integrity": "sha512-B5sTRW9B6uHaUVzSo9YiMEOEp3UX8lWevU0Fsv+xtRnsShmgCfIYX44bTH8bPJe6LQKqEXku3ulKuHLbxBS97Q==",
+            "version": "10.9.0",
+            "resolved": "https://registry.npmjs.org/jss/-/jss-10.9.0.tgz",
+            "integrity": "sha512-YpzpreB6kUunQBbrlArlsMpXYyndt9JATbt95tajx0t4MTJJcCJdd4hdNpHmOIDiUJrF/oX5wtVFrS3uofWfGw==",
             "requires": {
                 "@babel/runtime": "^7.3.1",
-                "csstype": "^2.6.5",
+                "csstype": "^3.0.2",
                 "is-in-browser": "^1.1.3",
                 "tiny-warning": "^1.0.2"
+            },
+            "dependencies": {
+                "csstype": {
+                    "version": "3.0.10",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
+                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
+                }
             }
         },
         "jss-plugin-camel-case": {
-            "version": "10.3.0",
-            "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.3.0.tgz",
-            "integrity": "sha512-tadWRi/SLWqLK3EUZEdDNJL71F3ST93Zrl9JYMjV0QDqKPAl0Liue81q7m/nFUpnSTXczbKDy4wq8rI8o7WFqA==",
+            "version": "10.9.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.9.0.tgz",
+            "integrity": "sha512-UH6uPpnDk413/r/2Olmw4+y54yEF2lRIV8XIZyuYpgPYTITLlPOsq6XB9qeqv+75SQSg3KLocq5jUBXW8qWWww==",
             "requires": {
                 "@babel/runtime": "^7.3.1",
                 "hyphenate-style-name": "^1.0.3",
-                "jss": "^10.3.0"
+                "jss": "10.9.0"
             }
         },
         "jss-plugin-default-unit": {
-            "version": "10.3.0",
-            "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.3.0.tgz",
-            "integrity": "sha512-tT5KkIXAsZOSS9WDSe8m8lEHIjoEOj4Pr0WrG0WZZsMXZ1mVLFCSsD2jdWarQWDaRNyMj/I4d7czRRObhOxSuw==",
+            "version": "10.9.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.9.0.tgz",
+            "integrity": "sha512-7Ju4Q9wJ/MZPsxfu4T84mzdn7pLHWeqoGd/D8O3eDNNJ93Xc8PxnLmV8s8ZPNRYkLdxZqKtm1nPQ0BM4JRlq2w==",
             "requires": {
                 "@babel/runtime": "^7.3.1",
-                "jss": "^10.3.0"
+                "jss": "10.9.0"
             }
         },
         "jss-plugin-global": {
-            "version": "10.3.0",
-            "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.3.0.tgz",
-            "integrity": "sha512-etYTG/y3qIR/vxZnKY+J3wXwObyBDNhBiB3l/EW9/pE3WHE//BZdK8LFvQcrCO48sZW1Z6paHo6klxUPP7WbzA==",
+            "version": "10.9.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.9.0.tgz",
+            "integrity": "sha512-4G8PHNJ0x6nwAFsEzcuVDiBlyMsj2y3VjmFAx/uHk/R/gzJV+yRHICjT4MKGGu1cJq2hfowFWCyrr/Gg37FbgQ==",
             "requires": {
                 "@babel/runtime": "^7.3.1",
-                "jss": "^10.3.0"
+                "jss": "10.9.0"
             }
         },
         "jss-plugin-nested": {
-            "version": "10.3.0",
-            "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.3.0.tgz",
-            "integrity": "sha512-qWiEkoXNEkkZ+FZrWmUGpf+zBsnEOmKXhkjNX85/ZfWhH9dfGxUCKuJFuOWFM+rjQfxV4csfesq4hY0jk8Qt0w==",
+            "version": "10.9.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.9.0.tgz",
+            "integrity": "sha512-2UJnDrfCZpMYcpPYR16oZB7VAC6b/1QLsRiAutOt7wJaaqwCBvNsosLEu/fUyKNQNGdvg2PPJFDO5AX7dwxtoA==",
             "requires": {
                 "@babel/runtime": "^7.3.1",
-                "jss": "^10.3.0",
+                "jss": "10.9.0",
                 "tiny-warning": "^1.0.2"
             }
         },
         "jss-plugin-props-sort": {
-            "version": "10.3.0",
-            "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.3.0.tgz",
-            "integrity": "sha512-boetORqL/lfd7BWeFD3K+IyPqyIC+l3CRrdZr+NPq7Noqp+xyg/0MR7QisgzpxCEulk+j2CRcEUoZsvgPC4nTg==",
+            "version": "10.9.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.9.0.tgz",
+            "integrity": "sha512-7A76HI8bzwqrsMOJTWKx/uD5v+U8piLnp5bvru7g/3ZEQOu1+PjHvv7bFdNO3DwNPC9oM0a//KwIJsIcDCjDzw==",
             "requires": {
                 "@babel/runtime": "^7.3.1",
-                "jss": "^10.3.0"
+                "jss": "10.9.0"
             }
         },
         "jss-plugin-rule-value-function": {
-            "version": "10.3.0",
-            "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.3.0.tgz",
-            "integrity": "sha512-7WiMrKIHH3rwxTuJki9+7nY11r1UXqaUZRhHvqTD4/ZE+SVhvtD5Tx21ivNxotwUSleucA/8boX+NF21oXzr5Q==",
+            "version": "10.9.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.9.0.tgz",
+            "integrity": "sha512-IHJv6YrEf8pRzkY207cPmdbBstBaE+z8pazhPShfz0tZSDtRdQua5jjg6NMz3IbTasVx9FdnmptxPqSWL5tyJg==",
             "requires": {
                 "@babel/runtime": "^7.3.1",
-                "jss": "^10.3.0",
+                "jss": "10.9.0",
                 "tiny-warning": "^1.0.2"
             }
         },
         "jss-plugin-vendor-prefixer": {
-            "version": "10.3.0",
-            "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.3.0.tgz",
-            "integrity": "sha512-sZQbrcZyP5V0ADjCLwUA1spVWoaZvM7XZ+2fSeieZFBj31cRsnV7X70FFDerMHeiHAXKWzYek+67nMDjhrZAVQ==",
+            "version": "10.9.0",
+            "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.9.0.tgz",
+            "integrity": "sha512-MbvsaXP7iiVdYVSEoi+blrW+AYnTDvHTW6I6zqi7JcwXdc6I9Kbm234nEblayhF38EftoenbM+5218pidmC5gA==",
             "requires": {
                 "@babel/runtime": "^7.3.1",
                 "css-vendor": "^2.0.8",
-                "jss": "^10.3.0"
+                "jss": "10.9.0"
             }
         },
         "jstransformer": {
@@ -8815,9 +35312,9 @@
             }
         },
         "jszip": {
-            "version": "3.5.0",
-            "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.5.0.tgz",
-            "integrity": "sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==",
+            "version": "3.7.1",
+            "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz",
+            "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==",
             "requires": {
                 "lie": "~3.3.0",
                 "pako": "~1.0.2",
@@ -8861,9 +35358,9 @@
             }
         },
         "kareem": {
-            "version": "2.3.1",
-            "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz",
-            "integrity": "sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw=="
+            "version": "2.3.2",
+            "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.2.tgz",
+            "integrity": "sha512-STHz9P7X2L4Kwn72fA4rGyqyXdmrMSdxqHx9IXon/FXluXieaFA6KJ2upcHAHxQPQ0LeM/OjLrhFxifHewOALQ=="
         },
         "kdbush": {
             "version": "3.0.0",
@@ -8883,6 +35380,14 @@
                 "tsscmp": "1.0.6"
             }
         },
+        "keyv": {
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz",
+            "integrity": "sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==",
+            "requires": {
+                "json-buffer": "3.0.1"
+            }
+        },
         "killable": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
@@ -8949,43 +35454,6 @@
                 "type-check": "~0.3.2"
             }
         },
-        "libxmljs": {
-            "version": "0.19.7",
-            "resolved": "https://registry.npmjs.org/libxmljs/-/libxmljs-0.19.7.tgz",
-            "integrity": "sha512-lFJyG9T1mVwTzNTw6ZkvIt0O+NsIR+FTE+RcC2QDFGU8YMnQrnyEOGrj6HWSe1AdwQK7s37BOp4NL+pcAqfK2g==",
-            "requires": {
-                "bindings": "~1.3.0",
-                "nan": "~2.14.0",
-                "node-pre-gyp": "~0.11.0"
-            },
-            "dependencies": {
-                "node-pre-gyp": {
-                    "version": "0.11.0",
-                    "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.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"
-                    }
-                },
-                "rimraf": {
-                    "version": "2.7.1",
-                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
-                    "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
-                    "requires": {
-                        "glob": "^7.1.3"
-                    }
-                }
-            }
-        },
         "lie": {
             "version": "3.3.0",
             "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
@@ -8995,9 +35463,9 @@
             }
         },
         "lines-and-columns": {
-            "version": "1.1.6",
-            "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
-            "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA="
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+            "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
         },
         "load-json-file": {
             "version": "1.1.0",
@@ -9027,10 +35495,9 @@
             }
         },
         "loader-runner": {
-            "version": "2.4.0",
-            "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
-            "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==",
-            "dev": true
+            "version": "4.2.0",
+            "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz",
+            "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw=="
         },
         "loader-utils": {
             "version": "1.4.0",
@@ -9062,9 +35529,14 @@
             }
         },
         "lodash": {
-            "version": "4.17.15",
-            "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
-            "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
+            "version": "4.17.21",
+            "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+            "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+        },
+        "lodash-es": {
+            "version": "4.17.21",
+            "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+            "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
         },
         "lodash._getnative": {
             "version": "3.9.1",
@@ -9201,10 +35673,33 @@
                 "signal-exit": "^3.0.0"
             }
         },
+        "loupe": {
+            "version": "2.3.4",
+            "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz",
+            "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==",
+            "requires": {
+                "get-func-name": "^2.0.0"
+            }
+        },
+        "lower-case": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+            "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+            "requires": {
+                "tslib": "^2.0.3"
+            },
+            "dependencies": {
+                "tslib": {
+                    "version": "2.3.1",
+                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+                    "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+                }
+            }
+        },
         "lowercase-keys": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
-            "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA=="
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
+            "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ=="
         },
         "lru-cache": {
             "version": "5.1.1",
@@ -9370,22 +35865,19 @@
             "resolved": "https://registry.npmjs.org/max-safe-integer/-/max-safe-integer-1.0.1.tgz",
             "integrity": "sha1-84BgvixWPYwC5tSK85Ei/YO29BA="
         },
-        "md5.js": {
-            "version": "1.3.5",
-            "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
-            "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
-            "dev": true,
-            "requires": {
-                "hash-base": "^3.0.0",
-                "inherits": "^2.0.1",
-                "safe-buffer": "^5.1.2"
-            }
-        },
         "media-typer": {
             "version": "0.3.0",
             "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
             "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
         },
+        "memfs": {
+            "version": "3.4.1",
+            "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz",
+            "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==",
+            "requires": {
+                "fs-monkey": "1.0.3"
+            }
+        },
         "memoize-one": {
             "version": "5.1.1",
             "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz",
@@ -9459,6 +35951,11 @@
             "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
             "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
         },
+        "merge-stream": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+            "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
+        },
         "methods": {
             "version": "1.1.2",
             "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
@@ -9490,24 +35987,6 @@
                 "to-regex": "^3.0.2"
             }
         },
-        "miller-rabin": {
-            "version": "4.0.1",
-            "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
-            "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
-            "dev": true,
-            "requires": {
-                "bn.js": "^4.0.0",
-                "brorand": "^1.0.1"
-            },
-            "dependencies": {
-                "bn.js": {
-                    "version": "4.12.0",
-                    "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
-                    "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
-                    "dev": true
-                }
-            }
-        },
         "mime": {
             "version": "1.6.0",
             "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
@@ -9526,6 +36005,11 @@
                 "mime-db": "1.44.0"
             }
         },
+        "mimic-fn": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+            "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
+        },
         "mimic-response": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
@@ -9537,12 +36021,6 @@
             "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
             "dev": true
         },
-        "minimalistic-crypto-utils": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
-            "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
-            "dev": true
-        },
         "minimatch": {
             "version": "3.0.4",
             "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -9556,23 +36034,6 @@
             "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
             "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
         },
-        "minipass": {
-            "version": "2.9.0",
-            "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
-            "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
-            "requires": {
-                "safe-buffer": "^5.1.2",
-                "yallist": "^3.0.0"
-            }
-        },
-        "minizlib": {
-            "version": "1.3.3",
-            "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
-            "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
-            "requires": {
-                "minipass": "^2.9.0"
-            }
-        },
         "mississippi": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz",
@@ -9626,9 +36087,9 @@
             "integrity": "sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g=="
         },
         "mobile-detect": {
-            "version": "1.4.4",
-            "resolved": "https://registry.npmjs.org/mobile-detect/-/mobile-detect-1.4.4.tgz",
-            "integrity": "sha512-vTgEjKjS89C5yHL5qWPpT6BzKuOVqABp+A3Szpbx34pIy3sngxlGaFpgHhfj6fKze1w0QKeOSDbU7SKu7wDvRQ=="
+            "version": "1.4.5",
+            "resolved": "https://registry.npmjs.org/mobile-detect/-/mobile-detect-1.4.5.tgz",
+            "integrity": "sha512-yc0LhH6tItlvfLBugVUEtgawwFU2sIe+cSdmRJJCTMZ5GEJyLxNyC/NIOAOGk67Fa8GNpOttO3Xz/1bHpXFD/g=="
         },
         "mobx": {
             "version": "5.15.7",
@@ -9647,12 +36108,14 @@
         "mobx-react-devtools": {
             "version": "6.1.1",
             "resolved": "https://registry.npmjs.org/mobx-react-devtools/-/mobx-react-devtools-6.1.1.tgz",
-            "integrity": "sha512-nc5IXLdEUFLn3wZal65KF3/JFEFd+mbH4KTz/IG5BOPyw7jo8z29w/8qm7+wiCyqVfUIgJ1gL4+HVKmcXIOgqA=="
+            "integrity": "sha512-nc5IXLdEUFLn3wZal65KF3/JFEFd+mbH4KTz/IG5BOPyw7jo8z29w/8qm7+wiCyqVfUIgJ1gL4+HVKmcXIOgqA==",
+            "requires": {}
         },
         "mobx-utils": {
-            "version": "5.6.1",
-            "resolved": "https://registry.npmjs.org/mobx-utils/-/mobx-utils-5.6.1.tgz",
-            "integrity": "sha512-bpTJzM8MXniGnXCZY+ImjPDqBKQ3+G3g/QFSPtNkH6HM3x14DAPqKH7No7NDyhbXBMv3FaVetsgnoEPozbi45Q=="
+            "version": "5.6.2",
+            "resolved": "https://registry.npmjs.org/mobx-utils/-/mobx-utils-5.6.2.tgz",
+            "integrity": "sha512-a/WlXyGkp6F12b01sTarENpxbmlRgPHFyR1Xv2bsSjQBm5dcOtd16ONb40/vOqck8L99NHpI+C9MXQ+SZ8f+yw==",
+            "requires": {}
         },
         "mocha": {
             "version": "5.2.0",
@@ -9741,31 +36204,39 @@
             }
         },
         "mongodb": {
-            "version": "3.5.9",
-            "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.9.tgz",
-            "integrity": "sha512-vXHBY1CsGYcEPoVWhwgxIBeWqP3dSu9RuRDsoLRPTITrcrgm1f0Ubu1xqF9ozMwv53agmEiZm0YGo+7WL3Nbug==",
+            "version": "3.7.3",
+            "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.7.3.tgz",
+            "integrity": "sha512-Psm+g3/wHXhjBEktkxXsFMZvd3nemI0r3IPsE0bU+4//PnvNWKkzhZcEsbPcYiWqe8XqXJJEg4Tgtr7Raw67Yw==",
             "requires": {
-                "bl": "^2.2.0",
+                "bl": "^2.2.1",
                 "bson": "^1.1.4",
                 "denque": "^1.4.1",
-                "require_optional": "^1.0.1",
+                "optional-require": "^1.1.8",
                 "safe-buffer": "^5.1.2",
                 "saslprep": "^1.0.0"
             },
             "dependencies": {
                 "bl": {
-                    "version": "2.2.0",
-                    "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz",
-                    "integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==",
+                    "version": "2.2.1",
+                    "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz",
+                    "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==",
                     "requires": {
                         "readable-stream": "^2.3.5",
                         "safe-buffer": "^5.1.1"
                     }
                 },
                 "bson": {
-                    "version": "1.1.4",
-                    "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.4.tgz",
-                    "integrity": "sha512-S/yKGU1syOMzO86+dGpg2qGoDL0zvzcb262G+gqEy6TgP6rt6z6qxSFX/8X6vLC91P7G7C3nLs0+bvDzmvBA3Q=="
+                    "version": "1.1.6",
+                    "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz",
+                    "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg=="
+                },
+                "optional-require": {
+                    "version": "1.1.8",
+                    "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.8.tgz",
+                    "integrity": "sha512-jq83qaUb0wNg9Krv1c5OQ+58EK+vHde6aBPzLvPPqJm89UQWsvSuFy9X/OSNJnFeSOKo7btE0n8Nl2+nE+z5nA==",
+                    "requires": {
+                        "require-at": "^1.0.6"
+                    }
                 },
                 "readable-stream": {
                     "version": "2.3.7",
@@ -9790,42 +36261,66 @@
             "requires": {
                 "bson": "~1.0.4",
                 "require_optional": "~1.0.0"
+            },
+            "dependencies": {
+                "bson": {
+                    "version": "1.0.9",
+                    "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.9.tgz",
+                    "integrity": "sha512-IQX9/h7WdMBIW/q/++tGd+emQr0XMdeZ6icnT/74Xk9fnabWn+gZgpE+9V+gujL3hhJOoNrnDVY7tWdzc7NUTg=="
+                }
             }
         },
         "mongoose": {
-            "version": "5.9.20",
-            "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.9.20.tgz",
-            "integrity": "sha512-vRP6Csu2obzSl3ed7kTQMrolBNgweiRJ/eBU1PSe/rJfjqWS1oqDE2D1ZPGxkVOsKXs7Gyd84GAXerj8IB2UWg==",
+            "version": "5.13.14",
+            "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.13.14.tgz",
+            "integrity": "sha512-j+BlQjjxgZg0iWn42kLeZTB91OejcxWpY2Z50bsZTiKJ7HHcEtcY21Godw496GMkBqJMTzmW7G/kZ04mW+Cb7Q==",
             "requires": {
+                "@types/bson": "1.x || 4.0.x",
+                "@types/mongodb": "^3.5.27",
                 "bson": "^1.1.4",
-                "kareem": "2.3.1",
-                "mongodb": "3.5.9",
+                "kareem": "2.3.2",
+                "mongodb": "3.7.3",
                 "mongoose-legacy-pluralize": "1.0.2",
-                "mpath": "0.7.0",
-                "mquery": "3.2.2",
+                "mpath": "0.8.4",
+                "mquery": "3.2.5",
                 "ms": "2.1.2",
+                "optional-require": "1.0.x",
                 "regexp-clone": "1.0.0",
-                "safe-buffer": "5.1.2",
-                "sift": "7.0.1",
+                "safe-buffer": "5.2.1",
+                "sift": "13.5.2",
                 "sliced": "1.0.1"
             },
             "dependencies": {
+                "@types/bson": {
+                    "version": "4.0.5",
+                    "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.5.tgz",
+                    "integrity": "sha512-vVLwMUqhYJSQ/WKcE60eFqcyuWse5fGH+NMAXHuKrUAPoryq3ATxk5o4bgYNtg5aOM4APVg7Hnb3ASqUYG0PKg==",
+                    "requires": {
+                        "@types/node": "*"
+                    }
+                },
                 "bson": {
-                    "version": "1.1.4",
-                    "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.4.tgz",
-                    "integrity": "sha512-S/yKGU1syOMzO86+dGpg2qGoDL0zvzcb262G+gqEy6TgP6rt6z6qxSFX/8X6vLC91P7G7C3nLs0+bvDzmvBA3Q=="
+                    "version": "1.1.6",
+                    "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz",
+                    "integrity": "sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg=="
                 },
                 "ms": {
                     "version": "2.1.2",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
                     "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+                },
+                "safe-buffer": {
+                    "version": "5.2.1",
+                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+                    "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
                 }
             }
         },
         "mongoose-legacy-pluralize": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz",
-            "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ=="
+            "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==",
+            "requires": {}
         },
         "move-concurrently": {
             "version": "1.0.1",
@@ -9853,14 +36348,14 @@
             }
         },
         "mpath": {
-            "version": "0.7.0",
-            "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.7.0.tgz",
-            "integrity": "sha512-Aiq04hILxhz1L+f7sjGyn7IxYzWm1zLNNXcfhDtx04kZ2Gk7uvFdgZ8ts1cWa/6d0TQmag2yR8zSGZUmp0tFNg=="
+            "version": "0.8.4",
+            "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.8.4.tgz",
+            "integrity": "sha512-DTxNZomBcTWlrMW76jy1wvV37X/cNNxPW1y2Jzd4DZkAaC5ZGsm8bfGfNOthcDuRJujXLqiuS6o3Tpy0JEoh7g=="
         },
         "mquery": {
-            "version": "3.2.2",
-            "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.2.tgz",
-            "integrity": "sha512-XB52992COp0KP230I3qloVUbkLUxJIu328HBP2t2EsxSFtf4W1HPSOBWOXf1bqxK4Xbb66lfMJ+Bpfd9/yZE1Q==",
+            "version": "3.2.5",
+            "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.5.tgz",
+            "integrity": "sha512-VjOKHHgU84wij7IUoZzFRU07IAxd5kWJaDmyUzQlbjHjyoeK5TNeeo8ZsFDtTYnSgpW6n/nMNIHvE3u8Lbrf4A==",
             "requires": {
                 "bluebird": "3.5.1",
                 "debug": "3.1.0",
@@ -9921,9 +36416,9 @@
             "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw=="
         },
         "nanoid": {
-            "version": "3.1.10",
-            "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.10.tgz",
-            "integrity": "sha512-iZFMXKeXWkxzlfmMfM91gw7YhN2sdJtixY+eZh9V6QWJWTOiurhpKhBMgr82pfzgSqglQgqYSCowEYsz8D++6w=="
+            "version": "2.1.11",
+            "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz",
+            "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA=="
         },
         "nanomatch": {
             "version": "1.2.13",
@@ -9953,20 +36448,10 @@
             "resolved": "https://registry.npmjs.org/native-or-bluebird/-/native-or-bluebird-1.2.0.tgz",
             "integrity": "sha1-OcR7/Xgl0fuf+tMiEK4l2q3xAck="
         },
-        "needle": {
-            "version": "2.4.1",
-            "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.1.tgz",
-            "integrity": "sha512-x/gi6ijr4B7fwl6WYL9FwlCvRQKGlUNvnceho8wxkwXqN8jvVmmmATTmZPRRG7b/yC1eode26C2HO9jl78Du9g==",
-            "requires": {
-                "debug": "^3.2.6",
-                "iconv-lite": "^0.4.4",
-                "sax": "^1.2.4"
-            }
-        },
         "negotiator": {
-            "version": "0.6.2",
-            "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
-            "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
+            "version": "0.6.3",
+            "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+            "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
         },
         "neo-async": {
             "version": "2.6.1",
@@ -9975,9 +36460,9 @@
             "dev": true
         },
         "next-tick": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
-            "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
+            "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
         },
         "nextafter": {
             "version": "1.0.0",
@@ -9993,6 +36478,22 @@
             "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
             "dev": true
         },
+        "no-case": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+            "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+            "requires": {
+                "lower-case": "^2.0.2",
+                "tslib": "^2.0.3"
+            },
+            "dependencies": {
+                "tslib": {
+                    "version": "2.3.1",
+                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+                    "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+                }
+            }
+        },
         "node-abi": {
             "version": "2.16.0",
             "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.16.0.tgz",
@@ -10081,83 +36582,10 @@
                 }
             }
         },
-        "node-libs-browser": {
-            "version": "2.2.1",
-            "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
-            "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==",
-            "dev": true,
-            "requires": {
-                "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.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.0",
-                "vm-browserify": "^1.0.1"
-            },
-            "dependencies": {
-                "buffer": {
-                    "version": "4.9.2",
-                    "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
-                    "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
-                    "dev": true,
-                    "requires": {
-                        "base64-js": "^1.0.2",
-                        "ieee754": "^1.1.4",
-                        "isarray": "^1.0.0"
-                    }
-                },
-                "punycode": {
-                    "version": "1.4.1",
-                    "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
-                    "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
-                    "dev": true
-                },
-                "readable-stream": {
-                    "version": "2.3.7",
-                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
-                    "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
-                    "dev": true,
-                    "requires": {
-                        "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"
-                    }
-                }
-            }
-        },
-        "node-notifier": {
-            "version": "5.4.3",
-            "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.3.tgz",
-            "integrity": "sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==",
-            "dev": true,
-            "requires": {
-                "growly": "^1.3.0",
-                "is-wsl": "^1.1.0",
-                "semver": "^5.5.0",
-                "shellwords": "^0.1.1",
-                "which": "^1.3.0"
-            }
+        "node-releases": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz",
+            "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg=="
         },
         "node-sass": {
             "version": "4.14.1",
@@ -10234,9 +36662,9 @@
             }
         },
         "node-stream-zip": {
-            "version": "1.11.2",
-            "resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.11.2.tgz",
-            "integrity": "sha512-cowCX+OyzS3tN2i4BMMFxCr/pE6cQlEMTbVCugmos0TNEJQNtcG04tR41CY8lumO1I7F5GFiLaU4WavomJthaA=="
+            "version": "1.15.0",
+            "resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.15.0.tgz",
+            "integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw=="
         },
         "nodemailer": {
             "version": "5.1.1",
@@ -10265,15 +36693,6 @@
             "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
             "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI="
         },
-        "nopt": {
-            "version": "4.0.3",
-            "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
-            "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
-            "requires": {
-                "abbrev": "1",
-                "osenv": "^0.1.4"
-            }
-        },
         "normalize-package-data": {
             "version": "2.5.0",
             "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
@@ -10290,23 +36709,27 @@
             "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
             "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
         },
+        "normalize-url": {
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
+            "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="
+        },
         "normalize.css": {
             "version": "8.0.1",
             "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz",
             "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg=="
         },
         "npm": {
-            "version": "6.14.5",
-            "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.5.tgz",
-            "integrity": "sha512-CDwa3FJd0XJpKDbWCST484H+mCNjF26dPrU+xnREW+upR0UODjMEfXPl3bxWuAwZIX6c2ASg1plLO7jP8ehWeA==",
+            "version": "6.14.16",
+            "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.16.tgz",
+            "integrity": "sha512-LMiLGYsVNJfVPlQg7v2NYjG7iRIapcLv+oMunlq7fkXVx0BATCjRu7XyWl0G+iuZzHy4CjtM32QB8ox8juTgaw==",
             "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.7",
+                "bin-links": "^1.1.8",
                 "bluebird": "^3.5.5",
                 "byte-size": "^5.0.1",
                 "cacache": "^12.0.3",
@@ -10327,29 +36750,30 @@
                 "find-npm-prefix": "^1.0.2",
                 "fs-vacuum": "~1.2.10",
                 "fs-write-stream-atomic": "~1.0.10",
-                "gentle-fs": "^2.3.0",
+                "gentle-fs": "^2.3.1",
                 "glob": "^7.1.6",
                 "graceful-fs": "^4.2.4",
                 "has-unicode": "~2.0.1",
-                "hosted-git-info": "^2.8.8",
+                "hosted-git-info": "^2.8.9",
                 "iferr": "^1.0.2",
                 "imurmurhash": "*",
                 "infer-owner": "^1.0.4",
                 "inflight": "~1.0.6",
                 "inherits": "^2.0.4",
-                "ini": "^1.3.5",
+                "ini": "^1.3.8",
                 "init-package-json": "^1.10.3",
                 "is-cidr": "^3.0.0",
                 "json-parse-better-errors": "^1.0.2",
+                "JSONStream": "^1.3.5",
                 "lazy-property": "~1.0.0",
-                "libcipm": "^4.0.7",
+                "libcipm": "^4.0.8",
                 "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.2",
+                "libnpx": "^10.2.4",
                 "lock-verify": "^2.1.0",
                 "lockfile": "^1.0.4",
                 "lodash._baseindexof": "*",
@@ -10364,26 +36788,26 @@
                 "lodash.uniq": "~4.5.0",
                 "lodash.without": "~4.4.0",
                 "lru-cache": "^5.1.1",
-                "meant": "~1.0.1",
+                "meant": "^1.0.2",
                 "mississippi": "^3.0.0",
                 "mkdirp": "^0.5.5",
                 "move-concurrently": "^1.0.1",
                 "node-gyp": "^5.1.0",
                 "nopt": "^4.0.3",
                 "normalize-package-data": "^2.5.0",
-                "npm-audit-report": "^1.3.2",
+                "npm-audit-report": "^1.3.3",
                 "npm-cache-filename": "~1.0.2",
                 "npm-install-checks": "^3.0.2",
-                "npm-lifecycle": "^3.1.4",
+                "npm-lifecycle": "^3.1.5",
                 "npm-package-arg": "^6.1.1",
                 "npm-packlist": "^1.4.8",
                 "npm-pick-manifest": "^3.0.2",
                 "npm-profile": "^4.0.4",
-                "npm-registry-fetch": "^4.0.4",
-                "npm-user-validate": "~1.0.0",
+                "npm-registry-fetch": "^4.0.7",
+                "npm-user-validate": "^1.0.1",
                 "npmlog": "~4.1.2",
                 "once": "~1.4.0",
-                "opener": "^1.5.1",
+                "opener": "^1.5.2",
                 "osenv": "^0.1.5",
                 "pacote": "^9.5.12",
                 "path-is-inside": "~1.0.2",
@@ -10407,9 +36831,9 @@
                 "slide": "~1.1.6",
                 "sorted-object": "~2.0.1",
                 "sorted-union-stream": "~2.1.3",
-                "ssri": "^6.0.1",
+                "ssri": "^6.0.2",
                 "stringify-package": "^1.0.1",
-                "tar": "^4.4.13",
+                "tar": "^4.4.19",
                 "text-table": "~0.2.0",
                 "tiny-relative-date": "^1.3.0",
                 "uid-number": "0.0.6",
@@ -10425,14 +36849,6 @@
                 "write-file-atomic": "^2.4.3"
             },
             "dependencies": {
-                "JSONStream": {
-                    "version": "1.3.5",
-                    "bundled": true,
-                    "requires": {
-                        "jsonparse": "^1.2.0",
-                        "through": ">=2.2.7 <3"
-                    }
-                },
                 "abbrev": {
                     "version": "1.1.1",
                     "bundled": true
@@ -10451,16 +36867,6 @@
                         "humanize-ms": "^1.2.1"
                     }
                 },
-                "ajv": {
-                    "version": "5.5.2",
-                    "bundled": true,
-                    "requires": {
-                        "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,
@@ -10565,7 +36971,7 @@
                     }
                 },
                 "bin-links": {
-                    "version": "1.1.7",
+                    "version": "1.1.8",
                     "bundled": true,
                     "requires": {
                         "bluebird": "^3.5.3",
@@ -10700,23 +37106,36 @@
                     }
                 },
                 "cliui": {
-                    "version": "4.1.0",
+                    "version": "5.0.0",
                     "bundled": true,
                     "requires": {
-                        "string-width": "^2.1.1",
-                        "strip-ansi": "^4.0.0",
-                        "wrap-ansi": "^2.0.0"
+                        "string-width": "^3.1.0",
+                        "strip-ansi": "^5.2.0",
+                        "wrap-ansi": "^5.1.0"
                     },
                     "dependencies": {
                         "ansi-regex": {
-                            "version": "3.0.0",
+                            "version": "4.1.0",
+                            "bundled": true
+                        },
+                        "is-fullwidth-code-point": {
+                            "version": "2.0.0",
                             "bundled": true
                         },
+                        "string-width": {
+                            "version": "3.1.0",
+                            "bundled": true,
+                            "requires": {
+                                "emoji-regex": "^7.0.1",
+                                "is-fullwidth-code-point": "^2.0.0",
+                                "strip-ansi": "^5.1.0"
+                            }
+                        },
                         "strip-ansi": {
-                            "version": "4.0.0",
+                            "version": "5.2.0",
                             "bundled": true,
                             "requires": {
-                                "ansi-regex": "^3.0.0"
+                                "ansi-regex": "^4.1.0"
                             }
                         }
                     }
@@ -10733,10 +37152,6 @@
                         "mkdirp": "~0.5.0"
                     }
                 },
-                "co": {
-                    "version": "4.6.0",
-                    "bundled": true
-                },
                 "code-point-at": {
                     "version": "1.1.0",
                     "bundled": true
@@ -10817,10 +37232,10 @@
                     }
                 },
                 "configstore": {
-                    "version": "3.1.2",
+                    "version": "3.1.5",
                     "bundled": true,
                     "requires": {
-                        "dot-prop": "^4.1.0",
+                        "dot-prop": "^4.2.1",
                         "graceful-fs": "^4.1.2",
                         "make-dir": "^1.0.0",
                         "unique-string": "^1.0.0",
@@ -10971,7 +37386,7 @@
                     }
                 },
                 "dot-prop": {
-                    "version": "4.2.0",
+                    "version": "4.2.1",
                     "bundled": true,
                     "requires": {
                         "is-obj": "^1.0.0"
@@ -11030,6 +37445,10 @@
                     "version": "1.0.0",
                     "bundled": true
                 },
+                "emoji-regex": {
+                    "version": "7.0.3",
+                    "bundled": true
+                },
                 "encoding": {
                     "version": "0.1.12",
                     "bundled": true,
@@ -11121,10 +37540,6 @@
                     "version": "1.3.0",
                     "bundled": true
                 },
-                "fast-deep-equal": {
-                    "version": "1.1.0",
-                    "bundled": true
-                },
                 "fast-json-stable-stringify": {
                     "version": "2.0.0",
                     "bundled": true
@@ -11137,13 +37552,6 @@
                     "version": "1.0.2",
                     "bundled": true
                 },
-                "find-up": {
-                    "version": "2.1.0",
-                    "bundled": true,
-                    "requires": {
-                        "locate-path": "^2.0.0"
-                    }
-                },
                 "flush-write-stream": {
                     "version": "1.0.3",
                     "bundled": true,
@@ -11321,7 +37729,7 @@
                     "bundled": true
                 },
                 "gentle-fs": {
-                    "version": "2.3.0",
+                    "version": "2.3.1",
                     "bundled": true,
                     "requires": {
                         "aproba": "^1.1.2",
@@ -11348,7 +37756,7 @@
                     }
                 },
                 "get-caller-file": {
-                    "version": "1.0.3",
+                    "version": "2.0.5",
                     "bundled": true
                 },
                 "get-stream": {
@@ -11416,11 +37824,31 @@
                     "bundled": true
                 },
                 "har-validator": {
-                    "version": "5.1.0",
+                    "version": "5.1.5",
                     "bundled": true,
                     "requires": {
-                        "ajv": "^5.3.0",
+                        "ajv": "^6.12.3",
                         "har-schema": "^2.0.0"
+                    },
+                    "dependencies": {
+                        "ajv": {
+                            "version": "6.12.6",
+                            "bundled": true,
+                            "requires": {
+                                "fast-deep-equal": "^3.1.1",
+                                "fast-json-stable-stringify": "^2.0.0",
+                                "json-schema-traverse": "^0.4.1",
+                                "uri-js": "^4.2.2"
+                            }
+                        },
+                        "fast-deep-equal": {
+                            "version": "3.1.3",
+                            "bundled": true
+                        },
+                        "json-schema-traverse": {
+                            "version": "0.4.1",
+                            "bundled": true
+                        }
                     }
                 },
                 "has": {
@@ -11443,7 +37871,7 @@
                     "bundled": true
                 },
                 "hosted-git-info": {
-                    "version": "2.8.8",
+                    "version": "2.8.9",
                     "bundled": true
                 },
                 "http-cache-semantics": {
@@ -11525,7 +37953,7 @@
                     "bundled": true
                 },
                 "ini": {
-                    "version": "1.3.5",
+                    "version": "1.3.8",
                     "bundled": true
                 },
                 "init-package-json": {
@@ -11542,10 +37970,6 @@
                         "validate-npm-package-name": "^3.0.0"
                     }
                 },
-                "invert-kv": {
-                    "version": "2.0.0",
-                    "bundled": true
-                },
                 "ip": {
                     "version": "1.1.5",
                     "bundled": true
@@ -11664,11 +38088,7 @@
                     "bundled": true
                 },
                 "json-schema": {
-                    "version": "0.2.3",
-                    "bundled": true
-                },
-                "json-schema-traverse": {
-                    "version": "0.3.1",
+                    "version": "0.4.0",
                     "bundled": true
                 },
                 "json-stringify-safe": {
@@ -11679,13 +38099,21 @@
                     "version": "1.3.1",
                     "bundled": true
                 },
+                "JSONStream": {
+                    "version": "1.3.5",
+                    "bundled": true,
+                    "requires": {
+                        "jsonparse": "^1.2.0",
+                        "through": ">=2.2.7 <3"
+                    }
+                },
                 "jsprim": {
-                    "version": "1.4.1",
+                    "version": "1.4.2",
                     "bundled": true,
                     "requires": {
                         "assert-plus": "1.0.0",
                         "extsprintf": "1.3.0",
-                        "json-schema": "0.2.3",
+                        "json-schema": "0.4.0",
                         "verror": "1.10.0"
                     }
                 },
@@ -11700,15 +38128,8 @@
                     "version": "1.0.0",
                     "bundled": true
                 },
-                "lcid": {
-                    "version": "2.0.0",
-                    "bundled": true,
-                    "requires": {
-                        "invert-kv": "^2.0.0"
-                    }
-                },
                 "libcipm": {
-                    "version": "4.0.7",
+                    "version": "4.0.8",
                     "bundled": true,
                     "requires": {
                         "bin-links": "^1.1.2",
@@ -11717,7 +38138,7 @@
                         "find-npm-prefix": "^1.0.2",
                         "graceful-fs": "^4.1.11",
                         "ini": "^1.3.5",
-                        "lock-verify": "^2.0.2",
+                        "lock-verify": "^2.1.0",
                         "mkdirp": "^0.5.1",
                         "npm-lifecycle": "^3.0.0",
                         "npm-logical-tree": "^1.2.1",
@@ -11863,7 +38284,7 @@
                     }
                 },
                 "libnpx": {
-                    "version": "10.2.2",
+                    "version": "10.2.4",
                     "bundled": true,
                     "requires": {
                         "dotenv": "^5.0.1",
@@ -11873,15 +38294,7 @@
                         "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"
+                        "yargs": "^14.2.3"
                     }
                 },
                 "lock-verify": {
@@ -11993,32 +38406,10 @@
                         "ssri": "^6.0.0"
                     }
                 },
-                "map-age-cleaner": {
-                    "version": "0.1.3",
-                    "bundled": true,
-                    "requires": {
-                        "p-defer": "^1.0.0"
-                    }
-                },
                 "meant": {
-                    "version": "1.0.1",
+                    "version": "1.0.2",
                     "bundled": true
                 },
-                "mem": {
-                    "version": "4.3.0",
-                    "bundled": true,
-                    "requires": {
-                        "map-age-cleaner": "^0.1.1",
-                        "mimic-fn": "^2.0.0",
-                        "p-is-promise": "^2.0.0"
-                    },
-                    "dependencies": {
-                        "mimic-fn": {
-                            "version": "2.1.0",
-                            "bundled": true
-                        }
-                    }
-                },
                 "mime-db": {
                     "version": "1.35.0",
                     "bundled": true
@@ -12037,6 +38428,10 @@
                         "brace-expansion": "^1.1.7"
                     }
                 },
+                "minimist": {
+                    "version": "1.2.5",
+                    "bundled": true
+                },
                 "minizlib": {
                     "version": "1.3.3",
                     "bundled": true,
@@ -12109,10 +38504,6 @@
                     "version": "0.0.7",
                     "bundled": true
                 },
-                "nice-try": {
-                    "version": "1.0.5",
-                    "bundled": true
-                },
                 "node-fetch-npm": {
                     "version": "2.0.2",
                     "bundled": true,
@@ -12167,7 +38558,7 @@
                     }
                 },
                 "npm-audit-report": {
-                    "version": "1.3.2",
+                    "version": "1.3.3",
                     "bundled": true,
                     "requires": {
                         "cli-table3": "^0.5.0",
@@ -12193,7 +38584,7 @@
                     }
                 },
                 "npm-lifecycle": {
-                    "version": "3.1.4",
+                    "version": "3.1.5",
                     "bundled": true,
                     "requires": {
                         "byline": "^5.0.0",
@@ -12252,12 +38643,12 @@
                     }
                 },
                 "npm-registry-fetch": {
-                    "version": "4.0.4",
+                    "version": "4.0.7",
                     "bundled": true,
                     "requires": {
-                        "JSONStream": "^1.3.4",
                         "bluebird": "^3.5.1",
                         "figgy-pudding": "^3.4.1",
+                        "JSONStream": "^1.3.4",
                         "lru-cache": "^5.1.1",
                         "make-fetch-happen": "^5.0.0",
                         "npm-package-arg": "^6.1.0",
@@ -12265,7 +38656,7 @@
                     },
                     "dependencies": {
                         "safe-buffer": {
-                            "version": "5.2.0",
+                            "version": "5.2.1",
                             "bundled": true
                         }
                     }
@@ -12278,7 +38669,7 @@
                     }
                 },
                 "npm-user-validate": {
-                    "version": "1.0.0",
+                    "version": "1.0.1",
                     "bundled": true
                 },
                 "npmlog": {
@@ -12323,48 +38714,13 @@
                     }
                 },
                 "opener": {
-                    "version": "1.5.1",
+                    "version": "1.5.2",
                     "bundled": true
                 },
                 "os-homedir": {
                     "version": "1.0.2",
                     "bundled": true
                 },
-                "os-locale": {
-                    "version": "3.1.0",
-                    "bundled": true,
-                    "requires": {
-                        "execa": "^1.0.0",
-                        "lcid": "^2.0.0",
-                        "mem": "^4.0.0"
-                    },
-                    "dependencies": {
-                        "cross-spawn": {
-                            "version": "6.0.5",
-                            "bundled": true,
-                            "requires": {
-                                "nice-try": "^1.0.4",
-                                "path-key": "^2.0.1",
-                                "semver": "^5.5.0",
-                                "shebang-command": "^1.2.0",
-                                "which": "^1.2.9"
-                            }
-                        },
-                        "execa": {
-                            "version": "1.0.0",
-                            "bundled": true,
-                            "requires": {
-                                "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"
-                            }
-                        }
-                    }
-                },
                 "os-tmpdir": {
                     "version": "1.0.2",
                     "bundled": true
@@ -12377,36 +38733,10 @@
                         "os-tmpdir": "^1.0.0"
                     }
                 },
-                "p-defer": {
-                    "version": "1.0.0",
-                    "bundled": true
-                },
                 "p-finally": {
                     "version": "1.0.0",
                     "bundled": true
                 },
-                "p-is-promise": {
-                    "version": "2.1.0",
-                    "bundled": true
-                },
-                "p-limit": {
-                    "version": "1.2.0",
-                    "bundled": true,
-                    "requires": {
-                        "p-try": "^1.0.0"
-                    }
-                },
-                "p-locate": {
-                    "version": "2.0.0",
-                    "bundled": true,
-                    "requires": {
-                        "p-limit": "^1.1.0"
-                    }
-                },
-                "p-try": {
-                    "version": "1.0.0",
-                    "bundled": true
-                },
                 "package-json": {
                     "version": "4.0.1",
                     "bundled": true,
@@ -12511,7 +38841,7 @@
                     "bundled": true
                 },
                 "path-parse": {
-                    "version": "1.0.6",
+                    "version": "1.0.7",
                     "bundled": true
                 },
                 "performance-now": {
@@ -12638,12 +38968,6 @@
                         "ini": "~1.3.0",
                         "minimist": "^1.2.0",
                         "strip-json-comments": "~2.0.1"
-                    },
-                    "dependencies": {
-                        "minimist": {
-                            "version": "1.2.5",
-                            "bundled": true
-                        }
                     }
                 },
                 "read": {
@@ -12758,7 +39082,7 @@
                     "bundled": true
                 },
                 "require-main-filename": {
-                    "version": "1.0.1",
+                    "version": "2.0.0",
                     "bundled": true
                 },
                 "resolve-from": {
@@ -12928,7 +39252,7 @@
                     }
                 },
                 "spdx-license-ids": {
-                    "version": "3.0.3",
+                    "version": "3.0.5",
                     "bundled": true
                 },
                 "split-on-first": {
@@ -12951,7 +39275,7 @@
                     }
                 },
                 "ssri": {
-                    "version": "6.0.1",
+                    "version": "6.0.2",
                     "bundled": true,
                     "requires": {
                         "figgy-pudding": "^3.5.1"
@@ -13003,6 +39327,19 @@
                     "version": "2.0.0",
                     "bundled": true
                 },
+                "string_decoder": {
+                    "version": "1.3.0",
+                    "bundled": true,
+                    "requires": {
+                        "safe-buffer": "~5.2.0"
+                    },
+                    "dependencies": {
+                        "safe-buffer": {
+                            "version": "5.2.0",
+                            "bundled": true
+                        }
+                    }
+                },
                 "string-width": {
                     "version": "2.1.1",
                     "bundled": true,
@@ -13028,19 +39365,6 @@
                         }
                     }
                 },
-                "string_decoder": {
-                    "version": "1.3.0",
-                    "bundled": true,
-                    "requires": {
-                        "safe-buffer": "~5.2.0"
-                    },
-                    "dependencies": {
-                        "safe-buffer": {
-                            "version": "5.2.0",
-                            "bundled": true
-                        }
-                    }
-                },
                 "stringify-package": {
                     "version": "1.0.1",
                     "bundled": true
@@ -13068,16 +39392,16 @@
                     }
                 },
                 "tar": {
-                    "version": "4.4.13",
+                    "version": "4.4.19",
                     "bundled": true,
                     "requires": {
-                        "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"
+                        "chownr": "^1.1.4",
+                        "fs-minipass": "^1.2.7",
+                        "minipass": "^2.9.0",
+                        "minizlib": "^1.3.3",
+                        "mkdirp": "^0.5.5",
+                        "safe-buffer": "^5.2.1",
+                        "yallist": "^3.1.1"
                     },
                     "dependencies": {
                         "minipass": {
@@ -13087,6 +39411,14 @@
                                 "safe-buffer": "^5.1.2",
                                 "yallist": "^3.0.0"
                             }
+                        },
+                        "safe-buffer": {
+                            "version": "5.2.1",
+                            "bundled": true
+                        },
+                        "yallist": {
+                            "version": "3.1.1",
+                            "bundled": true
                         }
                     }
                 },
@@ -13220,6 +39552,19 @@
                         "xdg-basedir": "^3.0.0"
                     }
                 },
+                "uri-js": {
+                    "version": "4.4.0",
+                    "bundled": true,
+                    "requires": {
+                        "punycode": "^2.1.0"
+                    },
+                    "dependencies": {
+                        "punycode": {
+                            "version": "2.1.1",
+                            "bundled": true
+                        }
+                    }
+                },
                 "url-parse-lax": {
                     "version": "1.0.0",
                     "bundled": true,
@@ -13321,20 +39666,36 @@
                     }
                 },
                 "wrap-ansi": {
-                    "version": "2.1.0",
+                    "version": "5.1.0",
                     "bundled": true,
                     "requires": {
-                        "string-width": "^1.0.1",
-                        "strip-ansi": "^3.0.1"
+                        "ansi-styles": "^3.2.0",
+                        "string-width": "^3.0.0",
+                        "strip-ansi": "^5.0.0"
                     },
                     "dependencies": {
+                        "ansi-regex": {
+                            "version": "4.1.0",
+                            "bundled": true
+                        },
+                        "is-fullwidth-code-point": {
+                            "version": "2.0.0",
+                            "bundled": true
+                        },
                         "string-width": {
-                            "version": "1.0.2",
+                            "version": "3.1.0",
                             "bundled": true,
                             "requires": {
-                                "code-point-at": "^1.0.0",
-                                "is-fullwidth-code-point": "^1.0.0",
-                                "strip-ansi": "^3.0.0"
+                                "emoji-regex": "^7.0.1",
+                                "is-fullwidth-code-point": "^2.0.0",
+                                "strip-ansi": "^5.1.0"
+                            }
+                        },
+                        "strip-ansi": {
+                            "version": "5.2.0",
+                            "bundled": true,
+                            "requires": {
+                                "ansi-regex": "^4.1.0"
                             }
                         }
                     }
@@ -13361,7 +39722,7 @@
                     "bundled": true
                 },
                 "y18n": {
-                    "version": "4.0.0",
+                    "version": "4.0.1",
                     "bundled": true
                 },
                 "yallist": {
@@ -13369,61 +39730,97 @@
                     "bundled": true
                 },
                 "yargs": {
-                    "version": "11.1.1",
+                    "version": "14.2.3",
                     "bundled": true,
                     "requires": {
-                        "cliui": "^4.0.0",
-                        "decamelize": "^1.1.1",
-                        "find-up": "^2.1.0",
-                        "get-caller-file": "^1.0.1",
-                        "os-locale": "^3.1.0",
+                        "cliui": "^5.0.0",
+                        "decamelize": "^1.2.0",
+                        "find-up": "^3.0.0",
+                        "get-caller-file": "^2.0.1",
                         "require-directory": "^2.1.1",
-                        "require-main-filename": "^1.0.1",
+                        "require-main-filename": "^2.0.0",
                         "set-blocking": "^2.0.0",
-                        "string-width": "^2.0.0",
+                        "string-width": "^3.0.0",
                         "which-module": "^2.0.0",
-                        "y18n": "^3.2.1",
-                        "yargs-parser": "^9.0.2"
+                        "y18n": "^4.0.0",
+                        "yargs-parser": "^15.0.1"
                     },
                     "dependencies": {
-                        "y18n": {
-                            "version": "3.2.1",
+                        "ansi-regex": {
+                            "version": "4.1.0",
+                            "bundled": true
+                        },
+                        "find-up": {
+                            "version": "3.0.0",
+                            "bundled": true,
+                            "requires": {
+                                "locate-path": "^3.0.0"
+                            }
+                        },
+                        "is-fullwidth-code-point": {
+                            "version": "2.0.0",
+                            "bundled": true
+                        },
+                        "locate-path": {
+                            "version": "3.0.0",
+                            "bundled": true,
+                            "requires": {
+                                "p-locate": "^3.0.0",
+                                "path-exists": "^3.0.0"
+                            }
+                        },
+                        "p-limit": {
+                            "version": "2.3.0",
+                            "bundled": true,
+                            "requires": {
+                                "p-try": "^2.0.0"
+                            }
+                        },
+                        "p-locate": {
+                            "version": "3.0.0",
+                            "bundled": true,
+                            "requires": {
+                                "p-limit": "^2.0.0"
+                            }
+                        },
+                        "p-try": {
+                            "version": "2.2.0",
                             "bundled": true
+                        },
+                        "string-width": {
+                            "version": "3.1.0",
+                            "bundled": true,
+                            "requires": {
+                                "emoji-regex": "^7.0.1",
+                                "is-fullwidth-code-point": "^2.0.0",
+                                "strip-ansi": "^5.1.0"
+                            }
+                        },
+                        "strip-ansi": {
+                            "version": "5.2.0",
+                            "bundled": true,
+                            "requires": {
+                                "ansi-regex": "^4.1.0"
+                            }
                         }
                     }
                 },
                 "yargs-parser": {
-                    "version": "9.0.2",
+                    "version": "15.0.1",
                     "bundled": true,
                     "requires": {
-                        "camelcase": "^4.1.0"
+                        "camelcase": "^5.0.0",
+                        "decamelize": "^1.2.0"
+                    },
+                    "dependencies": {
+                        "camelcase": {
+                            "version": "5.3.1",
+                            "bundled": true
+                        }
                     }
                 }
             }
         },
-        "npm-bundled": {
-            "version": "1.1.1",
-            "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
-            "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
-            "requires": {
-                "npm-normalize-package-bin": "^1.0.1"
-            }
-        },
-        "npm-normalize-package-bin": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
-            "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA=="
-        },
-        "npm-packlist": {
-            "version": "1.4.8",
-            "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
-            "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
-            "requires": {
-                "ignore-walk": "^3.0.1",
-                "npm-bundled": "^1.0.1",
-                "npm-normalize-package-bin": "^1.0.1"
-            }
-        },
         "npm-run-path": {
             "version": "2.0.2",
             "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
@@ -13444,11 +39841,11 @@
             }
         },
         "nth-check": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
-            "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
+            "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
             "requires": {
-                "boolbase": "~1.0.0"
+                "boolbase": "^1.0.0"
             }
         },
         "number-is-nan": {
@@ -13477,11 +39874,6 @@
             "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
             "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
         },
-        "object-component": {
-            "version": "0.0.3",
-            "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
-            "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE="
-        },
         "object-copy": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
@@ -13521,12 +39913,12 @@
             "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw=="
         },
         "object-is": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz",
-            "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==",
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
+            "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
             "requires": {
-                "define-properties": "^1.1.3",
-                "es-abstract": "^1.17.5"
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3"
             }
         },
         "object-keys": {
@@ -13633,10 +40025,18 @@
                 "wrappy": "1"
             }
         },
+        "onetime": {
+            "version": "5.1.2",
+            "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+            "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+            "requires": {
+                "mimic-fn": "^2.1.0"
+            }
+        },
         "opentype.js": {
-            "version": "1.3.3",
-            "resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-1.3.3.tgz",
-            "integrity": "sha512-/qIY/+WnKGlPIIPhbeNjynfD2PO15G9lA/xqlX2bDH+4lc3Xz5GCQ68mqxj3DdUv6AJqCeaPvuAoH8mVL0zcuA==",
+            "version": "1.3.4",
+            "resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-1.3.4.tgz",
+            "integrity": "sha512-d2JE9RP/6uagpQAVtJoF0pJJA/fgai89Cc50Yp0EJHk+eLp6QQ7gBoblsnubRULNY132I0J1QKMJ+JTbMqz4sw==",
             "requires": {
                 "string.prototype.codepointat": "^0.2.1",
                 "tiny-inflate": "^1.0.3"
@@ -13651,6 +40051,11 @@
                 "is-wsl": "^1.1.0"
             }
         },
+        "optional-require": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.0.3.tgz",
+            "integrity": "sha512-RV2Zp2MY2aeYK5G+B/Sps8lW5NHAzE5QClbFP15j+PWmP+T9PxlJXBOOLoSAdgwFvS4t0aMR4vpedMkbHfh0nA=="
+        },
         "optionator": {
             "version": "0.8.3",
             "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
@@ -13666,9 +40071,9 @@
             }
         },
         "orderedmap": {
-            "version": "1.1.1",
-            "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-1.1.1.tgz",
-            "integrity": "sha512-3Ux8um0zXbVacKUkcytc0u3HgC0b0bBLT+I60r2J/En72cI0nZffqrA7Xtf2Hqs27j1g82llR5Mhbd0Z1XW4AQ=="
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-1.1.5.tgz",
+            "integrity": "sha512-/fzlCGKRmfayGoI9UUXvJfc2nMZlJHW30QqEvwPvlg8tsX7jyiUSomYie6mYqx7Z9bOMGoag0H/q1PS/0PjYkg=="
         },
         "original": {
             "version": "1.0.2",
@@ -13679,12 +40084,6 @@
                 "url-parse": "^1.4.3"
             }
         },
-        "os-browserify": {
-            "version": "0.3.0",
-            "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
-            "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=",
-            "dev": true
-        },
         "os-homedir": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
@@ -13704,6 +40103,11 @@
                 "os-tmpdir": "^1.0.0"
             }
         },
+        "p-cancelable": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz",
+            "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw=="
+        },
         "p-debounce": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/p-debounce/-/p-debounce-1.0.0.tgz",
@@ -13759,6 +40163,31 @@
                 "registry-auth-token": "^3.0.1",
                 "registry-url": "^3.0.3",
                 "semver": "^5.1.0"
+            },
+            "dependencies": {
+                "got": {
+                    "version": "6.7.1",
+                    "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
+                    "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
+                    "requires": {
+                        "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"
+                    }
+                },
+                "lowercase-keys": {
+                    "version": "1.0.1",
+                    "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
+                    "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA=="
+                }
             }
         },
         "pako": {
@@ -13794,6 +40223,22 @@
                 }
             }
         },
+        "param-case": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
+            "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
+            "requires": {
+                "dot-case": "^3.0.4",
+                "tslib": "^2.0.3"
+            },
+            "dependencies": {
+                "tslib": {
+                    "version": "2.3.1",
+                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+                    "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+                }
+            }
+        },
         "parent-module": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -13802,65 +40247,48 @@
                 "callsites": "^3.0.0"
             }
         },
-        "parse-asn1": {
-            "version": "5.1.6",
-            "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz",
-            "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==",
-            "dev": true,
-            "requires": {
-                "asn1.js": "^5.2.0",
-                "browserify-aes": "^1.0.0",
-                "evp_bytestokey": "^1.0.0",
-                "pbkdf2": "^3.0.3",
-                "safe-buffer": "^5.1.1"
-            }
-        },
         "parse-json": {
-            "version": "5.0.0",
-            "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz",
-            "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==",
+            "version": "5.2.0",
+            "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+            "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
             "requires": {
                 "@babel/code-frame": "^7.0.0",
                 "error-ex": "^1.3.1",
-                "json-parse-better-errors": "^1.0.1",
+                "json-parse-even-better-errors": "^2.3.0",
                 "lines-and-columns": "^1.1.6"
             }
         },
-        "parse-passwd": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
-            "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
-            "dev": true
-        },
-        "parse5": {
-            "version": "3.0.3",
-            "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz",
-            "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==",
-            "requires": {
-                "@types/node": "*"
-            }
-        },
         "parseqs": {
-            "version": "0.0.5",
-            "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
-            "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
-            "requires": {
-                "better-assert": "~1.0.0"
-            }
+            "version": "0.0.6",
+            "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
+            "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
         },
         "parseuri": {
-            "version": "0.0.5",
-            "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
-            "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
-            "requires": {
-                "better-assert": "~1.0.0"
-            }
+            "version": "0.0.6",
+            "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
+            "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
         },
         "parseurl": {
             "version": "1.3.3",
             "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
             "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
         },
+        "pascal-case": {
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
+            "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
+            "requires": {
+                "no-case": "^3.0.4",
+                "tslib": "^2.0.3"
+            },
+            "dependencies": {
+                "tslib": {
+                    "version": "2.3.1",
+                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+                    "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+                }
+            }
+        },
         "pascalcase": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
@@ -13909,10 +40337,9 @@
             "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ="
         },
         "path-browserify": {
-            "version": "0.0.1",
-            "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
-            "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==",
-            "dev": true
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
+            "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="
         },
         "path-dirname": {
             "version": "1.0.2",
@@ -13955,28 +40382,15 @@
             "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
         },
         "pathval": {
-            "version": "1.1.0",
-            "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
-            "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA="
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
+            "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ=="
         },
         "pause": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
             "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
         },
-        "pbkdf2": {
-            "version": "3.1.2",
-            "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz",
-            "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==",
-            "dev": true,
-            "requires": {
-                "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": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/pdf-parse/-/pdf-parse-1.1.1.tgz",
@@ -13987,29 +40401,37 @@
             }
         },
         "pdfjs": {
-            "version": "2.3.9",
-            "resolved": "https://registry.npmjs.org/pdfjs/-/pdfjs-2.3.9.tgz",
-            "integrity": "sha512-SK4xI0UbtNo9Ub1BMVgxphdL0Cy1iN9p2SxWG98JLnVe569WF8IO60K03UxlnmSV2s5P0MV1DEuZOlUslRlinA==",
+            "version": "2.4.7",
+            "resolved": "https://registry.npmjs.org/pdfjs/-/pdfjs-2.4.7.tgz",
+            "integrity": "sha512-qGGZiQ7cz7nDgRgNSMm0qsZ4QPlAvZr+kWwB78hZzClojtfqGbGUT/gwzf8S2nniwvLMB56boBTTIppQohTJUA==",
             "requires": {
                 "@rkusa/linebreak": "^1.0.0",
                 "opentype.js": "^1.3.3",
-                "pako": "^1.0.11",
+                "pako": "^2.0.3",
                 "readable-stream": "^3.6.0",
                 "unorm": "^1.6.0",
-                "uuid": "^7.0.3"
+                "uuid": "^8.3.1"
             },
             "dependencies": {
+                "pako": {
+                    "version": "2.0.4",
+                    "resolved": "https://registry.npmjs.org/pako/-/pako-2.0.4.tgz",
+                    "integrity": "sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg=="
+                },
                 "uuid": {
-                    "version": "7.0.3",
-                    "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz",
-                    "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg=="
+                    "version": "8.3.2",
+                    "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+                    "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
                 }
             }
         },
         "pdfjs-dist": {
-            "version": "2.4.456",
-            "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.4.456.tgz",
-            "integrity": "sha512-yckJEHq3F48hcp6wStEpbN9McOj328Ib09UrBlGAKxvN2k+qYPN5iq6TH6jD1C0pso7zTep+g/CKsYgdrQd5QA=="
+            "version": "2.13.216",
+            "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.13.216.tgz",
+            "integrity": "sha512-qn/9a/3IHIKZarTK6ajeeFXBkG15Lg1Fx99PxU09PAU2i874X8mTcHJYyDJxu7WDfNhV6hM7bRQBZU384anoqQ==",
+            "requires": {
+                "web-streams-polyfill": "^3.2.0"
+            }
         },
         "pend": {
             "version": "1.2.0",
@@ -14021,10 +40443,15 @@
             "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
             "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
         },
+        "picocolors": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+            "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+        },
         "picomatch": {
-            "version": "2.2.2",
-            "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
-            "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
+            "version": "2.3.1",
+            "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+            "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
             "dev": true
         },
         "pify": {
@@ -14138,19 +40565,30 @@
             "dev": true
         },
         "popper.js": {
-            "version": "1.16.1",
-            "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
-            "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ=="
+            "version": "1.16.1-lts",
+            "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz",
+            "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA=="
         },
         "portfinder": {
-            "version": "1.0.26",
-            "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.26.tgz",
-            "integrity": "sha512-Xi7mKxJHHMI3rIUrnm/jjUgwhbYMkp/XKEcZX3aG4BrumLpq3nmoQMX+ClYnDZnZ/New7IatC1no5RX0zo1vXQ==",
+            "version": "1.0.28",
+            "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
+            "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==",
             "dev": true,
             "requires": {
                 "async": "^2.6.2",
                 "debug": "^3.1.1",
-                "mkdirp": "^0.5.1"
+                "mkdirp": "^0.5.5"
+            },
+            "dependencies": {
+                "mkdirp": {
+                    "version": "0.5.5",
+                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+                    "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+                    "dev": true,
+                    "requires": {
+                        "minimist": "^1.2.5"
+                    }
+                }
             }
         },
         "posix-character-classes": {
@@ -14275,6 +40713,15 @@
             "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
             "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw="
         },
+        "pretty-error": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
+            "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==",
+            "requires": {
+                "lodash": "^4.17.20",
+                "renderkid": "^3.0.0"
+            }
+        },
         "probe-image-size": {
             "version": "4.1.1",
             "resolved": "https://registry.npmjs.org/probe-image-size/-/probe-image-size-4.1.1.tgz",
@@ -14298,8 +40745,7 @@
         "process": {
             "version": "0.11.10",
             "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
-            "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
-            "dev": true
+            "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
         },
         "process-nextick-args": {
             "version": "2.0.1",
@@ -14336,9 +40782,9 @@
             }
         },
         "prosemirror-commands": {
-            "version": "1.1.4",
-            "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.1.4.tgz",
-            "integrity": "sha512-kj4Qi+8h3EpJtZuuEDwZ9h2/QNGWDsIX/CzjmClxi9GhxWyBUMVUvIFk0mgdqHyX20lLeGmOpc0TLA5aPzgpWg==",
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.2.1.tgz",
+            "integrity": "sha512-S/IkpXfpuLFsRynC2HQ5iYROUPiZskKS1+ClcWycGJvj4HMb/mVfeEkQrixYxgTl96EAh+RZQNWPC06GZXk5tQ==",
             "requires": {
                 "prosemirror-model": "^1.0.0",
                 "prosemirror-state": "^1.0.0",
@@ -14346,17 +40792,17 @@
             }
         },
         "prosemirror-dev-tools": {
-            "version": "3.0.1",
-            "resolved": "https://registry.npmjs.org/prosemirror-dev-tools/-/prosemirror-dev-tools-3.0.1.tgz",
-            "integrity": "sha512-ZLMqrdIpDstBG6kP6Pt62FB7sAiLKqtW/oWvoBqlHw1p2ovNErR1/5aR1Uxrs6dgb4nPDxbu2uMKf/yI7ok7zQ==",
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/prosemirror-dev-tools/-/prosemirror-dev-tools-3.1.0.tgz",
+            "integrity": "sha512-ghuxb6QD3WWl3EE8OK1O3P67OKjbTxt5mvagAUK6xd0kS7SBysSS4KE+HiNPxC8r812EEFFJ2S6asIs9i7rvkQ==",
             "requires": {
                 "@babel/runtime": "^7.0.0",
-                "@emotion/core": "^10.0.28",
-                "@emotion/styled": "^10.0.27",
-                "emotion": "^10.0.27",
+                "@emotion/css": "^11.1.3",
+                "@emotion/react": "^11.4.0",
+                "@emotion/styled": "^11.3.0",
                 "html": "^1.0.0",
                 "jsondiffpatch": "^0.4.1",
-                "nanoid": "^3.1.6",
+                "nanoid": "^2.1.11",
                 "prop-types": "^15.7.2",
                 "prosemirror-model": ">=1.0.0",
                 "prosemirror-state": ">=1.0.0",
@@ -14368,12 +40814,13 @@
         "prosemirror-find-replace": {
             "version": "0.9.0",
             "resolved": "https://registry.npmjs.org/prosemirror-find-replace/-/prosemirror-find-replace-0.9.0.tgz",
-            "integrity": "sha1-QgsENNF5xdBJD44hSNhVGpVJY4I="
+            "integrity": "sha1-QgsENNF5xdBJD44hSNhVGpVJY4I=",
+            "requires": {}
         },
         "prosemirror-history": {
-            "version": "1.1.3",
-            "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.1.3.tgz",
-            "integrity": "sha512-zGDotijea+vnfnyyUGyiy1wfOQhf0B/b6zYcCouBV8yo6JmrE9X23M5q7Nf/nATywEZbgRLG70R4DmfSTC+gfg==",
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.2.0.tgz",
+            "integrity": "sha512-B9v9xtf4fYbKxQwIr+3wtTDNLDZcmMMmGiI3TAPShnUzvo+Rmv1GiUrsQChY1meetHl7rhML2cppF3FTs7f7UQ==",
             "requires": {
                 "prosemirror-state": "^1.2.2",
                 "prosemirror-transform": "^1.0.0",
@@ -14381,73 +40828,73 @@
             }
         },
         "prosemirror-inputrules": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.1.2.tgz",
-            "integrity": "sha512-Ja5Z3BWestlHYGvtSGqyvxMeB8QEuBjlHM8YnKtLGUXMDp965qdDV4goV8lJb17kIWHk7e7JNj6Catuoa3302g==",
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.1.3.tgz",
+            "integrity": "sha512-ZaHCLyBtvbyIHv0f5p6boQTIJjlD6o2NPZiEaZWT2DA+j591zS29QQEMT4lBqwcLW3qRSf7ZvoKNbf05YrsStw==",
             "requires": {
                 "prosemirror-state": "^1.0.0",
                 "prosemirror-transform": "^1.0.0"
             }
         },
         "prosemirror-keymap": {
-            "version": "1.1.4",
-            "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.1.4.tgz",
-            "integrity": "sha512-Al8cVUOnDFL4gcI5IDlG6xbZ0aOD/i3B17VT+1JbHWDguCgt/lBHVTHUBcKvvbSg6+q/W4Nj1Fu6bwZSca3xjg==",
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.1.5.tgz",
+            "integrity": "sha512-8SZgPH3K+GLsHL2wKuwBD9rxhsbnVBTwpHCO4VUO5GmqUQlxd/2GtBVWTsyLq4Dp3N9nGgPd3+lZFKUDuVp+Vw==",
             "requires": {
                 "prosemirror-state": "^1.0.0",
                 "w3c-keyname": "^2.2.0"
             }
         },
         "prosemirror-model": {
-            "version": "1.10.0",
-            "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.10.0.tgz",
-            "integrity": "sha512-xTMbbO2q4abs5lJdeRvk/SrftNfZlMdvChKziTiK+OKtP8LkQI8uw39u4S5zqyflrmW3Or6+qnyFPf1p4v2u1g==",
+            "version": "1.16.1",
+            "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.16.1.tgz",
+            "integrity": "sha512-r1/w0HDU40TtkXp0DyKBnFPYwd8FSlUSJmGCGFv4DeynfeSlyQF2FD0RQbVEMOe6P3PpUSXM6LZBV7W/YNZ4mA==",
             "requires": {
                 "orderedmap": "^1.1.0"
             }
         },
         "prosemirror-schema-list": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.1.2.tgz",
-            "integrity": "sha512-dgM9PwtM4twa5WsgSYMB+J8bwjnR43DAD3L9MsR9rKm/nZR5Y85xcjB7gusVMSsbQ2NomMZF03RE6No6mTnclQ==",
+            "version": "1.1.6",
+            "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.1.6.tgz",
+            "integrity": "sha512-aFGEdaCWmJzouZ8DwedmvSsL50JpRkqhQ6tcpThwJONVVmCgI36LJHtoQ4VGZbusMavaBhXXr33zyD2IVsTlkw==",
             "requires": {
                 "prosemirror-model": "^1.0.0",
                 "prosemirror-transform": "^1.0.0"
             }
         },
         "prosemirror-state": {
-            "version": "1.3.3",
-            "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.3.3.tgz",
-            "integrity": "sha512-PLXh2VJsIgvlgSTH6I2Yg6vk1CzPDp21DFreVpQtDMY2S6WaMmrQgDTLRcsrD8X38v8Yc873H7+ogdGzyIPn+w==",
+            "version": "1.3.4",
+            "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.3.4.tgz",
+            "integrity": "sha512-Xkkrpd1y/TQ6HKzN3agsQIGRcLckUMA9u3j207L04mt8ToRgpGeyhbVv0HI7omDORIBHjR29b7AwlATFFf2GLA==",
             "requires": {
                 "prosemirror-model": "^1.0.0",
                 "prosemirror-transform": "^1.0.0"
             }
         },
         "prosemirror-transform": {
-            "version": "1.2.6",
-            "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.2.6.tgz",
-            "integrity": "sha512-DyV6cRip8//GIHTrqBe2B7I8VjPFQZYuBuB4clpguq1SrS9lLponoBt/0XRWxETkCVsxYSvwE76X0zo6AZhwaw==",
+            "version": "1.3.4",
+            "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.3.4.tgz",
+            "integrity": "sha512-gTsg3UIeaFuEY6+YmNPMgTpEkCKPedkFIUnsPpOMbclU701fEVI/e4VOXACXh3BO5rZJaBbEBwrnzB0mLp6eBA==",
             "requires": {
                 "prosemirror-model": "^1.0.0"
             }
         },
         "prosemirror-view": {
-            "version": "1.15.0",
-            "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.15.0.tgz",
-            "integrity": "sha512-a7Q76sO/DCZr2UX2Rv1Rbw52cr9kVIz8iJOf/rq4mPN1NA3lugq2BKJgUMwlB3U4utyw3olLigqouRHM48NJyg==",
+            "version": "1.23.7",
+            "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.23.7.tgz",
+            "integrity": "sha512-ugY+g/4UI2Ree1zzdvbyQWF2KpbFa7kxKMLHaEJcxiPaErnZiD5wZFqIgFinc7fY2v/QM3DLnJ++2I45ULRdrg==",
             "requires": {
-                "prosemirror-model": "^1.1.0",
+                "prosemirror-model": "^1.16.0",
                 "prosemirror-state": "^1.0.0",
                 "prosemirror-transform": "^1.1.0"
             }
         },
         "proxy-addr": {
-            "version": "2.0.6",
-            "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
-            "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
+            "version": "2.0.7",
+            "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+            "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
             "requires": {
-                "forwarded": "~0.1.2",
+                "forwarded": "0.2.0",
                 "ipaddr.js": "1.9.1"
             }
         },
@@ -14477,28 +40924,6 @@
             "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.7.tgz",
             "integrity": "sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A=="
         },
-        "public-encrypt": {
-            "version": "4.0.3",
-            "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
-            "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
-            "dev": true,
-            "requires": {
-                "bn.js": "^4.1.0",
-                "browserify-rsa": "^4.0.0",
-                "create-hash": "^1.1.0",
-                "parse-asn1": "^5.0.0",
-                "randombytes": "^2.0.1",
-                "safe-buffer": "^5.1.2"
-            },
-            "dependencies": {
-                "bn.js": {
-                    "version": "4.12.0",
-                    "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
-                    "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
-                    "dev": true
-                }
-            }
-        },
         "pug": {
             "version": "2.0.4",
             "resolved": "https://registry.npmjs.org/pug/-/pug-2.0.4.tgz",
@@ -14707,16 +41132,17 @@
             "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc="
         },
         "qs": {
-            "version": "6.5.2",
-            "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
-            "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
+            "version": "6.5.3",
+            "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
+            "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA=="
         },
         "query-string": {
-            "version": "6.13.1",
-            "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.1.tgz",
-            "integrity": "sha512-RfoButmcK+yCta1+FuU8REvisx1oEzhMKwhLUNcepQTPGcNMp1sIqjnfCtfnvGSQZQEhaBHvccujtWoUV3TTbA==",
+            "version": "6.14.1",
+            "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.14.1.tgz",
+            "integrity": "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==",
             "requires": {
                 "decode-uri-component": "^0.2.0",
+                "filter-obj": "^1.1.0",
                 "split-on-first": "^1.0.0",
                 "strict-uri-encode": "^2.0.0"
             }
@@ -14727,22 +41153,21 @@
             "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
             "dev": true
         },
-        "querystring-es3": {
-            "version": "0.2.1",
-            "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
-            "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
-            "dev": true
-        },
         "querystringify": {
-            "version": "2.1.1",
-            "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz",
-            "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==",
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+            "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
             "dev": true
         },
+        "quick-lru": {
+            "version": "5.1.1",
+            "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+            "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="
+        },
         "raf-schd": {
-            "version": "4.0.2",
-            "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.2.tgz",
-            "integrity": "sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ=="
+            "version": "4.0.3",
+            "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz",
+            "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ=="
         },
         "random-bytes": {
             "version": "1.0.0",
@@ -14753,18 +41178,7 @@
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
             "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
-            "dev": true,
-            "requires": {
-                "safe-buffer": "^5.1.0"
-            }
-        },
-        "randomfill": {
-            "version": "1.0.4",
-            "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
-            "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
-            "dev": true,
             "requires": {
-                "randombytes": "^2.0.5",
                 "safe-buffer": "^5.1.0"
             }
         },
@@ -14774,12 +41188,12 @@
             "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
         },
         "raw-body": {
-            "version": "2.4.0",
-            "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
-            "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
+            "version": "2.4.3",
+            "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz",
+            "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==",
             "requires": {
-                "bytes": "3.1.0",
-                "http-errors": "1.7.2",
+                "bytes": "3.1.2",
+                "http-errors": "1.8.1",
                 "iconv-lite": "0.4.24",
                 "unpipe": "1.0.0"
             }
@@ -14817,9 +41231,9 @@
             }
         },
         "rc-switch": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-1.9.0.tgz",
-            "integrity": "sha512-Isas+egaK6qSk64jaEw4GgPStY4umYDbT7ZY93bZF1Af+b/JEsKsJdNOU2qG3WI0Z6tXo2DDq0kJCv8Yhu0zww==",
+            "version": "1.9.2",
+            "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-1.9.2.tgz",
+            "integrity": "sha512-qaK7mY4FLDKy99Hq3A1tf8CcqfzKtHp9LPX8WTnZ0MzdHCTneSARb1XD7Eqeu8BactasYGsi2bF9p18Q+/5JEw==",
             "requires": {
                 "classnames": "^2.2.1",
                 "prop-types": "^15.5.6",
@@ -14827,9 +41241,9 @@
             }
         },
         "react": {
-            "version": "16.13.1",
-            "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz",
-            "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==",
+            "version": "16.14.0",
+            "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz",
+            "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==",
             "requires": {
                 "loose-envify": "^1.1.0",
                 "object-assign": "^4.1.1",
@@ -14839,7 +41253,8 @@
         "react-audio-waveform": {
             "version": "0.0.5",
             "resolved": "https://registry.npmjs.org/react-audio-waveform/-/react-audio-waveform-0.0.5.tgz",
-            "integrity": "sha512-4dJwhl+LQRuywzmmjuhWT5GueT3Ai2ZhB+loVxg0sRdhplyDp96xAu10/V/+J25Cl7YqNtl2DWSxvSoI/i6l6w=="
+            "integrity": "sha512-4dJwhl+LQRuywzmmjuhWT5GueT3Ai2ZhB+loVxg0sRdhplyDp96xAu10/V/+J25Cl7YqNtl2DWSxvSoI/i6l6w==",
+            "requires": {}
         },
         "react-autosuggest": {
             "version": "9.4.3",
@@ -14873,26 +41288,27 @@
             }
         },
         "react-beautiful-dnd": {
-            "version": "13.0.0",
-            "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.0.0.tgz",
-            "integrity": "sha512-87It8sN0ineoC3nBW0SbQuTFXM6bUqM62uJGY4BtTf0yzPl8/3+bHMWkgIe0Z6m8e+gJgjWxefGRVfpE3VcdEg==",
+            "version": "13.1.0",
+            "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.0.tgz",
+            "integrity": "sha512-aGvblPZTJowOWUNiwd6tNfEpgkX5OxmpqxHKNW/4VmvZTNTbeiq7bA3bn5T+QSF2uibXB0D1DmJsb1aC/+3cUA==",
             "requires": {
-                "@babel/runtime": "^7.8.4",
+                "@babel/runtime": "^7.9.2",
                 "css-box-model": "^1.2.0",
                 "memoize-one": "^5.1.1",
                 "raf-schd": "^4.0.2",
-                "react-redux": "^7.1.1",
+                "react-redux": "^7.2.0",
                 "redux": "^4.0.4",
                 "use-memo-one": "^1.1.1"
             }
         },
         "react-color": {
-            "version": "2.18.1",
-            "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.18.1.tgz",
-            "integrity": "sha512-X5XpyJS6ncplZs74ak0JJoqPi+33Nzpv5RYWWxn17bslih+X7OlgmfpmGC1fNvdkK7/SGWYf1JJdn7D2n5gSuQ==",
+            "version": "2.19.3",
+            "resolved": "https://registry.npmjs.org/react-color/-/react-color-2.19.3.tgz",
+            "integrity": "sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==",
             "requires": {
                 "@icons/material": "^0.2.4",
-                "lodash": "^4.17.11",
+                "lodash": "^4.17.15",
+                "lodash-es": "^4.17.15",
                 "material-colors": "^1.2.1",
                 "prop-types": "^15.5.10",
                 "reactcss": "^1.2.0",
@@ -14911,15 +41327,15 @@
             }
         },
         "react-datepicker": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-3.0.0.tgz",
-            "integrity": "sha512-Yrxan1tERAiWS0EzitpiaiXOIz0APTUtV75uWbaS+jSaKoGCR6wUN2FDwr1ACGlnEoGhR9QQ2Vq3odnWtgJsOA==",
+            "version": "3.8.0",
+            "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-3.8.0.tgz",
+            "integrity": "sha512-iFVNEp8DJoX5yEvEiciM7sJKmLGrvE70U38KhpG13XrulNSijeHw1RZkhd/0UmuXR71dcZB/kdfjiidifstZjw==",
             "requires": {
                 "classnames": "^2.2.6",
                 "date-fns": "^2.0.1",
                 "prop-types": "^15.7.2",
-                "react-onclickoutside": "^6.9.0",
-                "react-popper": "^1.3.4"
+                "react-onclickoutside": "^6.10.0",
+                "react-popper": "^1.3.8"
             }
         },
         "react-dock": {
@@ -14932,14 +41348,25 @@
             }
         },
         "react-dom": {
-            "version": "16.13.1",
-            "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz",
-            "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==",
+            "version": "16.14.0",
+            "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz",
+            "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==",
             "requires": {
                 "loose-envify": "^1.1.0",
                 "object-assign": "^4.1.1",
                 "prop-types": "^15.6.2",
                 "scheduler": "^0.19.1"
+            },
+            "dependencies": {
+                "scheduler": {
+                    "version": "0.19.1",
+                    "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
+                    "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
+                    "requires": {
+                        "loose-envify": "^1.1.0",
+                        "object-assign": "^4.1.1"
+                    }
+                }
             }
         },
         "react-draggable": {
@@ -14994,9 +41421,9 @@
             }
         },
         "react-input-autosize": {
-            "version": "2.2.2",
-            "resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-2.2.2.tgz",
-            "integrity": "sha512-jQJgYCA3S0j+cuOwzuCd1OjmBmnZLdqQdiLKRYrsMMzbjUrVDS5RvJUDwJqA7sKuksDuzFtm6hZGKFu7Mjk5aw==",
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-3.0.0.tgz",
+            "integrity": "sha512-nL9uS7jEs/zu8sqwFE5MAPx6pPkNAriACQ2rGLlqmKr2sPGtN7TXTyDdQt4lbNXVx7Uzadb40x8qotIuru6Rhg==",
             "requires": {
                 "prop-types": "^15.5.8"
             }
@@ -15017,24 +41444,54 @@
             }
         },
         "react-jsx-parser": {
-            "version": "1.25.1",
-            "resolved": "https://registry.npmjs.org/react-jsx-parser/-/react-jsx-parser-1.25.1.tgz",
-            "integrity": "sha512-lCHAw7iOocnPSpjfsHXZQGLyPpr9wlk3qtkVN44hZs0PwQuxcH8MWbLY+j6pqB9QSijM7qtulDLgFIYbAcqSmQ==",
-            "requires": {
-                "acorn": "^7.2.0",
-                "acorn-jsx": "^5.2.0",
-                "core-js": "^3.6.5"
+            "version": "1.29.0",
+            "resolved": "https://registry.npmjs.org/react-jsx-parser/-/react-jsx-parser-1.29.0.tgz",
+            "integrity": "sha512-u0svZd0UsPffRrIK0sTbox54jhEbTmg6ED9ob5FQahp1DeOpd2Rq+KiSTIFNYkZUL+WZi4Ntia/Oj5T4lDJafQ==",
+            "requires": {
+                "@types/jsdom": "^16.2.6",
+                "@types/react": "^17.0.1",
+                "@types/react-dom": "^17.0.0",
+                "acorn": "^8.0.5",
+                "acorn-jsx": "^5.3.1",
+                "browserslist": "^4.14.5",
+                "core-js": "^3.8.3"
             },
             "dependencies": {
+                "@types/react": {
+                    "version": "17.0.39",
+                    "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz",
+                    "integrity": "sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==",
+                    "optional": true,
+                    "requires": {
+                        "@types/prop-types": "*",
+                        "@types/scheduler": "*",
+                        "csstype": "^3.0.2"
+                    }
+                },
+                "@types/react-dom": {
+                    "version": "17.0.11",
+                    "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.11.tgz",
+                    "integrity": "sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q==",
+                    "optional": true,
+                    "requires": {
+                        "@types/react": "*"
+                    }
+                },
                 "acorn": {
-                    "version": "7.3.1",
-                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz",
-                    "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA=="
+                    "version": "8.7.0",
+                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
+                    "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ=="
                 },
                 "core-js": {
-                    "version": "3.6.5",
-                    "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz",
-                    "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA=="
+                    "version": "3.21.1",
+                    "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.21.1.tgz",
+                    "integrity": "sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig=="
+                },
+                "csstype": {
+                    "version": "3.0.10",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
+                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==",
+                    "optional": true
                 }
             }
         },
@@ -15046,12 +41503,13 @@
         "react-loading": {
             "version": "2.0.3",
             "resolved": "https://registry.npmjs.org/react-loading/-/react-loading-2.0.3.tgz",
-            "integrity": "sha512-Vdqy79zq+bpeWJqC+xjltUjuGApyoItPgL0vgVfcJHhqwU7bAMKzysfGW/ADu6i0z0JiOCRJjo+IkFNkRNbA3A=="
+            "integrity": "sha512-Vdqy79zq+bpeWJqC+xjltUjuGApyoItPgL0vgVfcJHhqwU7bAMKzysfGW/ADu6i0z0JiOCRJjo+IkFNkRNbA3A==",
+            "requires": {}
         },
         "react-measure": {
-            "version": "2.3.0",
-            "resolved": "https://registry.npmjs.org/react-measure/-/react-measure-2.3.0.tgz",
-            "integrity": "sha512-dwAvmiOeblj5Dvpnk8Jm7Q8B4THF/f1l1HtKVi0XDecsG6LXwGvzV5R1H32kq3TW6RW64OAf5aoQxpIgLa4z8A==",
+            "version": "2.5.2",
+            "resolved": "https://registry.npmjs.org/react-measure/-/react-measure-2.5.2.tgz",
+            "integrity": "sha512-M+rpbTLWJ3FD6FXvYV6YEGvQ5tMayQ3fGrZhRPHrE9bVlBYfDCLuDcgNttYfk8IqfOI03jz6cbpqMRTUclQnaA==",
             "requires": {
                 "@babel/runtime": "^7.2.0",
                 "get-node-dimensions": "^1.2.1",
@@ -15086,17 +41544,18 @@
             }
         },
         "react-onclickoutside": {
-            "version": "6.9.0",
-            "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.9.0.tgz",
-            "integrity": "sha512-8ltIY3bC7oGhj2nPAvWOGi+xGFybPNhJM0V1H8hY/whNcXgmDeaeoCMPPd8VatrpTsUWjb/vGzrmu6SrXVty3A=="
+            "version": "6.12.1",
+            "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.12.1.tgz",
+            "integrity": "sha512-a5Q7CkWznBRUWPmocCvE8b6lEYw1s6+opp/60dCunhO+G6E4tDTO2Sd2jKE+leEnnrLAE2Wj5DlDHNqj5wPv1Q==",
+            "requires": {}
         },
         "react-popper": {
-            "version": "1.3.7",
-            "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.7.tgz",
-            "integrity": "sha512-nmqYTx7QVjCm3WUZLeuOomna138R1luC4EqkW3hxJUrAe+3eNz3oFCLYdnPwILfn0mX1Ew2c3wctrjlUMYYUww==",
+            "version": "1.3.11",
+            "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.11.tgz",
+            "integrity": "sha512-VSA/bS+pSndSF2fiasHK/PTEEAyOpX60+H5EPAjoArr8JGm+oihu4UbrqcEBpQibJxBVCpYyjAX7abJ+7DoYVg==",
             "requires": {
                 "@babel/runtime": "^7.1.2",
-                "create-react-context": "^0.3.0",
+                "@hypnosphi/create-react-context": "^0.3.1",
                 "deep-equal": "^1.1.1",
                 "popper.js": "^1.14.4",
                 "prop-types": "^15.6.1",
@@ -15104,14 +41563,10 @@
                 "warning": "^4.0.2"
             },
             "dependencies": {
-                "create-react-context": {
-                    "version": "0.3.0",
-                    "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz",
-                    "integrity": "sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==",
-                    "requires": {
-                        "gud": "^1.0.0",
-                        "warning": "^4.0.3"
-                    }
+                "popper.js": {
+                    "version": "1.16.1",
+                    "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
+                    "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ=="
                 },
                 "warning": {
                     "version": "4.0.3",
@@ -15131,35 +41586,46 @@
                 "loose-envify": "^1.1.0",
                 "object-assign": "^4.1.1",
                 "scheduler": "^0.20.2"
+            }
+        },
+        "react-redux": {
+            "version": "7.2.6",
+            "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz",
+            "integrity": "sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==",
+            "requires": {
+                "@babel/runtime": "^7.15.4",
+                "@types/react-redux": "^7.1.20",
+                "hoist-non-react-statics": "^3.3.2",
+                "loose-envify": "^1.4.0",
+                "prop-types": "^15.7.2",
+                "react-is": "^17.0.2"
             },
             "dependencies": {
-                "scheduler": {
-                    "version": "0.20.2",
-                    "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
-                    "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
+                "@babel/runtime": {
+                    "version": "7.17.2",
+                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
+                    "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
                     "requires": {
-                        "loose-envify": "^1.1.0",
-                        "object-assign": "^4.1.1"
+                        "regenerator-runtime": "^0.13.4"
                     }
+                },
+                "react-is": {
+                    "version": "17.0.2",
+                    "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
+                    "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
                 }
             }
         },
-        "react-redux": {
-            "version": "7.2.0",
-            "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.0.tgz",
-            "integrity": "sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA==",
-            "requires": {
-                "@babel/runtime": "^7.5.5",
-                "hoist-non-react-statics": "^3.3.0",
-                "loose-envify": "^1.4.0",
-                "prop-types": "^15.7.2",
-                "react-is": "^16.9.0"
-            }
+        "react-refresh-typescript": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/react-refresh-typescript/-/react-refresh-typescript-2.0.3.tgz",
+            "integrity": "sha512-CnR+UkpCFUIxW3u+KPxc6UhSO3M3aQxd9XCvgdry4a22LFwwITnjbANFUKtPGxA/Skw3PbZJrLiZ08UQnpXprQ==",
+            "requires": {}
         },
         "react-resizable": {
-            "version": "1.10.1",
-            "resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-1.10.1.tgz",
-            "integrity": "sha512-Jd/bKOKx6+19NwC4/aMLRu/J9/krfxlDnElP41Oc+oLiUWs/zwV1S9yBfBZRnqAwQb6vQ/HRSk3bsSWGSgVbpw==",
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-1.11.1.tgz",
+            "integrity": "sha512-S70gbLaAYqjuAd49utRHibtHLrHXInh7GuOR+6OO6RO6uleQfuBnWmZjRABfqNEx3C3Z6VPLg0/0uOYFrkfu9Q==",
             "requires": {
                 "prop-types": "15.x",
                 "react-draggable": "^4.0.3"
@@ -15168,7 +41634,8 @@
         "react-resizable-rotatable-draggable": {
             "version": "0.2.0",
             "resolved": "https://registry.npmjs.org/react-resizable-rotatable-draggable/-/react-resizable-rotatable-draggable-0.2.0.tgz",
-            "integrity": "sha512-F8TPx3z7/AcmRViySbYV3LpUWXFpHlGAmKmNcYMgPlS+h1eYFazRG3xYS8Z6e48hWY1EcCny/YNrwRNUrap8CQ=="
+            "integrity": "sha512-F8TPx3z7/AcmRViySbYV3LpUWXFpHlGAmKmNcYMgPlS+h1eYFazRG3xYS8Z6e48hWY1EcCny/YNrwRNUrap8CQ==",
+            "requires": {}
         },
         "react-reveal": {
             "version": "1.2.2",
@@ -15179,9 +41646,9 @@
             }
         },
         "react-select": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/react-select/-/react-select-3.1.0.tgz",
-            "integrity": "sha512-wBFVblBH1iuCBprtpyGtd1dGMadsG36W5/t2Aj8OE6WbByDg5jIFyT7X5gT+l0qmT5TqWhxX+VsKJvCEl2uL9g==",
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/react-select/-/react-select-3.2.0.tgz",
+            "integrity": "sha512-B/q3TnCZXEKItO0fFN/I0tWOX3WJvi/X2wtdffmwSQVRwg5BpValScTO1vdic9AxlUgmeSzib2hAZAwIUQUZGQ==",
             "requires": {
                 "@babel/runtime": "^7.4.4",
                 "@emotion/cache": "^10.0.9",
@@ -15189,23 +41656,38 @@
                 "@emotion/css": "^10.0.9",
                 "memoize-one": "^5.0.0",
                 "prop-types": "^15.6.0",
-                "react-input-autosize": "^2.2.2",
+                "react-input-autosize": "^3.0.0",
                 "react-transition-group": "^4.3.0"
             },
             "dependencies": {
+                "@emotion/css": {
+                    "version": "10.0.27",
+                    "resolved": "https://registry.npmjs.org/@emotion/css/-/css-10.0.27.tgz",
+                    "integrity": "sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==",
+                    "requires": {
+                        "@emotion/serialize": "^0.11.15",
+                        "@emotion/utils": "0.11.3",
+                        "babel-plugin-emotion": "^10.0.27"
+                    }
+                },
+                "csstype": {
+                    "version": "3.0.10",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
+                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
+                },
                 "dom-helpers": {
-                    "version": "5.1.4",
-                    "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.4.tgz",
-                    "integrity": "sha512-TjMyeVUvNEnOnhzs6uAn9Ya47GmMo3qq7m+Lr/3ON0Rs5kHvb8I+SQYjLUSYn7qhEm0QjW0yrBkvz9yOrwwz1A==",
+                    "version": "5.2.1",
+                    "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+                    "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
                     "requires": {
                         "@babel/runtime": "^7.8.7",
-                        "csstype": "^2.6.7"
+                        "csstype": "^3.0.2"
                     }
                 },
                 "react-transition-group": {
-                    "version": "4.4.1",
-                    "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz",
-                    "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==",
+                    "version": "4.4.2",
+                    "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz",
+                    "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==",
                     "requires": {
                         "@babel/runtime": "^7.5.5",
                         "dom-helpers": "^5.0.1",
@@ -15216,14 +41698,10 @@
             }
         },
         "react-table": {
-            "version": "6.11.5",
-            "resolved": "https://registry.npmjs.org/react-table/-/react-table-6.11.5.tgz",
-            "integrity": "sha512-LM+AS9v//7Y7lAlgTWW/cW6Sn5VOb3EsSkKQfQTzOW8FngB1FUskLLNEVkAYsTX9LjOWR3QlGjykJqCE6eXT/g==",
-            "requires": {
-                "@types/react-table": "^6.8.5",
-                "classnames": "^2.2.5",
-                "react-is": "^16.8.1"
-            }
+            "version": "7.7.0",
+            "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.7.0.tgz",
+            "integrity": "sha512-jBlj70iBwOTvvImsU9t01LjFjy4sXEtclBovl3mTiqjz23Reu0DKnRza4zlLtOPACx6j2/7MrQIthIK1Wi+LIA==",
+            "requires": {}
         },
         "react-themeable": {
             "version": "1.1.0",
@@ -15257,11 +41735,11 @@
             }
         },
         "react-use-measure": {
-            "version": "2.0.4",
-            "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.0.4.tgz",
-            "integrity": "sha512-7K2HIGaPMl3Q9ZQiEVjen3tRXl4UDda8LiTPy/QxP8dP2rl5gPBhf7mMH6MVjjRNv3loU7sNzey/ycPNnHVTxQ==",
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz",
+            "integrity": "sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig==",
             "requires": {
-                "debounce": "^1.2.0"
+                "debounce": "^1.2.1"
             }
         },
         "reactcss": {
@@ -15409,12 +41887,11 @@
             "integrity": "sha1-JYx479FT3fk8tWEjf2EYTzaW4yc="
         },
         "redux": {
-            "version": "4.0.5",
-            "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz",
-            "integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==",
+            "version": "4.1.2",
+            "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz",
+            "integrity": "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==",
             "requires": {
-                "loose-envify": "^1.4.0",
-                "symbol-observable": "^1.2.0"
+                "@babel/runtime": "^7.9.2"
             }
         },
         "regenerator-runtime": {
@@ -15437,12 +41914,12 @@
             "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw=="
         },
         "regexp.prototype.flags": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz",
-            "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==",
+            "version": "1.4.1",
+            "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz",
+            "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==",
             "requires": {
-                "define-properties": "^1.1.3",
-                "es-abstract": "^1.17.0-next.1"
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3"
             }
         },
         "registry-auth-token": {
@@ -15462,11 +41939,92 @@
                 "rc": "^1.0.1"
             }
         },
+        "relateurl": {
+            "version": "0.2.7",
+            "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+            "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk="
+        },
         "remove-trailing-separator": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
             "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8="
         },
+        "renderkid": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz",
+            "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==",
+            "requires": {
+                "css-select": "^4.1.3",
+                "dom-converter": "^0.2.0",
+                "htmlparser2": "^6.1.0",
+                "lodash": "^4.17.21",
+                "strip-ansi": "^6.0.1"
+            },
+            "dependencies": {
+                "ansi-regex": {
+                    "version": "5.0.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                    "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+                },
+                "dom-serializer": {
+                    "version": "1.3.2",
+                    "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
+                    "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
+                    "requires": {
+                        "domelementtype": "^2.0.1",
+                        "domhandler": "^4.2.0",
+                        "entities": "^2.0.0"
+                    }
+                },
+                "domelementtype": {
+                    "version": "2.2.0",
+                    "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
+                    "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
+                },
+                "domhandler": {
+                    "version": "4.3.0",
+                    "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz",
+                    "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==",
+                    "requires": {
+                        "domelementtype": "^2.2.0"
+                    }
+                },
+                "domutils": {
+                    "version": "2.8.0",
+                    "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+                    "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+                    "requires": {
+                        "dom-serializer": "^1.0.1",
+                        "domelementtype": "^2.2.0",
+                        "domhandler": "^4.2.0"
+                    }
+                },
+                "entities": {
+                    "version": "2.2.0",
+                    "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+                    "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
+                },
+                "htmlparser2": {
+                    "version": "6.1.0",
+                    "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
+                    "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
+                    "requires": {
+                        "domelementtype": "^2.0.1",
+                        "domhandler": "^4.0.0",
+                        "domutils": "^2.5.2",
+                        "entities": "^2.0.0"
+                    }
+                },
+                "strip-ansi": {
+                    "version": "6.0.1",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                    "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                    "requires": {
+                        "ansi-regex": "^5.0.1"
+                    }
+                }
+            }
+        },
         "repeat-element": {
             "version": "1.1.3",
             "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
@@ -15521,24 +42079,53 @@
                         "combined-stream": "^1.0.6",
                         "mime-types": "^2.1.12"
                     }
+                },
+                "tough-cookie": {
+                    "version": "2.5.0",
+                    "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+                    "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+                    "requires": {
+                        "psl": "^1.1.28",
+                        "punycode": "^2.1.1"
+                    }
                 }
             }
         },
         "request-promise": {
-            "version": "4.2.5",
-            "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.5.tgz",
-            "integrity": "sha512-ZgnepCykFdmpq86fKGwqntyTiUrHycALuGggpyCZwMvGaZWgxW6yagT0FHkgo5LzYvOaCNvxYwWYIjevSH1EDg==",
+            "version": "4.2.6",
+            "resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.6.tgz",
+            "integrity": "sha512-HCHI3DJJUakkOr8fNoCc73E5nU5bqITjOYFMDrKHYOXWXrgD/SBaC7LjwuPymUprRyuF06UK7hd/lMHkmUXglQ==",
             "requires": {
                 "bluebird": "^3.5.0",
-                "request-promise-core": "1.1.3",
+                "request-promise-core": "1.1.4",
                 "stealthy-require": "^1.1.1",
                 "tough-cookie": "^2.3.3"
+            },
+            "dependencies": {
+                "request-promise-core": {
+                    "version": "1.1.4",
+                    "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
+                    "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
+                    "requires": {
+                        "lodash": "^4.17.19"
+                    }
+                },
+                "tough-cookie": {
+                    "version": "2.5.0",
+                    "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+                    "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+                    "requires": {
+                        "psl": "^1.1.28",
+                        "punycode": "^2.1.1"
+                    }
+                }
             }
         },
         "request-promise-core": {
             "version": "1.1.3",
             "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz",
             "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==",
+            "dev": true,
             "requires": {
                 "lodash": "^4.17.15"
             }
@@ -15552,23 +42139,20 @@
                 "request-promise-core": "1.1.3",
                 "stealthy-require": "^1.1.1",
                 "tough-cookie": "^2.3.3"
+            },
+            "dependencies": {
+                "tough-cookie": {
+                    "version": "2.5.0",
+                    "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+                    "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+                    "dev": true,
+                    "requires": {
+                        "psl": "^1.1.28",
+                        "punycode": "^2.1.1"
+                    }
+                }
             }
         },
-        "require-directory": {
-            "version": "2.1.1",
-            "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-            "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
-        },
-        "require-main-filename": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
-            "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
-        },
-        "require-package-name": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/require-package-name/-/require-package-name-2.0.1.tgz",
-            "integrity": "sha1-wR6XJ2tluOKSP3Xav1+y7ww4Qbk="
-        },
         "require_optional": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
@@ -15585,6 +42169,31 @@
                 }
             }
         },
+        "require-at": {
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz",
+            "integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g=="
+        },
+        "require-directory": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+            "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
+        },
+        "require-from-string": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+            "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="
+        },
+        "require-main-filename": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+            "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
+        },
+        "require-package-name": {
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/require-package-name/-/require-package-name-2.0.1.tgz",
+            "integrity": "sha1-wR6XJ2tluOKSP3Xav1+y7ww4Qbk="
+        },
         "requires-port": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
@@ -15604,6 +42213,11 @@
                 "path-parse": "^1.0.6"
             }
         },
+        "resolve-alpn": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
+            "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
+        },
         "resolve-cwd": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
@@ -15621,29 +42235,6 @@
                 }
             }
         },
-        "resolve-dir": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
-            "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=",
-            "dev": true,
-            "requires": {
-                "expand-tilde": "^2.0.0",
-                "global-modules": "^1.0.0"
-            },
-            "dependencies": {
-                "global-modules": {
-                    "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
-                    "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
-                    "dev": true,
-                    "requires": {
-                        "global-prefix": "^1.0.1",
-                        "is-windows": "^1.0.1",
-                        "resolve-dir": "^1.0.0"
-                    }
-                }
-            }
-        },
         "resolve-from": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@@ -15654,6 +42245,21 @@
             "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
             "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="
         },
+        "responselike": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz",
+            "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==",
+            "requires": {
+                "lowercase-keys": "^2.0.0"
+            },
+            "dependencies": {
+                "lowercase-keys": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+                    "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="
+                }
+            }
+        },
         "ret": {
             "version": "0.1.15",
             "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
@@ -15666,9 +42272,9 @@
             "dev": true
         },
         "reveal.js": {
-            "version": "4.0.2",
-            "resolved": "https://registry.npmjs.org/reveal.js/-/reveal.js-4.0.2.tgz",
-            "integrity": "sha512-LWZSUenufF1gpD7npxJ7KfoQQFKgc1D6XrLTFgKlwWNP1BQ74hT48KONFWMAw+8R/QUeaScCLCLrBVHDfFIEiw=="
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/reveal.js/-/reveal.js-4.3.0.tgz",
+            "integrity": "sha512-KzZxJjj1gmxVNyplY6g9MiGwtDvZJiYkMvG1Qmaita7vWT/8eoTEK+RuIPLvxDeyxOtTz56u2wrOETVO79qL4A=="
         },
         "right-align": {
             "version": "0.1.3",
@@ -15686,16 +42292,6 @@
                 "glob": "^7.1.3"
             }
         },
-        "ripemd160": {
-            "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
-            "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
-            "dev": true,
-            "requires": {
-                "hash-base": "^3.0.0",
-                "inherits": "^2.0.1"
-            }
-        },
         "rope-sequence": {
             "version": "1.3.2",
             "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.2.tgz",
@@ -15777,11 +42373,6 @@
                 }
             }
         },
-        "sax": {
-            "version": "1.2.4",
-            "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
-            "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
-        },
         "saxes": {
             "version": "3.1.11",
             "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz",
@@ -15792,9 +42383,9 @@
             }
         },
         "scheduler": {
-            "version": "0.19.1",
-            "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
-            "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
+            "version": "0.20.2",
+            "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz",
+            "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==",
             "requires": {
                 "loose-envify": "^1.1.0",
                 "object-assign": "^4.1.1"
@@ -15853,18 +42444,18 @@
             "dev": true
         },
         "selfsigned": {
-            "version": "1.10.7",
-            "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz",
-            "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==",
+            "version": "1.10.14",
+            "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.14.tgz",
+            "integrity": "sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA==",
             "dev": true,
             "requires": {
-                "node-forge": "0.9.0"
+                "node-forge": "^0.10.0"
             },
             "dependencies": {
                 "node-forge": {
-                    "version": "0.9.0",
-                    "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz",
-                    "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==",
+                    "version": "0.10.0",
+                    "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
+                    "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==",
                     "dev": true
                 }
             }
@@ -15888,9 +42479,9 @@
             }
         },
         "send": {
-            "version": "0.17.1",
-            "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
-            "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
+            "version": "0.17.2",
+            "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz",
+            "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==",
             "requires": {
                 "debug": "2.6.9",
                 "depd": "~1.1.2",
@@ -15899,9 +42490,9 @@
                 "escape-html": "~1.0.3",
                 "etag": "~1.8.1",
                 "fresh": "0.5.2",
-                "http-errors": "~1.7.2",
+                "http-errors": "1.8.1",
                 "mime": "1.6.0",
-                "ms": "2.1.1",
+                "ms": "2.1.3",
                 "on-finished": "~2.3.0",
                 "range-parser": "~1.2.1",
                 "statuses": "~1.5.0"
@@ -15921,6 +42512,11 @@
                             "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                         }
                     }
+                },
+                "ms": {
+                    "version": "2.1.3",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+                    "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
                 }
             }
         },
@@ -15992,14 +42588,14 @@
             }
         },
         "serve-static": {
-            "version": "1.14.1",
-            "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
-            "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
+            "version": "1.14.2",
+            "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz",
+            "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==",
             "requires": {
                 "encodeurl": "~1.0.2",
                 "escape-html": "~1.0.3",
                 "parseurl": "~1.3.3",
-                "send": "0.17.1"
+                "send": "0.17.2"
             }
         },
         "set-blocking": {
@@ -16044,25 +42640,14 @@
             "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
         },
         "setprototypeof": {
-            "version": "1.1.1",
-            "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
-            "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
-        },
-        "sha.js": {
-            "version": "2.4.11",
-            "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
-            "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
-            "dev": true,
-            "requires": {
-                "inherits": "^2.0.1",
-                "safe-buffer": "^5.0.1"
-            }
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+            "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
         },
         "shallow-clone": {
             "version": "3.0.1",
             "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
             "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
-            "dev": true,
             "requires": {
                 "kind-of": "^6.0.2"
             }
@@ -16152,25 +42737,36 @@
             "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
         },
         "shelljs": {
-            "version": "0.8.4",
-            "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz",
-            "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==",
+            "version": "0.8.5",
+            "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz",
+            "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
             "requires": {
                 "glob": "^7.0.0",
                 "interpret": "^1.0.0",
                 "rechoir": "^0.6.2"
             }
         },
-        "shellwords": {
-            "version": "0.1.1",
-            "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
-            "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==",
-            "dev": true
+        "side-channel": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+            "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+            "requires": {
+                "call-bind": "^1.0.0",
+                "get-intrinsic": "^1.0.2",
+                "object-inspect": "^1.9.0"
+            },
+            "dependencies": {
+                "object-inspect": {
+                    "version": "1.12.0",
+                    "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
+                    "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g=="
+                }
+            }
         },
         "sift": {
-            "version": "7.0.1",
-            "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz",
-            "integrity": "sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g=="
+            "version": "13.5.2",
+            "resolved": "https://registry.npmjs.org/sift/-/sift-13.5.2.tgz",
+            "integrity": "sha512-+gxdEOMA2J+AI+fVsCqeNn7Tgx3M9ZN9jdi95939l1IJ8cZsqS8sqpJyOkic2SJk+1+98Uwryt/gL6XDaV+UZA=="
         },
         "signal-exit": {
             "version": "3.0.3",
@@ -16349,18 +42945,23 @@
             }
         },
         "socket.io": {
-            "version": "2.3.0",
-            "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz",
-            "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==",
+            "version": "2.4.1",
+            "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.4.1.tgz",
+            "integrity": "sha512-Si18v0mMXGAqLqCVpTxBa8MGqriHGQh8ccEOhmsmNS3thNCGBwO8WGrwMibANsWtQQ5NStdZwHqZR3naJVFc3w==",
             "requires": {
                 "debug": "~4.1.0",
-                "engine.io": "~3.4.0",
+                "engine.io": "~3.5.0",
                 "has-binary2": "~1.0.2",
                 "socket.io-adapter": "~1.1.0",
-                "socket.io-client": "2.3.0",
+                "socket.io-client": "2.4.0",
                 "socket.io-parser": "~3.4.0"
             },
             "dependencies": {
+                "component-emitter": {
+                    "version": "1.2.1",
+                    "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+                    "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
+                },
                 "debug": {
                     "version": "4.1.1",
                     "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
@@ -16368,6 +42969,21 @@
                     "requires": {
                         "ms": "^2.1.1"
                     }
+                },
+                "isarray": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+                    "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+                },
+                "socket.io-parser": {
+                    "version": "3.4.1",
+                    "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz",
+                    "integrity": "sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A==",
+                    "requires": {
+                        "component-emitter": "1.2.1",
+                        "debug": "~4.1.0",
+                        "isarray": "2.0.1"
+                    }
                 }
             }
         },
@@ -16377,37 +42993,29 @@
             "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g=="
         },
         "socket.io-client": {
-            "version": "2.3.0",
-            "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz",
-            "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==",
+            "version": "2.4.0",
+            "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.4.0.tgz",
+            "integrity": "sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ==",
             "requires": {
                 "backo2": "1.0.2",
-                "base64-arraybuffer": "0.1.5",
                 "component-bind": "1.0.0",
-                "component-emitter": "1.2.1",
-                "debug": "~4.1.0",
-                "engine.io-client": "~3.4.0",
+                "component-emitter": "~1.3.0",
+                "debug": "~3.1.0",
+                "engine.io-client": "~3.5.0",
                 "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",
+                "parseqs": "0.0.6",
+                "parseuri": "0.0.6",
                 "socket.io-parser": "~3.3.0",
                 "to-array": "0.1.4"
             },
             "dependencies": {
-                "component-emitter": {
-                    "version": "1.2.1",
-                    "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
-                    "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
-                },
                 "debug": {
-                    "version": "4.1.1",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
-                    "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+                    "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
                     "requires": {
-                        "ms": "^2.1.1"
+                        "ms": "2.0.0"
                     }
                 },
                 "isarray": {
@@ -16415,95 +43023,89 @@
                     "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
                     "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
                 },
-                "socket.io-parser": {
-                    "version": "3.3.0",
-                    "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz",
-                    "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==",
-                    "requires": {
-                        "component-emitter": "1.2.1",
-                        "debug": "~3.1.0",
-                        "isarray": "2.0.1"
-                    },
-                    "dependencies": {
-                        "debug": {
-                            "version": "3.1.0",
-                            "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-                            "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
-                            "requires": {
-                                "ms": "2.0.0"
-                            }
-                        },
-                        "ms": {
-                            "version": "2.0.0",
-                            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
-                        }
+                "ms": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                },
+                "socket.io-parser": {
+                    "version": "3.3.2",
+                    "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.2.tgz",
+                    "integrity": "sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg==",
+                    "requires": {
+                        "component-emitter": "~1.3.0",
+                        "debug": "~3.1.0",
+                        "isarray": "2.0.1"
                     }
                 }
             }
         },
         "socket.io-parser": {
-            "version": "3.4.0",
-            "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.0.tgz",
-            "integrity": "sha512-/G/VOI+3DBp0+DJKW4KesGnQkQPFmUCbA/oO2QGT6CWxU7hLGWqU3tyuzeSK/dqcyeHsQg1vTe9jiZI8GU9SCQ==",
+            "version": "4.1.2",
+            "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.2.tgz",
+            "integrity": "sha512-j3kk71QLJuyQ/hh5F/L2t1goqzdTL0gvDzuhTuNSwihfuFUrcSji0qFZmJJPtG6Rmug153eOPsUizeirf1IIog==",
+            "dev": true,
             "requires": {
-                "component-emitter": "1.2.1",
-                "debug": "~4.1.0",
-                "isarray": "2.0.1"
+                "@socket.io/component-emitter": "~3.0.0",
+                "debug": "~4.3.1"
             },
             "dependencies": {
-                "component-emitter": {
-                    "version": "1.2.1",
-                    "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
-                    "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
-                },
                 "debug": {
-                    "version": "4.1.1",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
-                    "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+                    "version": "4.3.3",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
+                    "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
+                    "dev": true,
                     "requires": {
-                        "ms": "^2.1.1"
+                        "ms": "2.1.2"
                     }
                 },
-                "isarray": {
-                    "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
-                    "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+                    "dev": true
                 }
             }
         },
         "sockjs": {
-            "version": "0.3.20",
-            "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz",
-            "integrity": "sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA==",
+            "version": "0.3.24",
+            "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
+            "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==",
             "dev": true,
             "requires": {
-                "faye-websocket": "^0.10.0",
-                "uuid": "^3.4.0",
-                "websocket-driver": "0.6.5"
+                "faye-websocket": "^0.11.3",
+                "uuid": "^8.3.2",
+                "websocket-driver": "^0.7.4"
+            },
+            "dependencies": {
+                "uuid": {
+                    "version": "8.3.2",
+                    "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+                    "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+                    "dev": true
+                }
             }
         },
         "sockjs-client": {
-            "version": "1.4.0",
-            "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz",
-            "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.0.tgz",
+            "integrity": "sha512-qVHJlyfdHFht3eBFZdKEXKTlb7I4IV41xnVNo8yUKA1UHcPJwgW2SvTq9LhnjjCywSkSK7c/e4nghU0GOoMCRQ==",
             "dev": true,
             "requires": {
-                "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"
+                "debug": "^3.2.7",
+                "eventsource": "^1.1.0",
+                "faye-websocket": "^0.11.4",
+                "inherits": "^2.0.4",
+                "url-parse": "^1.5.10"
             },
             "dependencies": {
-                "faye-websocket": {
-                    "version": "0.11.3",
-                    "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz",
-                    "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==",
+                "debug": {
+                    "version": "3.2.7",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+                    "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
                     "dev": true,
                     "requires": {
-                        "websocket-driver": ">=0.5.1"
+                        "ms": "^2.1.1"
                     }
                 }
             }
@@ -16525,12 +43127,6 @@
                 }
             }
         },
-        "source-list-map": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
-            "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==",
-            "dev": true
-        },
         "source-map": {
             "version": "0.5.7",
             "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
@@ -16622,13 +43218,19 @@
             },
             "dependencies": {
                 "debug": {
-                    "version": "4.1.1",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
-                    "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+                    "version": "4.3.3",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
+                    "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
                     "dev": true,
                     "requires": {
-                        "ms": "^2.1.1"
+                        "ms": "2.1.2"
                     }
+                },
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+                    "dev": true
                 }
             }
         },
@@ -16647,13 +43249,19 @@
             },
             "dependencies": {
                 "debug": {
-                    "version": "4.1.1",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
-                    "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+                    "version": "4.3.3",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
+                    "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
                     "dev": true,
                     "requires": {
-                        "ms": "^2.1.1"
+                        "ms": "2.1.2"
                     }
+                },
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+                    "dev": true
                 }
             }
         },
@@ -16681,9 +43289,9 @@
             "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
         },
         "sshpk": {
-            "version": "1.16.1",
-            "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
-            "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+            "version": "1.17.0",
+            "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
+            "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==",
             "requires": {
                 "asn1": "~0.2.3",
                 "assert-plus": "^1.0.0",
@@ -16772,30 +43380,12 @@
             "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
         },
         "stream-browserify": {
-            "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
-            "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==",
-            "dev": true,
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz",
+            "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==",
             "requires": {
-                "inherits": "~2.0.1",
-                "readable-stream": "^2.0.2"
-            },
-            "dependencies": {
-                "readable-stream": {
-                    "version": "2.3.7",
-                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
-                    "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
-                    "dev": true,
-                    "requires": {
-                        "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"
-                    }
-                }
+                "inherits": "~2.0.4",
+                "readable-stream": "^3.5.0"
             }
         },
         "stream-each": {
@@ -16808,36 +43398,6 @@
                 "stream-shift": "^1.0.0"
             }
         },
-        "stream-http": {
-            "version": "2.8.3",
-            "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz",
-            "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==",
-            "dev": true,
-            "requires": {
-                "builtin-status-codes": "^3.0.0",
-                "inherits": "^2.0.1",
-                "readable-stream": "^2.3.6",
-                "to-arraybuffer": "^1.0.0",
-                "xtend": "^4.0.0"
-            },
-            "dependencies": {
-                "readable-stream": {
-                    "version": "2.3.7",
-                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
-                    "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
-                    "dev": true,
-                    "requires": {
-                        "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"
-                    }
-                }
-            }
-        },
         "stream-parser": {
             "version": "0.3.1",
             "resolved": "https://registry.npmjs.org/stream-parser/-/stream-parser-0.3.1.tgz",
@@ -16872,6 +43432,14 @@
             "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
             "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
         },
+        "string_decoder": {
+            "version": "1.1.1",
+            "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.0"
+            }
+        },
         "string-width": {
             "version": "2.1.1",
             "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
@@ -16924,14 +43492,6 @@
                 "es-abstract": "^1.17.5"
             }
         },
-        "string_decoder": {
-            "version": "1.1.1",
-            "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.0"
-            }
-        },
         "stringify-parameters": {
             "version": "0.0.4",
             "resolved": "https://registry.npmjs.org/stringify-parameters/-/stringify-parameters-0.0.4.tgz",
@@ -16975,6 +43535,11 @@
             "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
             "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
         },
+        "strip-final-newline": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+            "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="
+        },
         "strip-indent": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
@@ -17046,7 +43611,8 @@
         "stylis-rule-sheet": {
             "version": "0.0.10",
             "resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz",
-            "integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw=="
+            "integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw==",
+            "requires": {}
         },
         "supercluster": {
             "version": "7.1.4",
@@ -17093,20 +43659,6 @@
             "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
             "dev": true
         },
-        "tar": {
-            "version": "4.4.13",
-            "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz",
-            "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
-            "requires": {
-                "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"
-            }
-        },
         "tar-fs": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz",
@@ -17153,126 +43705,87 @@
             }
         },
         "terser": {
-            "version": "4.8.0",
-            "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
-            "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
-            "dev": true,
+            "version": "5.12.0",
+            "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.0.tgz",
+            "integrity": "sha512-R3AUhNBGWiFc77HXag+1fXpAxTAFRQTJemlJKjAgD9r8xXTpjNKqIXwHM/o7Rh+O0kUJtS3WQVdBeMKFk5sw9A==",
             "requires": {
+                "acorn": "^8.5.0",
                 "commander": "^2.20.0",
-                "source-map": "~0.6.1",
-                "source-map-support": "~0.5.12"
+                "source-map": "~0.7.2",
+                "source-map-support": "~0.5.20"
             },
             "dependencies": {
+                "acorn": {
+                    "version": "8.7.0",
+                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
+                    "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ=="
+                },
                 "source-map": {
-                    "version": "0.6.1",
-                    "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-                    "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-                    "dev": true
+                    "version": "0.7.3",
+                    "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+                    "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ=="
+                },
+                "source-map-support": {
+                    "version": "0.5.21",
+                    "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+                    "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+                    "requires": {
+                        "buffer-from": "^1.0.0",
+                        "source-map": "^0.6.0"
+                    },
+                    "dependencies": {
+                        "source-map": {
+                            "version": "0.6.1",
+                            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+                            "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+                        }
+                    }
                 }
             }
         },
         "terser-webpack-plugin": {
-            "version": "1.4.5",
-            "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz",
-            "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==",
-            "dev": true,
+            "version": "5.3.1",
+            "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz",
+            "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==",
             "requires": {
-                "cacache": "^12.0.2",
-                "find-cache-dir": "^2.1.0",
-                "is-wsl": "^1.1.0",
-                "schema-utils": "^1.0.0",
-                "serialize-javascript": "^4.0.0",
+                "jest-worker": "^27.4.5",
+                "schema-utils": "^3.1.1",
+                "serialize-javascript": "^6.0.0",
                 "source-map": "^0.6.1",
-                "terser": "^4.1.2",
-                "webpack-sources": "^1.4.0",
-                "worker-farm": "^1.7.0"
+                "terser": "^5.7.2"
             },
             "dependencies": {
-                "cacache": {
-                    "version": "12.0.4",
-                    "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
-                    "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
-                    "dev": true,
-                    "requires": {
-                        "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"
-                    }
-                },
-                "find-cache-dir": {
-                    "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
-                    "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
-                    "dev": true,
-                    "requires": {
-                        "commondir": "^1.0.1",
-                        "make-dir": "^2.0.0",
-                        "pkg-dir": "^3.0.0"
-                    }
-                },
-                "make-dir": {
-                    "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
-                    "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
-                    "dev": true,
-                    "requires": {
-                        "pify": "^4.0.1",
-                        "semver": "^5.6.0"
-                    }
-                },
-                "mississippi": {
-                    "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
-                    "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
-                    "dev": true,
+                "ajv": {
+                    "version": "6.12.6",
+                    "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+                    "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
                     "requires": {
-                        "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"
+                        "fast-deep-equal": "^3.1.1",
+                        "fast-json-stable-stringify": "^2.0.0",
+                        "json-schema-traverse": "^0.4.1",
+                        "uri-js": "^4.2.2"
                     }
                 },
-                "pkg-dir": {
-                    "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
-                    "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
-                    "dev": true,
-                    "requires": {
-                        "find-up": "^3.0.0"
-                    }
+                "ajv-keywords": {
+                    "version": "3.5.2",
+                    "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+                    "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+                    "requires": {}
                 },
-                "rimraf": {
-                    "version": "2.7.1",
-                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
-                    "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
-                    "dev": true,
+                "schema-utils": {
+                    "version": "3.1.1",
+                    "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+                    "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
                     "requires": {
-                        "glob": "^7.1.3"
+                        "@types/json-schema": "^7.0.8",
+                        "ajv": "^6.12.5",
+                        "ajv-keywords": "^3.5.2"
                     }
                 },
                 "serialize-javascript": {
-                    "version": "4.0.0",
-                    "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
-                    "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
-                    "dev": true,
+                    "version": "6.0.0",
+                    "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+                    "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
                     "requires": {
                         "randombytes": "^2.1.0"
                     }
@@ -17280,17 +43793,7 @@
                 "source-map": {
                     "version": "0.6.1",
                     "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-                    "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-                    "dev": true
-                },
-                "ssri": {
-                    "version": "6.0.2",
-                    "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
-                    "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
-                    "dev": true,
-                    "requires": {
-                        "figgy-pudding": "^3.5.1"
-                    }
+                    "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
                 }
             }
         },
@@ -17347,24 +43850,15 @@
             "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
             "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8="
         },
-        "timers-browserify": {
-            "version": "2.0.12",
-            "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz",
-            "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==",
-            "dev": true,
-            "requires": {
-                "setimmediate": "^1.0.4"
-            }
-        },
         "tiny-inflate": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
             "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="
         },
         "tiny-invariant": {
-            "version": "1.1.0",
-            "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz",
-            "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw=="
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz",
+            "integrity": "sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg=="
         },
         "tiny-warning": {
             "version": "1.0.3",
@@ -17372,21 +43866,15 @@
             "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
         },
         "tinycolor2": {
-            "version": "1.4.1",
-            "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
-            "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g="
+            "version": "1.4.2",
+            "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.2.tgz",
+            "integrity": "sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA=="
         },
         "to-array": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
             "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
         },
-        "to-arraybuffer": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
-            "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=",
-            "dev": true
-        },
         "to-fast-properties": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
@@ -17436,9 +43924,9 @@
             }
         },
         "toidentifier": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
-            "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+            "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
         },
         "token-stream": {
             "version": "0.0.1",
@@ -17464,12 +43952,13 @@
             }
         },
         "tough-cookie": {
-            "version": "2.5.0",
-            "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
-            "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
+            "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==",
             "requires": {
-                "psl": "^1.1.28",
-                "punycode": "^2.1.1"
+                "psl": "^1.1.33",
+                "punycode": "^2.1.1",
+                "universalify": "^0.1.2"
             }
         },
         "tr46": {
@@ -17566,29 +44055,27 @@
             }
         },
         "ts-node-dev": {
-            "version": "1.0.0-pre.49",
-            "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.0.0-pre.49.tgz",
-            "integrity": "sha512-iJd4QPPOaCAByl/WuEdmDX8xDR2GmoWYu6aKvGudGUcfP1sJRjpaHb7oFDuvBspQF1xhxUdbjfHuvEtZPwKZFQ==",
+            "version": "1.1.8",
+            "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.8.tgz",
+            "integrity": "sha512-Q/m3vEwzYwLZKmV6/0VlFxcZzVV/xcgOt+Tx/VjaaRHyiBcFlV0541yrT09QjzzCxlDZ34OzKjrFAynlmtflEg==",
             "dev": true,
             "requires": {
-                "chokidar": "^3.4.0",
-                "dateformat": "~1.0.4-1.2.3",
+                "chokidar": "^3.5.1",
                 "dynamic-dedupe": "^0.3.0",
                 "minimist": "^1.2.5",
                 "mkdirp": "^1.0.4",
-                "node-notifier": "^5.4.0",
                 "resolve": "^1.0.0",
                 "rimraf": "^2.6.1",
                 "source-map-support": "^0.5.12",
                 "tree-kill": "^1.2.2",
-                "ts-node": "^8.10.2",
+                "ts-node": "^9.0.0",
                 "tsconfig": "^7.0.0"
             },
             "dependencies": {
                 "anymatch": {
-                    "version": "3.1.1",
-                    "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
-                    "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+                    "version": "3.1.2",
+                    "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+                    "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
                     "dev": true,
                     "requires": {
                         "normalize-path": "^3.0.0",
@@ -17596,9 +44083,9 @@
                     }
                 },
                 "binary-extensions": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
-                    "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==",
+                    "version": "2.2.0",
+                    "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+                    "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
                     "dev": true
                 },
                 "braces": {
@@ -17611,19 +44098,19 @@
                     }
                 },
                 "chokidar": {
-                    "version": "3.4.0",
-                    "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz",
-                    "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==",
+                    "version": "3.5.3",
+                    "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+                    "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
                     "dev": true,
                     "requires": {
-                        "anymatch": "~3.1.1",
+                        "anymatch": "~3.1.2",
                         "braces": "~3.0.2",
-                        "fsevents": "~2.1.2",
-                        "glob-parent": "~5.1.0",
+                        "fsevents": "~2.3.2",
+                        "glob-parent": "~5.1.2",
                         "is-binary-path": "~2.1.0",
                         "is-glob": "~4.0.1",
                         "normalize-path": "~3.0.0",
-                        "readdirp": "~3.4.0"
+                        "readdirp": "~3.6.0"
                     }
                 },
                 "diff": {
@@ -17642,9 +44129,9 @@
                     }
                 },
                 "glob-parent": {
-                    "version": "5.1.1",
-                    "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
-                    "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+                    "version": "5.1.2",
+                    "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+                    "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
                     "dev": true,
                     "requires": {
                         "is-glob": "^4.0.1"
@@ -17672,9 +44159,9 @@
                     "dev": true
                 },
                 "readdirp": {
-                    "version": "3.4.0",
-                    "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
-                    "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
+                    "version": "3.6.0",
+                    "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+                    "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
                     "dev": true,
                     "requires": {
                         "picomatch": "^2.2.1"
@@ -17699,12 +44186,13 @@
                     }
                 },
                 "ts-node": {
-                    "version": "8.10.2",
-                    "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz",
-                    "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==",
+                    "version": "9.1.1",
+                    "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz",
+                    "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==",
                     "dev": true,
                     "requires": {
                         "arg": "^4.1.0",
+                        "create-require": "^1.1.0",
                         "diff": "^4.0.1",
                         "make-error": "^1.1.1",
                         "source-map-support": "^0.5.17",
@@ -17812,12 +44300,6 @@
                 "tslib": "^1.8.1"
             }
         },
-        "tty-browserify": {
-            "version": "0.0.0",
-            "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
-            "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
-            "dev": true
-        },
         "tunnel-agent": {
             "version": "0.6.0",
             "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@@ -17871,10 +44353,9 @@
             "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
         },
         "typescript": {
-            "version": "3.9.9",
-            "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.9.tgz",
-            "integrity": "sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==",
-            "dev": true
+            "version": "4.6.2",
+            "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz",
+            "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg=="
         },
         "typescript-collections": {
             "version": "1.3.3",
@@ -17982,6 +44463,24 @@
             "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz",
             "integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I="
         },
+        "unbox-primitive": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
+            "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==",
+            "requires": {
+                "function-bind": "^1.1.1",
+                "has-bigints": "^1.0.1",
+                "has-symbols": "^1.0.2",
+                "which-boxed-primitive": "^1.0.2"
+            },
+            "dependencies": {
+                "has-symbols": {
+                    "version": "1.0.3",
+                    "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+                    "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
+                }
+            }
+        },
         "unbzip2-stream": {
             "version": "1.4.3",
             "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz",
@@ -18152,7 +44651,8 @@
                 "create-react-context": {
                     "version": "0.1.6",
                     "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.1.6.tgz",
-                    "integrity": "sha512-eCnYYEUEc5i32LHwpE/W7NlddOB9oHwsPaWtWzYtflNkkwa3IfindIcoXdVWs12zCbwaMCavKNu84EXogVIWHw=="
+                    "integrity": "sha512-eCnYYEUEc5i32LHwpE/W7NlddOB9oHwsPaWtWzYtflNkkwa3IfindIcoXdVWs12zCbwaMCavKNu84EXogVIWHw==",
+                    "requires": {}
                 }
             }
         },
@@ -18242,9 +44742,9 @@
             }
         },
         "url-parse": {
-            "version": "1.4.7",
-            "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz",
-            "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==",
+            "version": "1.5.10",
+            "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+            "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
             "dev": true,
             "requires": {
                 "querystringify": "^2.1.1",
@@ -18285,25 +44785,22 @@
             }
         },
         "use-memo-one": {
-            "version": "1.1.1",
-            "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.1.tgz",
-            "integrity": "sha512-oFfsyun+bP7RX8X2AskHNTxu+R3QdE/RC5IefMbqptmACAA/gfol1KDD5KRzPsGMa62sWxGZw+Ui43u6x4ddoQ=="
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.2.tgz",
+            "integrity": "sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==",
+            "requires": {}
         },
         "util": {
-            "version": "0.11.1",
-            "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
-            "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==",
-            "dev": true,
+            "version": "0.12.4",
+            "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz",
+            "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==",
             "requires": {
-                "inherits": "2.0.3"
-            },
-            "dependencies": {
-                "inherits": {
-                    "version": "2.0.3",
-                    "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-                    "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
-                    "dev": true
-                }
+                "inherits": "^2.0.3",
+                "is-arguments": "^1.0.4",
+                "is-generator-function": "^1.0.7",
+                "is-typed-array": "^1.1.3",
+                "safe-buffer": "^5.1.2",
+                "which-typed-array": "^1.1.2"
             }
         },
         "util-deprecate": {
@@ -18311,6 +44808,11 @@
             "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
             "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
         },
+        "utila": {
+            "version": "0.4.0",
+            "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
+            "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw="
+        },
         "utility-types": {
             "version": "3.10.0",
             "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz",
@@ -18326,12 +44828,6 @@
             "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
             "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
         },
-        "v8-compile-cache": {
-            "version": "2.1.1",
-            "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz",
-            "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==",
-            "dev": true
-        },
         "valid-url": {
             "version": "1.0.9",
             "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz",
@@ -18366,12 +44862,6 @@
                 "extsprintf": "^1.2.0"
             }
         },
-        "vm-browserify": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
-            "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
-            "dev": true
-        },
         "void-elements": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
@@ -18463,136 +44953,12 @@
             }
         },
         "watchpack": {
-            "version": "1.7.5",
-            "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",
-            "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==",
-            "dev": true,
-            "requires": {
-                "chokidar": "^3.4.1",
-                "graceful-fs": "^4.1.2",
-                "neo-async": "^2.5.0",
-                "watchpack-chokidar2": "^2.0.1"
-            },
-            "dependencies": {
-                "anymatch": {
-                    "version": "3.1.2",
-                    "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
-                    "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
-                    "dev": true,
-                    "optional": true,
-                    "requires": {
-                        "normalize-path": "^3.0.0",
-                        "picomatch": "^2.0.4"
-                    }
-                },
-                "binary-extensions": {
-                    "version": "2.2.0",
-                    "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
-                    "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
-                    "dev": true,
-                    "optional": true
-                },
-                "braces": {
-                    "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
-                    "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
-                    "dev": true,
-                    "optional": true,
-                    "requires": {
-                        "fill-range": "^7.0.1"
-                    }
-                },
-                "chokidar": {
-                    "version": "3.5.1",
-                    "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz",
-                    "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==",
-                    "dev": true,
-                    "optional": true,
-                    "requires": {
-                        "anymatch": "~3.1.1",
-                        "braces": "~3.0.2",
-                        "fsevents": "~2.3.1",
-                        "glob-parent": "~5.1.0",
-                        "is-binary-path": "~2.1.0",
-                        "is-glob": "~4.0.1",
-                        "normalize-path": "~3.0.0",
-                        "readdirp": "~3.5.0"
-                    }
-                },
-                "fill-range": {
-                    "version": "7.0.1",
-                    "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
-                    "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
-                    "dev": true,
-                    "optional": true,
-                    "requires": {
-                        "to-regex-range": "^5.0.1"
-                    }
-                },
-                "fsevents": {
-                    "version": "2.3.2",
-                    "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
-                    "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
-                    "dev": true,
-                    "optional": true
-                },
-                "glob-parent": {
-                    "version": "5.1.2",
-                    "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
-                    "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
-                    "dev": true,
-                    "optional": true,
-                    "requires": {
-                        "is-glob": "^4.0.1"
-                    }
-                },
-                "is-binary-path": {
-                    "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
-                    "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
-                    "dev": true,
-                    "optional": true,
-                    "requires": {
-                        "binary-extensions": "^2.0.0"
-                    }
-                },
-                "is-number": {
-                    "version": "7.0.0",
-                    "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
-                    "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
-                    "dev": true,
-                    "optional": true
-                },
-                "readdirp": {
-                    "version": "3.5.0",
-                    "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
-                    "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
-                    "dev": true,
-                    "optional": true,
-                    "requires": {
-                        "picomatch": "^2.2.1"
-                    }
-                },
-                "to-regex-range": {
-                    "version": "5.0.1",
-                    "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
-                    "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
-                    "dev": true,
-                    "optional": true,
-                    "requires": {
-                        "is-number": "^7.0.0"
-                    }
-                }
-            }
-        },
-        "watchpack-chokidar2": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz",
-            "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==",
-            "dev": true,
-            "optional": true,
+            "version": "2.3.1",
+            "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz",
+            "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==",
             "requires": {
-                "chokidar": "^2.1.8"
+                "glob-to-regexp": "^0.4.1",
+                "graceful-fs": "^4.1.2"
             }
         },
         "wbuf": {
@@ -18612,6 +44978,11 @@
                 "request": "^2.69.0"
             }
         },
+        "web-streams-polyfill": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz",
+            "integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA=="
+        },
         "webidl-conversions": {
             "version": "4.0.2",
             "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
@@ -18619,204 +44990,339 @@
             "dev": true
         },
         "webpack": {
-            "version": "4.46.0",
-            "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz",
-            "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==",
-            "dev": true,
-            "requires": {
-                "@webassemblyjs/ast": "1.9.0",
-                "@webassemblyjs/helper-module-context": "1.9.0",
-                "@webassemblyjs/wasm-edit": "1.9.0",
-                "@webassemblyjs/wasm-parser": "1.9.0",
-                "acorn": "^6.4.1",
-                "ajv": "^6.10.2",
-                "ajv-keywords": "^3.4.1",
+            "version": "5.69.1",
+            "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.69.1.tgz",
+            "integrity": "sha512-+VyvOSJXZMT2V5vLzOnDuMz5GxEqLk7hKWQ56YxPW/PQRUuKimPqmEIJOx8jHYeyo65pKbapbW464mvsKbaj4A==",
+            "requires": {
+                "@types/eslint-scope": "^3.7.3",
+                "@types/estree": "^0.0.51",
+                "@webassemblyjs/ast": "1.11.1",
+                "@webassemblyjs/wasm-edit": "1.11.1",
+                "@webassemblyjs/wasm-parser": "1.11.1",
+                "acorn": "^8.4.1",
+                "acorn-import-assertions": "^1.7.6",
+                "browserslist": "^4.14.5",
                 "chrome-trace-event": "^1.0.2",
-                "enhanced-resolve": "^4.5.0",
-                "eslint-scope": "^4.0.3",
+                "enhanced-resolve": "^5.8.3",
+                "es-module-lexer": "^0.9.0",
+                "eslint-scope": "5.1.1",
+                "events": "^3.2.0",
+                "glob-to-regexp": "^0.4.1",
+                "graceful-fs": "^4.2.9",
                 "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.3",
-                "neo-async": "^2.6.1",
-                "node-libs-browser": "^2.2.1",
-                "schema-utils": "^1.0.0",
-                "tapable": "^1.1.3",
-                "terser-webpack-plugin": "^1.4.3",
-                "watchpack": "^1.7.4",
-                "webpack-sources": "^1.4.1"
+                "loader-runner": "^4.2.0",
+                "mime-types": "^2.1.27",
+                "neo-async": "^2.6.2",
+                "schema-utils": "^3.1.0",
+                "tapable": "^2.1.1",
+                "terser-webpack-plugin": "^5.1.3",
+                "watchpack": "^2.3.1",
+                "webpack-sources": "^3.2.3"
             },
             "dependencies": {
                 "acorn": {
-                    "version": "6.4.2",
-                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
-                    "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
-                    "dev": true
+                    "version": "8.7.0",
+                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
+                    "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ=="
+                },
+                "ajv": {
+                    "version": "6.12.6",
+                    "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+                    "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+                    "requires": {
+                        "fast-deep-equal": "^3.1.1",
+                        "fast-json-stable-stringify": "^2.0.0",
+                        "json-schema-traverse": "^0.4.1",
+                        "uri-js": "^4.2.2"
+                    }
+                },
+                "ajv-keywords": {
+                    "version": "3.5.2",
+                    "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+                    "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+                    "requires": {}
+                },
+                "enhanced-resolve": {
+                    "version": "5.9.1",
+                    "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.1.tgz",
+                    "integrity": "sha512-jdyZMwCQ5Oj4c5+BTnkxPgDZO/BJzh/ADDmKebayyzNwjVX1AFCeGkOfxNx0mHi2+8BKC5VxUYiw3TIvoT7vhw==",
+                    "requires": {
+                        "graceful-fs": "^4.2.4",
+                        "tapable": "^2.2.0"
+                    }
+                },
+                "graceful-fs": {
+                    "version": "4.2.9",
+                    "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
+                    "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ=="
+                },
+                "neo-async": {
+                    "version": "2.6.2",
+                    "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+                    "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
+                },
+                "schema-utils": {
+                    "version": "3.1.1",
+                    "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+                    "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+                    "requires": {
+                        "@types/json-schema": "^7.0.8",
+                        "ajv": "^6.12.5",
+                        "ajv-keywords": "^3.5.2"
+                    }
+                },
+                "tapable": {
+                    "version": "2.2.1",
+                    "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+                    "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="
+                }
+            }
+        },
+        "webpack-cli": {
+            "version": "4.9.2",
+            "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.2.tgz",
+            "integrity": "sha512-m3/AACnBBzK/kMTcxWHcZFPrw/eQuY4Df1TxvIWfWM2x7mRqBQCqKEd96oCUa9jkapLBaFfRce33eGDb4Pr7YQ==",
+            "requires": {
+                "@discoveryjs/json-ext": "^0.5.0",
+                "@webpack-cli/configtest": "^1.1.1",
+                "@webpack-cli/info": "^1.4.1",
+                "@webpack-cli/serve": "^1.6.1",
+                "colorette": "^2.0.14",
+                "commander": "^7.0.0",
+                "execa": "^5.0.0",
+                "fastest-levenshtein": "^1.0.12",
+                "import-local": "^3.0.2",
+                "interpret": "^2.2.0",
+                "rechoir": "^0.7.0",
+                "webpack-merge": "^5.7.3"
+            },
+            "dependencies": {
+                "commander": {
+                    "version": "7.2.0",
+                    "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+                    "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="
+                },
+                "cross-spawn": {
+                    "version": "7.0.3",
+                    "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+                    "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+                    "requires": {
+                        "path-key": "^3.1.0",
+                        "shebang-command": "^2.0.0",
+                        "which": "^2.0.1"
+                    }
+                },
+                "execa": {
+                    "version": "5.1.1",
+                    "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+                    "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+                    "requires": {
+                        "cross-spawn": "^7.0.3",
+                        "get-stream": "^6.0.0",
+                        "human-signals": "^2.1.0",
+                        "is-stream": "^2.0.0",
+                        "merge-stream": "^2.0.0",
+                        "npm-run-path": "^4.0.1",
+                        "onetime": "^5.1.2",
+                        "signal-exit": "^3.0.3",
+                        "strip-final-newline": "^2.0.0"
+                    }
+                },
+                "find-up": {
+                    "version": "4.1.0",
+                    "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+                    "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+                    "requires": {
+                        "locate-path": "^5.0.0",
+                        "path-exists": "^4.0.0"
+                    }
+                },
+                "get-stream": {
+                    "version": "6.0.1",
+                    "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+                    "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="
+                },
+                "import-local": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
+                    "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==",
+                    "requires": {
+                        "pkg-dir": "^4.2.0",
+                        "resolve-cwd": "^3.0.0"
+                    }
+                },
+                "interpret": {
+                    "version": "2.2.0",
+                    "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
+                    "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw=="
+                },
+                "is-stream": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+                    "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="
+                },
+                "locate-path": {
+                    "version": "5.0.0",
+                    "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+                    "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+                    "requires": {
+                        "p-locate": "^4.1.0"
+                    }
+                },
+                "npm-run-path": {
+                    "version": "4.0.1",
+                    "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+                    "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+                    "requires": {
+                        "path-key": "^3.0.0"
+                    }
+                },
+                "p-locate": {
+                    "version": "4.1.0",
+                    "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+                    "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+                    "requires": {
+                        "p-limit": "^2.2.0"
+                    }
+                },
+                "path-exists": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+                    "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
                 },
-                "enhanced-resolve": {
-                    "version": "4.5.0",
-                    "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz",
-                    "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==",
-                    "dev": true,
+                "path-key": {
+                    "version": "3.1.1",
+                    "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+                    "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
+                },
+                "pkg-dir": {
+                    "version": "4.2.0",
+                    "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+                    "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
                     "requires": {
-                        "graceful-fs": "^4.1.2",
-                        "memory-fs": "^0.5.0",
-                        "tapable": "^1.0.0"
-                    },
-                    "dependencies": {
-                        "memory-fs": {
-                            "version": "0.5.0",
-                            "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
-                            "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
-                            "dev": true,
-                            "requires": {
-                                "errno": "^0.1.3",
-                                "readable-stream": "^2.0.1"
-                            }
-                        }
+                        "find-up": "^4.0.0"
                     }
                 },
-                "memory-fs": {
-                    "version": "0.4.1",
-                    "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
-                    "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
-                    "dev": true,
+                "rechoir": {
+                    "version": "0.7.1",
+                    "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
+                    "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==",
                     "requires": {
-                        "errno": "^0.1.3",
-                        "readable-stream": "^2.0.1"
+                        "resolve": "^1.9.0"
                     }
                 },
-                "readable-stream": {
-                    "version": "2.3.7",
-                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
-                    "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
-                    "dev": true,
+                "resolve-cwd": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
+                    "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
                     "requires": {
-                        "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"
+                        "resolve-from": "^5.0.0"
                     }
-                }
-            }
-        },
-        "webpack-cli": {
-            "version": "3.3.12",
-            "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.12.tgz",
-            "integrity": "sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag==",
-            "dev": true,
-            "requires": {
-                "chalk": "^2.4.2",
-                "cross-spawn": "^6.0.5",
-                "enhanced-resolve": "^4.1.1",
-                "findup-sync": "^3.0.0",
-                "global-modules": "^2.0.0",
-                "import-local": "^2.0.0",
-                "interpret": "^1.4.0",
-                "loader-utils": "^1.4.0",
-                "supports-color": "^6.1.0",
-                "v8-compile-cache": "^2.1.1",
-                "yargs": "^13.3.2"
-            },
-            "dependencies": {
-                "cross-spawn": {
-                    "version": "6.0.5",
-                    "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
-                    "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
-                    "dev": true,
+                },
+                "resolve-from": {
+                    "version": "5.0.0",
+                    "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+                    "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="
+                },
+                "shebang-command": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+                    "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
                     "requires": {
-                        "nice-try": "^1.0.4",
-                        "path-key": "^2.0.1",
-                        "semver": "^5.5.0",
-                        "shebang-command": "^1.2.0",
-                        "which": "^1.2.9"
+                        "shebang-regex": "^3.0.0"
                     }
                 },
-                "interpret": {
-                    "version": "1.4.0",
-                    "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
-                    "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
-                    "dev": true
+                "shebang-regex": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+                    "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
                 },
-                "supports-color": {
-                    "version": "6.1.0",
-                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
-                    "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
-                    "dev": true,
+                "which": {
+                    "version": "2.0.2",
+                    "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+                    "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
                     "requires": {
-                        "has-flag": "^3.0.0"
+                        "isexe": "^2.0.0"
                     }
                 }
             }
         },
         "webpack-dev-middleware": {
-            "version": "3.7.3",
-            "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz",
-            "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==",
-            "dev": true,
+            "version": "5.3.1",
+            "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz",
+            "integrity": "sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==",
             "requires": {
-                "memory-fs": "^0.4.1",
-                "mime": "^2.4.4",
-                "mkdirp": "^0.5.1",
+                "colorette": "^2.0.10",
+                "memfs": "^3.4.1",
+                "mime-types": "^2.1.31",
                 "range-parser": "^1.2.1",
-                "webpack-log": "^2.0.0"
+                "schema-utils": "^4.0.0"
             },
             "dependencies": {
-                "memory-fs": {
-                    "version": "0.4.1",
-                    "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
-                    "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
-                    "dev": true,
+                "ajv": {
+                    "version": "8.10.0",
+                    "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz",
+                    "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==",
                     "requires": {
-                        "errno": "^0.1.3",
-                        "readable-stream": "^2.0.1"
+                        "fast-deep-equal": "^3.1.1",
+                        "json-schema-traverse": "^1.0.0",
+                        "require-from-string": "^2.0.2",
+                        "uri-js": "^4.2.2"
                     }
                 },
-                "mime": {
-                    "version": "2.5.2",
-                    "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz",
-                    "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==",
-                    "dev": true
+                "ajv-keywords": {
+                    "version": "5.1.0",
+                    "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+                    "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+                    "requires": {
+                        "fast-deep-equal": "^3.1.3"
+                    },
+                    "dependencies": {
+                        "fast-deep-equal": {
+                            "version": "3.1.3",
+                            "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+                            "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+                        }
+                    }
                 },
-                "readable-stream": {
-                    "version": "2.3.7",
-                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
-                    "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
-                    "dev": true,
+                "json-schema-traverse": {
+                    "version": "1.0.0",
+                    "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+                    "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+                },
+                "mime-db": {
+                    "version": "1.51.0",
+                    "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
+                    "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g=="
+                },
+                "mime-types": {
+                    "version": "2.1.34",
+                    "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
+                    "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
                     "requires": {
-                        "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"
+                        "mime-db": "1.51.0"
                     }
                 },
-                "webpack-log": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz",
-                    "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==",
-                    "dev": true,
+                "schema-utils": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
+                    "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==",
                     "requires": {
-                        "ansi-colors": "^3.0.0",
-                        "uuid": "^3.3.2"
+                        "@types/json-schema": "^7.0.9",
+                        "ajv": "^8.8.0",
+                        "ajv-formats": "^2.1.1",
+                        "ajv-keywords": "^5.0.0"
                     }
                 }
             }
         },
         "webpack-dev-server": {
-            "version": "3.11.0",
-            "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz",
-            "integrity": "sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg==",
+            "version": "3.11.3",
+            "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.3.tgz",
+            "integrity": "sha512-3x31rjbEQWKMNzacUZRE6wXvUFuGpH7vr0lIEbYpMAG9BOxi0928QU1BBswOAP3kg3H1O4hiS+sq4YyAn6ANnA==",
             "dev": true,
             "requires": {
-                "ansi-html": "0.0.7",
+                "ansi-html-community": "0.0.8",
                 "bonjour": "^3.5.0",
                 "chokidar": "^2.1.8",
                 "compression": "^1.7.4",
@@ -18836,11 +45342,11 @@
                 "p-retry": "^3.0.1",
                 "portfinder": "^1.0.26",
                 "schema-utils": "^1.0.0",
-                "selfsigned": "^1.10.7",
+                "selfsigned": "^1.10.8",
                 "semver": "^6.3.0",
                 "serve-index": "^1.9.1",
-                "sockjs": "0.3.20",
-                "sockjs-client": "1.4.0",
+                "sockjs": "^0.3.21",
+                "sockjs-client": "^1.5.0",
                 "spdy": "^4.0.2",
                 "strip-ansi": "^3.0.1",
                 "supports-color": "^6.1.0",
@@ -18858,12 +45364,49 @@
                     "dev": true
                 },
                 "debug": {
-                    "version": "4.1.1",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
-                    "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+                    "version": "4.3.3",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
+                    "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
                     "dev": true,
                     "requires": {
-                        "ms": "^2.1.1"
+                        "ms": "2.1.2"
+                    }
+                },
+                "memory-fs": {
+                    "version": "0.4.1",
+                    "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
+                    "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
+                    "dev": true,
+                    "requires": {
+                        "errno": "^0.1.3",
+                        "readable-stream": "^2.0.1"
+                    }
+                },
+                "mime": {
+                    "version": "2.6.0",
+                    "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+                    "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+                    "dev": true
+                },
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+                    "dev": true
+                },
+                "readable-stream": {
+                    "version": "2.3.7",
+                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+                    "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+                    "dev": true,
+                    "requires": {
+                        "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"
                     }
                 },
                 "semver": {
@@ -18890,6 +45433,19 @@
                         "has-flag": "^3.0.0"
                     }
                 },
+                "webpack-dev-middleware": {
+                    "version": "3.7.3",
+                    "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz",
+                    "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==",
+                    "dev": true,
+                    "requires": {
+                        "memory-fs": "^0.4.1",
+                        "mime": "^2.4.4",
+                        "mkdirp": "^0.5.1",
+                        "range-parser": "^1.2.1",
+                        "webpack-log": "^2.0.0"
+                    }
+                },
                 "webpack-log": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz",
@@ -18901,9 +45457,9 @@
                     }
                 },
                 "ws": {
-                    "version": "6.2.1",
-                    "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
-                    "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
+                    "version": "6.2.2",
+                    "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
+                    "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
                     "dev": true,
                     "requires": {
                         "async-limiter": "~1.0.0"
@@ -18912,30 +45468,36 @@
             }
         },
         "webpack-hot-middleware": {
-            "version": "2.25.0",
-            "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.25.0.tgz",
-            "integrity": "sha512-xs5dPOrGPCzuRXNi8F6rwhawWvQQkeli5Ro48PRuQh8pYPCPmNnltP9itiUPT4xI8oW+y0m59lyyeQk54s5VgA==",
+            "version": "2.25.1",
+            "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.25.1.tgz",
+            "integrity": "sha512-Koh0KyU/RPYwel/khxbsDz9ibDivmUbrRuKSSQvW42KSDdO4w23WI3SkHpSUKHE76LrFnnM/L7JCrpBwu8AXYw==",
             "dev": true,
             "requires": {
-                "ansi-html": "0.0.7",
-                "html-entities": "^1.2.0",
+                "ansi-html-community": "0.0.8",
+                "html-entities": "^2.1.0",
                 "querystring": "^0.2.0",
-                "strip-ansi": "^3.0.0"
+                "strip-ansi": "^6.0.0"
             },
             "dependencies": {
                 "ansi-regex": {
-                    "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-                    "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+                    "version": "5.0.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                    "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+                    "dev": true
+                },
+                "html-entities": {
+                    "version": "2.3.2",
+                    "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz",
+                    "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==",
                     "dev": true
                 },
                 "strip-ansi": {
-                    "version": "3.0.1",
-                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-                    "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+                    "version": "6.0.1",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                    "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
                     "dev": true,
                     "requires": {
-                        "ansi-regex": "^2.0.0"
+                        "ansi-regex": "^5.0.1"
                     }
                 }
             }
@@ -18952,39 +45514,37 @@
                 "uuid": "^3.1.0"
             }
         },
-        "webpack-sources": {
-            "version": "1.4.3",
-            "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
-            "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
-            "dev": true,
+        "webpack-merge": {
+            "version": "5.8.0",
+            "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz",
+            "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==",
             "requires": {
-                "source-list-map": "^2.0.0",
-                "source-map": "~0.6.1"
-            },
-            "dependencies": {
-                "source-map": {
-                    "version": "0.6.1",
-                    "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-                    "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-                    "dev": true
-                }
+                "clone-deep": "^4.0.1",
+                "wildcard": "^2.0.0"
             }
         },
+        "webpack-sources": {
+            "version": "3.2.3",
+            "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
+            "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w=="
+        },
         "webrtc-adapter": {
-            "version": "7.6.3",
-            "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-7.6.3.tgz",
-            "integrity": "sha512-DCMiS6cy29PknSuBz779twM6/VsPdoQNAU/I/q4VCEVSlCQ88xiwqGD5/osVO6UMyXCnTBiOl8kd+1cJi6Pkig==",
+            "version": "7.7.1",
+            "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-7.7.1.tgz",
+            "integrity": "sha512-TbrbBmiQBL9n0/5bvDdORc6ZfRY/Z7JnEj+EYOD1ghseZdpJ+nF2yx14k3LgQKc7JZnG7HAcL+zHnY25So9d7A==",
             "requires": {
                 "rtcpeerconnection-shim": "^1.2.15",
                 "sdp": "^2.12.0"
             }
         },
         "websocket-driver": {
-            "version": "0.6.5",
-            "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz",
-            "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=",
+            "version": "0.7.4",
+            "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
+            "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
             "dev": true,
             "requires": {
+                "http-parser-js": ">=0.5.1",
+                "safe-buffer": ">=5.1.0",
                 "websocket-extensions": ">=0.1.1"
             }
         },
@@ -19033,6 +45593,18 @@
                 "isexe": "^2.0.0"
             }
         },
+        "which-boxed-primitive": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+            "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+            "requires": {
+                "is-bigint": "^1.0.1",
+                "is-boolean-object": "^1.1.0",
+                "is-number-object": "^1.0.4",
+                "is-string": "^1.0.5",
+                "is-symbol": "^1.0.3"
+            }
+        },
         "which-module": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
@@ -19043,6 +45615,101 @@
             "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
             "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
         },
+        "which-typed-array": {
+            "version": "1.1.7",
+            "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz",
+            "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==",
+            "requires": {
+                "available-typed-arrays": "^1.0.5",
+                "call-bind": "^1.0.2",
+                "es-abstract": "^1.18.5",
+                "foreach": "^2.0.5",
+                "has-tostringtag": "^1.0.0",
+                "is-typed-array": "^1.1.7"
+            },
+            "dependencies": {
+                "es-abstract": {
+                    "version": "1.19.1",
+                    "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz",
+                    "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==",
+                    "requires": {
+                        "call-bind": "^1.0.2",
+                        "es-to-primitive": "^1.2.1",
+                        "function-bind": "^1.1.1",
+                        "get-intrinsic": "^1.1.1",
+                        "get-symbol-description": "^1.0.0",
+                        "has": "^1.0.3",
+                        "has-symbols": "^1.0.2",
+                        "internal-slot": "^1.0.3",
+                        "is-callable": "^1.2.4",
+                        "is-negative-zero": "^2.0.1",
+                        "is-regex": "^1.1.4",
+                        "is-shared-array-buffer": "^1.0.1",
+                        "is-string": "^1.0.7",
+                        "is-weakref": "^1.0.1",
+                        "object-inspect": "^1.11.0",
+                        "object-keys": "^1.1.1",
+                        "object.assign": "^4.1.2",
+                        "string.prototype.trimend": "^1.0.4",
+                        "string.prototype.trimstart": "^1.0.4",
+                        "unbox-primitive": "^1.0.1"
+                    }
+                },
+                "has-symbols": {
+                    "version": "1.0.3",
+                    "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+                    "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
+                },
+                "is-callable": {
+                    "version": "1.2.4",
+                    "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz",
+                    "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w=="
+                },
+                "is-regex": {
+                    "version": "1.1.4",
+                    "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+                    "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+                    "requires": {
+                        "call-bind": "^1.0.2",
+                        "has-tostringtag": "^1.0.0"
+                    }
+                },
+                "object-inspect": {
+                    "version": "1.12.0",
+                    "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
+                    "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g=="
+                },
+                "object.assign": {
+                    "version": "4.1.2",
+                    "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+                    "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+                    "requires": {
+                        "call-bind": "^1.0.0",
+                        "define-properties": "^1.1.3",
+                        "has-symbols": "^1.0.1",
+                        "object-keys": "^1.1.1"
+                    }
+                },
+                "string.prototype.trimend": {
+                    "version": "1.0.4",
+                    "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
+                    "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
+                    "requires": {
+                        "call-bind": "^1.0.2",
+                        "define-properties": "^1.1.3"
+                    }
+                },
+                "string.prototype.trimstart": {
+                    "version": "1.0.4",
+                    "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
+                    "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
+                    "requires": {
+                        "call-bind": "^1.0.2",
+                        "define-properties": "^1.1.3"
+                    }
+                }
+            }
+        },
         "wide-align": {
             "version": "1.1.3",
             "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
@@ -19060,15 +45727,20 @@
             }
         },
         "wikijs": {
-            "version": "6.0.1",
-            "resolved": "https://registry.npmjs.org/wikijs/-/wikijs-6.0.1.tgz",
-            "integrity": "sha512-67ZtXyVPspYM5/B5ci0NIwvPJyG23HPk33QQLgLbCcORQ6N0I3Mhxd/KsPRh3xyly87KDs/bh1xuIG6PVTCKGw==",
+            "version": "6.3.3",
+            "resolved": "https://registry.npmjs.org/wikijs/-/wikijs-6.3.3.tgz",
+            "integrity": "sha512-pYVaUuJyTd7VO2aNxbdN341zgio+QuGdc6HC3jyQqakyaLJdOD8Shj+bs7lnZnhv/wml2u+C+OME9YUkck06Cg==",
             "requires": {
-                "cheerio": "^1.0.0-rc.3",
                 "cross-fetch": "^3.0.2",
-                "infobox-parser": "3.3.1"
+                "hyntax": "^1.1.9",
+                "infobox-parser": "3.6.2"
             }
         },
+        "wildcard": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
+            "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw=="
+        },
         "window-size": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
@@ -19113,15 +45785,6 @@
                 "typical": "^2.6.1"
             }
         },
-        "worker-farm": {
-            "version": "1.7.0",
-            "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
-            "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
-            "dev": true,
-            "requires": {
-                "errno": "~0.1.7"
-            }
-        },
         "worker-rpc": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/worker-rpc/-/worker-rpc-0.1.1.tgz",
@@ -19184,7 +45847,8 @@
         "ws": {
             "version": "7.2.5",
             "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz",
-            "integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA=="
+            "integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA==",
+            "requires": {}
         },
         "xdg-basedir": {
             "version": "3.0.0",
@@ -19224,9 +45888,9 @@
             "integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ=="
         },
         "xmlhttprequest-ssl": {
-            "version": "1.5.5",
-            "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
-            "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
+            "version": "1.6.3",
+            "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz",
+            "integrity": "sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q=="
         },
         "xoauth2": {
             "version": "1.2.0",
@@ -19234,11 +45898,11 @@
             "integrity": "sha1-8u76wRRyyXHqO8RuVU60sSMhRuU="
         },
         "xregexp": {
-            "version": "4.3.0",
-            "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.3.0.tgz",
-            "integrity": "sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g==",
+            "version": "4.4.1",
+            "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.4.1.tgz",
+            "integrity": "sha512-2u9HwfadaJaY9zHtRRnH6BY6CQVNQKkYm3oLtC9gJXXzfsbACg5X5e4EZZGVAH+YIfa+QA9lsFQTTe3HURF3ag==",
             "requires": {
-                "@babel/runtime-corejs3": "^7.8.3"
+                "@babel/runtime-corejs3": "^7.12.1"
             }
         },
         "xtend": {
@@ -19258,9 +45922,9 @@
             "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
         },
         "yaml": {
-            "version": "1.10.0",
-            "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz",
-            "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg=="
+            "version": "1.10.2",
+            "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+            "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="
         },
         "yargs": {
             "version": "13.3.2",
@@ -19354,9 +46018,10 @@
             }
         },
         "zustand": {
-            "version": "3.4.1",
-            "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.4.1.tgz",
-            "integrity": "sha512-Kb91vSjy5vwBQ/PQ1a5GE6naS3gCxCgpkujT9zqZSO85+gnvmzgqraMW3ao1I0jR4PwHBXtLTf26r9j7EXoUiQ=="
+            "version": "3.7.1",
+            "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.1.tgz",
+            "integrity": "sha512-wHBCZlKj+bg03/hP+Tzv24YhnqqP8MCeN9ECPDXoF01062SIbnfl3j9O0znkDw1lNTY0a8WN3F///a0UhhaEqg==",
+            "requires": {}
         }
     }
 }
diff --git a/package.json b/package.json
index 810d0a026..71f56e3ce 100644
--- a/package.json
+++ b/package.json
@@ -20,78 +20,76 @@
         "tsc": "tsc"
     },
     "devDependencies": {
-        "@types/adm-zip": "^0.4.32",
+        "@types/adm-zip": "^0.4.34",
         "@types/animejs": "^2.0.2",
-        "@types/archiver": "^3.0.0",
+        "@types/archiver": "^3.1.1",
         "@types/async": "^2.4.1",
         "@types/bcrypt-nodejs": "0.0.30",
-        "@types/bluebird": "^3.5.32",
-        "@types/body-parser": "^1.17.1",
-        "@types/chai": "^4.2.7",
-        "@types/color": "^3.0.1",
+        "@types/bluebird": "^3.5.36",
+        "@types/body-parser": "^1.19.2",
+        "@types/chai": "^4.3.0",
+        "@types/color": "^3.0.3",
         "@types/connect-flash": "0.0.34",
         "@types/cookie-parser": "^1.4.2",
-        "@types/cookie-session": "^2.0.41",
+        "@types/cookie-session": "^2.0.44",
         "@types/dotenv": "^6.1.1",
-        "@types/exif": "^0.6.1",
-        "@types/express": "^4.17.2",
+        "@types/exif": "^0.6.3",
+        "@types/express": "^4.17.13",
         "@types/express-flash": "0.0.0",
-        "@types/express-session": "^1.15.16",
+        "@types/express-session": "^1.17.4",
         "@types/express-validator": "^3.0.0",
-        "@types/file-saver": "^2.0.1",
-        "@types/formidable": "^1.0.31",
+        "@types/file-saver": "^2.0.5",
         "@types/google-maps-react": "^2.0.5",
-        "@types/jquery": "^3.5.0",
-        "@types/libxmljs": "^0.18.5",
-        "@types/lodash": "^4.14.157",
+        "@types/jquery": "^3.5.14",
+        "@types/libxmljs": "^0.18.7",
+        "@types/lodash": "^4.14.179",
         "@types/mobile-detect": "^1.3.4",
         "@types/mocha": "^5.2.6",
-        "@types/mongodb": "^3.5.25",
-        "@types/mongoose": "^5.7.28",
-        "@types/node": "^10.17.26",
+        "@types/mongodb": "^3.6.20",
+        "@types/mongoose": "^5.11.97",
+        "@types/node": "^10.17.60",
         "@types/nodemailer": "^4.6.6",
-        "@types/passport": "^1.0.2",
-        "@types/passport-google-oauth20": "^2.0.3",
-        "@types/passport-local": "^1.0.33",
-        "@types/pdfjs-dist": "^2.1.4",
-        "@types/prosemirror-commands": "^1.0.1",
+        "@types/passport": "^1.0.7",
+        "@types/passport-google-oauth20": "^2.0.11",
+        "@types/passport-local": "^1.0.34",
+        "@types/pdfjs-dist": "^2.10.378",
+        "@types/prosemirror-commands": "^1.0.4",
         "@types/prosemirror-dev-tools": "^2.1.0",
-        "@types/prosemirror-history": "^1.0.1",
-        "@types/prosemirror-inputrules": "^1.0.2",
-        "@types/prosemirror-keymap": "^1.0.2",
-        "@types/prosemirror-menu": "^1.0.2",
-        "@types/prosemirror-model": "^1.7.0",
-        "@types/prosemirror-schema-list": "^1.0.1",
-        "@types/prosemirror-state": "^1.2.4",
-        "@types/prosemirror-transform": "^1.1.1",
-        "@types/prosemirror-view": "^1.11.2",
-        "@types/rc-switch": "^1.9.0",
-        "@types/react": "^16.9.41",
+        "@types/prosemirror-history": "^1.0.3",
+        "@types/prosemirror-inputrules": "^1.0.4",
+        "@types/prosemirror-keymap": "^1.0.4",
+        "@types/prosemirror-menu": "^1.0.6",
+        "@types/prosemirror-model": "^1.16.1",
+        "@types/prosemirror-schema-list": "^1.0.3",
+        "@types/prosemirror-state": "^1.2.8",
+        "@types/prosemirror-transform": "^1.1.5",
+        "@types/prosemirror-view": "^1.23.1",
+        "@types/rc-switch": "^1.9.2",
+        "@types/react": "^16.14.23",
         "@types/react-autosuggest": "^9.3.14",
-        "@types/react-color": "^2.17.4",
-        "@types/react-datepicker": "^3.0.2",
-        "@types/react-dom": "^16.9.8",
-        "@types/react-grid-layout": "^0.17.1",
-        "@types/react-measure": "^2.0.6",
-        "@types/react-select": "^3.0.13",
-        "@types/react-table": "^6.8.6",
-        "@types/request": "^2.48.5",
-        "@types/request-promise": "^4.1.45",
-        "@types/rimraf": "^2.0.3",
+        "@types/react-color": "^2.17.6",
+        "@types/react-datepicker": "^3.1.8",
+        "@types/react-dom": "^16.9.14",
+        "@types/react-grid-layout": "^0.17.2",
+        "@types/react-measure": "^2.0.8",
+        "@types/react-select": "^3.1.2",
+        "@types/react-table": "^6.8.9",
+        "@types/request": "^2.48.8",
+        "@types/request-promise": "^4.1.48",
+        "@types/rimraf": "^2.0.5",
         "@types/sharp": "^0.23.1",
-        "@types/shelljs": "^0.8.8",
-        "@types/socket.io": "^2.1.8",
-        "@types/socket.io-client": "^1.4.33",
+        "@types/shelljs": "^0.8.11",
+        "@types/socket.io": "^2.1.13",
+        "@types/socket.io-client": "^1.4.36",
         "@types/typescript": "^2.0.0",
-        "@types/uuid": "^3.4.6",
+        "@types/uuid": "^3.4.10",
         "@types/valid-url": "^1.0.3",
-        "@types/webpack": "^4.41.27",
-        "@types/webpack-dev-middleware": "^2.0.7",
-        "@types/webpack-hot-middleware": "^2.25.4",
-        "@types/xregexp": "^4.3.0",
+        "@types/webpack": "^4.41.32",
+        "@types/webpack-hot-middleware": "^2.25.6",
+        "@types/xregexp": "^4.4.0",
         "@types/youtube": "0.0.39",
         "awesome-typescript-loader": "^5.2.1",
-        "chai": "^4.2.0",
+        "chai": "^4.3.6",
         "copy-webpack-plugin": "^4.6.0",
         "cross-env": "^5.2.1",
         "css-loader": "^2.1.1",
@@ -105,179 +103,198 @@
         "style-loader": "^0.23.1",
         "ts-loader": "^5.3.3",
         "ts-node": "^7.0.1",
-        "ts-node-dev": "^1.0.0-pre.49",
+        "ts-node-dev": "^1.1.8",
         "tslint": "^5.20.1",
         "tslint-loader": "^3.6.0",
-        "typescript": "^3.9.9",
-        "webpack": "^4.46.0",
-        "webpack-cli": "^3.3.12",
-        "webpack-dev-middleware": "^3.7.3",
-        "webpack-dev-server": "^3.11.0",
-        "webpack-hot-middleware": "^2.24.3"
+        "typescript": "^4.6.2",
+        "webpack": "^5.69.1",
+        "webpack-dev-server": "^3.11.3",
+        "webpack-hot-middleware": "^2.25.1"
     },
     "dependencies": {
-        "@fortawesome/fontawesome-svg-core": "^1.2.35",
-        "@fortawesome/free-brands-svg-icons": "^5.14.0",
-        "@fortawesome/free-regular-svg-icons": "^5.13.1",
-        "@fortawesome/free-solid-svg-icons": "^5.15.3",
-        "@fortawesome/react-fontawesome": "^0.1.14",
-        "@hig/flyout": "^1.2.1",
+        "@fortawesome/fontawesome-svg-core": "^1.3.0",
+        "@fortawesome/free-brands-svg-icons": "^5.15.4",
+        "@fortawesome/free-regular-svg-icons": "^5.15.4",
+        "@fortawesome/free-solid-svg-icons": "^5.15.4",
+        "@fortawesome/react-fontawesome": "^0.1.17",
+        "@hig/flyout": "^1.3.1",
         "@hig/theme-context": "^2.1.3",
-        "@hig/theme-data": "^2.16.1",
-        "@material-ui/core": "^4.11.0",
+        "@hig/theme-data": "^2.23.1",
+        "@material-ui/core": "^4.12.3",
         "@react-google-maps/api": "^2.7.0",
-        "@react-three/fiber": "^6.0.16",
+        "@react-three/fiber": "^6.2.3",
         "@types/bezier-js": "^4.1.0",
-        "@types/cors": "^2.8.8",
-        "@types/d3-axis": "^2.0.0",
-        "@types/d3-color": "^2.0.1",
-        "@types/d3-scale": "^3.2.2",
-        "@types/d3-selection": "^2.0.0",
-        "@types/google-maps": "^3.2.2",
-        "@types/react-reconciler": "^0.26.1",
+        "@types/cors": "^2.8.12",
+        "@types/d3-axis": "^2.1.3",
+        "@types/d3-color": "^2.0.3",
+        "@types/d3-scale": "^3.3.2",
+        "@types/d3-selection": "^2.0.1",
+        "@types/dom-speech-recognition": "0.0.1",
+        "@types/formidable": "1.0.31",
+        "@types/google-maps": "^3.2.3",
+        "@types/react-reconciler": "^0.26.4",
         "@types/reveal": "^3.3.33",
+        "@types/supercluster": "^7.1.0",
         "@types/three": "^0.126.2",
-        "@types/webscopeio__react-textarea-autocomplete": "^4.6.1",
-        "@webscopeio/react-textarea-autocomplete": "^4.7.0",
+        "@types/web": "0.0.53",
+        "@types/webscopeio__react-textarea-autocomplete": "^4.7.2",
+        "@webscopeio/react-textarea-autocomplete": "^4.9.1",
         "adm-zip": "^0.4.16",
         "archiver": "^3.1.1",
         "array-batcher": "^1.2.3",
         "async": "^2.6.2",
         "axios": "^0.19.2",
-        "babel-runtime": "^6.26.0",
+        "babel": "^6.23.0",
         "bcrypt-nodejs": "0.0.3",
         "bezier-curve": "^1.0.0",
         "bezier-js": "^4.1.1",
         "bluebird": "^3.7.2",
-        "body-parser": "^1.18.3",
-        "bootstrap": "^4.5.0",
-        "canvas": "^2.5.0",
+        "body-parser": "^1.19.2",
+        "bootstrap": "^4.6.1",
+        "browser-assert": "^1.2.1",
+        "bson": "^4.6.1",
+        "canvas": "^2.9.0",
         "child_process": "^1.0.2",
         "chrome": "^0.1.0",
         "class-transformer": "^0.2.0",
-        "color": "^3.1.2",
+        "color": "^3.2.1",
         "colors": "^1.4.0",
         "connect-flash": "^0.1.1",
         "connect-mongo": "^2.0.3",
-        "cookie-parser": "^1.4.4",
-        "cookie-session": "^2.0.0-rc.1",
+        "cookie-parser": "^1.4.6",
+        "cookie-session": "^2.0.0",
         "cors": "^2.8.5",
+        "D": "^1.0.0",
         "depcheck": "^0.9.2",
         "equation-editor-react": "github:bobzel/equation-editor-react#useLocally",
         "exif": "^0.6.0",
         "exifr": "^7.1.3",
-        "express": "^4.16.4",
+        "express": "^4.17.3",
         "express-flash": "0.0.2",
-        "express-session": "^1.17.0",
+        "express-session": "^1.17.2",
         "express-validator": "^5.3.1",
         "expressjs": "^1.0.1",
-        "file-saver": "^2.0.2",
+        "file-saver": "^2.0.5",
         "find-in-files": "^0.5.0",
         "fit-curve": "^0.1.7",
         "flexlayout-react": "^0.3.11",
         "fluent-ffmpeg": "^2.1.2",
-        "formidable": "^1.2.1",
-        "function-plot": "^1.22.7",
+        "forever-agent": "^0.6.1",
+        "formidable": "1.2.1",
+        "function-plot": "^1.22.8",
         "golden-layout": "^1.5.9",
         "google-auth-library": "^4.2.4",
         "google-maps-react": "^2.0.6",
         "googleapis": "^40.0.0",
         "googlephotos": "^0.2.5",
-        "howler": "^2.2.0",
-        "html-to-image": "^0.1.0",
+        "got": "^12.0.1",
+        "howler": "^2.2.3",
+        "html-to-image": "^0.1.3",
         "html-to-text": "^5.1.1",
-        "i": "^0.3.6",
+        "html-webpack-plugin": "^5.5.0",
+        "http-browserify": "^1.7.0",
+        "https": "^1.0.0",
+        "https-browserify": "^1.0.0",
+        "i": "^0.3.7",
         "image-data-uri": "^2.0.1",
         "image-size": "^0.7.5",
         "image-size-stream": "^1.1.0",
         "js-datepicker": "^4.6.6",
-        "jsonschema": "^1.2.5",
-        "jszip": "^3.5.0",
-        "libxmljs": "^0.19.7",
-        "lodash": "^4.17.15",
+        "jsonschema": "^1.4.0",
+        "jszip": "^3.7.1",
+        "lodash": "^4.17.21",
         "material-ui": "^0.20.2",
         "memorystream": "^0.3.1",
-        "mobile-detect": "^1.4.4",
+        "mobile-detect": "^1.4.5",
         "mobx": "^5.15.7",
         "mobx-react": "^5.4.4",
         "mobx-react-devtools": "^6.1.1",
-        "mobx-utils": "^5.6.1",
-        "mongodb": "^3.5.9",
-        "mongoose": "^5.9.20",
+        "mobx-utils": "^5.6.2",
+        "mongodb": "^3.7.3",
+        "mongoose": "^5.13.14",
         "node-sass": "^4.14.1",
-        "node-stream-zip": "^1.11.2",
+        "node-stream-zip": "^1.15.0",
         "nodemailer": "^5.1.1",
         "nodemon": "^1.19.4",
         "normalize.css": "^8.0.1",
-        "npm": "^6.14.5",
+        "npm": "^6.14.16",
         "p-limit": "^2.2.0",
         "passport": "^0.4.0",
         "passport-google-oauth20": "^2.0.0",
         "passport-local": "^1.0.0",
+        "path-browserify": "^1.0.1",
         "pdf-parse": "^1.1.1",
-        "pdfjs": "^2.3.9",
-        "pdfjs-dist": "^2.4.456",
+        "pdfjs": "^2.4.7",
+        "pdfjs-dist": "^2.13.216",
         "probe-image-size": "^4.0.0",
-        "prosemirror-commands": "^1.1.3",
-        "prosemirror-dev-tools": "^3.0.1",
+        "process": "^0.11.10",
+        "prosemirror-commands": "^1.2.1",
+        "prosemirror-dev-tools": "^3.1.0",
         "prosemirror-find-replace": "^0.9.0",
-        "prosemirror-history": "^1.1.3",
-        "prosemirror-inputrules": "^1.1.2",
-        "prosemirror-keymap": "^1.1.4",
-        "prosemirror-model": "^1.10.0",
-        "prosemirror-schema-list": "^1.1.2",
-        "prosemirror-state": "^1.3.2",
-        "prosemirror-transform": "^1.2.6",
-        "prosemirror-view": "^1.15.0",
+        "prosemirror-history": "^1.2.0",
+        "prosemirror-inputrules": "^1.1.3",
+        "prosemirror-keymap": "^1.1.5",
+        "prosemirror-model": "^1.16.1",
+        "prosemirror-schema-list": "^1.1.6",
+        "prosemirror-state": "^1.3.4",
+        "prosemirror-transform": "^1.3.4",
+        "prosemirror-view": "^1.23.7",
         "pug": "^2.0.4",
         "puppeteer": "^3.3.0",
-        "query-string": "^6.13.1",
+        "query-string": "^6.14.1",
         "raw-loader": "^1.0.0",
-        "rc-switch": "^1.9.0",
-        "react": "^16.12.0",
+        "rc-switch": "^1.9.2",
+        "react": "^16.14.0",
         "react-audio-waveform": "0.0.5",
         "react-autosuggest": "^9.4.3",
-        "react-beautiful-dnd": "^13.0.0",
-        "react-color": "^2.18.1",
+        "react-beautiful-dnd": "^13.1.0",
+        "react-color": "^2.19.3",
         "react-compound-slider": "^2.5.0",
-        "react-datepicker": "^3.0.0",
-        "react-dom": "^16.12.0",
+        "react-datepicker": "^3.8.0",
+        "react-dom": "^16.14.0",
         "react-grid-layout": "^0.18.3",
         "react-image-lightbox-with-rotate": "^5.1.1",
-        "react-jsx-parser": "^1.25.1",
+        "react-jsx-parser": "^1.29.0",
         "react-loading": "^2.0.3",
-        "react-measure": "^2.2.4",
-        "react-resizable": "^1.10.1",
+        "react-measure": "^2.5.2",
+        "react-refresh-typescript": "^2.0.3",
+        "react-resizable": "^1.11.1",
         "react-resizable-rotatable-draggable": "^0.2.0",
         "react-reveal": "^1.2.2",
-        "react-select": "^3.1.0",
-        "react-table": "^6.11.5",
+        "react-select": "^3.2.0",
+        "react-table": "^7.7.0",
         "readline": "^1.3.0",
-        "request": "^2.88.0",
-        "request-promise": "^4.2.5",
-        "reveal.js": "^4.0.2",
+        "request": "^2.88.2",
+        "request-promise": "^4.2.6",
+        "reveal.js": "^4.3.0",
         "rimraf": "^3.0.0",
         "serializr": "^1.5.4",
         "sharp": "^0.23.4",
-        "shelljs": "^0.8.3",
-        "socket.io": "^2.3.0",
-        "socket.io-client": "^2.3.0",
+        "shelljs": "^0.8.5",
+        "socket.io": "^2.4.1",
+        "socket.io-client": "^2.4.0",
         "solr-node": "^1.2.1",
         "standard-http-error": "^2.0.1",
+        "stream-browserify": "^3.0.0",
         "styled-components": "^4.4.1",
+        "supercluster": "^7.1.4",
         "textarea-caret": "^3.1.0",
         "three": "^0.127.0",
+        "tough-cookie": "^4.0.0",
         "translate-google-api": "^1.0.4",
         "typescript-collections": "^1.3.3",
         "typescript-language-server": "^0.4.0",
         "url-loader": "^1.1.2",
+        "util": "^0.12.4",
         "uuid": "^3.4.0",
         "valid-url": "^1.0.9",
         "web-request": "^1.0.7",
-        "webrtc-adapter": "^7.6.3",
-        "wikijs": "^6.0.1",
+        "webpack-cli": "^4.9.2",
+        "webpack-dev-middleware": "^5.3.1",
+        "webrtc-adapter": "^7.7.1",
+        "wikijs": "^6.3.3",
         "words-to-numbers": "^1.5.1",
         "xoauth2": "^1.2.0",
-        "xregexp": "^4.3.0"
+        "xregexp": "^4.4.1"
     }
 }
diff --git a/src/.DS_Store b/src/.DS_Store
index bdc161da2..b0987293b 100644
Binary files a/src/.DS_Store and b/src/.DS_Store differ
diff --git a/src/Utils.ts b/src/Utils.ts
index 5f46ef5c9..b280badd7 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -2,8 +2,8 @@ import v4 = require('uuid/v4');
 import v5 = require("uuid/v5");
 import { ColorState } from 'react-color';
 import { Socket } from 'socket.io';
-import { Message } from './server/Message';
 import { Colors } from './client/views/global/globalEnums';
+import { Message } from './server/Message';
 import Color = require('color');
 
 export namespace Utils {
diff --git a/src/client/ClientRecommender.tsx b/src/client/ClientRecommender.tsx
deleted file mode 100644
index 1d4653471..000000000
--- a/src/client/ClientRecommender.tsx
+++ /dev/null
@@ -1,421 +0,0 @@
-import { Doc, FieldResult } from "../fields/Doc";
-import { StrCast, Cast } from "../fields/Types";
-import { List } from "../fields/List";
-import { CognitiveServices, Confidence, Tag, Service } from "./cognitive_services/CognitiveServices";
-import React = require("react");
-import { observer } from "mobx-react";
-import { observable, action, computed, reaction } from "mobx";
-// var assert = require('assert');
-// var sw = require('stopword');
-// var FeedParser = require('feedparser');
-// var https = require('https');
-import "./ClientRecommender.scss";
-import { JSXElement } from "babel-types";
-import { RichTextField } from "../fields/RichTextField";
-import { ToPlainText } from "../fields/FieldSymbols";
-import { listSpec } from "../fields/Schema";
-import { ComputedField } from "../fields/ScriptField";
-import { ImageField } from "../fields/URLField";
-import { KeyphraseQueryView } from "./views/KeyphraseQueryView";
-import { Networking } from "./Network";
-
-export interface RecommenderProps {
-    title: string;
-}
-
-/**
- * actualDoc: datadoc
- * vectorDoc: mean vector of text
- * score: similarity score to main doc
- */
-
-export interface RecommenderDocument {
-    actualDoc: Doc;
-    vectorDoc: number[];
-    score: number;
-}
-
-const fieldkey = "data";
-
-@observer
-export class ClientRecommender extends React.Component<RecommenderProps> {
-
-    static Instance: ClientRecommender;
-    private mainDoc?: RecommenderDocument;
-    private docVectors: Set<RecommenderDocument> = new Set();
-    public _queries: string[] = [];
-
-    @observable private corr_matrix = [[0, 0], [0, 0]]; // for testing
-
-    constructor(props: RecommenderProps) {
-        super(props);
-        if (!ClientRecommender.Instance) ClientRecommender.Instance = this;
-        ClientRecommender.Instance.docVectors = new Set();
-        //ClientRecommender.Instance.corr_matrix = [[0, 0], [0, 0]];
-    }
-
-    @action
-    public reset_docs() {
-        ClientRecommender.Instance.docVectors = new Set();
-        ClientRecommender.Instance.mainDoc = undefined;
-        ClientRecommender.Instance.corr_matrix = [[0, 0], [0, 0]];
-    }
-
-    /***
-     * Computes the cosine similarity between two vectors in Euclidean space. 
-     */
-
-    private distance(vector1: number[], vector2: number[], metric: string = "cosine") {
-        // assert(vector1.length === vector2.length, "Vectors are not the same length");
-        let similarity: number;
-        switch (metric) {
-            case "cosine":
-                var dotproduct = 0;
-                var mA = 0;
-                var mB = 0;
-                for (let i = 0; i < vector1.length; i++) { // here you missed the i++
-                    dotproduct += (vector1[i] * vector2[i]);
-                    mA += (vector1[i] * vector1[i]);
-                    mB += (vector2[i] * vector2[i]);
-                }
-                mA = Math.sqrt(mA);
-                mB = Math.sqrt(mB);
-                similarity = (dotproduct) / ((mA) * (mB)); // here you needed extra brackets
-                return similarity;
-            case "euclidian":
-                var sum = 0;
-                for (let i = 0; i < vector1.length; i++) {
-                    sum += Math.pow(vector1[i] - vector2[i], 2);
-                }
-                similarity = Math.sqrt(sum);
-                return similarity;
-            default:
-                return 0;
-        }
-    }
-
-    /**
-     * Returns list of {doc, similarity (to main doc)} in increasing score
-     */
-
-    public computeSimilarities(distance_metric: string) {
-        const parameters: any = {};
-        Networking.PostToServer("/IBMAnalysis", parameters).then(response => {
-            console.log("ANALYSIS RESULTS! ", response);
-        });
-        ClientRecommender.Instance.docVectors.forEach((doc: RecommenderDocument) => {
-            if (ClientRecommender.Instance.mainDoc) {
-                const distance = ClientRecommender.Instance.distance(ClientRecommender.Instance.mainDoc.vectorDoc, doc.vectorDoc, distance_metric);
-                doc.score = distance;
-            }
-        }
-        );
-        const doclist = Array.from(ClientRecommender.Instance.docVectors);
-        if (distance_metric === "euclidian") {
-            doclist.sort((a: RecommenderDocument, b: RecommenderDocument) => a.score - b.score);
-        }
-        else {
-            doclist.sort((a: RecommenderDocument, b: RecommenderDocument) => b.score - a.score);
-        }
-        return doclist;
-    }
-
-    /***
-     * Computes the mean of a set of vectors
-     */
-
-    public mean(paragraph: Set<number[]>) {
-        const n = 512;
-        const num_words = paragraph.size;
-        let meanVector = new Array<number>(n).fill(0); // mean vector
-        if (num_words > 0) { // check to see if paragraph actually was vectorized
-            paragraph.forEach((wordvec: number[]) => {
-                for (let i = 0; i < n; i++) {
-                    meanVector[i] += wordvec[i];
-                }
-            });
-            meanVector = meanVector.map(x => x / num_words);
-        }
-        return meanVector;
-    }
-
-    /***
-     * Processes sentence vector as Recommender Document, adds to Doc Set. 
-     */
-
-    public processVector(vector: number[], dataDoc: Doc, isMainDoc: boolean) {
-        if (vector.length > 0) {
-            const internalDoc: RecommenderDocument = { actualDoc: dataDoc, vectorDoc: vector, score: 0 };
-            ClientRecommender.Instance.addToDocSet(internalDoc, isMainDoc);
-        }
-    }
-
-    /***
-     * Adds to Doc set. Updates mainDoc (one clicked) if necessary.
-     */
-
-    private addToDocSet(internalDoc: RecommenderDocument, isMainDoc: boolean) {
-        if (ClientRecommender.Instance.docVectors) {
-            if (isMainDoc) ClientRecommender.Instance.mainDoc = internalDoc;
-            ClientRecommender.Instance.docVectors.add(internalDoc);
-        }
-    }
-
-    /***
-     * Generates tags for an image using Cognitive Services
-     */
-
-    generateMetadata = async (dataDoc: Doc, extDoc: Doc, threshold: Confidence = Confidence.Excellent) => {
-        const converter = (results: any) => {
-            const tagDoc = new Doc;
-            const tagsList = new List();
-            results.tags.map((tag: Tag) => {
-                tagsList.push(tag.name);
-                const sanitized = tag.name.replace(" ", "_");
-                tagDoc[sanitized] = ComputedField.MakeFunction(`(${tag.confidence} >= this.confidence) ? ${tag.confidence} : "${ComputedField.undefined}"`);
-            });
-            extDoc.generatedTags = tagsList;
-            tagDoc.title = "Generated Tags Doc";
-            tagDoc.confidence = threshold;
-            return tagDoc;
-        };
-        const url = this.url(dataDoc);
-        if (url) {
-            return CognitiveServices.Image.Appliers.ProcessImage(extDoc, ["generatedTagsDoc"], url, Service.ComputerVision, converter);
-        }
-    }
-
-    /***
-     * Gets URL of image
-     */
-
-    private url(dataDoc: Doc) {
-        const data = Cast(Doc.GetProto(dataDoc)[fieldkey], ImageField);
-        return data ? data.url.href : undefined;
-    }
-
-    /***
-     * Uses Cognitive Services to extract keywords from a document
-     */
-
-    public async extractText(dataDoc: Doc, extDoc: Doc, internal: boolean = true, api: string = "bing", isMainDoc: boolean = false, image: boolean = false) {
-        // STEP 1. Consolidate data of document. Depends on type of document. 
-        let data: string = "";
-        let taglist: FieldResult<List<string>> = undefined;
-        if (image) {
-            if (!extDoc.generatedTags) await this.generateMetadata(dataDoc, extDoc);  // TODO: Automatically generate tags. Need to ask Sam about this.
-            if (extDoc.generatedTags) {
-                taglist = Cast(extDoc.generatedTags, listSpec("string"));
-                taglist!.forEach(tag => {
-                    data += tag + ", ";
-                });
-            }
-        }
-        else {
-            const fielddata = Cast(dataDoc.data, RichTextField, null);
-            data = fielddata?.Text || "";
-        }
-
-        // STEP 2. Upon receiving response from Text Cognitive Services, do additional processing on keywords.
-        // Currently we are still using Cognitive Services for internal recommendations, but in the future this might not be necessary. 
-
-        const converter = async (results: any, data: string, isImage: boolean = false) => {
-            let keyterms = new List<string>(); // raw keywords
-            let kp_string: string = ""; // keywords*frequency concatenated into a string. input into TF
-            let highKP: string[] = [""]; // most frequent keyphrase
-            let high = 0;
-
-            if (isImage) { // no keyphrase processing necessary
-                kp_string = data;
-                if (taglist) {
-                    keyterms = taglist;
-                    highKP = [taglist[0]];
-                }
-            }
-            else { // text processing
-                results.documents.forEach((doc: any) => {
-                    const keyPhrases = doc.keyPhrases; // returned by Cognitive Services
-                    keyPhrases.map((kp: string) => {
-                        keyterms.push(kp);
-                        const frequency = this.countFrequencies(kp, data); // frequency of keyphrase in paragraph
-                        kp_string += kp + ", "; // ensures that if frequency is 0 for some reason kp is still added
-                        for (let i = 0; i < frequency - 1; i++) {
-                            kp_string += kp + ", "; // weights repeated keywords higher
-                        }
-                        // replaces highKP with new one
-                        if (frequency > high) {
-                            high = frequency;
-                            highKP = [kp];
-                        }
-                        // appends to current highKP phrase
-                        else if (frequency === high) {
-                            highKP.push(kp);
-                        }
-                    });
-                });
-            }
-            if (kp_string.length > 2) kp_string = kp_string.substring(0, kp_string.length - 2); // strips extra comma and space if there are a lot of keywords
-            console.log("kp_string: ", kp_string);
-
-            let ext_recs = "";
-            // Pushing keyword extraction to IBM for external recommendations. Should shift to internal eventually.
-            if (!internal) {
-                const parameters: any = {
-                    'language': 'en',
-                    'text': data,
-                    'features': {
-                        'keywords': {
-                            'sentiment': true,
-                            'emotion': true,
-                            'limit': 3
-                        }
-                    }
-                };
-                await Networking.PostToServer("/IBMAnalysis", parameters).then(response => {
-                    const sorted_keywords = response.result.keywords;
-                    if (sorted_keywords.length > 0) {
-                        console.log("IBM keyphrase", sorted_keywords[0]);
-                        highKP = [];
-                        for (let i = 0; i < 5; i++) {
-                            if (sorted_keywords[i]) {
-                                highKP.push(sorted_keywords[i].text);
-                            }
-                        }
-                        keyterms = new List<string>(highKP);
-                    }
-                });
-                //let kpqv = new KeyphraseQueryView({ keyphrases: ["hello"] });
-                ext_recs = await this.sendRequest([highKP[0]], api);
-            }
-
-            // keyterms: list for extDoc, kp_string: input to TF, ext_recs: {titles, urls} of retrieved results from highKP query
-            return { keyterms: keyterms, external_recommendations: ext_recs, kp_string: [kp_string] };
-        };
-
-        // STEP 3: Start recommendation pipeline. Branches off into internal and external in Cognitive Services 
-        if (data !== "") {
-            return CognitiveServices.Text.Appliers.analyzer(dataDoc, extDoc, ["key words"], data, converter, isMainDoc, internal);
-        }
-        return;
-    }
-
-    /**
-     * 
-     * Counts frequencies of keyphrase in paragraph. 
-     */
-
-    private countFrequencies(keyphrase: string, paragraph: string) {
-        const data = paragraph.split(/ |\n/); // splits by new lines and spaces
-        const kp_array = keyphrase.split(" ");
-        const num_keywords = kp_array.length;
-        const par_length = data.length;
-        let frequency = 0;
-        // slides keyphrase windows across paragraph and checks if it matches with corresponding paragraph slice
-        for (let i = 0; i <= par_length - num_keywords; i++) {
-            const window = data.slice(i, i + num_keywords);
-            if (JSON.stringify(window).toLowerCase() === JSON.stringify(kp_array).toLowerCase() || kp_array.every(val => window.includes(val))) {
-                frequency++;
-            }
-        }
-        return frequency;
-    }
-
-    /**
-     * 
-     * API for sending arXiv request.
-     */
-
-    private async sendRequest(keywords: string[], api: string) {
-        let query = "";
-        keywords.forEach((kp: string) => query += " " + kp);
-        if (api === "arxiv") {
-            return new Promise<any>(resolve => {
-                this.arxivrequest(query).then(resolve);
-            });
-        }
-        else if (api === "bing") {
-            return new Promise<any>(resolve => {
-                this.bingWebSearch(query).then(resolve);
-            });
-        }
-        else {
-            console.log("no api specified :(");
-        }
-
-    }
-
-    /**
-     * Request to Bing API. Most of code is in Cognitive Services. 
-     */
-
-    bingWebSearch = async (query: string) => {
-        const converter = async (results: any) => {
-            const title_vals: string[] = [];
-            const url_vals: string[] = [];
-            results.webPages.value.forEach((doc: any) => {
-                title_vals.push(doc.name);
-                url_vals.push(doc.url);
-            });
-            return { title_vals, url_vals };
-        };
-        return CognitiveServices.BingSearch.Appliers.analyzer(query, converter);
-    }
-
-    /**
-     * Actual request to the arXiv server for ML articles.
-     */
-
-    arxivrequest = async (query: string) => {
-        const xhttp = new XMLHttpRequest();
-        const serveraddress = "http://export.arxiv.org/api";
-        const maxresults = 5;
-        const endpoint = serveraddress + "/query?search_query=all:" + query + "&start=0&max_results=" + maxresults.toString();
-        const promisified = (resolve: any, reject: any) => {
-            xhttp.onreadystatechange = function () {
-                if (this.readyState === 4) {
-                    const result = xhttp.response;
-                    const xml = xhttp.responseXML;
-                    console.log("arXiv Result: ", xml);
-                    switch (this.status) {
-                        case 200:
-                            const title_vals: string[] = [];
-                            const url_vals: string[] = [];
-                            if (xml) {
-                                const titles = xml.getElementsByTagName("title");
-                                let counter = 1;
-                                if (titles && titles.length > 1) {
-                                    while (counter <= maxresults) {
-                                        const title = titles[counter].childNodes[0].nodeValue!;
-                                        title_vals.push(title);
-                                        counter++;
-                                    }
-                                }
-                                const ids = xml.getElementsByTagName("id");
-                                counter = 1;
-                                if (ids && ids.length > 1) {
-                                    while (counter <= maxresults) {
-                                        const url = ids[counter].childNodes[0].nodeValue!;
-                                        url_vals.push(url);
-                                        counter++;
-                                    }
-                                }
-                            }
-                            return resolve({ title_vals, url_vals });
-                        case 400:
-                        default:
-                            return reject(result);
-                    }
-                }
-            };
-            xhttp.open("GET", endpoint, true);
-            xhttp.send();
-        };
-        return new Promise<any>(promisified);
-    }
-
-    render() {
-        return (<div className="wrapper">
-        </div>);
-    }
-
-}
\ No newline at end of file
diff --git a/src/client/apis/GoogleAuthenticationManager.tsx b/src/client/apis/GoogleAuthenticationManager.tsx
index cda108058..855f48f7e 100644
--- a/src/client/apis/GoogleAuthenticationManager.tsx
+++ b/src/client/apis/GoogleAuthenticationManager.tsx
@@ -3,7 +3,7 @@ import { observer } from "mobx-react";
 import * as React from "react";
 import { Opt } from "../../fields/Doc";
 import { Networking } from "../Network";
-import { Scripting } from "../util/Scripting";
+import { ScriptingGlobals } from "../util/ScriptingGlobals";
 import { MainViewModal } from "../views/MainViewModal";
 import "./GoogleAuthenticationManager.scss";
 
@@ -165,4 +165,4 @@ export class GoogleAuthenticationManager extends React.Component<{}> {
 
 }
 
-Scripting.addGlobal("GoogleAuthenticationManager", GoogleAuthenticationManager);
\ No newline at end of file
+ScriptingGlobals.add("GoogleAuthenticationManager", GoogleAuthenticationManager);
\ No newline at end of file
diff --git a/src/client/cognitive_services/CognitiveServices.ts b/src/client/cognitive_services/CognitiveServices.ts
index 80961af14..2b2931a97 100644
--- a/src/client/cognitive_services/CognitiveServices.ts
+++ b/src/client/cognitive_services/CognitiveServices.ts
@@ -6,7 +6,6 @@ import { InkData } from "../../fields/InkField";
 import { UndoManager } from "../util/UndoManager";
 import requestPromise = require("request-promise");
 import { List } from "../../fields/List";
-import { ClientRecommender } from "../ClientRecommender";
 
 type APIManager<D> = { converter: BodyConverter<D>, requester: RequestExecutor };
 type RequestExecutor = (apiKey: string, body: string, service: Service) => Promise<string>;
@@ -379,7 +378,7 @@ export namespace CognitiveServices {
                         indices.forEach((ind: any) => {
                             vectorValues.push(wordvecs[ind]);
                         });
-                        ClientRecommender.Instance.processVector(vectorValues, dataDoc, mainDoc);
+                        //ClientRecommender.Instance.processVector(vectorValues, dataDoc, mainDoc);
                     } // adds document to internal doc set
                     else {
                         console.log("unsuccessful :( word(s) not in vocabulary");
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index df573a377..9bcd23aa0 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -23,7 +23,7 @@ import { DocumentManager } from "../util/DocumentManager";
 import { dropActionType } from "../util/DragManager";
 import { DirectoryImportBox } from "../util/Import & Export/DirectoryImportBox";
 import { LinkManager } from "../util/LinkManager";
-import { Scripting } from "../util/Scripting";
+import { ScriptingGlobals } from "../util/ScriptingGlobals";
 import { undoBatch, UndoManager } from "../util/UndoManager";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
 import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView";
@@ -61,7 +61,6 @@ import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo";
 import { DocumentType } from "./DocumentTypes";
 import { IconProp } from "@fortawesome/fontawesome-svg-core";
 import { MapBox } from "../views/nodes/MapBox/MapBox";
-const path = require('path');
 
 const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", ""));
 class EmptyBox {
@@ -180,7 +179,7 @@ export class DocumentOptions {
     layout?: string | Doc; // default layout string for a document
     contentPointerEvents?: string;  // pointer events allowed for content of a document view.  eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
     childLimitHeight?: number; // whether to limit the height of collection children.  0 - means  height can be no bigger than width
-    childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox or Buxton layout in tree view)
+    childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox layout in tree view)
     childLayoutString?: string; // template string for collection to use to render its children
     childDontRegisterViews?: boolean;
     childHideLinkButton?: boolean; // hide link buttons on all children
@@ -235,7 +234,7 @@ export class DocumentOptions {
     docColorBtn?: string;
     userColorBtn?: string;
     canClick?: string;
-    script?: string;
+    script?: ScriptField;
     numBtnType?: string;
     numBtnMax?: number;
     numBtnMin?: number;
@@ -361,31 +360,34 @@ export namespace Docs {
         const TemplateMap: TemplateMap = new Map([
             [DocumentType.RTF, {
                 layout: { view: FormattedTextBox, dataField: "text" },
-                options: { _height: 150, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any }
+                options: {
+                    _height: 150, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true,
+                    links: "@links(self)"
+                }
             }],
             [DocumentType.SEARCH, {
                 layout: { view: SearchBox, dataField: defaultDataKey },
-                options: { _width: 400, links: ComputedField.MakeFunction("links(self)") as any }
+                options: { _width: 400, links: "@links(self)" }
             }],
             [DocumentType.FILTER, {
                 layout: { view: FilterBox, dataField: defaultDataKey },
-                options: { _width: 400, links: ComputedField.MakeFunction("links(self)") as any }
+                options: { _width: 400, links: "@links(self)" }
             }],
             [DocumentType.COLOR, {
                 layout: { view: ColorBox, dataField: defaultDataKey },
-                options: { _nativeWidth: 220, _nativeHeight: 300, links: ComputedField.MakeFunction("links(self)") as any }
+                options: { _nativeWidth: 220, _nativeHeight: 300, links: "@links(self)" }
             }],
             [DocumentType.IMG, {
                 layout: { view: ImageBox, dataField: defaultDataKey },
-                options: { links: ComputedField.MakeFunction("links(self)") as any }
+                options: { links: "@links(self)" }
             }],
             [DocumentType.WEB, {
                 layout: { view: WebBox, dataField: defaultDataKey },
-                options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any }
+                options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: "@links(self)" }
             }],
             [DocumentType.COL, {
                 layout: { view: CollectionView, dataField: defaultDataKey },
-                options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1, links: ComputedField.MakeFunction("links(self)") as any }
+                options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1, links: "@links(self)" }
             }],
             [DocumentType.KVP, {
                 layout: { view: KeyValueBox, dataField: defaultDataKey },
@@ -393,19 +395,19 @@ export namespace Docs {
             }],
             [DocumentType.VID, {
                 layout: { view: VideoBox, dataField: defaultDataKey },
-                options: { _currentTimecode: 0, links: ComputedField.MakeFunction("links(self)") as any },
+                options: { _currentTimecode: 0, links: "@links(self)" },
             }],
             [DocumentType.AUDIO, {
                 layout: { view: AudioBox, dataField: defaultDataKey },
-                options: { _height: 100, backgroundColor: "lightGray", links: ComputedField.MakeFunction("links(self)") as any }
+                options: { _height: 100, backgroundColor: "lightGray", links: "@links(self)" }
             }],
             [DocumentType.PDF, {
                 layout: { view: PDFBox, dataField: defaultDataKey },
-                options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: ComputedField.MakeFunction("links(self)") as any }
+                options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: "@links(self)" }
             }],
             [DocumentType.MAP, {
                 layout: { view: MapBox, dataField: defaultDataKey },
-                options: { _height: 600, _width: 800, links: ComputedField.MakeFunction("links(self)") as any }
+                options: { _height: 600, _width: 800, links: "@links(self)" }
             }],
             [DocumentType.IMPORT, {
                 layout: { view: DirectoryImportBox, dataField: defaultDataKey },
@@ -416,7 +418,7 @@ export namespace Docs {
                 options: {
                     childDontRegisterViews: true, _isLinkButton: true, _height: 150, description: "", showCaption: "description",
                     backgroundColor: "lightblue", // lightblue is default color for linking dot and link documents text comment area
-                    links: ComputedField.MakeFunction("links(self)") as any,
+                    links: "@links(self)",
                     _removeDropProperties: new List(["_layerTags", "isLinkButton"]),
                 }
             }],
@@ -432,61 +434,61 @@ export namespace Docs {
             }],
             [DocumentType.SCRIPTING, {
                 layout: { view: ScriptingBox, dataField: defaultDataKey },
-                options: { links: ComputedField.MakeFunction("links(self)") as any }
+                options: { links: "@links(self)" }
             }],
             [DocumentType.YOUTUBE, {
                 layout: { view: YoutubeBox, dataField: defaultDataKey }
             }],
             [DocumentType.LABEL, {
                 layout: { view: LabelBox, dataField: defaultDataKey },
-                options: { links: ComputedField.MakeFunction("links(self)") as any }
+                options: { links: "@links(self)" }
             }],
             [DocumentType.EQUATION, {
                 layout: { view: EquationBox, dataField: defaultDataKey },
-                options: { links: ComputedField.MakeFunction("links(self)") as any, hideResizeHandles: true, hideDecorationTitle: true }
+                options: { links: "@links(self)", hideResizeHandles: true, hideDecorationTitle: true }
             }],
             [DocumentType.FUNCPLOT, {
                 layout: { view: FunctionPlotBox, dataField: defaultDataKey },
-                options: { links: ComputedField.MakeFunction("links(self)") as any }
+                options: { links: "@links(self)" }
             }],
             [DocumentType.BUTTON, {
                 layout: { view: LabelBox, dataField: "onClick" },
-                options: { links: ComputedField.MakeFunction("links(self)") as any }
+                options: { links: "@links(self)" }
             }],
             [DocumentType.SLIDER, {
                 layout: { view: SliderBox, dataField: defaultDataKey },
-                options: { links: ComputedField.MakeFunction("links(self)") as any }
+                options: { links: "@links(self)" }
             }],
             [DocumentType.PRES, {
                 layout: { view: PresBox, dataField: defaultDataKey },
-                options: { links: ComputedField.MakeFunction("links(self)") as any }
+                options: { links: "@links(self)" }
             }],
             [DocumentType.FONTICON, {
                 layout: { view: FontIconBox, dataField: defaultDataKey },
-                options: { hideLinkButton: true, _width: 40, _height: 40, borderRounding: "100%", links: ComputedField.MakeFunction("links(self)") as any },
+                options: { hideLinkButton: true, _width: 40, _height: 40, borderRounding: "100%", links: "@links(self)" },
             }],
             [DocumentType.WEBCAM, {
                 layout: { view: DashWebRTCVideo, dataField: defaultDataKey },
-                options: { links: ComputedField.MakeFunction("links(self)") as any }
+                options: { links: "@links(self)" }
             }],
             [DocumentType.PRESELEMENT, {
                 layout: { view: PresElementBox, dataField: defaultDataKey }
             }],
             [DocumentType.MARKER, {
                 layout: { view: CollectionView, dataField: defaultDataKey },
-                options: { links: ComputedField.MakeFunction("links(self)") as any, hideLinkButton: true }
+                options: { links: "@links(self)", hideLinkButton: true }
             }],
             [DocumentType.INK, { // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method
                 layout: { view: InkingStroke, dataField: defaultDataKey },
-                options: { links: ComputedField.MakeFunction("links(self)") as any }
+                options: { links: "@links(self)" }
             }],
             [DocumentType.SCREENSHOT, {
                 layout: { view: ScreenshotBox, dataField: defaultDataKey },
-                options: { links: ComputedField.MakeFunction("links(self)") as any }
+                options: { links: "@links(self)" }
             }],
             [DocumentType.COMPARISON, {
                 layout: { view: ComparisonBox, dataField: defaultDataKey },
-                options: { clipWidth: 50, backgroundColor: "gray", targetDropAction: "alias", links: ComputedField.MakeFunction("links(self)") as any }
+                options: { clipWidth: 50, backgroundColor: "gray", targetDropAction: "alias", links: "@links(self)" }
             }],
             [DocumentType.GROUPDB, {
                 data: new List<Doc>(),
@@ -495,7 +497,7 @@ export namespace Docs {
             }],
             [DocumentType.GROUP, {
                 layout: { view: EmptyBox, dataField: defaultDataKey },
-                options: { links: ComputedField.MakeFunction("links(self)") as any }
+                options: { links: "@links(self)" }
             }],
         ]);
 
@@ -592,6 +594,11 @@ export namespace Docs {
                 system: true, _layoutKey: "layout", title, type, baseProto: true, x: 0, y: 0, _width: 300, ...(template.options || {}),
                 layout: layout.view?.LayoutString(layout.dataField), data: template.data, layout_keyValue: KeyValueBox.LayoutString("")
             };
+            Object.entries(options).map(pair => {
+                if (typeof pair[1] === "string" && pair[1].startsWith("@")) {
+                    (options as any)[pair[0]] = ComputedField.MakeFunction(pair[1].substring(1));
+                }
+            });
             return Doc.assign(new Doc(prototypeId, true), options as any, undefined, true);
         }
     }
@@ -655,7 +662,7 @@ export namespace Docs {
 
         export function ImageDocument(url: string, options: DocumentOptions = {}) {
             const imgField = new ImageField(url);
-            return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: path.basename(url), ...options });
+            return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(url), ...options });
         }
 
         export function PresDocument(initial: List<Doc> = new List(), options: DocumentOptions = {}) {
@@ -758,7 +765,7 @@ export namespace Docs {
             I.data = new InkField(points);
             I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
             I["acl-Override"] = "None";
-            I.links = ComputedField.MakeFunction("links(self)") as any;
+            I.links = "@links(self)";
             I[Initializing] = false;
             return I;
         }
@@ -1443,13 +1450,13 @@ export namespace DocUtils {
         if (doc) {
             const proto = Doc.GetProto(doc);
             proto.text = result.rawText;
-            proto.fileUpload = basename(pathname).replace("upload_", "").replace(/\.[a-z0-9]*$/, "");
+            proto.fileUpload = pathname.replace(/.*\//, "").replace("upload_", "").replace(/\.[a-z0-9]*$/, "");
             if (Upload.isImageInformation(result)) {
                 const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim);
-                proto["data-nativeOrientation"] = result.exifData?.data?.image?.Orientation;
+                proto["data-nativeOrientation"] = result.exifData?.data?.image?.Orientation ?? ((StrCast((result.exifData?.data as any)?.Orientation).includes("Rotate 90")) ? 5 : undefined);
                 proto["data-nativeWidth"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim * result.nativeWidth / result.nativeHeight : maxNativeDim;
                 proto["data-nativeHeight"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
-                if (Number(result.exifData?.data?.image?.Orientation) >= 5) {
+                if (NumCast(proto["data-nativeOrientation"]) >= 5) {
                     proto["data-nativeHeight"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim * result.nativeWidth / result.nativeHeight : maxNativeDim;
                     proto["data-nativeWidth"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
                 }
@@ -1472,27 +1479,28 @@ export namespace DocUtils {
     export async function uploadYoutubeVideo(videoId: string, options: DocumentOptions) {
         const generatedDocuments: Doc[] = [];
         for (const { source: { name, type }, result } of await Networking.UploadYoutubeToServer(videoId)) {
-            processFileupload(generatedDocuments, name, type, result, options);
+            name && type && processFileupload(generatedDocuments, name, type, result, options);
         }
         return generatedDocuments;
     }
     export async function uploadFilesToDocs(files: File[], options: DocumentOptions) {
         const generatedDocuments: Doc[] = [];
-        for (const { source: { name, type }, result } of await Networking.UploadFilesToServer(files)) {
-            processFileupload(generatedDocuments, name, type, result, options);
+        const upfiles = await Networking.UploadFilesToServer(files);
+        for (const { source: { name, type }, result } of upfiles) {
+            name && type && processFileupload(generatedDocuments, name, type, result, options);
         }
         return generatedDocuments;
     }
 }
 
-Scripting.addGlobal("Docs", Docs);
-Scripting.addGlobal(function makeDelegate(proto: any) { const d = Docs.Create.DelegateDocument(proto, { title: "child of " + proto.title }); return d; });
-Scripting.addGlobal(function generateLinkTitle(self: Doc) {
+ScriptingGlobals.add("Docs", Docs);
+ScriptingGlobals.add(function makeDelegate(proto: any) { const d = Docs.Create.DelegateDocument(proto, { title: "child of " + proto.title }); return d; });
+ScriptingGlobals.add(function generateLinkTitle(self: Doc) {
     const anchor1title = self.anchor1 && self.anchor1 !== self ? Cast(self.anchor1, Doc, null).title : "<?>";
     const anchor2title = self.anchor2 && self.anchor2 !== self ? Cast(self.anchor2, Doc, null).title : "<?>";
     const relation = self.linkRelationship || "to";
     return `${anchor1title} (${relation}) ${anchor2title}`;
 });
-Scripting.addGlobal(function openTabAlias(tab: Doc) {
+ScriptingGlobals.add(function openTabAlias(tab: Doc) {
     CollectionDockingView.AddSplit(Doc.MakeAlias(tab), "right");
 });
\ No newline at end of file
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index a8b0da369..c0e3f66eb 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -7,7 +7,6 @@ import { List } from "../../fields/List";
 import { PrefetchProxy } from "../../fields/Proxy";
 import { RichTextField } from "../../fields/RichTextField";
 import { listSpec } from "../../fields/Schema";
-import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
 import { ComputedField, ScriptField } from "../../fields/ScriptField";
 import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types";
 import { nullAudio } from "../../fields/URLField";
@@ -16,9 +15,7 @@ import { Utils } from "../../Utils";
 import { DocServer } from "../DocServer";
 import { Docs, DocumentOptions, DocUtils } from "../documents/Documents";
 import { DocumentType } from "../documents/DocumentTypes";
-import { Networking } from "../Network";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
-import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView";
 import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
 import { TreeView } from "../views/collections/TreeView";
 import { Colors } from "../views/global/globalEnums";
@@ -32,7 +29,7 @@ import { DragManager } from "./DragManager";
 import { makeTemplate } from "./DropConverter";
 import { HistoryUtil } from "./History";
 import { LinkManager } from "./LinkManager";
-import { Scripting } from "./Scripting";
+import { ScriptingGlobals } from "./ScriptingGlobals";
 import { SearchUtil } from "./SearchUtil";
 import { SelectionManager } from "./SelectionManager";
 import { ColorScheme } from "./SettingsManager";
@@ -51,7 +48,6 @@ interface Button {
     numBtnMax?: number;
     switchToggle?: boolean;
     script?: string;
-    checkResult?: string;
     width?: number;
     list?: string[];
     ignoreClick?: boolean;
@@ -162,95 +158,41 @@ export class CurrentUserUtils {
             });
         }
 
-        if (doc["template-button-switch"] === undefined) {
-            const { FreeformDocument, MulticolumnDocument, TextDocument } = Docs.Create;
-
-            const yes = FreeformDocument([], { title: "yes", _height: 35, _width: 50, _dimUnit: DimUnit.Pixel, _dimMagnitude: 40, system: true });
-            const name = TextDocument("name", { title: "name", _height: 35, _width: 70, _dimMagnitude: 1, system: true });
-            const no = FreeformDocument([], { title: "no", _height: 100, _width: 100, system: true });
-            const labelTemplate = {
-                doc: {
-                    type: "doc", content: [{
-                        type: "paragraph",
-                        content: [{ type: "dashField", attrs: { fieldKey: "PARAMS", hideKey: true } }]
-                    }]
-                },
-                selection: { type: "text", anchor: 1, head: 1 },
-                storedMarks: []
-            };
-            Doc.GetProto(name).text = new RichTextField(JSON.stringify(labelTemplate), "PARAMS");
-            Doc.GetProto(yes).backgroundColor = ComputedField.MakeFunction("self[this.PARAMS] ? 'green':'red'");
-            // Doc.GetProto(no).backgroundColor = ComputedField.MakeFunction("!self[this.PARAMS] ? 'red':'white'");
-            // Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = true");
-            Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = !self[this.PARAMS]");
-            // Doc.GetProto(no).onClick = ScriptField.MakeScript("self[this.PARAMS] = false");
-            const box = MulticolumnDocument([/*no, */ yes, name], { title: "value", _width: 120, _height: 35, system: true });
-            box.isTemplateDoc = makeTemplate(box, true, "switch");
-
-            doc["template-button-switch"] = CurrentUserUtils.createToolButton({
-                onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
-                dragFactory: new PrefetchProxy(box) as any as Doc, title: "data switch", icon: "toggle-on", system: true,
-                btnType: ButtonType.ToolButton
-            });
-        }
-
-        if (doc["template-button-detail"] === undefined) {
-            const { TextDocument, MasonryDocument, CarouselDocument } = Docs.Create;
-
-            const openInTarget = ScriptField.MakeScript("openOnRight(self.doubleClickView)");
-            const carousel = CarouselDocument([], {
-                title: "data", _height: 350, _itemIndex: 0, "_carousel-caption-xMargin": 10, "_carousel-caption-yMargin": 10,
-                onChildDoubleClick: openInTarget, backgroundColor: "#9b9b9b3F", system: true
-            });
-
-            const details = TextDocument("", { title: "details", _height: 200, _autoHeight: true, system: true });
-            const short = TextDocument("", { title: "shortDescription", treeViewOpen: true, treeViewExpandedView: "layout", _height: 75, _autoHeight: true, system: true });
-            const long = TextDocument("", { title: "longDescription", treeViewOpen: false, treeViewExpandedView: "layout", _height: 150, _autoHeight: true, system: true });
-
-            const buxtonFieldKeys = ["year", "originalPrice", "degreesOfFreedom", "company", "attribute", "primaryKey", "secondaryKey", "dimensions"];
-            const detailedTemplate = {
-                doc: {
-                    type: "doc", content: buxtonFieldKeys.map(fieldKey => ({
-                        type: "paragraph",
-                        content: [{ type: "dashField", attrs: { fieldKey } }]
-                    }))
-                },
-                selection: { type: "text", anchor: 1, head: 1 },
-                storedMarks: []
-            };
-            details.text = new RichTextField(JSON.stringify(detailedTemplate), buxtonFieldKeys.join(" "));
-
-            const shared = { _autoHeight: true, _xMargin: 0 };
-            const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: "12px" };
-            const descriptionWrapperOpts = { title: "descriptions", _height: 300, _columnWidth: -1, treeViewHideTitle: true, _pivotField: "title", system: true };
-
-            const descriptionWrapper = MasonryDocument([details, short, long], { ...shared, ...descriptionWrapperOpts });
-            descriptionWrapper._columnHeaders = new List<SchemaHeaderField>([
-                new SchemaHeaderField("[A Short Description]", "dimgray", undefined, undefined, undefined, false),
-                new SchemaHeaderField("[Long Description]", "dimgray", undefined, undefined, undefined, true),
-                new SchemaHeaderField("[Details]", "dimgray", undefined, undefined, undefined, true),
-            ]);
-            const detailView = Docs.Create.StackingDocument([carousel, descriptionWrapper], { ...shared, ...detailViewOpts, _chromeHidden: true, system: true });
-            detailView.isTemplateDoc = makeTemplate(detailView);
-
-            details.title = "Details";
-            short.title = "A Short Description";
-            long.title = "Long Description";
-
-            doc["template-button-detail"] = CurrentUserUtils.createToolButton({
-                onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
-                dragFactory: new PrefetchProxy(detailView) as any as Doc,
-                title: "detailView",
-                icon: "window-maximize",
-                system: true,
-                btnType: ButtonType.ToolButton,
-            });
-        }
+        // if (doc["template-button-switch"] === undefined) {
+        //     const { FreeformDocument, MulticolumnDocument, TextDocument } = Docs.Create;
+
+        //     const yes = FreeformDocument([], { title: "yes", _height: 35, _width: 50, _dimUnit: DimUnit.Pixel, _dimMagnitude: 40, system: true });
+        //     const name = TextDocument("name", { title: "name", _height: 35, _width: 70, _dimMagnitude: 1, system: true });
+        //     const no = FreeformDocument([], { title: "no", _height: 100, _width: 100, system: true });
+        //     const labelTemplate = {
+        //         doc: {
+        //             type: "doc", content: [{
+        //                 type: "paragraph",
+        //                 content: [{ type: "dashField", attrs: { fieldKey: "PARAMS", hideKey: true } }]
+        //             }]
+        //         },
+        //         selection: { type: "text", anchor: 1, head: 1 },
+        //         storedMarks: []
+        //     };
+        //     Doc.GetProto(name).text = new RichTextField(JSON.stringify(labelTemplate), "PARAMS");
+        //     Doc.GetProto(yes).backgroundColor = ComputedField.MakeFunction("self[this.PARAMS] ? 'green':'red'");
+        //     // Doc.GetProto(no).backgroundColor = ComputedField.MakeFunction("!self[this.PARAMS] ? 'red':'white'");
+        //     // Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = true");
+        //     Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = !self[this.PARAMS]");
+        //     // Doc.GetProto(no).onClick = ScriptField.MakeScript("self[this.PARAMS] = false");
+        //     const box = MulticolumnDocument([/*no, */ yes, name], { title: "value", _width: 120, _height: 35, system: true });
+        //     box.isTemplateDoc = makeTemplate(box, true, "switch");
+
+        //     doc["template-button-switch"] = CurrentUserUtils.createToolButton({
+        //         onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
+        //         dragFactory: new PrefetchProxy(box) as any as Doc, title: "data switch", icon: "toggle-on", system: true,
+        //         btnType: ButtonType.ToolButton
+        //     });
+        // }
 
         const requiredTypes = [
             doc["template-button-slides"] as Doc,
             doc["template-mobile-button"] as Doc,
-            doc["template-button-detail"] as Doc,
             doc["template-button-link"] as Doc,
             //doc["template-button-switch"] as Doc]
         ];
@@ -538,11 +480,11 @@ export class CurrentUserUtils {
     // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
     static async setupCreatorButtons(doc: Doc) {
         let alreadyCreatedButtons: string[] = [];
-        const dragCreatorSet = await Cast(doc.myItemCreators, Doc, null);
+        const dragCreatorSet = Cast(doc.myItemCreators, Doc, null);
         if (dragCreatorSet) {
-            const dragCreators = await Cast(dragCreatorSet.data, listSpec(Doc));
+            const dragCreators = Cast(dragCreatorSet.data, listSpec(Doc));
             if (dragCreators) {
-                const dragDocs = await Promise.all(dragCreators);
+                const dragDocs = await Promise.all(Array.from(dragCreators));
                 alreadyCreatedButtons = dragDocs.map(d => StrCast(d.title));
             }
         }
@@ -735,12 +677,16 @@ export class CurrentUserUtils {
         ];
         return docProtoData.map(data => Docs.Create.FontIconDocument({
             _nativeWidth: 10, _nativeHeight: 10, _width: 10, _height: 10, title: data.title, icon: data.icon,
-            _dropAction: data.pointerDown ? "copy" : undefined, ignoreClick: data.ignoreClick,
+            _dropAction: data.pointerDown ? "copy" : undefined,
+            ignoreClick: data.ignoreClick,
             onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined,
             clipboard: data.clipboard,
-            onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined, onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined,
+            onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined,
+            onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined,
             backgroundColor: data.backgroundColor,
-            _removeDropProperties: new List<string>(["dropAction"]), dragFactory: data.dragFactory, system: true
+            _removeDropProperties: new List<string>(["dropAction"]),
+            dragFactory: data.dragFactory,
+            system: true
         }));
     }
 
@@ -987,37 +933,37 @@ export class CurrentUserUtils {
                     title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true,
                     list: ["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia",
                         "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"],
-                    script: 'setFont'
+                    script: 'setFont(value, _readOnly_)'
                 },
-                { title: "Font size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'setFontSize' },
-                { title: "Font color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script: 'setFontColor' },
-                { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold()', checkResult: 'toggleBold(true)' },
-                { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic()', checkResult: 'toggleItalic(true)' },
-                { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline()', checkResult: 'toggleUnderline(true)' },
-                { title: "Bullet List", toolTip: "Bullet", btnType: ButtonType.ToggleButton, icon: "list", click: 'setBulletList("bullet")', checkResult: 'setBulletList("bullet", true)' },
-                { title: "Number List", toolTip: "Number", btnType: ButtonType.ToggleButton, icon: "list-ol", click: 'setBulletList("decimal")', checkResult: 'setBulletList("decimal", true)' },
+                { title: "Font size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'setFontSize(value, _readOnly_)' },
+                { title: "Font color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script: 'setFontColor(value, _readOnly_)' },
+                { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold(_readOnly_)' },
+                { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic(_readOnly_)' },
+                { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline(_readOnly_)' },
+                { title: "Bullet List", toolTip: "Bullet", btnType: ButtonType.ToggleButton, icon: "list", click: 'setBulletList("bullet", _readOnly_)' },
+                { title: "Number List", toolTip: "Number", btnType: ButtonType.ToggleButton, icon: "list-ol", click: 'setBulletList("decimal", _readOnly_)' },
 
                 // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", click: 'toggleStrikethrough()'},
                 // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", click: 'toggleSuperscript()'},
                 // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", click: 'toggleSubscript()'},
-                { title: "Left align", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left")', checkResult: 'setAlignment("left", true)' },
-                { title: "Center align", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center")', checkResult: 'setAlignment("center", true)' },
-                { title: "Right align", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right")', checkResult: 'setAlignment("right", true)' },
+                { title: "Left align", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left", _readOnly_)' },
+                { title: "Center align", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center", _readOnly_)' },
+                { title: "Right align", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right", _readOnly_)' },
             ];
         return tools;
     }
 
     static inkTools(doc: Doc) {
         const tools: Button[] = [
-            { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("pen")', checkResult: 'setActiveInkTool("pen" , true)' },
-            { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", click: 'setActiveInkTool("eraser")', checkResult: 'setActiveInkTool("eraser" , true)' },
-            // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")', checkResult: 'setActiveInkTool("highlighter", true)' },
-            { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle")', checkResult: 'setActiveInkTool("circle" , true)' },
-            // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")', checkResult: 'setActiveInkTool("square" , true)' },
-            { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", click: 'setActiveInkTool("line")', checkResult: 'setActiveInkTool("line" , true)' },
-            { title: "Fill color", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setFillColor" },
-            { title: "Stroke width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, script: 'setStrokeWidth' },
-            { title: "Stroke color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, script: 'setStrokeColor' },
+            { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("pen", _readOnly_)' },
+            { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", click: 'setActiveInkTool("eraser", _readOnly_)' },
+            // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")' },
+            { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle", _readOnly_)' },
+            // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")' },
+            { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", click: 'setActiveInkTool("line", _readOnly_)' },
+            { title: "Fill color", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setFillColor(value, _readOnly_)" },
+            { title: "Stroke width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, script: 'setStrokeWidth(value, _readOnly_)' },
+            { title: "Stroke color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, script: 'setStrokeColor(value, _readOnly_)' },
         ];
         return tools;
     }
@@ -1031,8 +977,7 @@ export class CurrentUserUtils {
                     btnType: ButtonType.ToggleButton,
                     buttonText: "Show Preview",
                     icon: "eye",
-                    click: 'toggleSchemaPreview()',
-                    checkResult: 'toggleSchemaPreview(true)'
+                    click: 'toggleSchemaPreview(_readOnly_)',
                 },
             ];
         return tools;
@@ -1041,10 +986,10 @@ export class CurrentUserUtils {
     static webTools(doc: Doc) {
         const tools: Button[] =
             [
-                { title: "Back", toolTip: "Go back", btnType: ButtonType.ClickButton, icon: "arrow-left", click: 'webBack()' },
-                { title: "Forward", toolTip: "Go forward", btnType: ButtonType.ClickButton, icon: "arrow-right", click: 'webForward()' },
+                { title: "Back", toolTip: "Go back", btnType: ButtonType.ClickButton, icon: "arrow-left", click: 'webBack(_readOnly_)' },
+                { title: "Forward", toolTip: "Go forward", btnType: ButtonType.ClickButton, icon: "arrow-right", click: 'webForward(_readOnly_)' },
                 //{ title: "Reload", toolTip: "Reload webpage", btnType: ButtonType.ClickButton, icon: "redo-alt", click: 'webReload()' },
-                { title: "URL", toolTip: "URL", width: 250, btnType: ButtonType.EditableText, icon: "lock", ignoreClick: true, script: 'webSetURL' },
+                { title: "URL", toolTip: "URL", width: 250, btnType: ButtonType.EditableText, icon: "lock", ignoreClick: true, script: 'webSetURL(value, _readOnly_)' },
             ];
 
         return tools;
@@ -1059,17 +1004,17 @@ export class CurrentUserUtils {
                 CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel,
                 CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map,
                 CollectionViewType.Grid],
-                script: 'setView',
+                script: 'setView(value, _readOnly_)',
             }, // Always show
             {
                 title: "Background Color", toolTip: "Background Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip",
-                script: "setBackgroundColor", hidden: 'selectedDocumentType()'
+                script: "setBackgroundColor(value, _readOnly_)", hidden: 'selectedDocumentType()'
             }, // Only when a document is selected
             {
                 title: "Header Color", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading",
-                script: "setHeaderColor", hidden: 'selectedDocumentType()',
+                script: "setHeaderColor(value, _readOnly_)", hidden: 'selectedDocumentType()',
             }, // Only when a document is selected
-            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()', checkResult: 'toggleOverlay(true)', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform
+            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay(_readOnly_)', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform
             // { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden: 'selectedDocumentType()' }, // Only when a document is selected 
             { title: "Text", type: "textTools", subMenu: true, expanded: 'selectedDocumentType("rtf")' }, // Always available
             { title: "Ink", type: "inkTools", subMenu: true, expanded: 'selectedDocumentType("ink")' }, // Always available
@@ -1083,7 +1028,7 @@ export class CurrentUserUtils {
         if (doc.contextMenuBtns === undefined) {
             const docList: Doc[] = [];
 
-            (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, type, btnType, click, script, subMenu, hidden, expanded, checkResult }) => {
+            (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, type, btnType, click, script, subMenu, hidden, expanded }) => {
                 const menuDocList: Doc[] = [];
                 if (subMenu) {
                     // default is textTools
@@ -1105,7 +1050,8 @@ export class CurrentUserUtils {
                             tools = CurrentUserUtils.textTools(doc);
                             break;
                     }
-                    tools.map(({ title, toolTip, icon, btnType, numBtnType, numBtnMax, numBtnMin, click, script, width, list, ignoreClick, switchToggle, checkResult }) => {
+                    tools.map(({ title, toolTip, icon, btnType, numBtnType, numBtnMax, numBtnMin, click, script, width, list, ignoreClick, switchToggle }) => {
+                        const computed = click ? ComputedField.MakeFunction(click) as any : "transparent";
                         menuDocList.push(Docs.Create.FontIconDocument({
                             _nativeWidth: width ? width : 25,
                             _nativeHeight: 25,
@@ -1116,7 +1062,7 @@ export class CurrentUserUtils {
                             numBtnType,
                             numBtnMin,
                             numBtnMax,
-                            script,
+                            script: script ? ScriptField.MakeScript(script, { value: "any" }) : undefined,
                             btnType: btnType,
                             btnList: new List<string>(list),
                             ignoreClick: ignoreClick,
@@ -1128,10 +1074,10 @@ export class CurrentUserUtils {
                             title,
                             switchToggle,
                             color: Colors.WHITE,
-                            backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult) as any : "transparent",
+                            backgroundColor: computed,
                             _dropAction: "alias",
                             _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
-                            onClick: click ? ScriptField.MakeScript(click, { doc: Doc.name }) : undefined
+                            onClick: click ? ScriptField.MakeScript(click) : undefined
                         }));
                     });
                     docList.push(CurrentUserUtils.linearButtonList({
@@ -1141,7 +1087,7 @@ export class CurrentUserUtils {
                         linearViewExpandable: true,
                         icon: title,
                         _height: 30,
-                        backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult) as any : "transparent",
+                        // backgroundColor: hidden ? ComputedField.MakeFunction(hidden, {  }, { _readOnly_: true }) as any : "transparent",
                         linearViewIsExpanded: expanded ? !(ComputedField.MakeFunction(expanded) as any) : undefined,
                         hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined,
                     }, menuDocList));
@@ -1153,7 +1099,7 @@ export class CurrentUserUtils {
                         _height: 25,
                         icon,
                         toolTip,
-                        script,
+                        script: script ? ScriptField.MakeScript(script, { value: "any" }) : undefined,
                         btnType,
                         btnList: new List<string>(list),
                         ignoreClick,
@@ -1164,11 +1110,11 @@ export class CurrentUserUtils {
                         dontUndo: true,
                         title,
                         color: Colors.WHITE,
-                        backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult) as any : "transparent",
+                        // backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult, {}, {_readOnly_:true}) as any : "transparent",
                         _dropAction: "alias",
                         hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined,
                         _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
-                        onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }) : undefined
+                        onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }, { _readOnly_: false }) : undefined
                     }));
                 }
             });
@@ -1383,7 +1329,7 @@ export class CurrentUserUtils {
             if (response) {
                 const result: { id: string, email: string, cacheDocumentIds: string } = JSON.parse(response);
                 Doc.CurrentUserEmail = result.email;
-                resolvedPorts = JSON.parse(await Networking.FetchFromServer("/resolvedPorts"));
+                resolvedPorts = JSON.parse(await (await fetch("/resolvedPorts")).text());
                 DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, result.email);
                 result.cacheDocumentIds && (await DocServer.GetRefFields(result.cacheDocumentIds.split(";")));
                 return result;
@@ -1566,7 +1512,7 @@ export class CurrentUserUtils {
     @computed public static get SelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; }
 }
 
-Scripting.addGlobal(function openDragFactory(dragFactory: Doc) {
+ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) {
     const copy = Doc.copyDragFactory(dragFactory);
     if (copy) {
         CollectionDockingView.AddSplit(copy, "right");
@@ -1574,27 +1520,27 @@ Scripting.addGlobal(function openDragFactory(dragFactory: Doc) {
         view && SelectionManager.SelectView(view, false);
     }
 });
-Scripting.addGlobal(function MySharedDocs() { return Doc.SharingDoc(); },
+ScriptingGlobals.add(function MySharedDocs() { return Doc.SharingDoc(); },
     "document containing all shared Docs");
-Scripting.addGlobal(function IsNoviceMode() { return Doc.UserDoc().noviceMode; },
+ScriptingGlobals.add(function IsNoviceMode() { return Doc.UserDoc().noviceMode; },
     "is Dash in novice mode");
-Scripting.addGlobal(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); },
+ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); },
     "creates a snapshot copy of a dashboard");
-Scripting.addGlobal(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); },
+ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); },
     "creates a new dashboard when called");
-Scripting.addGlobal(function createNewPresentation() { return MainView.Instance.createNewPresentation(); },
+ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance.createNewPresentation(); },
     "creates a new presentation when called");
-Scripting.addGlobal(function createNewFolder() { return MainView.Instance.createNewFolder(); },
+ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); },
     "creates a new folder in myFiles when called");
-Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); },
+ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); },
     "returns all the links to the document or its annotations", "(doc: any)");
-Scripting.addGlobal(function importDocument() { return CurrentUserUtils.importDocument(); },
+ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); },
     "imports files from device directly into the import sidebar");
-Scripting.addGlobal(function shareDashboard(dashboard: Doc) {
+ScriptingGlobals.add(function shareDashboard(dashboard: Doc) {
     SharingManager.Instance.open(undefined, dashboard);
 },
     "opens sharing dialog for Dashboard");
-Scripting.addGlobal(async function removeDashboard(dashboard: Doc) {
+ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) {
     const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data);
     if (dashboards && dashboards.length > 1) {
         if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboards.find(doc => doc !== dashboard)!);
@@ -1602,7 +1548,7 @@ Scripting.addGlobal(async function removeDashboard(dashboard: Doc) {
     }
 },
     "Remove Dashboard from Dashboards");
-Scripting.addGlobal(async function addToDashboards(dashboard: Doc) {
+ScriptingGlobals.add(async function addToDashboards(dashboard: Doc) {
     const dashboardAlias = Doc.MakeAlias(dashboard);
 
     const allDocs = await DocListCastAsync(dashboard[DataSym]["data-all"]);
@@ -1626,7 +1572,7 @@ Scripting.addGlobal(async function addToDashboards(dashboard: Doc) {
 /**
  * Dynamically computes which docs should be rendered in the off-screen tabs tree of a dashboard.
  */
-Scripting.addGlobal(function dynamicOffScreenDocs(dashboard: Doc) {
+ScriptingGlobals.add(function dynamicOffScreenDocs(dashboard: Doc) {
     if (dashboard[DataSym] instanceof Doc) {
         const allDocs = DocListCast(dashboard["data-all"]);
         const onScreenTab = DocListCast(dashboard.data)[0];
@@ -1638,7 +1584,7 @@ Scripting.addGlobal(function dynamicOffScreenDocs(dashboard: Doc) {
     }
     return [];
 });
-Scripting.addGlobal(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkParent?: boolean) {
+ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkParent?: boolean) {
     let selected = SelectionManager.Docs().length ? SelectionManager.Docs()[0] : undefined;
     if (selected && checkParent) {
         const parentDoc: Doc = Cast(selected.context, Doc, null);
@@ -1649,11 +1595,11 @@ Scripting.addGlobal(function selectedDocumentType(docType?: DocumentType, colTyp
     else if (selected && !colType && !docType) return false;
     else return true;
 });
-Scripting.addGlobal(function makeTopLevelFolder() {
+ScriptingGlobals.add(function makeTopLevelFolder() {
     const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true });
     TreeView._editTitleOnLoad = { id: folder[Id], parent: undefined };
     return Doc.AddDocToList(Doc.UserDoc().myFilesystem as Doc, "data", folder);
 });
-Scripting.addGlobal(function toggleComicMode() {
+ScriptingGlobals.add(function toggleComicMode() {
     Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic";
 });
\ No newline at end of file
diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts
index a93b2f573..a6dcda4bc 100644
--- a/src/client/util/DictationManager.ts
+++ b/src/client/util/DictationManager.ts
@@ -1,4 +1,6 @@
 import * as interpreter from "words-to-numbers";
+// @ts-ignore  bcz: how are you supposed to include these definitions since dom-speech-recognition isn't a module?
+import type { } from "@types/dom-speech-recognition";
 import { Doc, Opt } from "../../fields/Doc";
 import { List } from "../../fields/List";
 import { RichTextField } from "../../fields/RichTextField";
@@ -13,6 +15,7 @@ import { DocumentView } from "../views/nodes/DocumentView";
 import { SelectionManager } from "./SelectionManager";
 import { UndoManager } from "./UndoManager";
 
+
 /**
  * This namespace provides a singleton instance of a manager that
  * handles the listening and text-conversion of user speech.
@@ -102,17 +105,17 @@ export namespace DictationManager {
             try {
                 results = await (pendingListen = listenImpl(options));
                 pendingListen = undefined;
-                if (results) {
-                    Utils.CopyText(results);
-                    if (overlay) {
-                        DictationOverlay.Instance.isListening = false;
-                        const execute = options?.tryExecute;
-                        DictationOverlay.Instance.dictatedPhrase = execute ? results.toLowerCase() : results;
-                        DictationOverlay.Instance.dictationSuccess = execute ? await DictationManager.Commands.execute(results) : true;
-                    }
-                    options?.tryExecute && await DictationManager.Commands.execute(results);
-                }
-            } catch (e) {
+                // if (results) {
+                //     Utils.CopyText(results);
+                //     if (overlay) {
+                //         DictationOverlay.Instance.isListening = false;
+                //         const execute = options?.tryExecute;
+                //         DictationOverlay.Instance.dictatedPhrase = execute ? results.toLowerCase() : results;
+                //         DictationOverlay.Instance.dictationSuccess = execute ? await DictationManager.Commands.execute(results) : true;
+                //     }
+                //     options?.tryExecute && await DictationManager.Commands.execute(results);
+                // }
+            } catch (e: any) {
                 console.log(e);
                 if (overlay) {
                     DictationOverlay.Instance.isListening = false;
@@ -188,7 +191,7 @@ export namespace DictationManager {
                         current && sessionResults.push(current);
                         sessionResults.length && resolve(sessionResults.join(inter || interSession));
                     } else {
-                        resolve(current);
+                        resolve(current || "");
                     }
                     current = undefined;
                     sessionResults = [];
@@ -222,168 +225,168 @@ export namespace DictationManager {
 
     }
 
-    export namespace Commands {
-
-        export const dictationFadeDuration = 2000;
-
-        export type IndependentAction = (target: DocumentView) => any | Promise<any>;
-        export type IndependentEntry = { action: IndependentAction, restrictTo?: DocumentType[] };
-
-        export type DependentAction = (target: DocumentView, matches: RegExpExecArray) => any | Promise<any>;
-        export type DependentEntry = { expression: RegExp, action: DependentAction, restrictTo?: DocumentType[] };
-
-        export const RegisterIndependent = (key: string, value: IndependentEntry) => Independent.set(key, value);
-        export const RegisterDependent = (entry: DependentEntry) => Dependent.push(entry);
-
-        export const execute = async (phrase: string) => {
-            return UndoManager.RunInBatch(async () => {
-                const targets = SelectionManager.Views();
-                if (!targets || !targets.length) {
-                    return;
-                }
-
-                phrase = phrase.toLowerCase();
-                const entry = Independent.get(phrase);
-
-                if (entry) {
-                    let success = false;
-                    const restrictTo = entry.restrictTo;
-                    for (const target of targets) {
-                        if (!restrictTo || validate(target, restrictTo)) {
-                            await entry.action(target);
-                            success = true;
-                        }
-                    }
-                    return success;
-                }
-
-                for (const entry of Dependent) {
-                    const regex = entry.expression;
-                    const matches = regex.exec(phrase);
-                    regex.lastIndex = 0;
-                    if (matches !== null) {
-                        let success = false;
-                        const restrictTo = entry.restrictTo;
-                        for (const target of targets) {
-                            if (!restrictTo || validate(target, restrictTo)) {
-                                await entry.action(target, matches);
-                                success = true;
-                            }
-                        }
-                        return success;
-                    }
-                }
-
-                return false;
-            }, "Execute Command");
-        };
-
-        const ConstructorMap = new Map<DocumentType, CastCtor>([
-            [DocumentType.COL, listSpec(Doc)],
-            [DocumentType.AUDIO, AudioField],
-            [DocumentType.IMG, ImageField],
-            [DocumentType.IMPORT, listSpec(Doc)],
-            [DocumentType.RTF, "string"]
-        ]);
-
-        const tryCast = (view: DocumentView, type: DocumentType) => {
-            const ctor = ConstructorMap.get(type);
-            if (!ctor) {
-                return false;
-            }
-            return Cast(Doc.GetProto(view.props.Document).data, ctor) !== undefined;
-        };
-
-        const validate = (target: DocumentView, types: DocumentType[]) => {
-            for (const type of types) {
-                if (tryCast(target, type)) {
-                    return true;
-                }
-            }
-            return false;
-        };
-
-        const interpretNumber = (number: string) => {
-            const initial = parseInt(number);
-            if (!isNaN(initial)) {
-                return initial;
-            }
-            const converted = interpreter.wordsToNumbers(number, { fuzzy: true });
-            if (converted === null) {
-                return NaN;
-            }
-            return typeof converted === "string" ? parseInt(converted) : converted;
-        };
-
-        const Independent = new Map<string, IndependentEntry>([
-
-            ["clear", {
-                action: (target: DocumentView) => Doc.GetProto(target.props.Document).data = new List(),
-                restrictTo: [DocumentType.COL]
-            }],
-
-            ["open fields", {
-                action: (target: DocumentView) => {
-                    const kvp = Docs.Create.KVPDocument(target.props.Document, { _width: 300, _height: 300 });
-                    target.props.addDocTab(kvp, "add:right");
-                }
-            }],
-
-            ["new outline", {
-                action: (target: DocumentView) => {
-                    const newBox = Docs.Create.TextDocument("", { _width: 400, _height: 200, title: "My Outline", _autoHeight: true });
-                    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":"ordered_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, "add:right");
-                }
-            }]
-
-        ]);
-
-        const Dependent = new Array<DependentEntry>(
-
-            {
-                expression: /create (\w+) documents of type (image|nested collection)/g,
-                action: (target: DocumentView, matches: RegExpExecArray) => {
-                    const count = interpretNumber(matches[1]);
-                    const what = matches[2];
-                    const dataDoc = Doc.GetProto(target.props.Document);
-                    const fieldKey = "data";
-                    if (isNaN(count)) {
-                        return;
-                    }
-                    for (let i = 0; i < count; i++) {
-                        let created: Doc | undefined;
-                        switch (what) {
-                            case "image":
-                                created = Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg");
-                                break;
-                            case "nested collection":
-                                created = Docs.Create.FreeformDocument([], {});
-                                break;
-                        }
-                        created && Doc.AddDocToList(dataDoc, fieldKey, created);
-                    }
-                },
-                restrictTo: [DocumentType.COL]
-            },
-
-            {
-                expression: /view as (freeform|stacking|masonry|schema|tree)/g,
-                action: (target: DocumentView, matches: RegExpExecArray) => {
-                    const mode = matches[1];
-                    mode && (target.props.Document._viewType = mode);
-                },
-                restrictTo: [DocumentType.COL]
-            }
-
-        );
-
-    }
+    // export namespace Commands {
+
+    //     export const dictationFadeDuration = 2000;
+
+    //     export type IndependentAction = (target: DocumentView) => any | Promise<any>;
+    //     export type IndependentEntry = { action: IndependentAction, restrictTo?: DocumentType[] };
+
+    //     export type DependentAction = (target: DocumentView, matches: RegExpExecArray) => any | Promise<any>;
+    //     export type DependentEntry = { expression: RegExp, action: DependentAction, restrictTo?: DocumentType[] };
+
+    //     export const RegisterIndependent = (key: string, value: IndependentEntry) => Independent.set(key, value);
+    //     export const RegisterDependent = (entry: DependentEntry) => Dependent.push(entry);
+
+    //     export const execute = async (phrase: string) => {
+    //         return UndoManager.RunInBatch(async () => {
+    //             const targets = SelectionManager.Views();
+    //             if (!targets || !targets.length) {
+    //                 return;
+    //             }
+
+    //             phrase = phrase.toLowerCase();
+    //             const entry = Independent.get(phrase);
+
+    //             if (entry) {
+    //                 let success = false;
+    //                 const restrictTo = entry.restrictTo;
+    //                 for (const target of targets) {
+    //                     if (!restrictTo || validate(target, restrictTo)) {
+    //                         await entry.action(target);
+    //                         success = true;
+    //                     }
+    //                 }
+    //                 return success;
+    //             }
+
+    //             for (const entry of Dependent) {
+    //                 const regex = entry.expression;
+    //                 const matches = regex.exec(phrase);
+    //                 regex.lastIndex = 0;
+    //                 if (matches !== null) {
+    //                     let success = false;
+    //                     const restrictTo = entry.restrictTo;
+    //                     for (const target of targets) {
+    //                         if (!restrictTo || validate(target, restrictTo)) {
+    //                             await entry.action(target, matches);
+    //                             success = true;
+    //                         }
+    //                     }
+    //                     return success;
+    //                 }
+    //             }
+
+    //             return false;
+    //         }, "Execute Command");
+    //     };
+
+    //     const ConstructorMap = new Map<DocumentType, CastCtor>([
+    //         [DocumentType.COL, listSpec(Doc)],
+    //         [DocumentType.AUDIO, AudioField],
+    //         [DocumentType.IMG, ImageField],
+    //         [DocumentType.IMPORT, listSpec(Doc)],
+    //         [DocumentType.RTF, "string"]
+    //     ]);
+
+    //     const tryCast = (view: DocumentView, type: DocumentType) => {
+    //         const ctor = ConstructorMap.get(type);
+    //         if (!ctor) {
+    //             return false;
+    //         }
+    //         return Cast(Doc.GetProto(view.props.Document).data, ctor) !== undefined;
+    //     };
+
+    //     const validate = (target: DocumentView, types: DocumentType[]) => {
+    //         for (const type of types) {
+    //             if (tryCast(target, type)) {
+    //                 return true;
+    //             }
+    //         }
+    //         return false;
+    //     };
+
+    //     const interpretNumber = (number: string) => {
+    //         const initial = parseInt(number);
+    //         if (!isNaN(initial)) {
+    //             return initial;
+    //         }
+    //         const converted = interpreter.wordsToNumbers(number, { fuzzy: true });
+    //         if (converted === null) {
+    //             return NaN;
+    //         }
+    //         return typeof converted === "string" ? parseInt(converted) : converted;
+    //     };
+
+    //     const Independent = new Map<string, IndependentEntry>([
+
+    //         ["clear", {
+    //             action: (target: DocumentView) => Doc.GetProto(target.props.Document).data = new List(),
+    //             restrictTo: [DocumentType.COL]
+    //         }],
+
+    //         ["open fields", {
+    //             action: (target: DocumentView) => {
+    //                 const kvp = Docs.Create.KVPDocument(target.props.Document, { _width: 300, _height: 300 });
+    //                 target.props.addDocTab(kvp, "add:right");
+    //             }
+    //         }],
+
+    //         ["new outline", {
+    //             action: (target: DocumentView) => {
+    //                 const newBox = Docs.Create.TextDocument("", { _width: 400, _height: 200, title: "My Outline", _autoHeight: true });
+    //                 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":"ordered_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, "add:right");
+    //             }
+    //         }]
+
+    //     ]);
+
+    //     const Dependent = new Array<DependentEntry>(
+
+    //         {
+    //             expression: /create (\w+) documents of type (image|nested collection)/g,
+    //             action: (target: DocumentView, matches: RegExpExecArray) => {
+    //                 const count = interpretNumber(matches[1]);
+    //                 const what = matches[2];
+    //                 const dataDoc = Doc.GetProto(target.props.Document);
+    //                 const fieldKey = "data";
+    //                 if (isNaN(count)) {
+    //                     return;
+    //                 }
+    //                 for (let i = 0; i < count; i++) {
+    //                     let created: Doc | undefined;
+    //                     switch (what) {
+    //                         case "image":
+    //                             created = Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg");
+    //                             break;
+    //                         case "nested collection":
+    //                             created = Docs.Create.FreeformDocument([], {});
+    //                             break;
+    //                     }
+    //                     created && Doc.AddDocToList(dataDoc, fieldKey, created);
+    //                 }
+    //             },
+    //             restrictTo: [DocumentType.COL]
+    //         },
+
+    //         {
+    //             expression: /view as (freeform|stacking|masonry|schema|tree)/g,
+    //             action: (target: DocumentView, matches: RegExpExecArray) => {
+    //                 const mode = matches[1];
+    //                 mode && (target.props.Document._viewType = mode);
+    //             },
+    //             restrictTo: [DocumentType.COL]
+    //         }
+
+    //     );
+
+    // }
 
 }
\ No newline at end of file
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 66b6a1e44..0a00ab6e0 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -9,7 +9,7 @@ import { CollectionView } from '../views/collections/CollectionView';
 import { LightboxView } from '../views/LightboxView';
 import { DocumentView, ViewAdjustment } from '../views/nodes/DocumentView';
 import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox';
-import { Scripting } from './Scripting';
+import { ScriptingGlobals } from './ScriptingGlobals';
 import { SelectionManager } from './SelectionManager';
 
 export class DocumentManager {
@@ -198,7 +198,7 @@ export class DocumentManager {
                 originalTarget, willZoom, scale: presZoom, afterFocus: (didFocus: boolean) =>
                     new Promise<ViewAdjustment>(res => {
                         focusAndFinish(didFocus);
-                        res();
+                        res(ViewAdjustment.doNothing);
                     })
             });
         } else {
@@ -227,7 +227,7 @@ export class DocumentManager {
                                     willZoom, afterFocus: (didFocus: boolean) =>
                                         new Promise<ViewAdjustment>(res => {
                                             !noSelect && focusAndFinish(didFocus);
-                                            res();
+                                            res(ViewAdjustment.doNothing);
                                         })
                                 }); // focus on the target in the context
                             } else if (delay > 1500) {
@@ -252,7 +252,7 @@ export class DocumentManager {
     }
 
 }
-Scripting.addGlobal(function DocFocusOrOpen(doc: any) {
+ScriptingGlobals.add(function DocFocusOrOpen(doc: any) {
     const dv = DocumentManager.Instance.getDocumentView(doc);
     if (dv && dv.props.Document === doc) {
         dv.props.focus(doc, { willZoom: true });
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index ae3fa3170..c9c499fff 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -499,7 +499,7 @@ export namespace DragManager {
                                 screenX: e.screenX,
                                 screenY: e.screenY,
                                 detail: e.detail,
-                                view: e.view ? e.view : new Window,
+                                view: e.view ? e.view : new Window as any,
                                 nativeEvent: new DragEvent("dashDragAutoScroll"),
                                 currentTarget: target,
                                 target: target,
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 32817eefd..082b6d8bd 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -7,7 +7,7 @@ import { Docs } from "../documents/Documents";
 import { ScriptField, ComputedField } from "../../fields/ScriptField";
 import { RichTextField } from "../../fields/RichTextField";
 import { ImageField } from "../../fields/URLField";
-import { Scripting } from "./Scripting";
+import { ScriptingGlobals } from "./ScriptingGlobals";
 import { listSpec } from "../../fields/Schema";
 
 // 
@@ -81,5 +81,5 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) {
         data.droppedDocuments[i] = dbox;
     });
 }
-Scripting.addGlobal(function convertToButtons(dragData: any) { convertDropDataToButtons(dragData as DragManager.DocumentDragData); },
+ScriptingGlobals.add(function convertToButtons(dragData: any) { convertDropDataToButtons(dragData as DragManager.DocumentDragData); },
     "converts the dropped data to buttons", "(dragData: any)");
\ No newline at end of file
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index cbe36b401..e6f75a7db 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -103,8 +103,7 @@ export namespace HistoryUtil {
                     return undefined;
                 }
                 const parser = requiredFields[required];
-                let value = opts[required];
-                value = parse(parser, value);
+                const value = parse(parser, opts[required]);
                 if (value !== null && value !== undefined) {
                     current[required] = value;
                 }
@@ -114,8 +113,7 @@ export namespace HistoryUtil {
                     continue;
                 }
                 const parser = optionalFields[opt];
-                let value = opts[opt];
-                value = parse(parser, value);
+                const value = parse(parser, opts[opt]);
                 if (value !== undefined) {
                     current[opt] = value;
                 }
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 925b74efa..39e9251a5 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx	
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx	
@@ -1,9 +1,8 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { BatchedArray } from "array-batcher";
-import "fs";
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import * as path from 'path';
+import { extname } from "path";
 import Measure, { ContentRect } from "react-measure";
 import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc";
 import { Id } from "../../../fields/FieldSymbols";
@@ -86,7 +85,7 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> {
         for (let i = 0; i < files.length; i++) {
             const file = files.item(i);
             if (file && !unsupported.includes(file.type)) {
-                const ext = path.extname(file.name).toLowerCase();
+                const ext = extname(file.name).toLowerCase();
                 if (AcceptableMedia.imageFormats.includes(ext)) {
                     validated.push(file);
                 }
@@ -120,7 +119,7 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> {
             }
             const { accessPaths, exifData } = result;
             const path = Utils.prepend(accessPaths.agnostic.client);
-            const document = await DocUtils.DocumentFromType(type, path, { _width: 300, title: name });
+            const document = type && await DocUtils.DocumentFromType(type, path, { _width: 300 });
             const { data, error } = exifData;
             if (document) {
                 Doc.GetProto(document).exif = error || Doc.Get.FromJson({ data });
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 62b13e2c6..df2c02a8d 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -194,7 +194,7 @@ export class LinkManager {
                             afterFocus: (didFocus: boolean) => {
                                 finished?.();
                                 res(ViewAdjustment.resetView);
-                                return new Promise<ViewAdjustment>(res2 => res2());
+                                return new Promise<ViewAdjustment>(res2 => res2(ViewAdjustment.doNothing));
                             }
                         });
                     } else {
diff --git a/src/client/util/ScriptManager.ts b/src/client/util/ScriptManager.ts
index 94806a7ba..42a6493ea 100644
--- a/src/client/util/ScriptManager.ts
+++ b/src/client/util/ScriptManager.ts
@@ -1,9 +1,9 @@
 import { Doc, DocListCast } from "../../fields/Doc";
 import { List } from "../../fields/List";
-import { Scripting } from "./Scripting";
-import { StrCast, Cast } from "../../fields/Types";
 import { listSpec } from "../../fields/Schema";
+import { Cast, StrCast } from "../../fields/Types";
 import { Docs } from "../documents/Documents";
+import { ScriptingGlobals } from "./ScriptingGlobals";
 
 export class ScriptManager {
 
@@ -44,7 +44,7 @@ export class ScriptManager {
 
     public deleteScript(scriptDoc: Doc): boolean {
         if (scriptDoc.name) {
-            Scripting.removeGlobal(StrCast(scriptDoc.name));
+            ScriptingGlobals.removeGlobal(StrCast(scriptDoc.name));
         }
         const scriptList = this.getAllScripts();
         const index = scriptList.indexOf(scriptDoc);
@@ -60,7 +60,7 @@ export class ScriptManager {
 
     public static addScriptToGlobals(scriptDoc: Doc): void {
 
-        Scripting.removeGlobal(StrCast(scriptDoc.name));
+        ScriptingGlobals.removeGlobal(StrCast(scriptDoc.name));
 
         const params = Cast(scriptDoc["data-params"], listSpec("string"), []);
         const paramNames = params.reduce((o: string, p: string) => {
@@ -86,9 +86,9 @@ export class ScriptManager {
         });
 
         if (parameters === "(") {
-            Scripting.addGlobal(f, StrCast(scriptDoc.description));
+            ScriptingGlobals.add(f, StrCast(scriptDoc.description));
         } else {
-            Scripting.addGlobal(f, StrCast(scriptDoc.description), parameters);
+            ScriptingGlobals.add(f, StrCast(scriptDoc.description), parameters);
         }
     }
 }
\ No newline at end of file
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 40b94024e..3b0a47b54 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -1,16 +1,15 @@
-import * as ts from "typescript";
-export { ts };
-
 // export const ts = (window as any).ts;
-
 // // @ts-ignore
 // import * as typescriptlib from '!!raw-loader!../../../node_modules/typescript/lib/lib.d.ts'
 // // @ts-ignore
 // import * as typescriptes5 from '!!raw-loader!../../../node_modules/typescript/lib/lib.es5.d.ts'
-
 // @ts-ignore
 import * as typescriptlib from '!!raw-loader!./type_decls.d';
-import { Doc, Field } from '../../fields/Doc';
+import * as ts from "typescript";
+import { Doc, Field } from "../../fields/Doc";
+import { scriptingGlobals, ScriptingGlobals } from "./ScriptingGlobals";
+export { ts };
+
 
 export interface ScriptSuccess {
     success: true;
@@ -47,98 +46,6 @@ export function isCompileError(toBeDetermined: CompileResult): toBeDetermined is
     return false;
 }
 
-export namespace Scripting {
-    export function addGlobal(global: { name: string }): void;
-    export function addGlobal(name: string, global: any): void;
-
-    export function addGlobal(global: { name: string }, decription?: string, params?: string): void;
-
-    export function addGlobal(first: any, second?: any, third?: string) {
-        let n: any;
-        let obj: any;
-
-        if (second !== undefined) {
-            if (typeof first === "string") {
-                n = first;
-                obj = second;
-            } else {
-                obj = first;
-                n = first.name;
-                _scriptingDescriptions[n] = second;
-                if (third !== undefined) {
-                    _scriptingParams[n] = third;
-                }
-            }
-        } else if (first && typeof first.name === "string") {
-            n = first.name;
-            obj = first;
-        } else {
-            throw new Error("Must either register an object with a name, or give a name and an object");
-        }
-        if (n === undefined || n === "undefined") {
-            return false;
-        } else if (_scriptingGlobals.hasOwnProperty(n)) {
-            throw new Error(`Global with name ${n} is already registered, choose another name`);
-        }
-        _scriptingGlobals[n] = obj;
-    }
-
-    export function makeMutableGlobalsCopy(globals?: { [name: string]: any }) {
-        return { ..._scriptingGlobals, ...(globals || {}) };
-    }
-
-    export function setScriptingGlobals(globals: { [key: string]: any }) {
-        scriptingGlobals = globals;
-    }
-
-    export function removeGlobal(name: string) {
-        if (getGlobals().includes(name)) {
-            delete _scriptingGlobals[name];
-            if (_scriptingDescriptions[name]) {
-                delete _scriptingDescriptions[name];
-            }
-            if (_scriptingParams[name]) {
-                delete _scriptingParams[name];
-            }
-            return true;
-        }
-        return false;
-    }
-
-    export function resetScriptingGlobals() {
-        scriptingGlobals = _scriptingGlobals;
-    }
-
-    // const types = Object.keys(ts.SyntaxKind).map(kind => ts.SyntaxKind[kind]);
-    export function printNodeType(node: any, indentation = "") {
-        console.log(indentation + ts.SyntaxKind[node.kind]);
-    }
-
-    export function getGlobals() {
-        return Object.keys(_scriptingGlobals);
-    }
-
-    export function getGlobalObj() {
-        return _scriptingGlobals;
-    }
-
-    export function getDescriptions() {
-        return _scriptingDescriptions;
-    }
-
-    export function getParameters() {
-        return _scriptingParams;
-    }
-}
-
-export function scriptingGlobal(constructor: { new(...args: any[]): any }) {
-    Scripting.addGlobal(constructor);
-}
-
-export const _scriptingGlobals: { [name: string]: any } = {};
-let scriptingGlobals: { [name: string]: any } = _scriptingGlobals;
-const _scriptingDescriptions: { [name: string]: any } = {};
-const _scriptingParams: { [name: string]: any } = {};
 
 function Run(script: string | undefined, customParams: string[], diagnostics: any[], originalScript: string, options: ScriptOptions): CompileResult {
     const errors = diagnostics.filter(diag => diag.category === ts.DiagnosticCategory.Error);
@@ -273,7 +180,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
     if (options.params && !options.params.this) options.params.this = Doc.name;
     if (options.params && !options.params.self) options.params.self = Doc.name;
     if (options.globals) {
-        Scripting.setScriptingGlobals(options.globals);
+        ScriptingGlobals.setScriptingGlobals(options.globals);
     }
     const host = new ScriptingCompilerHost;
     if (options.traverser) {
@@ -331,9 +238,9 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
     const result = Run(outputText, paramNames, diagnostics, script, options);
 
     if (options.globals) {
-        Scripting.resetScriptingGlobals();
+        ScriptingGlobals.resetScriptingGlobals();
     }
     return result;
 }
 
-Scripting.addGlobal(CompileScript);
\ No newline at end of file
+ScriptingGlobals.add(CompileScript);
diff --git a/src/client/util/ScriptingGlobals.ts b/src/client/util/ScriptingGlobals.ts
new file mode 100644
index 000000000..f151acd81
--- /dev/null
+++ b/src/client/util/ScriptingGlobals.ts
@@ -0,0 +1,81 @@
+
+import * as ts from "typescript";
+export { ts };
+
+export namespace ScriptingGlobals {
+    export function add(global: { name: string }): void;
+    export function add(name: string, global: any): void;
+    export function add(global: { name: string }, decription?: string, params?: string): void;
+    export function add(first: any, second?: any, third?: string) {
+        let n: any;
+        let obj: any;
+
+        if (second !== undefined) {
+            if (typeof first === "string") {
+                n = first;
+                obj = second;
+            } else {
+                obj = first;
+                n = first.name;
+                _scriptingDescriptions[n] = second;
+                if (third !== undefined) {
+                    _scriptingParams[n] = third;
+                }
+            }
+        } else if (first && typeof first.name === "string") {
+            n = first.name;
+            obj = first;
+        } else {
+            throw new Error("Must either register an object with a name, or give a name and an object");
+        }
+        if (n === undefined || n === "undefined") {
+            return false;
+        } else if (_scriptingGlobals.hasOwnProperty(n)) {
+            throw new Error(`Global with name ${n} is already registered, choose another name`);
+        }
+        _scriptingGlobals[n] = obj;
+    }
+    export function makeMutableGlobalsCopy(globals?: { [name: string]: any }) {
+        return { ..._scriptingGlobals, ...(globals || {}) };
+    }
+
+    export function setScriptingGlobals(globals: { [key: string]: any }) {
+        scriptingGlobals = globals;
+    }
+
+    export function removeGlobal(name: string) {
+        if (getGlobals().includes(name)) {
+            delete _scriptingGlobals[name];
+            if (_scriptingDescriptions[name]) {
+                delete _scriptingDescriptions[name];
+            }
+            if (_scriptingParams[name]) {
+                delete _scriptingParams[name];
+            }
+            return true;
+        }
+        return false;
+    }
+
+    export function resetScriptingGlobals() { scriptingGlobals = _scriptingGlobals; }
+
+    // const types = Object.keys(ts.SyntaxKind).map(kind => ts.SyntaxKind[kind]);
+    export function printNodeType(node: any, indentation = "") { console.log(indentation + ts.SyntaxKind[node.kind]); }
+
+    export function getGlobals() { return Object.keys(_scriptingGlobals); }
+
+    export function getGlobalObj() { return _scriptingGlobals; }
+
+    export function getDescriptions() { return _scriptingDescriptions; }
+
+    export function getParameters() { return _scriptingParams; }
+}
+
+export function scriptingGlobal(constructor: { new(...args: any[]): any }) {
+    ScriptingGlobals.add(constructor);
+}
+
+const _scriptingGlobals: { [name: string]: any } = {};
+export let scriptingGlobals: { [name: string]: any } = _scriptingGlobals;
+const _scriptingDescriptions: { [name: string]: any } = {};
+const _scriptingParams: { [name: string]: any } = {};
\ No newline at end of file
diff --git a/src/client/util/UndoManager.ts b/src/client/util/UndoManager.ts
index 44e44d335..d1f1a0099 100644
--- a/src/client/util/UndoManager.ts
+++ b/src/client/util/UndoManager.ts
@@ -1,5 +1,4 @@
 import { observable, action, runInAction } from "mobx";
-import 'source-map-support/register';
 import { Without } from "../../Utils";
 
 function getBatchName(target: any, key: string | symbol): string {
@@ -107,7 +106,7 @@ export namespace UndoManager {
     }
     export function FilterBatches(fieldTypes: string[]) {
         const fieldCounts: { [key: string]: number } = {};
-        const lastStack = UndoManager.undoStack.lastElement();
+        const lastStack = UndoManager.undoStack.slice(-1)[0];//.lastElement();
         if (lastStack) {
             lastStack.forEach(ev => fieldTypes.includes(ev.prop) && (fieldCounts[ev.prop] = (fieldCounts[ev.prop] || 0) + 1));
             const fieldCount2: { [key: string]: number } = {};
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index b9772fd57..2e6ea1faa 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -20,10 +20,10 @@ export interface DocComponentProps {
     LayoutTemplate?: () => Opt<Doc>;
     LayoutTemplateString?: string;
 }
-export function DocComponent<P extends DocComponentProps, T>(schemaCtor: (doc: Doc) => T) {
+export function DocComponent<P extends DocComponentProps>() {
     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 Document() { return this.props.Document; }
         // This is the "The Document" -- it encapsulates, data, layout, and any templates
         @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; }
         // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info
@@ -48,7 +48,7 @@ interface ViewBoxBaseProps {
     renderDepth: number;
     rootSelected: (outsideReaction?: boolean) => boolean;
 }
-export function ViewBoxBaseComponent<P extends ViewBoxBaseProps, T>(schemaCtor: (doc: Doc) => T) {
+export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() {
     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); }
@@ -92,17 +92,17 @@ export interface ViewBoxAnnotatableProps {
     renderDepth: number;
     isAnnotationOverlay?: boolean;
 }
-export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps, T>(schemaCtor: (doc: Doc) => T) {
+export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>() {
     class Component extends Touchable<P> {
         @observable _annotationKeySuffix = () => "annotations";
 
         @observable _isAnyChildContentActive = 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); }
+        @computed get Document() { return this.props.Document; }
         // This is the "The Document" -- it encapsulates, data, layout, and any templates
         @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; }
         // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info
-        @computed get layoutDoc() { return schemaCtor(Doc.Layout(this.props.Document)); }
+        @computed get layoutDoc() { return Doc.Layout(this.props.Document); }
         // This is the data part of a document -- ie, the data that is constant across all views of the document
         @computed get dataDoc() { return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym]; }
 
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index cb95a1237..657d92b8a 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -22,13 +22,13 @@ import { CollectionDockingView } from './collections/CollectionDockingView';
 import { DocumentButtonBar } from './DocumentButtonBar';
 import './DocumentDecorations.scss';
 import { KeyManager } from './GlobalKeyHandler';
+import { InkingStroke } from './InkingStroke';
 import { InkStrokeProperties } from './InkStrokeProperties';
 import { LightboxView } from './LightboxView';
 import { DocumentView } from "./nodes/DocumentView";
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import React = require("react");
-import { InkingStroke } from './InkingStroke';
-import e = require('express');
+import { CollectionFreeFormView } from './collections/collectionFreeForm';
 
 @observer
 export class DocumentDecorations extends React.Component<{ PanelWidth: number, PanelHeight: number, boundsLeft: number, boundsTop: number }, { value: string }> {
@@ -148,7 +148,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                 pageY: e.pageY,
                 preventDefault: emptyFunction,
                 button: 0
-            }, [SelectionManager.Views().lastElement().rootDoc]);
+            }, [SelectionManager.Views().slice(-1)[0].rootDoc]);
             return true;
         }, emptyFunction, this.onMaximizeClick, false, false);
     }
@@ -233,7 +233,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         this._resizeUndo = UndoManager.StartBatch("DocDecs resize");
         this._snapX = e.pageX;
         this._snapY = e.pageY;
-        SelectionManager.Views().forEach(docView => this._dragHeights.set(docView.layoutDoc, { start: NumCast(docView.rootDoc._height), lowest: NumCast(docView.rootDoc._height) }));
+        const ffviewSet = new Set<CollectionFreeFormView>();
+        SelectionManager.Views().forEach(docView => {
+            const ffview = docView.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+            ffview && ffviewSet.add(ffview);
+            this._dragHeights.set(docView.layoutDoc, { start: NumCast(docView.rootDoc._height), lowest: NumCast(docView.rootDoc._height) });
+        });
+        Array.from(ffviewSet).map(ffview => ffview.setupDragLines(false));
     }
 
     onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
@@ -395,10 +401,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         this._inkDragDocs.map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] }))
             .forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => {
                 Doc.GetProto(doc).data = new InkField(inkPts.map(ipt =>  // (new x — oldx) + newWidth * (oldxpoint /oldWidth)
-                    ({
-                        X: (NumCast(doc.x) - x) + NumCast(doc.width) * ipt.X / width,
-                        Y: (NumCast(doc.y) - y) + NumCast(doc.height) * ipt.Y / height
-                    })));
+                ({
+                    X: (NumCast(doc.x) - x) + NumCast(doc.width) * ipt.X / width,
+                    Y: (NumCast(doc.y) - y) + NumCast(doc.height) * ipt.Y / height
+                })));
                 Doc.SetNativeWidth(doc, undefined);
                 Doc.SetNativeHeight(doc, undefined);
             });
@@ -424,7 +430,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
 
     render() {
         const bounds = this.Bounds;
-        const seldoc = SelectionManager.Views().lastElement();
+        const seldoc = SelectionManager.Views().slice(-1)[0];
         if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
             return (null);
         }
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 04abdbf37..50dca0a99 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -13,7 +13,7 @@ import { CognitiveServices } from "../cognitive_services/CognitiveServices";
 import { DocUtils } from "../documents/Documents";
 import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { InteractionUtils } from "../util/InteractionUtils";
-import { Scripting } from "../util/Scripting";
+import { ScriptingGlobals } from "../util/ScriptingGlobals";
 import { SelectionManager } from "../util/SelectionManager";
 import { Transform } from "../util/Transform";
 import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu";
@@ -943,11 +943,11 @@ export enum ToolglassTools {
     None = "none",
 }
 
-Scripting.addGlobal("GestureOverlay", GestureOverlay);
-Scripting.addGlobal(function setToolglass(tool: any) {
+ScriptingGlobals.add("GestureOverlay", GestureOverlay);
+ScriptingGlobals.add(function setToolglass(tool: any) {
     runInAction(() => GestureOverlay.Instance.Tool = tool);
 });
-Scripting.addGlobal(function setPen(width: any, color: any, fill: any, arrowStart: any, arrowEnd: any, dash: any) {
+ScriptingGlobals.add(function setPen(width: any, color: any, fill: any, arrowStart: any, arrowEnd: any, dash: any) {
     runInAction(() => {
         GestureOverlay.Instance.SavedColor = ActiveInkColor();
         SetActiveInkColor(color);
@@ -959,12 +959,12 @@ Scripting.addGlobal(function setPen(width: any, color: any, fill: any, arrowStar
         SetActiveDash(dash);
     });
 });
-Scripting.addGlobal(function resetPen() {
+ScriptingGlobals.add(function resetPen() {
     runInAction(() => {
         SetActiveInkColor(GestureOverlay.Instance.SavedColor ?? "rgb(0, 0, 0)");
         SetActiveInkWidth(GestureOverlay.Instance.SavedWidth?.toString() ?? "2");
     });
 }, "resets the pen tool");
-Scripting.addGlobal(function createText(text: any, x: any, y: any) {
+ScriptingGlobals.add(function createText(text: any, x: any, y: any) {
     GestureOverlay.Instance.dispatchGesture("text", [{ X: x, Y: y }], text);
 }, "creates a text document with inputted text and coordinates", "(text: any, x: any, y: any)");
diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx
index 24f796105..a7f511ab4 100644
--- a/src/client/views/InkControlPtHandles.tsx
+++ b/src/client/views/InkControlPtHandles.tsx
@@ -51,6 +51,7 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
             const handleIndexB = (order === 3 ? controlIndex + 2 : controlIndex + 1) % this.props.inkCtrlPoints.length;
             const brokenIndices = Cast(this.props.inkDoc.brokenInkIndices, listSpec("number"));
             const wasSelected = InkStrokeProperties.Instance._currentPoint === controlIndex;
+            if (!wasSelected) InkStrokeProperties.Instance._currentPoint = -1;
             const origInk = this.props.inkCtrlPoints.slice();
             setupMoveUpEvents(this, e,
                 action((e: PointerEvent, down: number[], delta: number[]) => {
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 9dbd97c16..40fe6aa68 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -24,9 +24,7 @@ import React = require("react");
 import { action, IReactionDisposer, observable, reaction } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, WidthSym } from "../../fields/Doc";
-import { documentSchema } from "../../fields/documentSchemas";
 import { InkData, InkField, InkTool } from "../../fields/InkField";
-import { makeInterface } from "../../fields/Schema";
 import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types";
 import { TraceMobx } from "../../fields/util";
 import { OmitKeys, returnFalse, setupMoveUpEvents } from "../../Utils";
@@ -46,11 +44,8 @@ import { FieldView, FieldViewProps } from "./nodes/FieldView";
 import { FormattedTextBox } from "./nodes/formattedText/FormattedTextBox";
 import Color = require("color");
 
-type InkDocument = makeInterface<[typeof documentSchema]>;
-const InkDocument = makeInterface(documentSchema);
-
 @observer
-export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocument>(InkDocument) {
+export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
     static readonly MaskDim = 50000; // choose a really big number to make sure mask fits over container (which in theory can be arbitrarily big)
     public static LayoutString(fieldStr: string) { return FieldView.LayoutString(InkingStroke, fieldStr); }
     public static IsClosed(inkData: InkData) {
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 7553c8118..8560ccb29 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -1,13 +1,15 @@
-import { MainView } from "./MainView";
-import { Docs } from "../documents/Documents";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import * as ReactDOM from 'react-dom';
+// if ((module as any).hot) {
+//     (module as any).hot.accept();
+// }
+
 import * as React from 'react';
-import { DocServer } from "../DocServer";
+import * as ReactDOM from 'react-dom';
 import { AssignAllExtensions } from "../../extensions/General/Extensions";
-import { Networking } from "../Network";
-import { CollectionView } from "./collections/CollectionView";
+import { Docs } from "../documents/Documents";
+import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { LinkManager } from "../util/LinkManager";
+import { CollectionView } from "./collections/CollectionView";
+import { MainView } from "./MainView";
 
 AssignAllExtensions();
 
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index fc1c694a2..8c0795881 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -5,7 +5,6 @@ import * as fa from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, configure, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
-import "normalize.css";
 import * as React from 'react';
 import * as ReactDOM from 'react-dom';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
@@ -24,7 +23,7 @@ import { DocumentManager } from '../util/DocumentManager';
 import { GroupManager } from '../util/GroupManager';
 import { HistoryUtil } from '../util/History';
 import { Hypothesis } from '../util/HypothesisUtils';
-import { Scripting } from '../util/Scripting';
+import { ScriptingGlobals } from '../util/ScriptingGlobals';
 import { SelectionManager } from '../util/SelectionManager';
 import { ColorScheme, SettingsManager } from '../util/SettingsManager';
 import { SharingManager } from '../util/SharingManager';
@@ -45,7 +44,6 @@ import { GestureOverlay } from './GestureOverlay';
 import { DASHBOARD_SELECTOR_HEIGHT, LEFT_MENU_WIDTH } from './global/globalCssVariables.scss';
 import { Colors } from './global/globalEnums';
 import { KeyManager } from './GlobalKeyHandler';
-import { InkStrokeProperties } from './InkStrokeProperties';
 import { LightboxView } from './LightboxView';
 import { LinkMenu } from './linking/LinkMenu';
 import "./MainView.scss";
@@ -165,28 +163,28 @@ export class MainView extends React.Component {
             }
         }
 
-        library.add(fa.faEdit, fa.faTrash, fa.faTrashAlt, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faCalendar,
-            fa.faSquare, far.faSquare as any, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faMapMarker, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock,
-            fa.faLock, fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointLeft, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard,
-            fa.faQuestion, fa.faTasks, fa.faPalette, fa.faAngleLeft, fa.faAngleRight, fa.faBell, fa.faCamera, fa.faExpand, fa.faCaretDown, fa.faCaretLeft, fa.faCaretRight,
-            fa.faCaretSquareDown, fa.faCaretSquareRight, fa.faArrowsAltH, fa.faPlus, fa.faMinus, fa.faTerminal, fa.faToggleOn, fa.faFile, fa.faLocationArrow,
-            fa.faSearch, fa.faFileDownload, fa.faFileUpload, fa.faStop, fa.faCalculator, fa.faWindowMaximize, fa.faAddressCard, fa.faQuestionCircle, fa.faArrowLeft,
-            fa.faArrowRight, fa.faArrowDown, fa.faArrowUp, fa.faBolt, fa.faBullseye, fa.faCaretUp, fa.faCat, fa.faCheck, fa.faChevronRight, fa.faChevronLeft, fa.faChevronDown, fa.faChevronUp,
-            fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt,
-            fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer,
-            fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV,
-            fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes,
-            fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
-            fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
-            fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any,
-            fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle as any,
-            fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer as any, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp,
-            fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
-            fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo,
-            fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
-            fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
-            fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines,
-            fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faSearchPlus, fa.faVolumeUp, fa.faVolumeMute, fa.faMapMarkerAlt);
+        library.add(...[fa.faEdit, fa.faTrash, fa.faTrashAlt, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faCalendar,
+        fa.faSquare, far.faSquare as any, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faMapMarker, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock,
+        fa.faLock, fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointLeft, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard,
+        fa.faQuestion, fa.faTasks, fa.faPalette, fa.faAngleLeft, fa.faAngleRight, fa.faBell, fa.faCamera, fa.faExpand, fa.faCaretDown, fa.faCaretLeft, fa.faCaretRight,
+        fa.faCaretSquareDown, fa.faCaretSquareRight, fa.faArrowsAltH, fa.faPlus, fa.faMinus, fa.faTerminal, fa.faToggleOn, fa.faFile, fa.faLocationArrow,
+        fa.faSearch, fa.faFileDownload, fa.faFileUpload, fa.faStop, fa.faCalculator, fa.faWindowMaximize, fa.faAddressCard, fa.faQuestionCircle, fa.faArrowLeft,
+        fa.faArrowRight, fa.faArrowDown, fa.faArrowUp, fa.faBolt, fa.faBullseye, fa.faCaretUp, fa.faCat, fa.faCheck, fa.faChevronRight, fa.faChevronLeft, fa.faChevronDown, fa.faChevronUp,
+        fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt,
+        fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer,
+        fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV,
+        fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes,
+        fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
+        fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
+        fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any,
+        fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle as any,
+        fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer as any, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp,
+        fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
+        fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo,
+        fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
+        fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
+        fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines,
+        fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt]);
         this.initAuthenticationRouters();
     }
 
@@ -696,4 +694,4 @@ export class MainView extends React.Component {
     }
 }
 
-Scripting.addGlobal(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); });
\ No newline at end of file
+ScriptingGlobals.add(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); });
\ No newline at end of file
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 7cf388872..0f51cf9b2 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -5,17 +5,17 @@ import ReactLoading from 'react-loading';
 import { Doc } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { Cast, NumCast } from "../../fields/Types";
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils, emptyPath } from "../../Utils";
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, setupMoveUpEvents, Utils } from "../../Utils";
+import { DocUtils } from "../documents/Documents";
 import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { DragManager } from "../util/DragManager";
-import { Scripting } from "../util/Scripting";
+import { ScriptingGlobals } from "../util/ScriptingGlobals";
 import { Transform } from "../util/Transform";
 import { CollectionFreeFormLinksView } from "./collections/collectionFreeForm/CollectionFreeFormLinksView";
 import { DocumentView } from "./nodes/DocumentView";
 import './OverlayView.scss';
 import { ScriptingRepl } from './ScriptingRepl';
 import { DefaultStyleProvider } from "./StyleProvider";
-import { DocUtils } from "../documents/Documents";
 
 export type OverlayDisposer = () => void;
 
@@ -226,6 +226,6 @@ export class OverlayView extends React.Component {
     }
 }
 // bcz: ugh ... want to be able to pass ScriptingRepl as tag argument, but that doesn't seem to work.. runtime error
-Scripting.addGlobal(function addOverlayWindow(type: string, options: OverlayElementOptions) {
+ScriptingGlobals.add(function addOverlayWindow(type: string, options: OverlayElementOptions) {
     OverlayView.Instance.addWindow(<ScriptingRepl />, options);
 });
\ No newline at end of file
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index f9dab9f82..dd6448654 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -18,6 +18,10 @@ import React = require("react");
 import { Colors } from "./global/globalEnums";
 import { CollectionFreeFormView } from "./collections/collectionFreeForm";
 import { DocumentManager } from "../util/DocumentManager";
+import { pasteImageBitmap } from "./nodes/WebBoxRenderer";
+import { VideoBox } from "./nodes/VideoBox";
+import { Id } from "../../fields/FieldSymbols";
+import { ImageField } from "../../fields/URLField";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -92,6 +96,20 @@ export class PropertiesButtons extends React.Component<{}, {}> {
     @computed get groupButton() {
         return this.propertyToggleBtn("Group", "isGroup", on => `Display collection as a Group`, on => "object-group", (dv, doc) => { doc.isGroup = !doc.isGroup; doc.forceActive = doc.isGroup; });
     }
+    @computed get freezeThumb() {
+        return this.propertyToggleBtn("Freeze\Thumb", "_thumb-frozen", on => `${on ? "Freeze" : "Unfreeze"} thumbnail`, on => "arrows-alt-h", (dv, doc) => {
+            if (doc["thumb-frozen"]) doc["thumb-frozen"] = undefined;
+            else {
+                document.body.focus(); // so that we can access the clipboard without an error
+                setTimeout(() =>
+                    pasteImageBitmap((data: any, error: any) => {
+                        error && console.log(error);
+                        data && VideoBox.convertDataUri(data, doc[Id] + "-thumb-frozen", true).then(
+                            returnedfilename => doc["thumb-frozen"] = new ImageField(returnedfilename));
+                    }));
+            }
+        });
+    }
     @computed get snapButton() {
         return this.propertyToggleBtn("Snap\xA0Lines", "showSnapLines", on => `Display snapping lines when objects are dragged`, on => "border-all", undefined, true);
     }
@@ -222,6 +240,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                 {toggle(this.dictationButton, { display: isNovice ? "none" : "" })}
                 {toggle(this.onClickButton)}
                 {toggle(this.fitWidthButton)}
+                {toggle(this.freezeThumb)}
                 {toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? "none" : "" })}
                 {toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? "none" : "" })}
                 {toggle(this.maskButton, { display: !isInk ? "none" : "" })}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index f53944805..47a5cd07e 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -33,6 +33,7 @@ import { PropertiesDocContextSelector } from "./PropertiesDocContextSelector";
 import "./PropertiesView.scss";
 import { DefaultStyleProvider } from "./StyleProvider";
 import { PresBox } from "./nodes/trails";
+import { IconLookup } from "@fortawesome/fontawesome-svg-core";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -1301,7 +1302,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                 onPointerDown={this.toggleAnchor} onClick={e => e.stopPropagation()}
                                 className="propertiesButton"
                             >
-                                <FontAwesomeIcon className="fa-icon" icon={faAnchor} size="lg" />
+                                <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
                             </button>
                         </div>
                         <div className="propertiesView-input inline" id="propertiesView-displayArrow">
@@ -1311,7 +1312,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                                 onPointerDown={this.toggleArrow} onClick={e => e.stopPropagation()}
                                 className="propertiesButton"
                             >
-                                <FontAwesomeIcon className="fa-icon" icon={faArrowRight} size="lg" />
+                                <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
                             </button>
                         </div>
                     </div>
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index e2b5d8dc3..b7ea124b9 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -1,17 +1,17 @@
-import * as React from "react";
+import { action, observable } from "mobx";
 import { observer } from "mobx-react";
-import { observable, action } from "mobx";
-
-import "./ScriptBox.scss";
-import { OverlayView } from "./OverlayView";
-import { DocumentIconContainer } from "./nodes/DocumentIcon";
-import { Opt, Doc } from "../../fields/Doc";
-import { emptyFunction } from "../../Utils";
-import { ScriptCast } from "../../fields/Types";
-import { CompileScript } from "../util/Scripting";
+import * as React from "react";
+import { Doc, Opt } from "../../fields/Doc";
 import { ScriptField } from "../../fields/ScriptField";
+import { ScriptCast } from "../../fields/Types";
+import { emptyFunction } from "../../Utils";
 import { DragManager } from "../util/DragManager";
+import { CompileScript } from "../util/Scripting";
 import { EditableView } from "./EditableView";
+import { DocumentIconContainer } from "./nodes/DocumentIcon";
+import { OverlayView } from "./OverlayView";
+import "./ScriptBox.scss";
+
 
 export interface ScriptBoxProps {
     onSave: (text: string, onError: (error: string) => void) => void;
diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx
index c598b2861..4ed6da24a 100644
--- a/src/client/views/ScriptingRepl.tsx
+++ b/src/client/views/ScriptingRepl.tsx
@@ -3,7 +3,8 @@ import { action, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { DocumentManager } from '../util/DocumentManager';
-import { CompileScript, Scripting, Transformer, ts } from '../util/Scripting';
+import { CompileScript, Transformer, ts } from '../util/Scripting';
+import { ScriptingGlobals } from '../util/ScriptingGlobals';
 import { DocumentIconContainer } from './nodes/DocumentIcon';
 import { OverlayView } from './OverlayView';
 import './ScriptingRepl.scss';
@@ -81,7 +82,7 @@ export class ScriptingRepl extends React.Component {
             transformer: context => {
                 const knownVars: { [name: string]: number } = {};
                 const usedDocuments: number[] = [];
-                Scripting.getGlobals().forEach(global => knownVars[global] = 1);
+                ScriptingGlobals.getGlobals().forEach((global: any) => knownVars[global] = 1);
                 return root => {
                     function visit(node: ts.Node) {
                         let skip = false;
@@ -124,7 +125,7 @@ export class ScriptingRepl extends React.Component {
             case "Enter": {
                 const docGlobals: { [name: string]: any } = {};
                 DocumentManager.Instance.DocumentViews.forEach((dv, i) => docGlobals[`d${i}`] = dv.props.Document);
-                const globals = Scripting.makeMutableGlobalsCopy(docGlobals);
+                const globals = ScriptingGlobals.makeMutableGlobalsCopy(docGlobals);
                 const script = CompileScript(this.commandString, { typecheck: false, addReturn: true, editable: true, params: { args: "any" }, transformer: this.getTransformer(), globals });
                 if (!script.compiled) {
                     this.commands.push({ command: this.commandString, result: script.errors });
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index ce363cc47..2782574c5 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -7,7 +7,7 @@ import { Doc, Opt, StrListCast } from "../../fields/Doc";
 import { List } from '../../fields/List';
 import { listSpec } from '../../fields/Schema';
 import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types";
-import { lightOrDark, DashColor } from '../../Utils';
+import { DashColor, lightOrDark } from '../../Utils';
 import { DocumentType } from '../documents/DocumentTypes';
 import { CurrentUserUtils } from '../util/CurrentUserUtils';
 import { ColorScheme } from '../util/SettingsManager';
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index ff3f92364..b3a24e031 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -5,9 +5,9 @@ import { List } from "../../fields/List";
 import { ScriptField } from "../../fields/ScriptField";
 import { Cast, StrCast } from "../../fields/Types";
 import { TraceMobx } from "../../fields/util";
-import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../Utils";
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../Utils";
 import { Docs, DocUtils } from "../documents/Documents";
-import { Scripting } from "../util/Scripting";
+import { ScriptingGlobals } from "../util/ScriptingGlobals";
 import { Transform } from "../util/Transform";
 import { undoBatch } from "../util/UndoManager";
 import { CollectionTreeView } from "./collections/CollectionTreeView";
@@ -164,7 +164,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
     }
 }
 
-Scripting.addGlobal(function switchView(doc: Doc, template: Doc | undefined) {
+ScriptingGlobals.add(function switchView(doc: Doc, template: Doc | undefined) {
     if (template?.dragFactory) {
         template = Cast(template.dragFactory, Doc, null);
     }
diff --git a/src/client/views/animationtimeline/Keyframe.tsx b/src/client/views/animationtimeline/Keyframe.tsx
index 82b0218bf..92d3e2bed 100644
--- a/src/client/views/animationtimeline/Keyframe.tsx
+++ b/src/client/views/animationtimeline/Keyframe.tsx
@@ -1,18 +1,17 @@
-import * as React from "react";
-import "./Keyframe.scss";
-import "./Timeline.scss";
-import "../global/globalCssVariables.scss";
+import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { observable, reaction, action, IReactionDisposer, observe, computed, runInAction, trace } from "mobx";
-import { Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc";
-import { Cast, NumCast } from "../../../fields/Types";
+import * as React from "react";
+import { Doc, DocListCast, Opt } from "../../../fields/Doc";
 import { List } from "../../../fields/List";
-import { createSchema, defaultSpec, makeInterface, listSpec } from "../../../fields/Schema";
-import { Transform } from "../../util/Transform";
-import { TimelineMenu } from "./TimelineMenu";
+import { createSchema, defaultSpec, listSpec, makeInterface } from "../../../fields/Schema";
+import { Cast, NumCast } from "../../../fields/Types";
 import { Docs } from "../../documents/Documents";
+import { Transform } from "../../util/Transform";
 import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { emptyPath } from "../../../Utils";
+import "../global/globalCssVariables.scss";
+import "./Keyframe.scss";
+import "./Timeline.scss";
+import { TimelineMenu } from "./TimelineMenu";
 
 
 /**
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index c7e62c15d..e80ba6f36 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -13,6 +13,7 @@ import { TimelineOverview } from "./TimelineOverview";
 import { Track } from "./Track";
 import clamp from "../../util/clamp";
 import { DocumentType } from "../../documents/DocumentTypes";
+import { IconLookup } from "@fortawesome/fontawesome-svg-core";
 
 /**
  * Timeline class controls most of timeline functions besides individual keyframe and track mechanism. Main functions are 
@@ -346,9 +347,9 @@ export class Timeline extends React.Component<FieldViewProps> {
         return (
             <div key="timeline_toolbox" className="timeline-toolbox" style={{ height: `${size}px` }}>
                 <div className="playbackControls">
-                    <div className="timeline-icon" key="timeline_windBack" onClick={this.windBackward} title="Slow Down Animation"> <FontAwesomeIcon icon={faBackward} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div>
-                    <div className="timeline-icon" key=" timeline_play" onClick={this.onPlay} title="Play/Pause"> <FontAwesomeIcon icon={this._playButton} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div>
-                    <div className="timeline-icon" key="timeline_windForward" onClick={this.windForward} title="Speed Up Animation"> <FontAwesomeIcon icon={faForward} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div>
+                    <div className="timeline-icon" key="timeline_windBack" onClick={this.windBackward} title="Slow Down Animation"> <FontAwesomeIcon icon={faBackward as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div>
+                    <div className="timeline-icon" key=" timeline_play" onClick={this.onPlay} title="Play/Pause"> <FontAwesomeIcon icon={this._playButton as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div>
+                    <div className="timeline-icon" key="timeline_windForward" onClick={this.windForward} title="Speed Up Animation"> <FontAwesomeIcon icon={faForward as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div>
                 </div>
                 <div className="grid-box overview-tool">
                     <div className="overview-box">
@@ -506,7 +507,7 @@ export class Timeline extends React.Component<FieldViewProps> {
                             {this.children.map(doc => <div style={{ height: `${(this._titleHeight)}px` }} className="datapane" onPointerOver={() => { Doc.BrushDoc(doc); }} onPointerOut={() => { Doc.UnBrushDoc(doc); }}><p>{doc.title}</p></div>)}
                         </div>
                         <div key="timeline_resize" onPointerDown={this.onResizeDown}>
-                            <FontAwesomeIcon className="resize" icon={faGripLines} />
+                            <FontAwesomeIcon className="resize" icon={faGripLines as IconLookup} />
                         </div>
                     </div>
                 </div>
diff --git a/src/client/views/animationtimeline/TimelineMenu.tsx b/src/client/views/animationtimeline/TimelineMenu.tsx
index 53ca9acad..aa422c092 100644
--- a/src/client/views/animationtimeline/TimelineMenu.tsx
+++ b/src/client/views/animationtimeline/TimelineMenu.tsx
@@ -5,6 +5,7 @@ import "./TimelineMenu.scss";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { faChartLine, faRoad, faClipboard, faPen, faTrash, faTable } from "@fortawesome/free-solid-svg-icons";
 import { Utils } from "../../../Utils";
+import { IconLookup } from "@fortawesome/fontawesome-svg-core";
 
 
 @observer
@@ -41,7 +42,7 @@ export class TimelineMenu extends React.Component {
         if (type === "input") {
             const inputRef = React.createRef<HTMLInputElement>();
             let text = "";
-            this._currentMenu.push(<div key={Utils.GenerateGuid()} className="timeline-menu-item"><FontAwesomeIcon icon={faClipboard} size="lg" /><input className="timeline-menu-input" ref={inputRef} placeholder={title} onChange={(e) => {
+            this._currentMenu.push(<div key={Utils.GenerateGuid()} className="timeline-menu-item"><FontAwesomeIcon icon={faClipboard as IconLookup} size="lg" /><input className="timeline-menu-input" ref={inputRef} placeholder={title} onChange={(e) => {
                 e.stopPropagation();
                 text = e.target.value;
             }} onKeyDown={(e) => {
@@ -52,7 +53,7 @@ export class TimelineMenu extends React.Component {
                 }
             }} /></div>);
         } else if (type === "button") {
-            this._currentMenu.push(<div key={Utils.GenerateGuid()} className="timeline-menu-item"><FontAwesomeIcon icon={faChartLine} size="lg" /><p className="timeline-menu-desc" onClick={(e) => {
+            this._currentMenu.push(<div key={Utils.GenerateGuid()} className="timeline-menu-item"><FontAwesomeIcon icon={faChartLine as IconLookup} size="lg" /><p className="timeline-menu-desc" onClick={(e) => {
                 e.preventDefault();
                 e.stopPropagation();
                 event(e);
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index 3c66faf0c..01f41869e 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -3,23 +3,17 @@ import { computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc } from '../../../fields/Doc';
-import { collectionSchema, documentSchema } from '../../../fields/documentSchemas';
 import { Id } from '../../../fields/FieldSymbols';
-import { makeInterface } from '../../../fields/Schema';
-import { ScriptField } from '../../../fields/ScriptField';
 import { NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { OmitKeys, returnFalse, Utils } from '../../../Utils';
 import { DragManager } from '../../util/DragManager';
 import { DocumentView } from '../nodes/DocumentView';
+import { StyleProp } from '../StyleProvider';
 import "./CollectionCarousel3DView.scss";
 import { CollectionSubView } from './CollectionSubView';
-import { StyleProp } from '../StyleProvider';
-
-type Carousel3DDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
-const Carousel3DDocument = makeInterface(documentSchema, collectionSchema);
 
 @observer
-export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocument) {
+export class CollectionCarousel3DView extends CollectionSubView() {
     @computed get scrollSpeed() {
         return this.layoutDoc._autoScrollSpeed ? NumCast(this.layoutDoc._autoScrollSpeed) : 1000; //default scroll speed
     }
@@ -28,7 +22,7 @@ export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocume
 
     componentWillUnmount() { this._dropDisposer?.(); }
 
-    protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
+    protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view
         this._dropDisposer?.();
         if (ele) {
             this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 6c2c27e8e..467c2893f 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -3,8 +3,6 @@ import { computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, Opt } from '../../../fields/Doc';
-import { collectionSchema, documentSchema } from '../../../fields/documentSchemas';
-import { makeInterface } from '../../../fields/Schema';
 import { NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { OmitKeys, returnFalse } from '../../../Utils';
 import { DragManager } from '../../util/DragManager';
@@ -12,18 +10,15 @@ import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { StyleProp } from '../StyleProvider';
 import "./CollectionCarouselView.scss";
-import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-
-type CarouselDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>;
-const CarouselDocument = makeInterface(documentSchema, collectionSchema);
+import { CollectionSubView } from './CollectionSubView';
 
 @observer
-export class CollectionCarouselView extends CollectionSubView(CarouselDocument) {
+export class CollectionCarouselView extends CollectionSubView() {
     private _dropDisposer?: DragManager.DragDropDisposer;
 
     componentWillUnmount() { this._dropDisposer?.(); }
 
-    protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
+    protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view
         this._dropDisposer?.();
         if (ele) {
             this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index f543d924d..9e8374605 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -4,31 +4,31 @@ import { action, IReactionDisposer, observable, reaction, runInAction } from "mo
 import { observer } from "mobx-react";
 import * as ReactDOM from 'react-dom';
 import * as GoldenLayout from "../../../client/goldenLayout";
-import { Doc, DocListCast, Opt, DocListCastAsync, DataSym } from "../../../fields/Doc";
+import { DataSym, Doc, DocListCast, DocListCastAsync, Opt } from "../../../fields/Doc";
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
+import { listSpec } from '../../../fields/Schema';
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { inheritParentAcls } from '../../../fields/util';
 import { DocServer } from "../../DocServer";
 import { Docs } from '../../documents/Documents';
+import { DocumentType } from '../../documents/DocumentTypes';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { DragManager } from "../../util/DragManager";
 import { InteractionUtils } from '../../util/InteractionUtils';
-import { Scripting } from '../../util/Scripting';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { undoBatch, UndoManager } from "../../util/UndoManager";
+import { LightboxView } from '../LightboxView';
 import "./CollectionDockingView.scss";
 import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView";
 import { CollectionViewType } from './CollectionView';
 import { TabDocView } from './TabDocView';
 import React = require("react");
-import { DocumentType } from '../../documents/DocumentTypes';
-import { listSpec } from '../../../fields/Schema';
-import { LightboxView } from '../LightboxView';
-import { inheritParentAcls } from '../../../fields/util';
 const _global = (window /* browser */ || global /* node */) as any;
 
 @observer
-export class CollectionDockingView extends CollectionSubView(doc => doc) {
+export class CollectionDockingView extends CollectionSubView() {
     @observable public static Instance: CollectionDockingView;
     public static makeDocumentConfig(document: Doc, panelName?: string, width?: number) {
         return {
@@ -468,8 +468,8 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
     }
 }
 
-Scripting.addGlobal(function openInLightbox(doc: any) { LightboxView.AddDocTab(doc, "lightbox"); },
+ScriptingGlobals.add(function openInLightbox(doc: any) { LightboxView.AddDocTab(doc, "lightbox"); },
     "opens up document in a lightbox", "(doc: any)");
-Scripting.addGlobal(function openOnRight(doc: any) { return CollectionDockingView.AddSplit(doc, "right"); },
+ScriptingGlobals.add(function openOnRight(doc: any) { return CollectionDockingView.AddSplit(doc, "right"); },
     "opens up document in tab on right side of the screen", "(doc: any)");
-Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); });
\ No newline at end of file
+ScriptingGlobals.add(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); });
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 131f5ba46..364a2440e 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -20,7 +20,7 @@ import { Docs } from "../../documents/Documents";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 import { DragManager } from "../../util/DragManager";
-import { Scripting } from "../../util/Scripting";
+import { ScriptingGlobals } from "../../util/ScriptingGlobals";
 import { SelectionManager } from "../../util/SelectionManager";
 import { Transform } from "../../util/Transform";
 import { undoBatch } from "../../util/UndoManager";
@@ -1285,7 +1285,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
         );
     }
 }
-Scripting.addGlobal(function gotoFrame(doc: any, newFrame: any) {
+ScriptingGlobals.add(function gotoFrame(doc: any, newFrame: any) {
     const dataField = doc[Doc.LayoutFieldKey(doc)];
     const childDocs = DocListCast(dataField);
     const currentFrame = Cast(doc._currentFrame, "number", null);
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index bc1407c53..0a336c544 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -13,7 +13,7 @@ import { CollectionSubView } from "./CollectionSubView";
 import React = require("react");
 
 @observer
-export class CollectionPileView extends CollectionSubView(doc => doc) {
+export class CollectionPileView extends CollectionSubView() {
     _originalChrome: any = "";
     _disposers: { [name: string]: IReactionDisposer } = {};
 
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index bd7d0083b..e0b947211 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -11,7 +11,7 @@ import { computedFn } from "mobx-utils";
 import { Doc, DocListCast } from "../../../fields/Doc";
 import { Id } from "../../../fields/FieldSymbols";
 import { List } from "../../../fields/List";
-import { listSpec, makeInterface } from "../../../fields/Schema";
+import { listSpec } from "../../../fields/Schema";
 import { ComputedField, ScriptField } from "../../../fields/ScriptField";
 import { Cast, NumCast } from "../../../fields/Types";
 import {
@@ -25,7 +25,7 @@ import { Docs } from "../../documents/Documents";
 import { DocumentManager } from "../../util/DocumentManager";
 import { DragManager } from "../../util/DragManager";
 import { LinkManager } from "../../util/LinkManager";
-import { Scripting } from "../../util/Scripting";
+import { ScriptingGlobals } from "../../util/ScriptingGlobals";
 import { SelectionManager } from "../../util/SelectionManager";
 import { SnappingManager } from "../../util/SnappingManager";
 import { Transform } from "../../util/Transform";
@@ -43,21 +43,6 @@ import {
 import { LabelBox } from "../nodes/LabelBox";
 import "./CollectionStackedTimeline.scss";
 
-
-
-/**
- * CollectionStackedTimeline
- * Main component: CollectionStackedTimeline.tsx
- * Supporting Components: AudioWaveform
- * 
- * CollectionStackedTimeline is a collection view used for audio and video nodes to display a timeline of the temporal media documents with an audio waveform and markers for links and annotations
- * The actual media is handled in the containing classes (AudioBox, VideoBox) but the timeline deals with rendering and updating timecodes, links, and trimming.
- * When trimming there are two pairs of times that are tracked: trimStart and trimEnd are the bounds of the trim controls, clipStart and clipEnd are the actual trimmed playback bounds of the clip
- */
-
-
-type PanZoomDocument = makeInterface<[]>;
-const PanZoomDocument = makeInterface();
 export type CollectionStackedTimelineProps = {
     Play: () => void;
     Pause: () => void;
@@ -82,12 +67,9 @@ export enum TrimScope {
 
 
 @observer
-export class CollectionStackedTimeline extends CollectionSubView<
-    PanZoomDocument,
-    CollectionStackedTimelineProps
->(PanZoomDocument) {
-    @observable static SelectingRegion: CollectionStackedTimeline | undefined; // timeline selection region
-    @observable public static CurrentlyPlaying: Doc[]; // tracks all currently playing audio and video docs
+export class CollectionStackedTimeline extends CollectionSubView<CollectionStackedTimelineProps>() {
+    @observable static SelectingRegion: CollectionStackedTimeline | undefined = undefined;
+    @observable public static CurrentlyPlaying: Doc[];
 
     static RangeScript: ScriptField;
     static LabelScript: ScriptField;
@@ -983,7 +965,9 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
                         <div
                             key="right"
                             className="collectionStackedTimeline-resizer"
-                            onPointerDown={(e) => this.onAnchorDown(e, this.props.mark, false)}
+                            onPointerDown={(e) =>
+                                this.onAnchorDown(e, this.props.mark, false)
+                            }
                         />
                     </>
                 )}
@@ -991,12 +975,12 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
         );
     }
 }
-Scripting.addGlobal(function formatToTime(time: number): any {
+ScriptingGlobals.add(function formatToTime(time: number): any {
     return formatTime(time);
 });
-Scripting.addGlobal(function min(num1: number, num2: number): number {
+ScriptingGlobals.add(function min(num1: number, num2: number): number {
     return Math.min(num1, num2);
 });
-Scripting.addGlobal(function max(num1: number, num2: number): number {
+ScriptingGlobals.add(function max(num1: number, num2: number): number {
     return Math.max(num1, num2);
 });
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index cdc680a08..8634ea139 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -3,16 +3,15 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { CursorProperty } from "csstype";
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { DataSym, Doc, HeightSym, Opt, WidthSym, DocListCast } from "../../../fields/Doc";
-import { collectionSchema, documentSchema } from "../../../fields/documentSchemas";
+import { DataSym, Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
 import { Id } from "../../../fields/FieldSymbols";
 import { List } from "../../../fields/List";
-import { listSpec, makeInterface } from "../../../fields/Schema";
+import { listSpec } from "../../../fields/Schema";
 import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
 import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, Utils, returnTrue, returnEmptyDoclist, returnEmptyFilter } from "../../../Utils";
-import { DocUtils, Docs } from "../../documents/Documents";
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils";
+import { Docs, DocUtils } from "../../documents/Documents";
 import { DragManager, dropActionType } from "../../util/DragManager";
 import { SnappingManager } from "../../util/SnappingManager";
 import { Transform } from "../../util/Transform";
@@ -23,18 +22,14 @@ import { EditableView } from "../EditableView";
 import { LightboxView } from "../LightboxView";
 import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
 import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from "../nodes/DocumentView";
-import { StyleProp, DefaultStyleProvider } from "../StyleProvider";
+import { StyleProp } from "../StyleProvider";
 import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow";
 import "./CollectionStackingView.scss";
 import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
 import { CollectionSubView } from "./CollectionSubView";
 import { CollectionViewType } from "./CollectionView";
-import { FontIconBox } from "../nodes/button/FontIconBox";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 const _global = (window /* browser */ || global /* node */) as any;
 
-type StackingDocument = makeInterface<[typeof collectionSchema, typeof documentSchema]>;
-const StackingDocument = makeInterface(collectionSchema, documentSchema);
 
 export type collectionStackingViewProps = {
     chromeHidden?: boolean;
@@ -44,7 +39,7 @@ export type collectionStackingViewProps = {
 };
 
 @observer
-export class CollectionStackingView extends CollectionSubView<StackingDocument, Partial<collectionStackingViewProps>>(StackingDocument) {
+export class CollectionStackingView extends CollectionSubView<Partial<collectionStackingViewProps>>() {
     _masonryGridRef: HTMLDivElement | null = null;
     _draggerRef = React.createRef<HTMLDivElement>();
     _pivotFieldDisposer?: IReactionDisposer;
diff --git a/src/client/views/collections/CollectionStaffView.tsx b/src/client/views/collections/CollectionStaffView.tsx
index c5c3f96e8..c025e94a8 100644
--- a/src/client/views/collections/CollectionStaffView.tsx
+++ b/src/client/views/collections/CollectionStaffView.tsx
@@ -6,7 +6,7 @@ import "./CollectionStaffView.scss";
 import { observer } from "mobx-react";
 
 @observer
-export class CollectionStaffView extends CollectionSubView(doc => doc) {
+export class CollectionStaffView extends CollectionSubView() {
     private _reactionDisposer: IReactionDisposer | undefined;
     @observable private _staves = NumCast(this.props.Document.staves);
 
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 34209ebc9..42e157396 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -10,14 +10,14 @@ import { Cast, ScriptCast, NumCast, StrCast } from "../../../fields/Types";
 import { GestureUtils } from "../../../pen-gestures/GestureUtils";
 import { Utils, returnFalse, returnEmptyFilter } from "../../../Utils";
 import { DocServer } from "../../DocServer";
-import { Networking } from "../../Network";
 import { ImageUtils } from "../../util/Import & Export/ImageUtils";
 import { InteractionUtils } from "../../util/InteractionUtils";
 import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { DocComponent } from "../DocComponent";
 import React = require("react");
-import * as rp from 'request-promise';
 import ReactLoading from 'react-loading';
+import * as rp from 'request-promise';
+import { Networking } from "../../Network";
 
 
 export interface SubCollectionViewProps extends CollectionViewProps {
@@ -25,15 +25,15 @@ export interface SubCollectionViewProps extends CollectionViewProps {
     isAnyChildContentActive: () => boolean;
 }
 
-export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: X) {
-    class CollectionSubView extends DocComponent<X & SubCollectionViewProps, T>(schemaCtor) {
+export function CollectionSubView<X>(moreProps?: X) {
+    class CollectionSubView extends DocComponent<X & SubCollectionViewProps>() {
         private dropDisposer?: DragManager.DragDropDisposer;
         private gestureDisposer?: GestureUtils.GestureEventDisposer;
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
         protected _mainCont?: HTMLDivElement;
         @observable _focusFilters: Opt<string[]>; // docFilters that are overridden when previewing a link to an anchor which has docFilters set on it
         @observable _focusRangeFilters: Opt<string[]>; // docRangeFilters that are overridden when previewing a link to an anchor which has docRangeFilters set on it
-        protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
+        protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view
             this.dropDisposer?.();
             this.gestureDisposer?.();
             this._multiTouchDisposer?.();
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 292dfd77c..d6398fda5 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -1,17 +1,17 @@
 import { toUpper } from "lodash";
 import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocCastAsync, Opt, StrListCast } from "../../../fields/Doc";
+import { Doc, Opt, StrListCast } from "../../../fields/Doc";
 import { List } from "../../../fields/List";
 import { ObjectField } from "../../../fields/ObjectField";
 import { RichTextField } from "../../../fields/RichTextField";
 import { listSpec } from "../../../fields/Schema";
 import { ComputedField, ScriptField } from "../../../fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents, returnEmptyString } from "../../../Utils";
-import { Docs, DocUtils } from "../../documents/Documents";
+import { emptyFunction, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from "../../../Utils";
+import { Docs } from "../../documents/Documents";
 import { DocumentManager } from "../../util/DocumentManager";
-import { Scripting } from "../../util/Scripting";
+import { ScriptingGlobals } from "../../util/ScriptingGlobals";
 import { ContextMenu } from "../ContextMenu";
 import { ContextMenuProps } from "../ContextMenuItem";
 import { EditableView } from "../EditableView";
@@ -20,13 +20,10 @@ import { ViewDefBounds } from "./collectionFreeForm/CollectionFreeFormLayoutEngi
 import { CollectionFreeFormView } from "./collectionFreeForm/CollectionFreeFormView";
 import { CollectionSubView } from "./CollectionSubView";
 import "./CollectionTimeView.scss";
-const higflyout = require("@hig/flyout");
-export const { anchorPoints } = higflyout;
-export const Flyout = higflyout.default;
 import React = require("react");
 
 @observer
-export class CollectionTimeView extends CollectionSubView(doc => doc) {
+export class CollectionTimeView extends CollectionSubView() {
     _changing = false;
     @observable _layoutEngine = "pivot";
     @observable _collapsed: boolean = false;
@@ -236,7 +233,7 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) {
     }
 }
 
-Scripting.addGlobal(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBounds) {
+ScriptingGlobals.add(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBounds) {
     let prevFilterIndex = NumCast(pivotDoc._prevFilterIndex);
     pivotDoc["_prevDocFilter" + prevFilterIndex] = ObjectField.MakeCopy(pivotDoc._docFilters as ObjectField);
     pivotDoc["_prevDocRangeFilters" + prevFilterIndex] = ObjectField.MakeCopy(pivotDoc._docRangeFilters as ObjectField);
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index ea077ea40..e84517f40 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -3,11 +3,11 @@ import { observer } from "mobx-react";
 import { DataSym, Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
-import { Document, listSpec } from '../../../fields/Schema';
+import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, OmitKeys, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnOne } from '../../../Utils';
+import { emptyFunction, OmitKeys, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { DocumentManager } from '../../util/DocumentManager';
@@ -40,7 +40,7 @@ export type collectionTreeViewProps = {
 };
 
 @observer
-export class CollectionTreeView extends CollectionSubView<Document, Partial<collectionTreeViewProps>>(Document) {
+export class CollectionTreeView extends CollectionSubView<Partial<collectionTreeViewProps>>() {
     private _treedropDisposer?: DragManager.DragDropDisposer;
     private _mainEle?: HTMLDivElement;
     private _titleRef?: HTMLDivElement | HTMLInputElement | null;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 681a15e3d..ee2c28b5f 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,15 +1,14 @@
-import { computed, observable, runInAction, action } from 'mobx';
+import { computed, observable, runInAction } from 'mobx';
 import { observer } from "mobx-react";
 import * as React from 'react';
 import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app
-import { Doc, DocListCast, StrListCast } from '../../../fields/Doc';
-import { documentSchema } from '../../../fields/documentSchemas';
+import { Doc, DocListCast } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { ObjectField } from '../../../fields/ObjectField';
-import { makeInterface } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
 import { Cast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
+import { returnEmptyString } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
 import { BranchCreate, BranchTask } from '../../documents/Gitlike';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
@@ -33,10 +32,7 @@ import { CollectionStackingView } from './CollectionStackingView';
 import { SubCollectionViewProps } from './CollectionSubView';
 import { CollectionTimeView } from './CollectionTimeView';
 import { CollectionTreeView } from "./CollectionTreeView";
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import './CollectionView.scss';
-import { returnEmptyString } from '../../../Utils';
-import { InkTool } from '../../../fields/InkField';
 export const COLLECTION_BORDER_WIDTH = 2;
 const path = require('path');
 
@@ -84,11 +80,8 @@ export interface CollectionViewProps extends FieldViewProps {
     childClickScript?: ScriptField;
     childDoubleClickScript?: ScriptField;
 }
-
-type CollectionDocument = makeInterface<[typeof documentSchema]>;
-const CollectionDocument = makeInterface(documentSchema);
 @observer
-export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & CollectionViewProps, CollectionDocument>(CollectionDocument) {
+export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & CollectionViewProps>() {
     public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); }
 
     @observable private static _safeMode = false;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index aeda71d01..e2ea81392 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -4,13 +4,12 @@ import { observer } from "mobx-react";
 import { computedFn } from "mobx-utils";
 import { DateField } from "../../../../fields/DateField";
 import { Doc, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc";
-import { collectionSchema, documentSchema } from "../../../../fields/documentSchemas";
 import { Id } from "../../../../fields/FieldSymbols";
 import { InkData, InkField, InkTool, PointData, Segment } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
 import { ObjectField } from "../../../../fields/ObjectField";
 import { RichTextField } from "../../../../fields/RichTextField";
-import { createSchema, listSpec, makeInterface } from "../../../../fields/Schema";
+import { createSchema, listSpec } from "../../../../fields/Schema";
 import { ScriptField } from "../../../../fields/ScriptField";
 import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
 import { TraceMobx } from "../../../../fields/util";
@@ -41,7 +40,6 @@ import { LightboxView } from "../../LightboxView";
 import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
 import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from "../../nodes/DocumentView";
 import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
-import { pageSchema } from "../../nodes/ImageBox";
 import { PresBox } from "../../nodes/trails/PresBox";
 import { StyleLayers, StyleProp } from "../../StyleProvider";
 import { CollectionDockingView } from "../CollectionDockingView";
@@ -67,8 +65,6 @@ export const panZoomSchema = createSchema({
     scrollHeight: "number"    // this will be set when the collection is an annotation overlay for a PDF/Webpage
 });
 
-type PanZoomDocument = makeInterface<[typeof panZoomSchema, typeof collectionSchema, typeof documentSchema, typeof pageSchema]>;
-const PanZoomDocument = makeInterface(panZoomSchema, collectionSchema, documentSchema, pageSchema);
 export type collectionFreeformViewProps = {
     annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox)
     viewDefDivClick?: ScriptField;
@@ -81,7 +77,7 @@ export type collectionFreeformViewProps = {
 };
 
 @observer
-export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, Partial<collectionFreeformViewProps>>(PanZoomDocument) {
+export class CollectionFreeFormView extends CollectionSubView<Partial<collectionFreeformViewProps>>() {
     public get displayName() { return "CollectionFreeFormView(" + this.props.Document.title?.toString() + ")"; } // this makes mobx trace() statements more descriptive
 
     private _lastNudge: any;
@@ -95,6 +91,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     private _clusterDistance: number = 75;
     private _hitCluster: number = -1;
     private _disposers: { [name: string]: IReactionDisposer } = {};
+    private _renderCutoffData = observable.map<string, boolean>();
     private _layoutPoolData = observable.map<string, PoolData>();
     private _layoutSizeData = observable.map<string, { width?: number, height?: number }>();
     private _cachedPool: Map<string, PoolData> = new Map();
@@ -109,6 +106,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     @observable _viewTransition: number = 0;  // sets the pan/zoom transform ease time- used by nudge(), focus() etc to smoothly zoom/pan.  set to 0 to use document's transition time or default of 0
     @observable _hLines: number[] | undefined;
     @observable _vLines: number[] | undefined;
+    @observable _firstRender = true; // this turns off rendering of the collection's content so that there's instant feedback when a tab is switched of what content will be shown.
     @observable _pullCoords: number[] = [0, 0];
     @observable _pullDirection: string = "";
     @observable _showAnimTimeline = false;
@@ -163,7 +161,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         this.layoutDoc._panY = vals.bounds.cy;
         this.layoutDoc._viewScale = vals.scale;
     }
-    freeformData = (force?: boolean) => this.fitToContent || force ? this.fitToContentVals : undefined;
+    freeformData = (force?: boolean) => !this._firstRender && (this.fitToContent || force) ? this.fitToContentVals : undefined;
     reverseNativeScaling = () => this.fitToContent ? true : false;
     panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document._panX);
     panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY);
@@ -205,7 +203,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 }
             }
             if (this.Document._currentFrame !== undefined && !this.props.isAnnotationOverlay) {
-                CollectionFreeFormDocumentView.setupKeyframes(newBoxes, this.Document._currentFrame, true);
+                CollectionFreeFormDocumentView.setupKeyframes(newBoxes, NumCast(this.Document._currentFrame), true);
             }
         }
         return retVal;
@@ -235,12 +233,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             const d = docDragData.droppedDocuments[i];
             const layoutDoc = Doc.Layout(d);
             if (this.Document._currentFrame !== undefined) {
-                CollectionFreeFormDocumentView.setupKeyframes([d], this.Document._currentFrame, false);
+                CollectionFreeFormDocumentView.setupKeyframes([d], NumCast(this.Document._currentFrame), false);
                 const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000));
                 vals.x = x + (vals.x || 0) - dropPos[0];
                 vals.y = y + (vals.y || 0) - dropPos[1];
                 vals._scrollTop = this.Document.editScrollProgressivize ? vals._scrollTop : undefined;
-                CollectionFreeFormDocumentView.setValues(this.Document._currentFrame, d, vals);
+                CollectionFreeFormDocumentView.setValues(NumCast(this.Document._currentFrame), d, vals);
             } else {
                 d.x = x + NumCast(d.x) - dropPos[0];
                 d.y = y + NumCast(d.y) - dropPos[1];
@@ -635,7 +633,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     @action
     pan = (e: PointerEvent | React.Touch | { clientX: number, clientY: number }): void => {
         const [dx, dy] = this.getTransform().transformDirection(e.clientX - this._lastX, e.clientY - this._lastY);
-        this.setPan((this.Document._panX || 0) - dx, (this.Document._panY || 0) - dy, 0, true);
+        this.setPan(NumCast(this.Document._panX) - dx, NumCast(this.Document._panY) - dy, 0, true);
         this._lastX = e.clientX;
         this._lastY = e.clientY;
     }
@@ -967,10 +965,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 .filter(({ pos, size }) => pos && size).map(({ pos, size }) => ({ pos: pos!, size: size! }));
             if (measuredDocs.length) {
                 const ranges = measuredDocs.reduce(({ xrange, yrange }, { pos, size }) =>  // computes range of content
-                    ({
-                        xrange: { min: Math.min(xrange.min, pos.x), max: Math.max(xrange.max, pos.x + (size.width || 0)) },
-                        yrange: { min: Math.min(yrange.min, pos.y), max: Math.max(yrange.max, pos.y + (size.height || 0)) }
-                    })
+                ({
+                    xrange: { min: Math.min(xrange.min, pos.x), max: Math.max(xrange.max, pos.x + (size.width || 0)) },
+                    yrange: { min: Math.min(yrange.min, pos.y), max: Math.max(yrange.max, pos.y + (size.height || 0)) }
+                })
                     , {
                         xrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE },
                         yrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE }
@@ -1045,11 +1043,11 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         if (state.type === "doc" && this.Document._panX !== undefined && this.Document._panY !== undefined) {
             const init = state.initializers![this.Document[Id]];
             if (!init) {
-                state.initializers![this.Document[Id]] = { panX: this.Document._panX, panY: this.Document._panY };
+                state.initializers![this.Document[Id]] = { panX: NumCast(this.Document._panX), panY: NumCast(this.Document._panY) };
                 HistoryUtil.pushState(state);
             } else if (init.panX !== this.Document._panX || init.panY !== this.Document._panY) {
-                init.panX = this.Document._panX;
-                init.panY = this.Document._panY;
+                init.panX = NumCast(this.Document._panX);
+                init.panY = NumCast(this.Document._panY);
                 HistoryUtil.pushState(state);
             }
         }
@@ -1144,7 +1142,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
 
-    getChildDocView(entry: PoolData) {
+    getChildDocView(entry: PoolData, renderIndex: number) {
         const childLayout = entry.pair.layout;
         const childData = entry.pair.data;
         const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
@@ -1153,6 +1151,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             Document={childLayout}
             renderDepth={this.props.renderDepth + 1}
             replica={entry.replica}
+            renderIndex={renderIndex}
+            renderCutoffProvider={this.renderCutoffProvider}
             ContainingCollectionView={this.props.CollectionView}
             ContainingCollectionDoc={this.props.Document}
             CollectionFreeFormView={this}
@@ -1213,7 +1213,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         const { z, color, zIndex } = params.pair.layout;
         const { x, y, opacity } = this.Document._currentFrame === undefined ?
             { x: params.pair.layout.x, y: params.pair.layout.y, opacity: this.props.styleProvider?.(params.pair.layout, this.props, StyleProp.Opacity) } :
-            CollectionFreeFormDocumentView.getValues(params.pair.layout, this.Document._currentFrame);
+            CollectionFreeFormDocumentView.getValues(params.pair.layout, NumCast(this.Document._currentFrame));
         return {
             x: NumCast(x), y: NumCast(y), z: Cast(z, "number"), color: StrCast(color), zIndex: Cast(zIndex, "number"),
             transition: StrCast(layoutDoc.dataTransition), opacity: this._keyframeEditing ? 1 : Cast(opacity, "number", null),
@@ -1256,6 +1256,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         }
     }
 
+
+    renderCutoffProvider = computedFn(function renderCutoffProvider(this: any, doc: Doc) {
+        return !this._renderCutoffData.get(doc[Id] + "");
+    }.bind(this));
+
+
     childPositionProviderUnmemoized = (doc: Doc, replica: string) => {
         return this._layoutPoolData.get(doc[Id] + (replica || ""));
     }
@@ -1300,6 +1306,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         return { newPool, computedElementData: this.doFreeformLayout(newPool) };
     }
 
+    @observable _numLoaded = 1;
     get doLayoutComputation() {
         const { newPool, computedElementData } = this.doInternalLayoutComputation;
         const array = Array.from(newPool.entries());
@@ -1318,9 +1325,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         this._cachedPool.clear();
         Array.from(newPool.entries()).forEach(k => this._cachedPool.set(k[0], k[1]));
         const elements = computedElementData.slice();
-        Array.from(newPool.entries()).filter(entry => this.isCurrent(entry[1].pair.layout)).forEach(entry =>
+        Array.from(newPool.entries()).filter(entry => this.isCurrent(entry[1].pair.layout)).forEach((entry, i) =>
             elements.push({
-                ele: this.getChildDocView(entry[1]),
+                ele: this.getChildDocView(entry[1], i),
                 bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica)
             }));
 
@@ -1369,35 +1376,38 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     componentDidMount() {
         super.componentDidMount?.();
         this.props.setContentView?.(this);
-        this._disposers.layoutComputation = reaction(() => this.doLayoutComputation,
-            (elements) => this._layoutElements = elements || [],
-            { fireImmediately: true, name: "doLayout" });
-
-        this._marqueeRef.current?.addEventListener("dashDragAutoScroll", this.onDragAutoScroll as any);
-
-        this._disposers.groupBounds = reaction(() => {
-            if (this.props.Document._isGroup && this.childDocs.length === this.childDocList?.length) {
-                const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: cd[WidthSym](), height: cd[HeightSym]() }));
-                return aggregateBounds(clist, NumCast(this.layoutDoc._xPadding), NumCast(this.layoutDoc._yPadding));
-            }
-            return undefined;
-        },
-            (cbounds) => {
-                if (cbounds) {
-                    const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[HeightSym]() / 2];
-                    const p = [NumCast(this.layoutDoc._panX), NumCast(this.layoutDoc._panY)];
-                    const pbounds = {
-                        x: (cbounds.x - p[0]) * this.zoomScaling() + c[0], y: (cbounds.y - p[1]) * this.zoomScaling() + c[1],
-                        r: (cbounds.r - p[0]) * this.zoomScaling() + c[0], b: (cbounds.b - p[1]) * this.zoomScaling() + c[1]
-                    };
-                    this.layoutDoc._width = (pbounds.r - pbounds.x);
-                    this.layoutDoc._height = (pbounds.b - pbounds.y);
-                    this.layoutDoc._panX = (cbounds.r + cbounds.x) / 2;
-                    this.layoutDoc._panY = (cbounds.b + cbounds.y) / 2;
-                    this.layoutDoc.x = pbounds.x;
-                    this.layoutDoc.y = pbounds.y;
+        setTimeout(action(() => {
+            this._firstRender = false;
+            this._disposers.layoutComputation = reaction(() => this.doLayoutComputation,
+                (elements) => this._layoutElements = elements || [],
+                { fireImmediately: true, name: "doLayout" });
+
+            this._marqueeRef.current?.addEventListener("dashDragAutoScroll", this.onDragAutoScroll as any);
+
+            this._disposers.groupBounds = reaction(() => {
+                if (this.props.Document._isGroup && this.childDocs.length === this.childDocList?.length) {
+                    const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: cd[WidthSym](), height: cd[HeightSym]() }));
+                    return aggregateBounds(clist, NumCast(this.layoutDoc._xPadding), NumCast(this.layoutDoc._yPadding));
                 }
-            }, { fireImmediately: true });
+                return undefined;
+            },
+                (cbounds) => {
+                    if (cbounds) {
+                        const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[HeightSym]() / 2];
+                        const p = [NumCast(this.layoutDoc._panX), NumCast(this.layoutDoc._panY)];
+                        const pbounds = {
+                            x: (cbounds.x - p[0]) * this.zoomScaling() + c[0], y: (cbounds.y - p[1]) * this.zoomScaling() + c[1],
+                            r: (cbounds.r - p[0]) * this.zoomScaling() + c[0], b: (cbounds.b - p[1]) * this.zoomScaling() + c[1]
+                        };
+                        this.layoutDoc._width = (pbounds.r - pbounds.x);
+                        this.layoutDoc._height = (pbounds.b - pbounds.y);
+                        this.layoutDoc._panX = (cbounds.r + cbounds.x) / 2;
+                        this.layoutDoc._panY = (cbounds.b + cbounds.y) / 2;
+                        this.layoutDoc.x = pbounds.x;
+                        this.layoutDoc.y = pbounds.y;
+                    }
+                }, { fireImmediately: true });
+        }));
     }
 
     componentWillUnmount() {
@@ -1449,8 +1459,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         const height = Math.max(...docs.map(doc => NumCast(doc._height))) + 20;
         const dim = Math.ceil(Math.sqrt(docs.length));
         docs.forEach((doc, i) => {
-            doc.x = (this.Document._panX || 0) + (i % dim) * width - width * dim / 2;
-            doc.y = (this.Document._panY || 0) + Math.floor(i / dim) * height - height * dim / 2;
+            doc.x = NumCast(this.Document._panX) + (i % dim) * width - width * dim / 2;
+            doc.y = NumCast(this.Document._panY) + Math.floor(i / dim) * height - height * dim / 2;
         });
     }
 
@@ -1557,58 +1567,35 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         e.stopPropagation();
     }
 
+    incrementalRender = action(() => {
+        if (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath())) {
+            const unrendered = this.childDocs.filter(doc => !this._renderCutoffData.get(doc[Id]));
+            const loadIncrement = 5;
+            for (var i = 0; i < Math.min(unrendered.length, loadIncrement); i++) {
+                this._renderCutoffData.set(unrendered[i][Id] + "", true);
+            }
+        }
+        this.childDocs.some(doc => !this._renderCutoffData.get(doc[Id])) && setTimeout(this.incrementalRender, 1);
+    });
+
     children = () => {
+        this.incrementalRender();
         const children = typeof this.props.children === "function" ? (this.props.children as any)() as JSX.Element[] : [];
-        return [...children, ...this.views, <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />];
-    }
-
-    chooseGridSpace = (gridSpace: number): number => {
-        if (!this.zoomScaling()) return 50;
-        const divisions = this.props.PanelWidth() / this.zoomScaling() / gridSpace + 3;
-        return divisions < 60 ? gridSpace : this.chooseGridSpace(gridSpace * 10);
-    }
-
-    @computed get backgroundGrid() {
-        const gridSpace = this.chooseGridSpace(NumCast(this.layoutDoc["_backgroundGrid-spacing"], 50));
-        const shiftX = (this.props.isAnnotationOverlay ? 0 : -this.panX() % gridSpace - gridSpace) * this.zoomScaling();
-        const shiftY = (this.props.isAnnotationOverlay ? 0 : -this.panY() % gridSpace - gridSpace) * this.zoomScaling();
-        const renderGridSpace = gridSpace * this.zoomScaling();
-        const w = this.props.PanelWidth() + 2 * renderGridSpace;
-        const h = this.props.PanelHeight() + 2 * renderGridSpace;
-        const strokeStyle = CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "rgba(255,255,255,0.5)" : "rgba(0, 0,0,0.5)";
-        return <canvas className="collectionFreeFormView-grid" width={w} height={h} style={{ transform: `translate(${shiftX}px, ${shiftY}px)` }}
-            ref={(el) => {
-                const ctx = el?.getContext('2d');
-                if (ctx) {
-                    const Cx = this.cachedCenteringShiftX % renderGridSpace;
-                    const Cy = this.cachedCenteringShiftY % renderGridSpace;
-                    ctx.lineWidth = Math.min(1, Math.max(0.5, this.zoomScaling()));
-                    ctx.setLineDash(gridSpace > 50 ? [3, 3] : [1, 5]);
-                    ctx.clearRect(0, 0, w, h);
-                    if (ctx) {
-                        ctx.strokeStyle = strokeStyle;
-                        ctx.beginPath();
-                        for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace) {
-                            ctx.moveTo(x, Cy - h);
-                            ctx.lineTo(x, Cy + h);
-                        }
-                        for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) {
-                            ctx.moveTo(Cx - w, y);
-                            ctx.lineTo(Cx + w, y);
-                        }
-                        ctx.stroke();
-                    }
-                }
-            }} />;
+        return [
+            ...children,
+            ...this.views,
+            <CollectionFreeFormRemoteCursors {...this.props} key="remoteCursors" />
+        ];
     }
 
     @computed get placeholder() {
-        return <div className="collectionfreeformview-placeholder" style={{ background: this.Document.backgroundColor }}>
+        return <div className="collectionfreeformview-placeholder" style={{ background: StrCast(this.Document.backgroundColor) }}>
             <span className="collectionfreeformview-placeholderSpan">{this.props.Document.title?.toString()}</span>
         </div>;
     }
 
     @computed get marqueeView() {
+        TraceMobx();
         return <MarqueeView
             {...this.props}
             ungroup={this.props.Document._isGroup ? this.promoteCollection : undefined}
@@ -1623,7 +1610,18 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             getTransform={this.getTransform}
             isAnnotationOverlay={this.isAnnotationOverlay}>
             <div className="marqueeView-div" ref={this._marqueeRef} style={{ opacity: this.props.dontRenderDocuments ? 0 : undefined }}>
-                {this.layoutDoc._backgroundGridShow ? this.backgroundGrid : (null)}
+                {this.layoutDoc._backgroundGridShow ?
+                    <CollectionFreeFormBackgroundGrid
+                        PanelWidth={this.props.PanelWidth}
+                        PanelHeight={this.props.PanelHeight}
+                        panX={this.panX}
+                        panY={this.panY}
+                        zoomScaling={this.zoomScaling}
+                        layoutDoc={this.layoutDoc}
+                        isAnnotationOverlay={this.isAnnotationOverlay}
+                        cachedCenteringShiftX={this.cachedCenteringShiftX}
+                        cachedCenteringShiftY={this.cachedCenteringShiftY}
+                    /> : (null)}
                 <CollectionFreeFormViewPannableContents
                     isAnnotationOverlay={this.isAnnotationOverlay}
                     isAnnotationOverlayScrollable={this.props.isAnnotationOverlayScrollable}
@@ -1642,7 +1640,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     }
 
     @computed get contentScaling() {
-        if (this.props.isAnnotationOverlay && !this.props.annotationLayerHostsContent) return 0;
+        if (this._firstRender || (this.props.isAnnotationOverlay && !this.props.annotationLayerHostsContent)) return 0;
         const nw = this.nativeWidth;
         const nh = this.nativeHeight;
         const hscale = nh ? this.props.PanelHeight() / nh : 1;
@@ -1675,9 +1673,9 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                     this.backgroundEvents ? "all" : this.props.pointerEvents as any,
                 transform: `scale(${this.contentScaling || 1})`,
                 width: `${100 / (this.contentScaling || 1)}%`,
-                height: this.isAnnotationOverlay && this.Document.scrollHeight ? this.Document.scrollHeight : `${100 / (this.contentScaling || 1)}%`// : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
+                height: this.isAnnotationOverlay && this.Document.scrollHeight ? NumCast(this.Document.scrollHeight) : `${100 / (this.contentScaling || 1)}%`// : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
             }}>
-            {this.Document._freeformLOD && !this.props.isContentActive() && !this.props.isAnnotationOverlay && this.props.renderDepth > 0 ?
+            {this._firstRender || (this.Document._freeformLOD && !this.props.isContentActive() && !this.props.isAnnotationOverlay && this.props.renderDepth > 0) ?
                 this.placeholder : this.marqueeView}
             {this.props.noOverlay ? (null) : <CollectionFreeFormOverlayView elements={this.elementFunc} />}
 
@@ -1854,4 +1852,59 @@ class CollectionFreeFormViewPannableContents extends React.Component<CollectionF
             {this.zoomProgressivize}
         </div>;
     }
+}
+
+interface CollectionFreeFormViewBackgroundGridProps {
+    panX: () => number;
+    panY: () => number;
+    PanelWidth: () => number;
+    PanelHeight: () => number;
+    isAnnotationOverlay?: boolean;
+    zoomScaling: () => number;
+    layoutDoc: Doc;
+    cachedCenteringShiftX: number;
+    cachedCenteringShiftY: number;
+}
+@observer
+class CollectionFreeFormBackgroundGrid extends React.Component<CollectionFreeFormViewBackgroundGridProps> {
+
+
+    chooseGridSpace = (gridSpace: number): number => {
+        if (!this.props.zoomScaling()) return 50;
+        const divisions = this.props.PanelWidth() / this.props.zoomScaling() / gridSpace + 3;
+        return divisions < 60 ? gridSpace : this.chooseGridSpace(gridSpace * 10);
+    }
+    render() {
+        const gridSpace = this.chooseGridSpace(NumCast(this.props.layoutDoc["_backgroundGrid-spacing"], 50));
+        const shiftX = (this.props.isAnnotationOverlay ? 0 : -this.props.panX() % gridSpace - gridSpace) * this.props.zoomScaling();
+        const shiftY = (this.props.isAnnotationOverlay ? 0 : -this.props.panY() % gridSpace - gridSpace) * this.props.zoomScaling();
+        const renderGridSpace = gridSpace * this.props.zoomScaling();
+        const w = this.props.PanelWidth() + 2 * renderGridSpace;
+        const h = this.props.PanelHeight() + 2 * renderGridSpace;
+        const strokeStyle = CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "rgba(255,255,255,0.5)" : "rgba(0, 0,0,0.5)";
+        return <canvas className="collectionFreeFormView-grid" width={w} height={h} style={{ transform: `translate(${shiftX}px, ${shiftY}px)` }}
+            ref={(el) => {
+                const ctx = el?.getContext('2d');
+                if (ctx) {
+                    const Cx = this.props.cachedCenteringShiftX % renderGridSpace;
+                    const Cy = this.props.cachedCenteringShiftY % renderGridSpace;
+                    ctx.lineWidth = Math.min(1, Math.max(0.5, this.props.zoomScaling()));
+                    ctx.setLineDash(gridSpace > 50 ? [3, 3] : [1, 5]);
+                    ctx.clearRect(0, 0, w, h);
+                    if (ctx) {
+                        ctx.strokeStyle = strokeStyle;
+                        ctx.beginPath();
+                        for (let x = Cx - renderGridSpace; x <= w - Cx; x += renderGridSpace) {
+                            ctx.moveTo(x, Cy - h);
+                            ctx.lineTo(x, Cy + h);
+                        }
+                        for (let y = Cy - renderGridSpace; y <= h - Cy; y += renderGridSpace) {
+                            ctx.moveTo(Cx - w, y);
+                            ctx.lineTo(Cx + w, y);
+                        }
+                        ctx.stroke();
+                    }
+                }
+            }} />;
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 08da682bb..b10b0912f 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -30,6 +30,9 @@ import "./MarqueeView.scss";
 import React = require("react");
 import { StyleLayers } from "../../StyleProvider";
 import { TreeView } from "../TreeView";
+import { VideoBox } from "../../nodes/VideoBox";
+import { ImageField, WebField } from "../../../../fields/URLField";
+import { pasteImageBitmap } from "../../nodes/WebBoxRenderer";
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -134,17 +137,15 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 })();
                 e.stopPropagation();
             } else if (e.key === "b" && e.ctrlKey) {
-                // e.preventDefault();
-                // navigator.clipboard.readText().then(text => {
-                //     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 {
-                //         this.pasteTable(ns, x, y);
-                //     }
-                // });
-                // e.stopPropagation();
-
+                document.body.focus(); // so that we can access the clipboard without an error
+                setTimeout(() =>
+                    pasteImageBitmap((data: any, error: any) => {
+                        error && console.log(error);
+                        data && VideoBox.convertDataUri(data, this.props.Document[Id] + "-thumb-frozen").then(returnedfilename => {
+                            this.props.Document["thumb-frozen"] = new ImageField(returnedfilename);
+                        });
+                    }));
+            } else if (e.key === "s" && e.ctrlKey) {
                 e.preventDefault();
                 const slide = Doc.copyDragFactory(Doc.UserDoc().emptySlide as Doc)!;
                 slide.x = x;
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index b0030471d..58ea7410d 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -2,9 +2,7 @@ import { action, computed, Lambda, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from "react";
 import { Doc, Opt } from '../../../../fields/Doc';
-import { documentSchema } from '../../../../fields/documentSchemas';
 import { Id } from '../../../../fields/FieldSymbols';
-import { makeInterface } from '../../../../fields/Schema';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, OmitKeys, returnFalse, setupMoveUpEvents } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
@@ -20,11 +18,8 @@ import { CollectionSubView } from '../CollectionSubView';
 import "./CollectionGridView.scss";
 import Grid, { Layout } from "./Grid";
 
-type GridSchema = makeInterface<[typeof documentSchema]>;
-const GridSchema = makeInterface(documentSchema);
-
 @observer
-export class CollectionGridView extends CollectionSubView(GridSchema) {
+export class CollectionGridView extends CollectionSubView() {
     private _containerRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _changeListenerDisposer: Opt<Lambda>; // listens for changes in this.childLayoutPairs
     private _resetListenerDisposer: Opt<Lambda>; // listens for when the reset button is clicked
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 70c8c9436..160134b60 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -4,9 +4,7 @@ import { action, IReactionDisposer, observable, reaction, runInAction } from 'mo
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, HeightSym, Opt, WidthSym } from '../../../../fields/Doc';
-import { documentSchema } from '../../../../fields/documentSchemas';
 import { Id } from '../../../../fields/FieldSymbols';
-import { makeInterface } from '../../../../fields/Schema';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnTrue, Utils } from '../../../../Utils';
 import { DocUtils } from '../../../documents/Documents';
@@ -25,9 +23,6 @@ import { CollectionViewType } from '../CollectionView';
 import "./CollectionLinearView.scss";
 
 
-type LinearDocument = makeInterface<[typeof documentSchema,]>;
-const LinearDocument = makeInterface(documentSchema);
-
 /**
  * CollectionLinearView is the class for rendering the horizontal collection 
  * of documents, it useful for horizontal menus. It can either be expandable
@@ -37,7 +32,7 @@ const LinearDocument = makeInterface(documentSchema);
  * - It is used for the context sensitive toolbar at the top (see contMenuButtons() in CollectionMenu.tsx)
  */
 @observer
-export class CollectionLinearView extends CollectionSubView(LinearDocument) {
+export class CollectionLinearView extends CollectionSubView() {
     @observable public addMenuToggle = React.createRef<HTMLInputElement>();
     @observable private _selectedIndex = -1;
     private _dropDisposer?: DragManager.DragDropDisposer;
@@ -78,7 +73,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
             { fireImmediately: true }
         );
     }
-    protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
+    protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view
         this._dropDisposer && this._dropDisposer();
         if (ele) {
             this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index ec1cbadd5..2bdf92417 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -2,11 +2,9 @@ import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from "react";
 import { Doc } from '../../../../fields/Doc';
-import { documentSchema } from '../../../../fields/documentSchemas';
 import { List } from '../../../../fields/List';
-import { makeInterface } from '../../../../fields/Schema';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { returnFalse, emptyPath, returnEmptyDoclist, emptyFunction } from '../../../../Utils';
+import { emptyFunction, returnFalse } from '../../../../Utils';
 import { DragManager, dropActionType } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
@@ -16,8 +14,6 @@ import "./CollectionMulticolumnView.scss";
 import ResizeBar from './MulticolumnResizer';
 import WidthLabel from './MulticolumnWidthLabel';
 
-type MulticolumnDocument = makeInterface<[typeof documentSchema]>;
-const MulticolumnDocument = makeInterface(documentSchema);
 
 interface WidthSpecifier {
     magnitude: number;
@@ -38,7 +34,7 @@ const resolvedUnits = Object.values(DimUnit);
 const resizerWidth = 8;
 
 @observer
-export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocument) {
+export class CollectionMulticolumnView extends CollectionSubView() {
 
     /**
      * @returns the list of layout documents whose width unit is
@@ -51,7 +47,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu
 
     @computed
     private get minimumDim() {
-        return Math.min(...this.ratioDefinedDocs.filter(layout => layout._dimMagnitude).map(layout => NumCast(layout._dimMagnitude)));
+        const ratioDocs = this.ratioDefinedDocs.filter(layout => layout._dimMagnitude);
+        return ratioDocs.length ? Math.min(...ratioDocs.map(layout => NumCast(layout._dimMagnitude))) : 1;
     }
 
     /**
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index a2d51e2e7..7e2b83230 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -2,11 +2,9 @@ import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from "react";
 import { Doc } from '../../../../fields/Doc';
-import { documentSchema } from '../../../../fields/documentSchemas';
 import { List } from '../../../../fields/List';
-import { makeInterface } from '../../../../fields/Schema';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { returnFalse, emptyPath, returnEmptyDoclist, emptyFunction } from '../../../../Utils';
+import { emptyFunction, returnFalse } from '../../../../Utils';
 import { DragManager, dropActionType } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
@@ -16,9 +14,6 @@ import "./CollectionMultirowView.scss";
 import HeightLabel from './MultirowHeightLabel';
 import ResizeBar from './MultirowResizer';
 
-type MultirowDocument = makeInterface<[typeof documentSchema]>;
-const MultirowDocument = makeInterface(documentSchema);
-
 interface HeightSpecifier {
     magnitude: number;
     unit: string;
@@ -38,7 +33,7 @@ const resolvedUnits = Object.values(DimUnit);
 const resizerHeight = 8;
 
 @observer
-export class CollectionMultirowView extends CollectionSubView(MultirowDocument) {
+export class CollectionMultirowView extends CollectionSubView() {
 
     /**
      * @returns the list of layout documents whose width unit is
@@ -51,7 +46,8 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument)
 
     @computed
     private get minimumDim() {
-        return Math.min(...this.ratioDefinedDocs.filter(layout => layout._dimMagnitude).map(layout => NumCast(layout._dimMagnitude)));
+        const ratioDocs = this.ratioDefinedDocs.filter(layout => layout._dimMagnitude);
+        return ratioDocs.length ? Math.min(...ratioDocs.map(layout => NumCast(layout._dimMagnitude))) : 1;
     }
 
     /**
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
index a439a7998..c2bb3b3ac 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
@@ -2,19 +2,18 @@ import React = require("react");
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
+import { extname } from "path";
 import DatePicker from "react-datepicker";
-import "react-datepicker/dist/react-datepicker.css";
 import { CellInfo } from "react-table";
-import "react-table/react-table.css";
 import { DateField } from "../../../../fields/DateField";
 import { Doc, DocListCast, Field, Opt } from "../../../../fields/Doc";
 import { Id } from "../../../../fields/FieldSymbols";
 import { List } from "../../../../fields/List";
 import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
 import { ComputedField } from "../../../../fields/ScriptField";
-import { BoolCast, Cast, DateCast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
+import { BoolCast, Cast, DateCast, FieldValue, StrCast } from "../../../../fields/Types";
 import { ImageField } from "../../../../fields/URLField";
-import { Utils, emptyFunction } from "../../../../Utils";
+import { emptyFunction, Utils } from "../../../../Utils";
 import { Docs } from "../../../documents/Documents";
 import { DocumentType } from "../../../documents/DocumentTypes";
 import { DocumentManager } from "../../../util/DocumentManager";
@@ -29,9 +28,8 @@ import { EditableView } from "../../EditableView";
 import { MAX_ROW_HEIGHT } from '../../global/globalCssVariables.scss';
 import { DocumentIconContainer } from "../../nodes/DocumentIcon";
 import { OverlayView } from "../../OverlayView";
-import "./CollectionSchemaView.scss";
 import { CollectionView } from "../CollectionView";
-const path = require('path');
+import "./CollectionSchemaView.scss";
 
 // intialize cell properties
 export interface CellProps {
@@ -495,8 +493,8 @@ export class CollectionSchemaImageCell extends CollectionSchemaCell {
         if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href); // otherwise, put it through the cors proxy erver
         if (!/\.(png|jpg|jpeg|gif|webp)$/.test(url.href.toLowerCase())) return url.href;//Why is this here — good question
 
-        const ext = path.extname(url.href); // the extension of the file
-        return url.href.replace(ext, "_o" + path.extname(url.href));
+        const ext = extname(url.href);
+        return url.href.replace(ext, "_o" + ext);
     }
 
     render() {
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index b89246489..8b73351d5 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -4,7 +4,6 @@ import { action, computed, observable, untracked, trace } from "mobx";
 import { observer } from "mobx-react";
 import Measure from "react-measure";
 import { Resize } from "react-table";
-import "react-table/react-table.css";
 import { Doc, Opt } from "../../../../fields/Doc";
 import { List } from "../../../../fields/List";
 import { listSpec } from "../../../../fields/Schema";
@@ -47,7 +46,7 @@ const columnTypes: Map<string, ColumnType> = new Map([
 ]);
 
 @observer
-export class CollectionSchemaView extends CollectionSubView(doc => doc) {
+export class CollectionSchemaView extends CollectionSubView() {
     private _previewCont?: HTMLDivElement;
 
     @observable _previewDoc: Doc | undefined = undefined;
@@ -147,43 +146,43 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
 
         const anyType = <div className={"columnMenu-option" + (type === ColumnType.Any ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Any)}>
             <FontAwesomeIcon icon={"align-justify"} size="sm" />
-                Any
-            </div>;
+            Any
+        </div>;
 
         const numType = <div className={"columnMenu-option" + (type === ColumnType.Number ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Number)}>
             <FontAwesomeIcon icon={"hashtag"} size="sm" />
-                Number
-            </div>;
+            Number
+        </div>;
 
         const textType = <div className={"columnMenu-option" + (type === ColumnType.String ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.String)}>
             <FontAwesomeIcon icon={"font"} size="sm" />
             Text
-            </div>;
+        </div>;
 
         const boolType = <div className={"columnMenu-option" + (type === ColumnType.Boolean ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Boolean)}>
             <FontAwesomeIcon icon={"check-square"} size="sm" />
             Checkbox
-            </div>;
+        </div>;
 
         const listType = <div className={"columnMenu-option" + (type === ColumnType.List ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.List)}>
             <FontAwesomeIcon icon={"list-ul"} size="sm" />
             List
-            </div>;
+        </div>;
 
         const docType = <div className={"columnMenu-option" + (type === ColumnType.Doc ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Doc)}>
             <FontAwesomeIcon icon={"file"} size="sm" />
             Document
-            </div>;
+        </div>;
 
         const imageType = <div className={"columnMenu-option" + (type === ColumnType.Image ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Image)}>
             <FontAwesomeIcon icon={"image"} size="sm" />
             Image
-            </div>;
+        </div>;
 
         const dateType = <div className={"columnMenu-option" + (type === ColumnType.Date ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Date)}>
             <FontAwesomeIcon icon={"calendar"} size="sm" />
-                Date
-                </div>;
+            Date
+        </div>;
 
 
         const allColumnTypes = <div className="columnMenu-types">
@@ -557,7 +556,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
             style={{
                 overflow: this.props.scrollOverflow === true ? "scroll" : undefined, backgroundColor: "white",
                 pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined,
-                width: name === "collectionSchemaView-searchContainer" ? "auto" : this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative",
+                width: this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative",
             }}  >
             <div className="collectionSchemaView-tableContainer"
                 style={{ width: `calc(100% - ${this.previewWidth()}px)` }}
diff --git a/src/client/views/collections/collectionSchema/SchemaTable.tsx b/src/client/views/collections/collectionSchema/SchemaTable.tsx
index 2219345f6..605481ddf 100644
--- a/src/client/views/collections/collectionSchema/SchemaTable.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx
@@ -4,7 +4,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable, trace } from "mobx";
 import { observer } from "mobx-react";
 import ReactTable, { CellInfo, Column, ComponentPropsGetterR, Resize, SortingRule } from "react-table";
-import "react-table/react-table.css";
 import { DateField } from "../../../../fields/DateField";
 import { AclPrivate, AclReadonly, DataSym, Doc, DocListCast, Field, Opt } from "../../../../fields/Doc";
 import { Id } from "../../../../fields/FieldSymbols";
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index f5de31fcb..be18cc1de 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -42,9 +42,6 @@ declare class MediaRecorder {
     constructor(e: any); // whatever MediaRecorder has
 }
 
-type AudioDocument = makeInterface<[typeof documentSchema]>;
-const AudioDocument = makeInterface(documentSchema);
-
 enum media_state {
     PendingRecording = "pendingRecording",
     Recording = "recording",
@@ -54,7 +51,7 @@ enum media_state {
 
 
 @observer
-export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, AudioDocument>(AudioDocument) {
+export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
 
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(AudioBox, fieldKey); }
     public static Enabled = false;
@@ -240,7 +237,9 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this.dataDoc[this.fieldKey + "-recordingStart"] = new DateField();
         DocUtils.ActiveRecordings.push(this);
         this._recorder.ondataavailable = async (e: any) => {
+            console.log("Data available", e);
             const [{ result }] = await Networking.UploadFilesToServer(e.data);
+            console.log("Data result", result);
             if (!(result instanceof Error)) {
                 this.props.Document[this.fieldKey] = new AudioField(result.accessPaths.agnostic.client);
             }
@@ -660,4 +659,4 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             {!this.path ? this.recordingControls : this.playbackControls}
         </div>;
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index fe34d6687..c2a526804 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -1,7 +1,6 @@
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, Opt } from "../../../fields/Doc";
-import { Document } from "../../../fields/documentSchemas";
 import { List } from "../../../fields/List";
 import { listSpec } from "../../../fields/Schema";
 import { ComputedField } from "../../../fields/ScriptField";
@@ -23,25 +22,27 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined;
     sizeProvider?: (doc: Doc, replica: string) => { width: number, height: number } | undefined;
     layerProvider: ((doc: Doc, assign?: boolean) => boolean) | undefined;
+    renderCutoffProvider: (doc: Doc) => boolean;
     zIndex?: number;
     highlight?: boolean;
     jitterRotation: number;
     dataTransition?: string;
     replica: string;
+    renderIndex: number;
     CollectionFreeFormView: CollectionFreeFormView;
 }
 
 @observer
-export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps, Document>(Document) {
+export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeFormDocumentViewProps>() {
     public static animFields = ["_height", "_width", "x", "y", "_scrollTop", "opacity"];  // fields that are configured to be animatable using animation frames
     @observable _animPos: number[] | undefined = undefined;
     @observable _contentView: DocumentView | undefined | null;
     get displayName() { return "CollectionFreeFormDocumentView(" + this.rootDoc.title + ")"; } // this makes mobx trace() statements more descriptive
     get maskCentering() { return this.props.Document.isInkMask ? InkingStroke.MaskDim / 2 : 0; }
     get transform() { return `translate(${this.X - this.maskCentering}px, ${this.Y - this.maskCentering}px) rotate(${this.props.jitterRotation}deg)`; }
-    get X() { return this.dataProvider ? this.dataProvider.x : (this.Document.x || 0); }
-    get Y() { return this.dataProvider ? this.dataProvider.y : (this.Document.y || 0); }
-    get ZInd() { return this.dataProvider ? this.dataProvider.zIndex : (this.Document.zIndex || 0); }
+    get X() { return this.dataProvider ? this.dataProvider.x : NumCast(this.Document.x); }
+    get Y() { return this.dataProvider ? this.dataProvider.y : NumCast(this.Document.y); }
+    get ZInd() { return this.dataProvider ? this.dataProvider.zIndex : NumCast(this.Document.zIndex); }
     get Opacity() { return this.dataProvider ? this.dataProvider.opacity : undefined; }
     get Highlight() { return this.dataProvider?.highlight; }
     @computed get dataProvider() { return this.props.dataProvider?.(this.props.Document, this.props.replica); }
@@ -176,7 +177,11 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 mixBlendMode,
                 display: this.ZInd === -99 ? "none" : undefined
             }} >
-            <DocumentView {...divProps} ref={action((r: DocumentView | null) => this._contentView = r)} />
+            {this.props.renderCutoffProvider(this.props.Document) ?
+                <div style={{ position: "absolute", width: this.panelWidth(), height: this.panelHeight(), background: "lightGreen" }} />
+                :
+                <DocumentView {...divProps} ref={action((r: DocumentView | null) => this._contentView = r)} />
+            }
         </div>;
     }
 }
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 8da5cd1b1..d975baf9b 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -3,9 +3,7 @@ import { action } from "mobx";
 import { observer } from "mobx-react";
 import { ColorState, SketchPicker } from 'react-color';
 import { Doc, HeightSym, WidthSym } from '../../../fields/Doc';
-import { documentSchema } from "../../../fields/documentSchemas";
 import { InkTool } from "../../../fields/InkField";
-import { makeInterface } from "../../../fields/Schema";
 import { StrCast } from "../../../fields/Types";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
@@ -17,11 +15,8 @@ import "./ColorBox.scss";
 import { FieldView, FieldViewProps } from './FieldView';
 import { RichTextMenu } from "./formattedText/RichTextMenu";
 
-type ColorDocument = makeInterface<[typeof documentSchema]>;
-const ColorDocument = makeInterface(documentSchema);
-
 @observer
-export class ColorBox extends ViewBoxBaseComponent<FieldViewProps, ColorDocument>(ColorDocument) {
+export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ColorBox, fieldKey); }
 
     @undoBatch
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index d47e8340c..cbc61ffdb 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -2,8 +2,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, observable } from 'mobx';
 import { observer } from "mobx-react";
 import { Doc, Opt } from '../../../fields/Doc';
-import { documentSchema } from '../../../fields/documentSchemas';
-import { createSchema, makeInterface } from '../../../fields/Schema';
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
 import { emptyFunction, OmitKeys, returnFalse, setupMoveUpEvents } from '../../../Utils';
 import { DragManager } from '../../util/DragManager';
@@ -15,15 +13,10 @@ import "./ComparisonBox.scss";
 import { DocumentView, DocumentViewProps } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import React = require("react");
-import { DocumentType } from '../../documents/DocumentTypes';
 
-export const comparisonSchema = createSchema({});
-
-type ComparisonDocument = makeInterface<[typeof comparisonSchema, typeof documentSchema]>;
-const ComparisonDocument = makeInterface(comparisonSchema, documentSchema);
 
 @observer
-export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ComparisonDocument>(ComparisonDocument) {
+export class ComparisonBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ComparisonBox, fieldKey); }
     protected _multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined;
     private _disposers: (DragManager.DragDropDisposer | undefined)[] = [undefined, undefined];
diff --git a/src/client/views/nodes/DocumentIcon.tsx b/src/client/views/nodes/DocumentIcon.tsx
index 123212608..433a0bf48 100644
--- a/src/client/views/nodes/DocumentIcon.tsx
+++ b/src/client/views/nodes/DocumentIcon.tsx
@@ -2,7 +2,7 @@ import { observer } from "mobx-react";
 import * as React from "react";
 import { DocumentView } from "./DocumentView";
 import { DocumentManager } from "../../util/DocumentManager";
-import { Transformer, Scripting, ts } from "../../util/Scripting";
+import { Transformer, ts } from "../../util/Scripting";
 import { Field } from "../../../fields/Doc";
 
 @observer
diff --git a/src/client/views/nodes/DocumentLinksButton.scss b/src/client/views/nodes/DocumentLinksButton.scss
index 228e1bdcb..9ab3171d3 100644
--- a/src/client/views/nodes/DocumentLinksButton.scss
+++ b/src/client/views/nodes/DocumentLinksButton.scss
@@ -1,6 +1,8 @@
 @import "../global/globalCssVariables.scss";
 
-
+.documentLinksButton-wrapper {
+    transform-origin: top left;
+}
 .documentLinksButton-menu {
     width: 100%;
     height: 100%;
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 93cd02d93..7e6ca4248 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -20,6 +20,7 @@ import { DocumentView } from "./DocumentView";
 import { LinkDescriptionPopup } from "./LinkDescriptionPopup";
 import { TaskCompletionBox } from "./TaskCompletedBox";
 import React = require("react");
+import { Transform } from "../../util/Transform";
 
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
@@ -31,6 +32,7 @@ interface DocumentLinksButtonProps {
     AlwaysOn?: boolean;
     InMenu?: boolean;
     StartLink?: boolean; //whether the link HAS been started (i.e. now needs to be completed)
+    ContentScaling?: () => number;
 }
 @observer
 export class DocumentLinksButton extends React.Component<DocumentLinksButtonProps, {}> {
@@ -302,16 +304,16 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
         const title = this.props.InMenu ? menuTitle : buttonTitle;
 
         //render circular tooltip if it isn't set to invisible and show the number of doc links the node has, and render inner-menu link button for starting/stopping links if currently in menu
-        return !Array.from(this.filteredLinks).length && !this.props.AlwaysOn ? (null) :
-            this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink) ?
-                <Tooltip title={<div className="dash-tooltip">{title}</div>}>
-                    {this.linkButtonInner}
-                </Tooltip>
-                :
-                !DocumentLinksButton.LinkEditorDocView && !this.props.InMenu ?
-                    <Tooltip title={<div className="dash-tooltip">{title}</div>}>
-                        {this.linkButtonInner}
-                    </Tooltip>
-                    : this.linkButtonInner;
+        return (!Array.from(this.filteredLinks).length && !this.props.AlwaysOn) ? (null) :
+            <div className="documentLinksButton-wrapper" >
+                {
+                    (this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink)) ||
+                        (!DocumentLinksButton.LinkEditorDocView && !this.props.InMenu) ?
+                        <Tooltip title={<div className="dash-tooltip">{title}</div>}>
+                            {this.linkButtonInner}
+                        </Tooltip>
+                        : this.linkButtonInner
+                }
+            </div>;
     }
 }
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 9fcd45e72..4565f8504 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -58,9 +58,10 @@
     .documentView-audioBackground {
         display: inline-block;
         width: 10%;
+        height: 25;
         position: absolute;
-        top: 0px;
-        left: 0px;
+        top: 10px;
+        left: 10px;
         border-radius: 25px;
         background: white;
         opacity: 0.3;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 707c15bff..e7de87ea5 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,3 +1,4 @@
+import { IconProp } from "@fortawesome/fontawesome-svg-core";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
@@ -9,11 +10,11 @@ import { List } from "../../../fields/List";
 import { ObjectField } from "../../../fields/ObjectField";
 import { listSpec } from "../../../fields/Schema";
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
+import { BoolCast, Cast, ImageCast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
 import { AudioField } from "../../../fields/URLField";
 import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
 import { MobileInterface } from '../../../mobile/MobileInterface';
-import { emptyFunction, hasDescendantTarget, OmitKeys, returnTrue, returnVal, Utils, lightOrDark, simulateMouseClick, returnEmptyString } from "../../../Utils";
+import { emptyFunction, hasDescendantTarget, lightOrDark, OmitKeys, returnEmptyString, returnTrue, returnVal, simulateMouseClick, Utils } from "../../../Utils";
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -23,7 +24,7 @@ import { DocumentManager } from "../../util/DocumentManager";
 import { DragManager, dropActionType } from "../../util/DragManager";
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { LinkManager } from '../../util/LinkManager';
-import { Scripting } from '../../util/Scripting';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SelectionManager } from "../../util/SelectionManager";
 import { SharingManager } from '../../util/SharingManager';
 import { SnappingManager } from '../../util/SnappingManager';
@@ -48,8 +49,6 @@ import { RadialMenu } from './RadialMenu';
 import { ScriptingBox } from "./ScriptingBox";
 import { PresBox } from './trails/PresBox';
 import React = require("react");
-import { IconProp } from "@fortawesome/fontawesome-svg-core";
-import { ColorScheme } from "../../util/SettingsManager";
 const { Howl } = require('howler');
 
 interface Window {
@@ -110,6 +109,7 @@ export interface DocumentViewSharedProps {
     fitContentsToDoc?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitToBox property on a Document
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
+    thumbShown?: () => boolean;
     setContentView?: (view: DocComponentView) => any;
     CollectionFreeFormDocumentView?: () => CollectionFreeFormDocumentView;
     PanelWidth: () => number;
@@ -179,11 +179,12 @@ export interface DocumentViewInternalProps extends DocumentViewProps {
 }
 
 @observer
-export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps, Document>(Document) {
+export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps>() {
     public static SelectAfterContextMenu = true; //  whether a document should be selected after it's contextmenu is triggered.
     @observable _animateScalingTo = 0;
     @observable _mediaState = 0;
     @observable _pendingDoubleClick = false;
+    private _disposers: { [name: string]: IReactionDisposer } = {};
     private _downX: number = 0;
     private _downY: number = 0;
     private _firstX: number = -1;
@@ -204,6 +205,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     public get LayoutFieldKey() { return Doc.LayoutFieldKey(this.layoutDoc); }
     @computed get ShowTitle() { return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.ShowTitle) as (Opt<string>); }
     @computed get ContentScale() { return this.props.ContentScaling?.() || 1; }
+    @computed get thumb() { return ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url.href.replace(".png", "_m.png"); }
     @computed get hidden() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Hidden); }
     @computed get opacity() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Opacity); }
     @computed get boxShadow() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BoxShadow); }
@@ -239,6 +241,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         this._multiTouchDisposer?.();
         this._holdDisposer?.();
         unbrush && Doc.UnBrushDoc(this.props.Document);
+        Object.values(this._disposers).forEach(disposer => disposer?.());
     }
 
     handle1PointerHoldStart = (e: Event, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): any => {
@@ -485,6 +488,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 const func = () => this.onClickHandler.script.run({
                     this: this.layoutDoc,
                     self: this.rootDoc,
+                    _readOnly_: false,
                     scriptContext: this.props.scriptContext,
                     thisContainer: this.props.ContainingCollectionDoc,
                     documentView: this.props.DocumentView(),
@@ -836,14 +840,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             this._componentView?.isAnyChildContentActive?.() ||
             this.props.isContentActive()) ? true : undefined;
     }
+    @observable _retryThumb = 1;
+    thumbShown = () => !this.props.isSelected() && LightboxView.LightboxDoc !== this.rootDoc && this.thumb &&
+        !this._componentView?.isAnyChildContentActive?.() ? true : false;
     @computed get contents() {
         TraceMobx();
         const audioView = !this.layoutDoc._showAudio ? (null) :
-            <div className="documentView-audioBackground"
-                onPointerDown={this.recordAudioAnnotation}
-                onPointerEnter={this.onPointerEnter}
-                style={{ height: 25, position: "absolute", top: 10, left: 10 }}
-            >
+            <div className="documentView-audioBackground" onPointerDown={this.recordAudioAnnotation} onPointerEnter={this.onPointerEnter} >
                 <FontAwesomeIcon className="documentView-audioFont"
                     style={{ color: [DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]).length ? "blue" : "gray", "green", "red"][this._mediaState] }}
                     icon={!DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]).length ? "microphone" : "file-audio"} size="sm" />
@@ -853,8 +856,17 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 pointerEvents: this.props.pointerEvents as any ? this.props.pointerEvents as any : (this.rootDoc.type !== DocumentType.INK && ((this.props.contentPointerEvents as any) || (this.isContentActive())) ? "all" : "none"),
                 height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
             }}>
-            <DocumentContentsView key={1} {...this.props}
+            {!this._retryThumb || !this.thumbShown() ? (null) :
+                <img style={{ background: "white", top: 0, position: "absolute" }} src={this.thumb} // + '?d=' + (new Date()).getTime()}
+                    width={this.props.PanelWidth()} height={this.props.PanelHeight()}
+                    onError={(e: any) => {
+                        setTimeout(action(() => this._retryThumb = 0), 0);
+                        setTimeout(action(() => this._retryThumb = 1), 150);
+                    }} />}
+            <DocumentContentsView key={1}
+                {...this.props}
                 docViewPath={this.props.viewPath}
+                thumbShown={this.thumbShown}
                 setContentView={this.setContentView}
                 scaling={this.contentScaling}
                 PanelHeight={this.panelHeight}
@@ -866,11 +878,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 focus={this.focus}
                 layoutKey={this.finalLayoutKey} />
             {this.layoutDoc.hideAllLinks ? (null) : this.allLinkEndpoints}
-            {this.hideLinkButton || this.props.renderDepth === -1 ? (null) :
-                <div style={{ transformOrigin: "top left", transform: `scale(${Math.min(1, this.props.ScreenToLocalTransform().scale(this.props.ContentScaling?.() || 1).Scale)})` }}>
-                    <DocumentLinksButton View={this.props.DocumentView()} Offset={[this.topMost ? 0 : -15, undefined, undefined, this.topMost ? 10 : -20]} />
-                </div>}
-
+            {this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? (null) :
+                <DocumentLinksButton View={this.props.DocumentView()}
+                    ContentScaling={this.props.ContentScaling}
+                    Offset={[this.topMost ? 0 : -15, undefined, undefined, this.topMost ? 10 : -20]} />
+            }
             {audioView}
         </div>;
     }
@@ -984,7 +996,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewInternalProps>, property: string) => this.props?.styleProvider?.(doc, props, property + ":caption");
     @computed get innards() {
         TraceMobx();
-        const ffscale = (this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.ScreenToLocalTransform().Scale || 1);
+        const ffscale = () => (this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.ScreenToLocalTransform().Scale || 1);
         const showTitle = this.ShowTitle?.split(":")[0];
         const showTitleHover = this.ShowTitle?.includes(":hover");
         const showCaption = !this.props.hideCaptions && this.Document._viewType !== CollectionViewType.Carousel ? StrCast(this.layoutDoc._showCaption) : undefined;
@@ -992,14 +1004,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             <div className="documentView-captionWrapper"
                 style={{
                     pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : this.isContentActive() || this.props.isDocumentActive?.() ? "all" : undefined,
-                    minWidth: 50 * ffscale,
-                    maxHeight: `max(100%, ${20 * ffscale}px)`
+                    minWidth: 50 * ffscale(),
+                    maxHeight: `max(100%, ${20 * ffscale()}px)`
                 }}>
                 <FormattedTextBox {...OmitKeys(this.props, ['children']).omit}
                     yPadding={10}
                     xPadding={10}
                     fieldKey={showCaption}
-                    fontSize={12 * Math.max(1, 2 * ffscale / 3)}
+                    fontSize={12 * Math.max(1, 2 * ffscale() / 3)}
                     styleProvider={this.captionStyleProvider}
                     dontRegisterView={true}
                     noSidebar={true}
@@ -1023,19 +1035,18 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     display={"block"}
                     fontSize={10}
                     GetValue={() => showTitle.split(";").length === 1 ? showTitle + "=" + Field.toString(targetDoc[showTitle.split(";")[0]] as any as Field) : "#" + showTitle}
-                    SetValue={undoBatch(input => {
+                    SetValue={undoBatch((input: string) => {
                         if (input?.startsWith("#")) {
                             if (this.props.showTitle) {
                                 this.rootDoc._showTitle = input?.substring(1) ? input.substring(1) : undefined;
                             } else {
                                 Doc.UserDoc().showTitle = input?.substring(1) ? input.substring(1) : "creationDate";
                             }
-                            return true;
                         } else {
-                            var value = input.replace(new RegExp(showTitle + "="), "");
+                            var value = input.replace(new RegExp(showTitle + "="), "") as string | number;
                             if (showTitle !== "title" && Number(value).toString() === value) value = Number(value);
                             if (showTitle.includes("Date") || showTitle === "author") return true;
-                            return Doc.SetInPlace(targetDoc, showTitle, value, true) ? true : true;
+                            Doc.SetInPlace(targetDoc, showTitle, value, true);
                         }
                         return true;
                     })}
@@ -1048,15 +1059,17 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 {captionView}
             </div>;
     }
+    @observable _: string = "";
     @computed get renderDoc() {
         TraceMobx();
-        const isButton: boolean = this.props.Document.type === DocumentType.FONTICON;
+        const thumb = ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url.href.replace(".png", "_m.png");
+        const isButton = this.props.Document.type === DocumentType.FONTICON;
         if (!(this.props.Document instanceof Doc) || GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate || this.hidden) return null;
         return this.docContents ??
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 id={this.props.Document[Id]}
                 style={{
-                    background: isButton ? undefined : this.backgroundColor,
+                    background: isButton || thumb ? undefined : this.backgroundColor,
                     opacity: this.opacity,
                     color: StrCast(this.layoutDoc.color, "inherit"),
                     fontFamily: StrCast(this.Document._fontFamily, "inherit"),
@@ -1065,6 +1078,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined,
                     transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : `transform 0.5s ease-${this._animateScalingTo < 1 ? "in" : "out"}`,
                 }}>
+
                 {this.innards}
                 {this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <div className="documentView-contentBlocker" /> : (null)}
                 {this.widgetDecorations ?? null}
@@ -1143,7 +1157,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     get LayoutFieldKey() { return this.docView?.LayoutFieldKey || "layout"; }
     get fitWidth() { return this.props.fitWidth?.(this.rootDoc) || this.layoutDoc.fitWidth; }
 
-    @computed get docViewPath() { return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this]; }
+    @computed get docViewPath(): DocumentView[] { return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this]; }
     @computed get layoutDoc() { return Doc.Layout(this.Document, this.props.LayoutTemplate?.()); }
     @computed get nativeWidth() {
         return this.docView?._componentView?.reverseNativeScaling?.() ? 0 :
@@ -1281,7 +1295,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     }
 }
 
-Scripting.addGlobal(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) {
+ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) {
     if (dv.Document.layoutKey === "layout_" + detailLayoutKeySuffix) dv.switchViews(false, "layout");
     else dv.switchViews(true, detailLayoutKeySuffix);
 });
\ No newline at end of file
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index f1f802c13..c170f9867 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -3,25 +3,18 @@ import { action, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { WidthSym } from '../../../fields/Doc';
-import { documentSchema } from '../../../fields/documentSchemas';
 import { Id } from '../../../fields/FieldSymbols';
-import { createSchema, makeInterface } from '../../../fields/Schema';
 import { NumCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
 import { Docs } from '../../documents/Documents';
 import { ViewBoxBaseComponent } from '../DocComponent';
 import { LightboxView } from '../LightboxView';
-import { FieldView, FieldViewProps } from './FieldView';
 import './EquationBox.scss';
+import { FieldView, FieldViewProps } from './FieldView';
 
 
-const EquationSchema = createSchema({});
-
-type EquationDocument = makeInterface<[typeof EquationSchema, typeof documentSchema]>;
-const EquationDocument = makeInterface(EquationSchema, documentSchema);
-
 @observer
-export class EquationBox extends ViewBoxBaseComponent<FieldViewProps, EquationDocument>(EquationDocument) {
+export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(EquationBox, fieldKey); }
     public static SelectOnLoad: string = "";
     _ref: React.RefObject<EquationEditor> = React.createRef();
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index fb8e89da9..ba65acee0 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -4,18 +4,17 @@ import { action, computed, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import Select from "react-select";
 import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt } from "../../../fields/Doc";
-import { documentSchema } from "../../../fields/documentSchemas";
 import { List } from "../../../fields/List";
 import { RichTextField } from "../../../fields/RichTextField";
-import { listSpec, makeInterface } from "../../../fields/Schema";
+import { listSpec } from "../../../fields/Schema";
 import { ComputedField, ScriptField } from "../../../fields/ScriptField";
-import { Cast, StrCast, NumCast } from "../../../fields/Types";
+import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 import { UserOptions } from "../../util/GroupManager";
-import { Scripting } from "../../util/Scripting";
+import { ScriptingGlobals } from "../../util/ScriptingGlobals";
 import { SelectionManager } from "../../util/SelectionManager";
 import { CollectionTreeView } from "../collections/CollectionTreeView";
 import { CollectionView } from "../collections/CollectionView";
@@ -30,11 +29,8 @@ const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
-type FilterBoxDocument = makeInterface<[typeof documentSchema]>;
-const FilterBoxDocument = makeInterface(documentSchema);
-
 @observer
-export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDocument>(FilterBoxDocument) {
+export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     constructor(props: Readonly<FieldViewProps>) {
         super(props);
@@ -425,7 +421,6 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
                     treeViewHideTitle={true}
                     focus={returnFalse}
                     treeViewHideHeaderFields={false}
-                    onCheckedClick={this.scriptField}
                     dontRegisterView={true}
                     styleProvider={this.FilterStyleProvider}
                     layerProvider={this.props.layerProvider}
@@ -475,7 +470,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc
     }
 }
 
-Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) {
+ScriptingGlobals.add(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) {
     const docFilters = Cast(layoutDoc._docFilters, listSpec("string"), []);
     for (const filter of docFilters) {
         const fields = filter.split(":"); // split into key:value:modifiers
@@ -485,7 +480,7 @@ Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader:
     }
     return undefined;
 });
-Scripting.addGlobal(function readFacetData(layoutDoc: Doc, childKey: string, facetHeader: string) {
+ScriptingGlobals.add(function readFacetData(layoutDoc: Doc, childKey: string, facetHeader: string) {
     const allCollectionDocs = new Set<Doc>();
     const activeTabs = DocListCast(layoutDoc[childKey]);
     SearchBox.foreachRecursiveDoc(activeTabs, (depth: number, doc: Doc) => allCollectionDocs.add(doc));
diff --git a/src/client/views/nodes/FunctionPlotBox.tsx b/src/client/views/nodes/FunctionPlotBox.tsx
index 5050fc2d2..3ab0a3ff2 100644
--- a/src/client/views/nodes/FunctionPlotBox.tsx
+++ b/src/client/views/nodes/FunctionPlotBox.tsx
@@ -19,7 +19,7 @@ type EquationDocument = makeInterface<[typeof EquationSchema, typeof documentSch
 const EquationDocument = makeInterface(EquationSchema, documentSchema);
 
 @observer
-export class FunctionPlotBox extends ViewBoxBaseComponent<FieldViewProps, EquationDocument>(EquationDocument) {
+export class FunctionPlotBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FunctionPlotBox, fieldKey); }
     public static GraphCount = 0;
     _plot: any;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 89f70985c..0a4168698 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,12 +1,12 @@
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
 import { observer } from "mobx-react";
+import { extname } from 'path';
 import { DataSym, Doc, DocListCast, WidthSym } from '../../../fields/Doc';
-import { documentSchema } from '../../../fields/documentSchemas';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
 import { List } from '../../../fields/List';
 import { ObjectField } from '../../../fields/ObjectField';
-import { createSchema, makeInterface } from '../../../fields/Schema';
+import { createSchema } from '../../../fields/Schema';
 import { ComputedField } from '../../../fields/ScriptField';
 import { Cast, NumCast } from '../../../fields/Types';
 import { ImageField } from '../../../fields/URLField';
@@ -29,16 +29,11 @@ import { FaceRectangles } from './FaceRectangles';
 import { FieldView, FieldViewProps } from './FieldView';
 import "./ImageBox.scss";
 import React = require("react");
-import { SnappingManager } from '../../util/SnappingManager';
-const path = require('path');
 
 export const pageSchema = createSchema({
     googlePhotosUrl: "string",
     googlePhotosTags: "string"
 });
-type ImageDocument = makeInterface<[typeof pageSchema, typeof documentSchema]>;
-const ImageDocument = makeInterface(pageSchema, documentSchema);
-
 const uploadIcons = {
     idle: "downarrow.png",
     loading: "loading.gif",
@@ -47,7 +42,7 @@ const uploadIcons = {
 };
 
 @observer
-export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ImageDocument>(ImageDocument) {
+export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
     protected _multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined;
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); }
     private _imgRef: React.RefObject<HTMLImageElement> = React.createRef();
@@ -190,7 +185,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href);
         if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) return url.href;  //Why is this here
 
-        const ext = path.extname(url.href);
+        const ext = extname(url.href);
         return url.href.replace(ext, this._curSuffix + ext);
     }
 
@@ -286,7 +281,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         }
 
         return <div className="imageBox-cont" key={this.layoutDoc[Id]} ref={this.createDropTarget} onPointerDown={this.marqueeDown}>
-            <div className="imageBox-fader" style={{ overflow: this.props.docViewPath?.().lastElement().fitWidth ? "auto" : undefined }} >
+            <div className="imageBox-fader" style={{ overflow: Array.from(this.props.docViewPath?.()).slice(-1)[0].fitWidth ? "auto" : undefined }} >
                 <img key="paths" ref={this._imgRef}
                     src={srcpath}
                     style={{ transform, transformOrigin }}
@@ -376,7 +371,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     scrollTop={0}
                     down={this._marqueeing}
                     scaling={this.props.scaling}
-                    docView={this.props.docViewPath().lastElement()}
+                    docView={this.props.docViewPath().slice(-1)[0]}
                     addDocument={this.addDocument}
                     finishMarquee={this.finishMarquee}
                     savedAnnotations={this._savedAnnotations}
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 111509fdb..c44c8c828 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -58,7 +58,7 @@ export class KeyValueBox extends React.Component<FieldViewProps> {
         value = eq ? value.substr(1) : value;
         const dubEq = value.startsWith(":=") ? "computed" : value.startsWith(";=") ? "script" : false;
         value = dubEq ? value.substr(2) : value;
-        const options: ScriptOptions = { addReturn: true, params: { this: "Doc", _last_: "any" }, editable: false };
+        const options: ScriptOptions = { addReturn: true, params: { this: Doc.name, self: Doc.name, _last_: "any", _readOnly_: "boolean" }, editable: false };
         if (dubEq) options.typecheck = false;
         const script = CompileScript(value, options);
         return !script.compiled ? undefined : { script, type: dubEq, onDelegate: eq };
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index dae72bb78..0015f0b71 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -2,9 +2,8 @@ import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast } from '../../../fields/Doc';
-import { documentSchema } from '../../../fields/documentSchemas';
 import { List } from '../../../fields/List';
-import { createSchema, listSpec, makeInterface } from '../../../fields/Schema';
+import { listSpec } from '../../../fields/Schema';
 import { Cast, StrCast } from '../../../fields/Types';
 import { DragManager } from '../../util/DragManager';
 import { undoBatch } from '../../util/UndoManager';
@@ -16,17 +15,13 @@ import { FieldView, FieldViewProps } from './FieldView';
 import BigText from './LabelBigText';
 import './LabelBox.scss';
 
-const LabelSchema = createSchema({});
-
-type LabelDocument = makeInterface<[typeof LabelSchema, typeof documentSchema]>;
-const LabelDocument = makeInterface(LabelSchema, documentSchema);
 
 export interface LabelBoxProps {
     label?: string;
 }
 
 @observer
-export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxProps), LabelDocument>(LabelDocument) {
+export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxProps)>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LabelBox, fieldKey); }
     public static LayoutStringWithTitle(fieldType: { name: string }, fieldStr: string, label: string) {
         return `<${fieldType.name} fieldKey={'${fieldStr}'} label={'${label}'} {...props} />`;  //e.g., "<ImageBox {...props} fieldKey={"data} />"
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index a7bfb93eb..437d29f39 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -2,8 +2,6 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, observable } from "mobx";
 import { observer } from "mobx-react";
 import { Doc } from "../../../fields/Doc";
-import { documentSchema } from "../../../fields/documentSchemas";
-import { makeInterface } from "../../../fields/Schema";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { TraceMobx } from "../../../fields/util";
 import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils';
@@ -23,11 +21,9 @@ const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
-type LinkAnchorSchema = makeInterface<[typeof documentSchema]>;
-const LinkAnchorDocument = makeInterface(documentSchema);
 
 @observer
-export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps, LinkAnchorSchema>(LinkAnchorDocument) {
+export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkAnchorBox, fieldKey); }
     _doubleTap = false;
     _lastTap: number = 0;
diff --git a/src/client/views/nodes/LinkBox.tsx b/src/client/views/nodes/LinkBox.tsx
index 879a63248..43f4b43fb 100644
--- a/src/client/views/nodes/LinkBox.tsx
+++ b/src/client/views/nodes/LinkBox.tsx
@@ -1,7 +1,5 @@
 import React = require("react");
 import { observer } from "mobx-react";
-import { documentSchema } from "../../../fields/documentSchemas";
-import { makeInterface } from "../../../fields/Schema";
 import { emptyFunction, returnFalse } from "../../../Utils";
 import { ViewBoxBaseComponent } from "../DocComponent";
 import { StyleProp } from "../StyleProvider";
@@ -9,11 +7,8 @@ import { ComparisonBox } from "./ComparisonBox";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./LinkBox.scss";
 
-type LinkDocument = makeInterface<[typeof documentSchema]>;
-const LinkDocument = makeInterface(documentSchema);
-
 @observer
-export class LinkBox extends ViewBoxBaseComponent<FieldViewProps, LinkDocument>(LinkDocument) {
+export class LinkBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkBox, fieldKey); }
     isContentActiveFunc = () => this.isContentActive();
     render() {
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 50444c73a..aa2130af5 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,14 +1,11 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
-import * as dotenv from 'dotenv';
 import { action, computed, IReactionDisposer, observable, ObservableMap } from 'mobx';
 import { observer } from "mobx-react";
 import * as React from "react";
 import { Doc, DocListCast, Opt, WidthSym } from '../../../../fields/Doc';
-import { documentSchema } from '../../../../fields/documentSchemas';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
-import { makeInterface } from '../../../../fields/Schema';
 import { NumCast, StrCast } from '../../../../fields/Types';
 import { TraceMobx } from '../../../../fields/util';
 import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
@@ -43,9 +40,6 @@ import { MapBoxInfoWindow } from './MapBoxInfoWindow';
 
 // const _global = (window /* browser */ || global /* node */) as any;
 
-type MapDocument = makeInterface<[typeof documentSchema]>;
-const MapDocument = makeInterface(documentSchema);
-
 const mapContainerStyle = {
     height: '100%',
 };
@@ -59,7 +53,6 @@ const mapOptions = {
     fullscreenControl: false,
 };
 
-dotenv.config({ path: __dirname + '/.env' });
 const apiKey = process.env.GOOGLE_MAPS;
 
 const script = document.createElement('script');
@@ -93,7 +86,7 @@ const options = {
 } as google.maps.places.AutocompleteOptions;
 
 @observer
-export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>, MapDocument>(MapDocument) {
+export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
 
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _disposers: { [name: string]: IReactionDisposer } = {};
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index f44355929..8a68f9647 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -248,6 +248,7 @@
 .pdfBox-background {
     width: 100%;
     height: 100%;
+    cursor: ew-resize;
     background: lightGray;
 }
 
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index d54b65d92..9807cee7c 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -3,17 +3,14 @@ import { action, computed, IReactionDisposer, observable, reaction, runInAction
 import { observer } from "mobx-react";
 import * as Pdfjs from "pdfjs-dist";
 import "pdfjs-dist/web/pdf_viewer.css";
-import { Doc, DocListCast, Opt, WidthSym, StrListCast } from "../../../fields/Doc";
-import { documentSchema } from '../../../fields/documentSchemas';
-import { makeInterface, listSpec } from "../../../fields/Schema";
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { Doc, DocListCast, Opt, WidthSym } from "../../../fields/Doc";
+import { Cast, NumCast, StrCast, ImageCast } from '../../../fields/Types';
 import { PdfField } from "../../../fields/URLField";
 import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
 import { Docs } from '../../documents/Documents';
 import { KeyCodes } from '../../util/KeyCodes';
 import { undoBatch } from '../../util/UndoManager';
-import { panZoomSchema } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
@@ -22,18 +19,14 @@ import { AnchorMenu } from '../pdf/AnchorMenu';
 import { PDFViewer } from "../pdf/PDFViewer";
 import { SidebarAnnos } from '../SidebarAnnos';
 import { FieldView, FieldViewProps } from './FieldView';
-import { pageSchema } from "./ImageBox";
 import "./PDFBox.scss";
 import React = require("react");
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
-
-type PdfDocument = makeInterface<[typeof documentSchema, typeof panZoomSchema, typeof pageSchema]>;
-const PdfDocument = makeInterface(documentSchema, panZoomSchema, pageSchema);
 
 @observer
-export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, PdfDocument>(PdfDocument) {
+export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PDFBox, fieldKey); }
     public static openSidebarWidth = 250;
+    public static sidebarResizerWidth = 5;
     private _searchString: string = "";
     private _initialScrollTarget: Opt<Doc>;
     private _pdfViewer: PDFViewer | undefined;
@@ -46,6 +39,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable private _pageControls = false;
 
     @computed get pdfUrl() { return Cast(this.dataDoc[this.props.fieldKey], PdfField); }
+    @computed get pdfThumb() { return ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url; }
 
     constructor(props: any) {
         super(props);
@@ -54,7 +48,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         !this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
         if (this.pdfUrl) {
             if (PDFBox.pdfcache.get(this.pdfUrl.url.href)) runInAction(() => this._pdf = PDFBox.pdfcache.get(this.pdfUrl!.url.href));
-            else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action(pdf => this._pdf = pdf));
+            else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action((pdf: any) => this._pdf = pdf));
         }
     }
 
@@ -80,7 +74,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const anchor =
             AnchorMenu.Instance?.GetAnchor() ??
             Docs.Create.TextanchorDocument({
-                title: StrCast(this.rootDoc.title + "@" + this.layoutDoc._scrollTop?.toFixed(0)),
+                title: StrCast(this.rootDoc.title + "@" + NumCast(this.layoutDoc._scrollTop)?.toFixed(0)),
                 y: NumCast(this.layoutDoc._scrollTop),
                 unrendered: true
             });
@@ -110,8 +104,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     });
     public prevAnnotation = () => this._pdfViewer?.prevAnnotation();
     public nextAnnotation = () => this._pdfViewer?.nextAnnotation();
-    public backPage = () => { this.Document._curPage = (this.Document._curPage || 1) - 1; return true; };
-    public forwardPage = () => { this.Document._curPage = (this.Document._curPage || 1) + 1; return true; };
+    public backPage = () => { this.Document._curPage = (NumCast(this.Document._curPage) || 1) - 1; return true; };
+    public forwardPage = () => { this.Document._curPage = (NumCast(this.Document._curPage) || 1) + 1; return true; };
     public gotoPage = (p: number) => this.Document._curPage = p;
 
     @undoBatch
@@ -142,15 +136,15 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (!this.layoutDoc._showSidebar) this.toggleSidebar();
         return this.addDocument(doc, sidebarKey);
     }
-    sidebarBtnDown = (e: React.PointerEvent) => {
+    sidebarBtnDown = (e: React.PointerEvent, onButton: boolean) => { // onButton determines whether the width of the pdf box changes, or just the ratio of the sidebar to the pdf
         setupMoveUpEvents(this, e, (e, down, delta) => {
             const localDelta = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformDirection(delta[0], delta[1]);
             const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
             const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
-            const ratio = (curNativeWidth + localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
+            const ratio = (curNativeWidth + (onButton ? 1 : -1) * localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
             if (ratio >= 1) {
                 this.layoutDoc.nativeWidth = nativeWidth * ratio;
-                this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0];
+                onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
                 this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
             }
             return false;
@@ -160,16 +154,17 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable _previewWidth: Opt<number> = undefined;
     toggleSidebar = action((preview: boolean = false) => {
         const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
-        const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
+        const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
+        const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth + PDFBox.sidebarResizerWidth : 0) + nativeWidth) / nativeWidth;
         const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
         if (preview) {
-            this._previewNativeWidth = nativeWidth * ratio;
-            this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth;
+            this._previewNativeWidth = nativeWidth * sideratio;
+            this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * sideratio / curNativeWidth;
             this._showSidebar = true;
         }
         else {
-            this.layoutDoc.nativeWidth = nativeWidth * ratio;
-            this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth;
+            this.layoutDoc.nativeWidth = nativeWidth * pdfratio;
+            this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * pdfratio / curNativeWidth;
             this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
         }
     });
@@ -185,7 +180,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             </button>
         </>;
         const searchTitle = `${!this._searching ? "Open" : "Close"} Search Bar`;
-        const curPage = this.Document._curPage || 1;
+        const curPage = NumCast(this.Document._curPage) || 1;
         return !this.props.isContentActive() ? (null) :
             <div className="pdfBox-ui" onKeyDown={e => [KeyCodes.BACKSPACE, KeyCodes.DELETE].includes(e.keyCode) ? e.stopPropagation() : true}
                 onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? "flex" : "none" }}>
@@ -223,7 +218,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         top: StrCast(this.rootDoc._showTitle) === "title" ? 20 : 5,
                         backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK
                     }}
-                    onPointerDown={this.sidebarBtnDown} >
+                    onPointerDown={e => this.sidebarBtnDown(e, true)} >
                     <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
                 </div>
             </div>;
@@ -253,18 +248,20 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get SidebarShown() { return this._showSidebar || this.layoutDoc._showSidebar ? true : false; }
 
     contentScaling = () => 1;
+    isPdfContentActive = () => this.isAnyChildContentActive() || this.props.isSelected();
     @computed get renderPdfView() {
         TraceMobx();
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
         const scale = previewScale * (this.props.scaling?.() || 1);
         return <div className={"pdfBox"} onContextMenu={this.specificContextMenu}
             style={{
+                display: this.props.thumbShown?.() ? "none" : undefined,
                 height: this.props.Document._scrollTop && !this.Document._fitWidth && (window.screen.width > 600) ?
                     NumCast(this.Document._height) * this.props.PanelWidth() / NumCast(this.Document._width) : undefined
             }}>
-            <div className="pdfBox-background" />
+            <div className="pdfBox-background" onPointerDown={e => this.sidebarBtnDown(e, false)} />
             <div style={{
-                width: `calc(${100 / scale}% - ${this.sidebarWidth() / scale * (this._previewWidth ? scale : 1)}px)`,
+                width: `calc(${100 / scale}% - ${(this.sidebarWidth() + PDFBox.sidebarResizerWidth) / scale * (this._previewWidth ? scale : 1)}px)`,
                 height: `${100 / scale}%`,
                 transform: `scale(${scale})`,
                 position: "absolute",
@@ -277,7 +274,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     dataDoc={this.dataDoc}
                     pdf={this._pdf!}
                     url={this.pdfUrl!.url.pathname}
-                    isContentActive={this.props.isContentActive}
+                    isContentActive={this.isPdfContentActive}
                     anchorMenuClick={this.anchorMenuClick}
                     loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined}
                     setPdfViewer={this.setPdfViewer}
@@ -307,17 +304,22 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
     static pdfcache = new Map<string, Pdfjs.PDFDocumentProxy>();
-    static pdfpromise = new Map<string, Pdfjs.PDFPromise<Pdfjs.PDFDocumentProxy>>();
+    static pdfpromise = new Map<string, Promise<Pdfjs.PDFDocumentProxy>>();
     render() {
         TraceMobx();
-        if (this._pdf) return this.renderPdfView;
+        if (this._pdf) {
+            if (!this.props.thumbShown?.()) {
+                return this.renderPdfView;
+            }
+            return null;
+        }
 
         const href = this.pdfUrl?.url.href;
         if (href) {
             if (PDFBox.pdfcache.get(href)) setTimeout(action(() => this._pdf = PDFBox.pdfcache.get(href)));
             else {
                 if (!PDFBox.pdfpromise.get(href)) PDFBox.pdfpromise.set(href, Pdfjs.getDocument(href).promise);
-                PDFBox.pdfpromise.get(href)?.then(action(pdf => PDFBox.pdfcache.set(href, this._pdf = pdf)));
+                PDFBox.pdfpromise.get(href)?.then(action((pdf: any) => PDFBox.pdfcache.set(href, this._pdf = pdf)));
             }
         }
         return this.renderTitleBox;
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 0c631e5f9..dbb567d3a 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -1,20 +1,17 @@
 import React = require("react");
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 // import { Canvas } from '@react-three/fiber';
-import { action, computed, observable, reaction, trace, runInAction } from "mobx";
+import { computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
 // import { BufferAttribute, Camera, Vector2, Vector3 } from 'three';
 import { DateField } from "../../../fields/DateField";
-import { Doc, WidthSym, HeightSym } from "../../../fields/Doc";
-import { documentSchema } from "../../../fields/documentSchemas";
+import { Doc, HeightSym, WidthSym } from "../../../fields/Doc";
 import { Id } from "../../../fields/FieldSymbols";
-import { InkTool } from "../../../fields/InkField";
-import { makeInterface } from "../../../fields/Schema";
 import { ComputedField } from "../../../fields/ScriptField";
 import { Cast, NumCast } from "../../../fields/Types";
 import { AudioField, VideoField } from "../../../fields/URLField";
 import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, numberRange, OmitKeys, returnFalse, returnOne, Utils } from "../../../Utils";
+import { emptyFunction, OmitKeys, returnFalse, returnOne } from "../../../Utils";
 import { DocUtils } from "../../documents/Documents";
 import { DocumentType } from "../../documents/DocumentTypes";
 import { Networking } from "../../Network";
@@ -32,9 +29,6 @@ declare class MediaRecorder {
     constructor(e: any, options?: any);  // whatever MediaRecorder has
 }
 
-type ScreenshotDocument = makeInterface<[typeof documentSchema]>;
-const ScreenshotDocument = makeInterface(documentSchema);
-
 // interface VideoTileProps {
 //     raised: { coord: Vector2, off: Vector3 }[];
 //     setRaised: (r: { coord: Vector2, off: Vector3 }[]) => void;
@@ -112,7 +106,7 @@ const ScreenshotDocument = makeInterface(documentSchema);
 // }
 
 @observer
-export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ScreenshotDocument>(ScreenshotDocument) {
+export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScreenshotBox, fieldKey); }
     private _audioRec: any;
     private _videoRec: any;
diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx
index 183140cd7..366c3fc2f 100644
--- a/src/client/views/nodes/ScriptingBox.tsx
+++ b/src/client/views/nodes/ScriptingBox.tsx
@@ -1,18 +1,19 @@
 import ReactTextareaAutocomplete from "@webscopeio/react-textarea-autocomplete";
 import "@webscopeio/react-textarea-autocomplete/style.css";
-import { action, computed, observable, runInAction, trace } from "mobx";
+import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import * as React from "react";
 import { Doc } from "../../../fields/Doc";
-import { documentSchema } from "../../../fields/documentSchemas";
 import { List } from "../../../fields/List";
-import { createSchema, listSpec, makeInterface } from "../../../fields/Schema";
+import { listSpec } from "../../../fields/Schema";
 import { ScriptField } from "../../../fields/ScriptField";
-import { Cast, NumCast, ScriptCast, StrCast, BoolCast } from "../../../fields/Types";
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
+import { TraceMobx } from "../../../fields/util";
 import { returnEmptyString } from "../../../Utils";
 import { DragManager } from "../../util/DragManager";
 import { InteractionUtils } from "../../util/InteractionUtils";
-import { CompileScript, Scripting, ScriptParam } from "../../util/Scripting";
+import { CompileScript, ScriptParam } from "../../util/Scripting";
+import { ScriptingGlobals } from "../../util/ScriptingGlobals";
 import { ScriptManager } from "../../util/ScriptManager";
 import { ContextMenu } from "../ContextMenu";
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
@@ -21,15 +22,10 @@ import { FieldView, FieldViewProps } from "../nodes/FieldView";
 import { OverlayView } from "../OverlayView";
 import { DocumentIconContainer } from "./DocumentIcon";
 import "./ScriptingBox.scss";
-import { TraceMobx } from "../../../fields/util";
 const _global = (window /* browser */ || global /* node */) as any;
 
-const ScriptingSchema = createSchema({});
-type ScriptingDocument = makeInterface<[typeof ScriptingSchema, typeof documentSchema]>;
-const ScriptingDocument = makeInterface(ScriptingSchema, documentSchema);
-
 @observer
-export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, ScriptingDocument>(ScriptingDocument) {
+export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
 
     private dropDisposer?: DragManager.DragDropDisposer;
     protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer | undefined;
@@ -42,9 +38,9 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
     @observable private _function: boolean = false;
     @observable private _spaced: boolean = false;
 
-    @observable private _scriptKeys: any = Scripting.getGlobals();
-    @observable private _scriptingDescriptions: any = Scripting.getDescriptions();
-    @observable private _scriptingParams: any = Scripting.getParameters();
+    @observable private _scriptKeys: any = ScriptingGlobals.getGlobals();
+    @observable private _scriptingDescriptions: any = ScriptingGlobals.getDescriptions();
+    @observable private _scriptingParams: any = ScriptingGlobals.getParameters();
 
     @observable private _currWord: string = "";
     @observable private _suggestions: string[] = [];
@@ -224,9 +220,9 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
 
         ScriptManager.Instance.addScript(this.dataDoc);
 
-        this._scriptKeys = Scripting.getGlobals();
-        this._scriptingDescriptions = Scripting.getDescriptions();
-        this._scriptingParams = Scripting.getParameters();
+        this._scriptKeys = ScriptingGlobals.getGlobals();
+        this._scriptingDescriptions = ScriptingGlobals.getDescriptions();
+        this._scriptingParams = ScriptingGlobals.getParameters();
     }
 
     // overlays document numbers (ex. d32) over all documents when clicked on
@@ -577,8 +573,8 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatable
         </div>;
     }
 
-    renderFuncListElement(value: string) {
-        return <div>
+    renderFuncListElement(value: string | object) {
+        return (typeof value !== "string") ? (null) : <div>
             <div style={{ fontSize: "14px" }}>
                 {value}
             </div>
diff --git a/src/client/views/nodes/SliderBox.tsx b/src/client/views/nodes/SliderBox.tsx
index 92d1f7446..b96977f32 100644
--- a/src/client/views/nodes/SliderBox.tsx
+++ b/src/client/views/nodes/SliderBox.tsx
@@ -2,30 +2,19 @@ import { runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Handles, Rail, Slider, Ticks, Tracks } from 'react-compound-slider';
-import { documentSchema } from '../../../fields/documentSchemas';
-import { createSchema, makeInterface } from '../../../fields/Schema';
 import { NumCast, ScriptCast, StrCast } from '../../../fields/Types';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { ViewBoxBaseComponent } from '../DocComponent';
 import { ScriptBox } from '../ScriptBox';
+import { StyleProp } from '../StyleProvider';
 import { FieldView, FieldViewProps } from './FieldView';
 import { Handle, Tick, TooltipRail, Track } from './SliderBox-components';
 import './SliderBox.scss';
-import { StyleProp } from '../StyleProvider';
-
-const SliderSchema = createSchema({
-    _sliderMin: "number",
-    _sliderMax: "number",
-    _sliderMinThumb: "number",
-    _sliderMaxThumb: "number",
-});
 
-type SliderDocument = makeInterface<[typeof SliderSchema, typeof documentSchema]>;
-const SliderDocument = makeInterface(SliderSchema, documentSchema);
 
 @observer
-export class SliderBox extends ViewBoxBaseComponent<FieldViewProps, SliderDocument>(SliderDocument) {
+export class SliderBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SliderBox, fieldKey); }
 
     get minThumbKey() { return this.fieldKey + "-minThumb"; }
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 9797178b2..913123cda 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -2,11 +2,10 @@ import React = require("react");
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, untracked } from "mobx";
 import { observer } from "mobx-react";
+import { basename } from "path";
 import * as rp from 'request-promise';
 import { Doc, DocListCast } from "../../../fields/Doc";
-import { documentSchema } from "../../../fields/documentSchemas";
 import { InkTool } from "../../../fields/InkField";
-import { makeInterface } from "../../../fields/Schema";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { AudioField, VideoField } from "../../../fields/URLField";
 import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from "../../../Utils";
@@ -44,13 +43,8 @@ const path = require('path');
  * Like images, users can zoom and pan and it has an overlay layer allowing for annotations on top of the video at different times
  */
 
-
-type VideoDocument = makeInterface<[typeof documentSchema]>;
-const VideoDocument = makeInterface(documentSchema);
-
-
 @observer
-export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, VideoDocument>(VideoDocument) {
+export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(VideoBox, fieldKey); }
     static async convertDataUri(imageUri: string, returnedFilename: string) {
         try {
@@ -129,7 +123,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             if (!nativeWidth || !nativeHeight) {
                 if (!nativeWidth) Doc.SetNativeWidth(this.dataDoc, 600);
                 Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 600) / youtubeaspect);
-                this.layoutDoc._height = (this.layoutDoc._width || 0) / youtubeaspect;
+                this.layoutDoc._height = NumCast(this.layoutDoc._width) / youtubeaspect;
             }
         }
         this.player && this.setPlayheadTime(0);
@@ -219,7 +213,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     // creates and links snapshot photo of current video frame
     @action public Snapshot(downX?: number, downY?: number) {
-        const width = (this.layoutDoc._width || 0);
+        const width = NumCast(this.layoutDoc._width);
         const canvas = document.createElement('canvas');
         canvas.width = 640;
         canvas.height = 640 * Doc.NativeHeight(this.layoutDoc) / (Doc.NativeWidth(this.layoutDoc) || 1);
@@ -233,7 +227,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
         if (!this._videoRef) {
             const b = Docs.Create.LabelDocument({
-                x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 1),
+                x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y, 1),
                 _width: 150, _height: 50, title: (this.layoutDoc._currentTimecode || 0).toString(),
                 _isLinkButton: true
             });
@@ -254,7 +248,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
             // if you want to preview the captured image,
             const retitled = StrCast(this.rootDoc.title).replace(/[ -\.]/g, "");
-            const filename = path.basename(encodeURIComponent("snapshot" + retitled + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_")));
+            const encodedFilename = encodeURIComponent("snapshot" + retitled + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_"));
+            const filename = basename(encodedFilename);
             VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) =>
                 returnedFilename && this.createRealSummaryLink(returnedFilename, downX, downY));
         }
@@ -263,12 +258,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     // creates link for snapshot
     createRealSummaryLink = (imagePath: string, downX?: number, downY?: number) => {
         const url = !imagePath.startsWith("/") ? Utils.CorsProxy(imagePath) : imagePath;
-        const width = this.layoutDoc._width || 1;
-        const height = this.layoutDoc._height || 0;
+        const width = NumCast(this.layoutDoc._width) || 1;
+        const height = NumCast(this.layoutDoc._height);
         const imageSummary = Docs.Create.ImageDocument(url, {
             _nativeWidth: Doc.NativeWidth(this.layoutDoc), _nativeHeight: Doc.NativeHeight(this.layoutDoc),
-            x: (this.layoutDoc.x || 0) + width, y: (this.layoutDoc.y || 0), _isLinkButton: true,
-            _width: 150, _height: height / width * 150, title: "--snapshot" + (this.layoutDoc._currentTimecode || 0) + " image-"
+            x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y), _isLinkButton: true,
+            _width: 150, _height: height / width * 150, title: "--snapshot" + NumCast(this.layoutDoc._currentTimecode) + " image-"
         });
         Doc.SetNativeWidth(Doc.GetProto(imageSummary), Doc.NativeWidth(this.layoutDoc));
         Doc.SetNativeHeight(Doc.GetProto(imageSummary), Doc.NativeHeight(this.layoutDoc));
@@ -292,7 +287,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         const aspect = this.player!.videoWidth / this.player!.videoHeight;
         Doc.SetNativeWidth(this.dataDoc, this.player!.videoWidth);
         Doc.SetNativeHeight(this.dataDoc, this.player!.videoHeight);
-        this.layoutDoc._height = (this.layoutDoc._width || 0) / aspect;
+        this.layoutDoc._height = NumCast(this.layoutDoc._width) / aspect;
         if (Number.isFinite(this.player!.duration)) {
             this.rawDuration = this.player!.duration;
         }
@@ -320,7 +315,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             // @ts-ignore
             // vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen);
             this._disposers.reactionDisposer?.();
-            this._disposers.reactionDisposer = reaction(() => (this.layoutDoc._currentTimecode || 0),
+            this._disposers.reactionDisposer = reaction(() => NumCast(this.layoutDoc._currentTimecode),
                 time => !this._playing && (vref.currentTime = time), { fireImmediately: true });
         }
     }
@@ -419,7 +414,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         const onYoutubePlayerReady = (event: any) => {
             this._disposers.reactionDisposer?.();
             this._disposers.youtubeReactionDisposer?.();
-            this._disposers.reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek((this.layoutDoc._currentTimecode || 0)));
+            this._disposers.reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek(NumCast(this.layoutDoc._currentTimecode)));
             this._disposers.youtubeReactionDisposer = reaction(
                 () => CurrentUserUtils.SelectedTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
                 (interactive) => iframe.style.pointerEvents = interactive ? "all" : "none", { fireImmediately: true });
@@ -503,7 +498,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
         this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
         const classname = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
-        const start = untracked(() => Math.round((this.layoutDoc._currentTimecode || 0)));
+        const start = untracked(() => Math.round(NumCast(this.layoutDoc._currentTimecode)));
         return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
             onPointerLeave={this.updateTimecode}
             onLoad={this.youtubeIframeLoaded} className={classname} width={Doc.NativeWidth(this.layoutDoc) || 640} height={Doc.NativeHeight(this.layoutDoc) || 390}
@@ -683,7 +678,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     // renders video controls
     @computed get uIButtons() {
-        const curTime = (this.layoutDoc._currentTimecode || 0) - (this.timeline?.clipStart || 0);
+        const curTime = NumCast(this.layoutDoc._currentTimecode) - (this.timeline?.clipStart || 0);
         return <div className="videoBox-ui" style={this._fullScreen || this.heightPercent == 100 ? { fontSize: "40px", minWidth: "80%" } : {}}>
             <div className="videobox-button"
                 title={this._playing ? "play" : "pause"}
@@ -797,7 +792,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             style={{
                 pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined,
                 borderRadius,
-                overflow: this.props.docViewPath?.().lastElement().fitWidth ? "auto" : undefined
+                overflow: this.props.docViewPath?.().slice(-1)[0].fitWidth ? "auto" : undefined
             }} onWheel={e => { e.stopPropagation(); e.preventDefault(); }}>
             <div className="videoBox-viewer" onPointerDown={this.marqueeDown} >
                 <div style={{
@@ -833,7 +828,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         scrollTop={0}
                         down={this._marqueeing}
                         scaling={this.marqueeFitScaling}
-                        docView={this.props.docViewPath().lastElement()}
+                        docView={this.props.docViewPath().slice(-1)[0]}
                         containerOffset={this.marqueeOffset}
                         addDocument={this.addDocWithTimecode}
                         finishMarquee={this.finishMarquee}
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index 79289abaa..7dc970496 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -5,6 +5,12 @@
     height: 100%;
     position: relative;
     display: flex;
+    .webBox-background { 
+        width: 100%;
+        height: 100%;
+        cursor: ew-resize;
+        background: lightGray;
+    }
 
     .webBox-ui {
         position: absolute;
@@ -137,6 +143,7 @@
         transform-origin: top left;
         width: 100%;
         height: 100%;
+        position: absolute;
 
         .webBox-htmlSpan {
             position: absolute;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 9956cc36b..7ff47107e 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -3,21 +3,20 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio
 import { observer } from "mobx-react";
 import * as WebRequest from 'web-request';
 import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
-import { documentSchema } from "../../../fields/documentSchemas";
 import { Id } from "../../../fields/FieldSymbols";
 import { HtmlField } from "../../../fields/HtmlField";
 import { InkTool } from "../../../fields/InkField";
 import { List } from "../../../fields/List";
-import { listSpec, makeInterface } from "../../../fields/Schema";
+import { listSpec } from "../../../fields/Schema";
 import { ComputedField } from "../../../fields/ScriptField";
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { WebField } from "../../../fields/URLField";
+import { Cast, ImageCast, NumCast, StrCast } from "../../../fields/Types";
+import { ImageField, WebField } from "../../../fields/URLField";
 import { TraceMobx } from "../../../fields/util";
 import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 import { KeyCodes } from "../../util/KeyCodes";
-import { Scripting } from "../../util/Scripting";
+import { ScriptingGlobals } from "../../util/ScriptingGlobals";
 import { SnappingManager } from "../../util/SnappingManager";
 import { undoBatch } from "../../util/UndoManager";
 import { MarqueeOptionsMenu } from "../collections/collectionFreeForm";
@@ -36,18 +35,18 @@ import { StyleProp } from "../StyleProvider";
 import { DocumentViewProps } from "./DocumentView";
 import { FieldView, FieldViewProps } from './FieldView';
 import { LinkDocPreview } from "./LinkDocPreview";
+import { VideoBox } from "./VideoBox";
 import "./WebBox.scss";
 import React = require("react");
+const { CreateImage } = require("./WebBoxRenderer");
 const _global = (window /* browser */ || global /* node */) as any;
 const htmlToText = require("html-to-text");
 
-type WebDocument = makeInterface<[typeof documentSchema]>;
-const WebDocument = makeInterface(documentSchema);
-
 @observer
-export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, WebDocument>(WebDocument) {
+export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); }
     public static openSidebarWidth = 250;
+    public static sidebarResizerWidth = 5;
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
     private _outerRef: React.RefObject<HTMLDivElement> = React.createRef();
@@ -63,6 +62,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable private _searching: boolean = false;
     @observable private _showSidebar = false;
     @observable private _scrollTimer: any;
+    @observable private _webPageHasBeenRendered = false;
     @observable private _overlayAnnoInfo: Opt<Doc>;
     @observable private _marqueeing: number[] | undefined;
     @observable private _isAnnotating = false;
@@ -76,6 +76,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); }
     @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); }
     @computed get webField() { return Cast(this.dataDoc[this.props.fieldKey], WebField)?.url; }
+    @computed get webThumb() { return ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url; }
 
     constructor(props: any) {
         super(props);
@@ -103,7 +104,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         return true;
     }
     async componentDidMount() {
-        this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
+        this.props.setContentView?.(this); // this tells the DocumentView that this WebBox is the "content" of the document.  this allows the DocumentView to call WebBox relevant methods to configure the UI (eg, show back/forward buttons)
 
         runInAction(() => {
             this._annotationKeySuffix = () => this._urlHash + "-annotations";
@@ -111,6 +112,48 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this.dataDoc[this.fieldKey + "-annotations"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`);
             this.dataDoc[this.fieldKey + "-sidebar"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`);
         });
+        reaction(() => this.props.isSelected(),
+            async (selected) => {
+                if (selected) {
+                    this._webPageHasBeenRendered = true;
+                    setTimeout(action(() => {
+                        this._scrollHeight = Math.max(this.scrollHeight, this._iframe?.contentDocument?.body.scrollHeight || 0);
+                        if (this._initialScroll !== undefined && this._outerRef.current) {
+                            setTimeout(() => {
+                                this._outerRef.current!.scrollTop = this._initialScroll!;
+                                this._initialScroll = undefined;
+                            });
+                        }
+                    }));
+                } else if (!this.props.isContentActive() &&
+                    !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && /// don't create a thumbnail when double-clicking to enter lightbox because thumbnail will be empty
+                    LightboxView.LightboxDoc !== this.rootDoc) { // don't create a thumbnail if entering Lightbox from maximize either, since thumb will be empty.
+                    const imageBitmap = ImageCast(this.layoutDoc["thumb-frozen"])?.url.href;
+                    if (this._iframe && !imageBitmap) {
+                        var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument);
+                        if (!htmlString) {
+                            htmlString = await (await fetch(Utils.CorsProxy(this.webField!.href))).text();
+                        }
+                        this.layoutDoc.thumb = undefined;
+                        const nativeWidth = NumCast(this.layoutDoc.nativeWidth);
+                        CreateImage(
+                            this._webUrl.endsWith("/") ? this._webUrl.substring(0, this._webUrl.length - 1) : this._webUrl,
+                            this._iframe.contentDocument?.styleSheets ?? [],
+                            htmlString,
+                            nativeWidth,
+                            nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(),
+                            NumCast(this.layoutDoc._scrollTop)
+                        ).then
+                            ((dataUrl: any) => {
+                                VideoBox.convertDataUri(dataUrl, this.layoutDoc[Id] + "-thumb" + (new Date()).getTime(), true).then(
+                                    returnedfilename => setTimeout(action(() => this.layoutDoc.thumb = new ImageField(returnedfilename)), 500));
+                            })
+                            .catch(function (error: any) {
+                                console.error('oops, something went wrong!', error);
+                            });
+                    }
+                }
+            });
 
         this._disposers.autoHeight = reaction(() => this.layoutDoc._autoHeight,
             autoHeight => {
@@ -292,12 +335,6 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             iframe.contentDocument.addEventListener("pointerdown", this.iframeDown);
             this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument.body.scrollHeight);
             setTimeout(action(() => this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument?.body.scrollHeight || 0)), 5000);
-            const initialScroll = this._initialScroll;
-            if (initialScroll !== undefined && this._outerRef.current) {
-                // bcz: not sure why this happens, but if the webpage isn't ready yet, it's scroll height seems to be limited.  So we need to wait tp set scroll location to what we want.
-                setTimeout(() => this._outerRef.current!.scrollTop = initialScroll);
-                this._initialScroll = undefined;
-            }
             iframe.setAttribute("enable-annotation", "true");
             iframe.contentDocument.addEventListener("click", undoBatch(action((e: MouseEvent) => {
                 let href = "";
@@ -353,42 +390,46 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
     }
 
-    @action
-    forward = () => {
+    forward = (checkAvailable?: boolean) => {
         const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"), []);
         const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), []);
-        if (future.length) {
-            const curUrl = this._url;
-            this.dataDoc[this.fieldKey + "-history"] = new List<string>([...history, this._url]);
-            this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!));
-            if (this._webUrl === this._url) {
-                this._webUrl = curUrl;
-                setTimeout(action(() => this._webUrl = this._url));
-            } else {
-                this._webUrl = this._url;
+        if (checkAvailable) return future.length;
+        runInAction(() => {
+            if (future.length) {
+                const curUrl = this._url;
+                this.dataDoc[this.fieldKey + "-history"] = new List<string>([...history, this._url]);
+                this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!));
+                if (this._webUrl === this._url) {
+                    this._webUrl = curUrl;
+                    setTimeout(action(() => this._webUrl = this._url));
+                } else {
+                    this._webUrl = this._url;
+                }
+                return true;
             }
-            return true;
-        }
+        });
         return false;
     }
 
-    @action
-    back = () => {
+    back = (checkAvailable?: boolean) => {
         const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"));
         const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), []);
-        if (history.length) {
-            const curUrl = this._url;
-            if (future === undefined) this.dataDoc[this.fieldKey + "-future"] = new List<string>([this._url]);
-            else this.dataDoc[this.fieldKey + "-future"] = new List<string>([...future, this._url]);
-            this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!));
-            if (this._webUrl === this._url) {
-                this._webUrl = curUrl;
-                setTimeout(action(() => this._webUrl = this._url));
-            } else {
-                this._webUrl = this._url;
+        if (checkAvailable) return history.length;
+        runInAction(() => {
+            if (history.length) {
+                const curUrl = this._url;
+                if (future === undefined) this.dataDoc[this.fieldKey + "-future"] = new List<string>([this._url]);
+                else this.dataDoc[this.fieldKey + "-future"] = new List<string>([...future, this._url]);
+                this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!));
+                if (this._webUrl === this._url) {
+                    this._webUrl = curUrl;
+                    setTimeout(action(() => this._webUrl = this._url));
+                } else {
+                    this._webUrl = this._url;
+                }
+                return true;
             }
-            return true;
-        }
+        });
         return false;
     }
 
@@ -452,8 +493,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     <button className="submitUrl" onClick={() => this.submitURL(this._keyInput.current!.value)} onDragOver={e => e.stopPropagation()} onDrop={this.onWebUrlDrop}>
                         GO
                     </button>
-                    <button className="submitUrl" onClick={this.back}> <FontAwesomeIcon icon="caret-left" size="lg" /> </button>
-                    <button className="submitUrl" onClick={this.forward}> <FontAwesomeIcon icon="caret-right" size="lg" />  </button>
+                    <button className="submitUrl" onClick={() => this.back}> <FontAwesomeIcon icon="caret-left" size="lg" /> </button>
+                    <button className="submitUrl" onClick={() => this.forward}> <FontAwesomeIcon icon="caret-right" size="lg" />  </button>
                 </div>
             </div>
         );
@@ -512,7 +553,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
     @computed get urlContent() {
-        if (this._hackHide) return (null);
+        if (this._hackHide || (this.webThumb && (!this._webPageHasBeenRendered && LightboxView.LightboxDoc !== this.rootDoc))) return (null);
         const field = this.dataDoc[this.props.fieldKey];
         let view;
         if (field instanceof HtmlField) {
@@ -530,6 +571,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 style={{ pointerEvents: this._scrollTimer ? "none" : undefined }} // if we allow pointer events when scrolling is on, then reversing direction does not work smoothly
                 ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={"https://crossorigin.me/https://cs.brown.edu"} />;
         }
+        setTimeout(action(() => this._webPageHasBeenRendered = true));
         return view;
     }
 
@@ -542,34 +584,37 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (!this.layoutDoc._showSidebar) this.toggleSidebar();
         return this.addDocumentWrapper(doc, sidebarKey);
     }
-    sidebarBtnDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, (e, down, delta) => {
+    @observable _draggingSidebar = false;
+    sidebarBtnDown = (e: React.PointerEvent, onButton: boolean) => { // onButton determines whether the width of the pdf box changes, or just the ratio of the sidebar to the pdf
+        setupMoveUpEvents(this, e, action((e, down, delta) => {
+            this._draggingSidebar = true;
             const localDelta = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformDirection(delta[0], delta[1]);
             const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
             const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
-            const ratio = (curNativeWidth + localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
+            const ratio = (curNativeWidth + (onButton ? 1 : -1) * localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
             if (ratio >= 1) {
                 this.layoutDoc.nativeWidth = nativeWidth * ratio;
-                this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0];
+                onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
                 this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
             }
             return false;
-        }, emptyFunction, () => this.toggleSidebar());
+        }), action(() => this._draggingSidebar = false), () => this.toggleSidebar());
     }
     @observable _previewNativeWidth: Opt<number> = undefined;
     @observable _previewWidth: Opt<number> = undefined;
     toggleSidebar = action((preview: boolean = false) => {
         const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
-        const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
+        const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
+        const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth + WebBox.sidebarResizerWidth : 0) + nativeWidth) / nativeWidth;
         const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
         if (preview) {
-            this._previewNativeWidth = nativeWidth * ratio;
-            this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth;
+            this._previewNativeWidth = nativeWidth * sideratio;
+            this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * sideratio / curNativeWidth;
             this._showSidebar = true;
         }
         else {
-            this.layoutDoc.nativeWidth = nativeWidth * ratio;
-            this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth;
+            this.layoutDoc.nativeWidth = nativeWidth * pdfratio;
+            this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * pdfratio / curNativeWidth;
             this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
         }
     });
@@ -633,7 +678,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
         return this.props.styleProvider?.(doc, props, property);
     }
-    pointerEvents = () => this.props.isContentActive() && this.props.pointerEvents !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none";
+    pointerEvents = () => !this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none";
     render() {
         const pointerEvents = this.props.layerProvider?.(this.layoutDoc) === false ? "none" : this.props.pointerEvents ? this.props.pointerEvents as any : undefined;
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
@@ -664,13 +709,17 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 pointerEvents={this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
         return (
             <div className="webBox" ref={this._mainCont}
-                style={{ pointerEvents: this.pointerEvents() }} >
-                <div className={`webBox-container`} style={{ pointerEvents }} onContextMenu={this.specificContextMenu}>
+                style={{ pointerEvents: this.pointerEvents(), display: this.props.thumbShown?.() ? "none" : undefined }} >
+                <div className="webBox-background" onPointerDown={e => this.sidebarBtnDown(e, false)} />
+                <div className="webBox-container" style={{
+                    position: "absolute",
+                    width: `calc(${100 / scale}% - ${(this.sidebarWidth() + WebBox.sidebarResizerWidth) / scale * (this._previewWidth ? scale : 1)}px)`,
+                    transform: `scale(${scale})`,
+                    pointerEvents
+                }} onContextMenu={this.specificContextMenu}>
                     <div className={"webBox-outerContent"} ref={this._outerRef}
                         style={{
-                            width: `calc(${100 / scale}% - ${this.sidebarWidth() / scale * (this._previewWidth ? scale : 1)}px)`,
                             height: `${100 / scale}%`,
-                            transform: `scale(${scale})`,
                             pointerEvents
                         }}
                         onWheel={e => { e.stopPropagation(); e.preventDefault(); }} // block wheel events from propagating since they're handled by the iframe
@@ -703,6 +752,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 </div >
                 <SidebarAnnos ref={this._sidebarRef}
                     {...this.props}
+                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                     fieldKey={this.fieldKey + "-" + this._urlHash}
                     rootDoc={this.rootDoc}
                     layoutDoc={this.layoutDoc}
@@ -720,11 +770,11 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         top: StrCast(this.rootDoc._showTitle) === "title" ? 20 : 5,
                         backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK
                     }}
-                    onPointerDown={this.sidebarBtnDown} >
+                    onPointerDown={e => this.sidebarBtnDown(e, true)} >
                     <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
                 </div>
                 {!this.props.isContentActive() ? (null) : this.searchUI}
             </div>);
     }
 }
-Scripting.addGlobal(function urlHash(url: string) { return WebBox.urlHash(url); });
\ No newline at end of file
+ScriptingGlobals.add(function urlHash(url: string) { return WebBox.urlHash(url); });
\ No newline at end of file
diff --git a/src/client/views/nodes/WebBoxRenderer.js b/src/client/views/nodes/WebBoxRenderer.js
new file mode 100644
index 000000000..08a5746d1
--- /dev/null
+++ b/src/client/views/nodes/WebBoxRenderer.js
@@ -0,0 +1,395 @@
+/**
+ * 
+ * @param {StyleSheetList} styleSheets 
+ */
+var ForeignHtmlRenderer = function (styleSheets) {
+
+    const self = this;
+
+    /**
+     * 
+     * @param {String} binStr 
+     */
+    const binaryStringToBase64 = function (binStr) {
+        return new Promise(function (resolve) {
+            const reader = new FileReader();
+            reader.readAsDataURL(binStr);
+            reader.onloadend = function () {
+                resolve(reader.result);
+            }
+        });
+    };
+
+    function prepend(extension) {
+        return window.location.origin + extension;
+    }
+    function CorsProxy(url) {
+        return prepend("/corsProxy/") + encodeURIComponent(url);
+    }
+    /**
+     * 
+     * @param {String} url 
+     * @returns {Promise}
+     */
+    const getResourceAsBase64 = function (webUrl, inurl) {
+        return new Promise(function (resolve, reject) {
+            const xhr = new XMLHttpRequest();
+            //const url = inurl.startsWith("/") && !inurl.startsWith("//") ? webUrl + inurl : inurl;
+            //const url = CorsProxy(inurl.startsWith("/") && !inurl.startsWith("//") ? webUrl + inurl : inurl);// inurl.startsWith("http") ? CorsProxy(inurl) : inurl;
+            var url = inurl;
+            if (inurl.startsWith("/static")) {
+                url = (new URL(webUrl).origin + inurl);
+            } else
+                if ((inurl.startsWith("/") && !inurl.startsWith("//"))) {
+                    url = CorsProxy(new URL(webUrl).origin + inurl);
+                } else if (!inurl.startsWith("http") && !inurl.startsWith("//")) {
+                    url = CorsProxy(webUrl + "/" + inurl);
+                }
+            xhr.open("GET", url);
+            xhr.responseType = 'blob';
+
+            xhr.onreadystatechange = async function () {
+                if (xhr.readyState === 4 && xhr.status === 200) {
+                    const resBase64 = await binaryStringToBase64(xhr.response);
+
+                    resolve(
+                        {
+                            "resourceUrl": inurl,
+                            "resourceBase64": resBase64
+                        }
+                    );
+                } else if (xhr.readyState === 4) {
+                    console.log("COULDN'T FIND: " + (inurl.startsWith("/") ? webUrl + inurl : inurl));
+                    resolve(
+                        {
+                            "resourceUrl": "",
+                            "resourceBase64": inurl
+                        }
+                    );
+                }
+            };
+
+            xhr.send(null);
+        });
+    };
+
+    /**
+     * 
+     * @param {String[]} urls 
+     * @returns {Promise}
+     */
+    const getMultipleResourcesAsBase64 = function (webUrl, urls) {
+        const promises = [];
+        for (let i = 0; i < urls.length; i++) {
+            promises.push(getResourceAsBase64(webUrl, urls[i]));
+        }
+        return Promise.all(promises);
+    };
+
+    /**
+     * 
+     * @param {String} str 
+     * @param {Number} startIndex 
+     * @param {String} prefixToken 
+     * @param {String[]} suffixTokens
+     * 
+     * @returns {String|null} 
+     */
+    const parseValue = function (str, startIndex, prefixToken, suffixTokens) {
+        const idx = str.indexOf(prefixToken, startIndex);
+        if (idx === -1) {
+            return null;
+        }
+
+        let val = '';
+        for (let i = idx + prefixToken.length; i < str.length; i++) {
+            if (suffixTokens.indexOf(str[i]) !== -1) {
+                break;
+            }
+
+            val += str[i];
+        }
+
+        return {
+            "foundAtIndex": idx,
+            "value": val
+        }
+    };
+
+    /**
+     * 
+     * @param {String} cssRuleStr 
+     * @returns {String[]}
+     */
+    const getUrlsFromCssString = function (cssRuleStr, selector = "url(", delimiters = [')'], mustEndWithQuote = false) {
+        const urlsFound = [];
+        let searchStartIndex = 0;
+
+        while (true) {
+            const url = parseValue(cssRuleStr, searchStartIndex, selector, delimiters);
+            if (url === null) {
+                break;
+            }
+            searchStartIndex = url.foundAtIndex + url.value.length;
+            if (mustEndWithQuote && url.value[url.value.length - 1] !== '"') continue;
+            const unquoted = removeQuotes(url.value);
+            if (!unquoted  /* || (!unquoted.startsWith('http')&& !unquoted.startsWith("/") )*/ || unquoted === 'http://' || unquoted === 'https://') {
+                continue;
+            }
+
+            unquoted && urlsFound.push(unquoted);
+        }
+
+        return urlsFound;
+    };
+
+    /**
+     * 
+     * @param {String} html 
+     * @returns {String[]}
+     */
+    const getImageUrlsFromFromHtml = function (html) {
+        return getUrlsFromCssString(html, "src=", [' ', '>', '\t'], true);
+    };
+    const getSourceUrlsFromFromHtml = function (html) {
+        return getUrlsFromCssString(html, "source=", [' ', '>', '\t'], true);
+    };
+
+    /**
+     * 
+     * @param {String} str
+     * @returns {String}
+     */
+    const removeQuotes = function (str) {
+        return str.replace(/["']/g, "");
+    };
+
+    const escapeRegExp = function (string) {
+        return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
+    };
+
+    /**
+     * 
+     * @param {String} contentHtml 
+     * @param {Number} width
+     * @param {Number} height
+     * 
+     * @returns {Promise<String>}
+     */
+    const buildSvgDataUri = async function (webUrl, contentHtml, width, height, scroll) {
+
+        return new Promise(async function (resolve, reject) {
+
+            /* !! The problems !!
+            *  1. CORS (not really an issue, expect perhaps for images, as this is a general security consideration to begin with)
+            *  2. Platform won't wait for external assets to load (fonts, images, etc.)
+            */
+
+            // copy styles
+            let cssStyles = "";
+            let urlsFoundInCss = [];
+
+            for (let i = 0; i < styleSheets.length; i++) {
+                try {
+                    const rules = styleSheets[i].cssRules
+                    for (let j = 0; j < rules.length; j++) {
+                        const cssRuleStr = rules[j].cssText;
+                        urlsFoundInCss.push(...getUrlsFromCssString(cssRuleStr));
+                        cssStyles += cssRuleStr;
+                    }
+                } catch (e) {
+
+                }
+            }
+
+            // const fetchedResourcesFromStylesheets = await getMultipleResourcesAsBase64(webUrl, urlsFoundInCss);
+            // for (let i = 0; i < fetchedResourcesFromStylesheets.length; i++) {
+            //     const r = fetchedResourcesFromStylesheets[i];
+            //     if (r.resourceUrl) {
+            //         cssStyles = cssStyles.replace(new RegExp(escapeRegExp(r.resourceUrl), "g"), r.resourceBase64);
+            //     }
+            // }
+
+            contentHtml = contentHtml.replace(/<source[^>]*>/g, "") // <picture> tags have a <source> which has a srcset field of image refs.  instead of converting each, just use the default <img> of the picture
+                .replace(/noscript/g, "div").replace(/<div class="mediaset"><\/div>/g, "")  // when scripting isn't available (ie, rendering web pages here), <noscript> tags should become <div>'s.  But for Brown CS, there's a layout problem if you leave the empty <mediaset> tag
+                .replace(/<link[^>]*>/g, "")  // don't need to keep any linked style sheets because we've already processed all style sheets above
+                .replace(/srcset="([^ "]*)[^"]*"/g, "src=\"$1\""); // instead of converting each item in the srcset to a data url, just convert the first one and use that
+            let urlsFoundInHtml = getImageUrlsFromFromHtml(contentHtml);
+            const fetchedResources = await getMultipleResourcesAsBase64(webUrl, urlsFoundInHtml);
+            for (let i = 0; i < fetchedResources.length; i++) {
+                const r = fetchedResources[i];
+                if (r.resourceUrl) {
+                    contentHtml = contentHtml.replace(new RegExp(escapeRegExp(r.resourceUrl), "g"), r.resourceBase64);
+                }
+            }
+
+            const styleElem = document.createElement("style");
+            styleElem.innerHTML = cssStyles.replace("&gt;", ">").replace("&lt;", "<");
+
+            const styleElemString = new XMLSerializer().serializeToString(styleElem).replace(/&gt;/g, ">").replace(/&lt;/g, "<");
+
+            // create DOM element string that encapsulates styles + content
+            const contentRootElem = document.createElement("body");
+            contentRootElem.style.zIndex = "1111";
+            // contentRootElem.style.transform = "scale(0.08)"
+            contentRootElem.innerHTML = styleElemString + contentHtml;
+            contentRootElem.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
+            //document.body.appendChild(contentRootElem);
+
+            const contentRootElemString = new XMLSerializer().serializeToString(contentRootElem);
+
+            // build SVG string
+            const svg = `<svg xmlns='http://www.w3.org/2000/svg' width='${width}' height='${height}'>
+                        <foreignObject x='0' y='${-scroll}' width='${width}' height='${scroll + height}'>
+                            ${contentRootElemString}
+                        </foreignObject>
+                </svg>`;
+
+            // convert SVG to data-uri
+            const dataUri = `data:image/svg+xml;base64,${window.btoa(unescape(encodeURIComponent(svg)))}`;
+
+            resolve(dataUri);
+        });
+    };
+
+    /**
+     * @param {String} html
+     * @param {Number} width
+     * @param {Number} height
+     * 
+     * @return {Promise<Image>}
+     */
+    this.renderToImage = async function (webUrl, html, width, height, scroll) {
+        return new Promise(async function (resolve, reject) {
+            const img = new Image();
+            console.log("BUILDING SVG for:" + webUrl);
+            img.src = await buildSvgDataUri(webUrl, html, width, height, scroll);
+
+            img.onload = function () {
+                console.log("IMAGE SVG created:" + webUrl);
+                resolve(img);
+            };
+        });
+    };
+
+    /**
+     * @param {String} html
+     * @param {Number} width
+     * @param {Number} height
+     * 
+     * @return {Promise<Image>}
+     */
+    this.renderToCanvas = async function (webUrl, html, width, height, scroll) {
+        return new Promise(async function (resolve, reject) {
+            const img = await self.renderToImage(webUrl, html, width, height, scroll);
+
+            const canvas = document.createElement('canvas');
+            canvas.width = img.width;
+            canvas.height = img.height;
+
+            const canvasCtx = canvas.getContext('2d');
+            canvasCtx.drawImage(img, 0, 0, img.width, img.height);
+
+            resolve(canvas);
+        });
+    };
+
+    /**
+     * @param {String} html
+     * @param {Number} width
+     * @param {Number} height
+     * 
+     * @return {Promise<String>}
+     */
+    this.renderToBase64Png = async function (webUrl, html, width, height, scroll) {
+        return new Promise(async function (resolve, reject) {
+            const canvas = await self.renderToCanvas(webUrl, html, width, height, scroll);
+            resolve(canvas.toDataURL('image/png'));
+        });
+    };
+
+};
+
+
+export function CreateImage(webUrl, styleSheets, html, width, height, scroll) {
+    const val = (new ForeignHtmlRenderer(styleSheets)).renderToBase64Png(webUrl, html.replace(/\n/g, "").replace(/<script((?!\/script).)*<\/script>/g, ""), width, height, scroll);
+    return val;
+}
+
+
+
+var ClipboardUtils = new function () {
+    var permissions = {
+        'image/bmp': true,
+        'image/gif': true,
+        'image/png': true,
+        'image/jpeg': true,
+        'image/tiff': true
+    };
+
+    function getType(types) {
+        for (var j = 0; j < types.length; ++j) {
+            var type = types[j];
+            if (permissions[type]) {
+                return type;
+            }
+        }
+        return null;
+    }
+    function getItem(items) {
+        for (var i = 0; i < items.length; ++i) {
+            var item = items[i];
+            if (item) {
+                var type = getType(item.types);
+                if (type) {
+                    return item.getType(type);
+                }
+            }
+        }
+        return null;
+    }
+    function loadFile(file, callback) {
+        if (window.FileReader) {
+            var reader = new FileReader();
+            reader.onload = function () {
+                callback(reader.result, null);
+            };
+            reader.onerror = function () {
+                callback(null, 'Incorrect file.');
+            };
+            reader.readAsDataURL(file);
+        } else {
+            callback(null, 'File api is not supported.');
+        }
+    }
+    this.readImage = function (callback) {
+        if (navigator.clipboard) {
+            var promise = navigator.clipboard.read();
+            promise
+                .then(function (items) {
+                    var promise = getItem(items);
+                    if (promise == null) {
+                        callback(null, null);
+                        return;
+                    }
+                    promise
+                        .then(function (result) {
+                            loadFile(result, callback);
+                        })
+                        .catch(function (error) {
+                            callback(null, 'Reading clipboard error.');
+                        });
+                })
+                .catch(function (error) {
+                    callback(null, 'Reading clipboard error.');
+                });
+        } else {
+            callback(null, 'Clipboard is not supported.');
+        }
+    };
+};
+
+
+export function pasteImageBitmap(callback) {
+    return ClipboardUtils.readImage(callback);
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/button/ButtonScripts.ts b/src/client/views/nodes/button/ButtonScripts.ts
index bb4dd8bc9..f3731b8f9 100644
--- a/src/client/views/nodes/button/ButtonScripts.ts
+++ b/src/client/views/nodes/button/ButtonScripts.ts
@@ -1,14 +1,14 @@
-import { Scripting } from "../../../util/Scripting";
+import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
 import { SelectionManager } from "../../../util/SelectionManager";
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function changeView(view: string) {
+ScriptingGlobals.add(function changeView(view: string) {
     const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
     selected ? selected.Document._viewType = view : console.log("[FontIconBox.tsx] changeView failed");
 });
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function toggleOverlay() {
+ScriptingGlobals.add(function toggleOverlay() {
     const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
     selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("failed");
 });
\ No newline at end of file
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index bd103dcf7..ca13590de 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -5,31 +5,30 @@ import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { ColorState, SketchPicker } from 'react-color';
-import { Doc, StrListCast, WidthSym, HeightSym } from '../../../../fields/Doc';
+import { Doc, StrListCast } from '../../../../fields/Doc';
 import { InkTool } from '../../../../fields/InkField';
-import { createSchema, makeInterface } from '../../../../fields/Schema';
+import { createSchema } from '../../../../fields/Schema';
 import { ScriptField } from '../../../../fields/ScriptField';
-import { BoolCast, Cast, NumCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { WebField } from '../../../../fields/URLField';
+import { Utils } from '../../../../Utils';
 import { DocumentType } from '../../../documents/DocumentTypes';
-import { Scripting } from "../../../util/Scripting";
+import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
 import { SelectionManager } from '../../../util/SelectionManager';
-import { UndoManager, undoBatch } from '../../../util/UndoManager';
+import { undoBatch, UndoManager } from '../../../util/UndoManager';
 import { CollectionViewType } from '../../collections/CollectionView';
 import { ContextMenu } from '../../ContextMenu';
 import { DocComponent } from '../../DocComponent';
 import { EditableView } from '../../EditableView';
 import { GestureOverlay } from '../../GestureOverlay';
 import { Colors } from '../../global/globalEnums';
-import { SetActiveInkColor, ActiveFillColor, SetActiveFillColor, ActiveInkWidth, ActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
+import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
 import { StyleProp } from '../../StyleProvider';
 import { FieldView, FieldViewProps } from '.././FieldView';
 import { RichTextMenu } from '../formattedText/RichTextMenu';
-import { Utils } from '../../../../Utils';
-import { IButtonProps } from './ButtonInterface';
+import { WebBox } from '../WebBox';
 import { FontIconBadge } from './FontIconBadge';
 import './FontIconBox.scss';
-import { WebBox } from '../WebBox';
 const FontIconSchema = createSchema({
     icon: "string",
 });
@@ -57,11 +56,8 @@ export enum NumButtonType {
 export interface ButtonProps extends FieldViewProps {
     type?: ButtonType;
 }
-
-type FontIconDocument = makeInterface<[typeof FontIconSchema]>;
-const FontIconDocument = makeInterface(FontIconSchema);
 @observer
-export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(FontIconDocument) {
+export class FontIconBox extends DocComponent<ButtonProps>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FontIconBox, fieldKey); }
     showTemplate = (): void => {
         const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null);
@@ -105,16 +101,11 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
      */
     @computed get numberButton() {
         const numBtnType: string = StrCast(this.rootDoc.numBtnType);
-        const setValue = (value: number) => {
-            // Script for running the toggle
-            const script: string = StrCast(this.rootDoc.script) + "(" + value + ")";
-            ScriptField.MakeScript(script)?.script.run();
-        };
+        const numScript = ScriptCast(this.rootDoc.script);
+        const setValue = (value: number) => numScript?.script.run({ value, _readOnly_: false });
 
         // Script for checking the outcome of the toggle
-        const checkScript: string = StrCast(this.rootDoc.script) + "(0, true)";
-        const checkResult: number = ScriptField.MakeScript(checkScript)?.script.run().result || 0;
-
+        const checkResult: number = numScript?.script.run({ value: 0, _readOnly_: true }).result || 0;
 
         if (numBtnType === NumButtonType.Slider) {
             const dropdown =
@@ -237,34 +228,37 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
 
-        const script: string = StrCast(this.rootDoc.script);
+        const script = ScriptCast(this.rootDoc.script);
 
         let noviceList: string[] = [];
         let text: string | undefined;
         let dropdown = true;
         let icon: IconProp = "caret-down";
-
-        if (script === 'setView') {
-            const selected = SelectionManager.Docs().lastElement();
-            if (selected) {
-                if (StrCast(selected.type) === DocumentType.COL) {
-                    text = StrCast(selected._viewType);
+        try {
+            if (script.script.originalScript.startsWith('setView')) {
+                const selected = SelectionManager.Docs().lastElement();
+                if (selected) {
+                    if (StrCast(selected.type) === DocumentType.COL) {
+                        text = StrCast(selected._viewType);
+                    } else {
+                        dropdown = false;
+                        text = selected.type === DocumentType.RTF ? "Text" : StrCast(selected.type);
+                        icon = Doc.toIcon(selected);
+                    }
                 } else {
                     dropdown = false;
-                    text = selected.type === DocumentType.RTF ? "Text" : StrCast(selected.type);
-                    icon = Doc.toIcon(selected);
+                    icon = "globe-asia";
+                    text = "User Default";
                 }
-            } else {
-                dropdown = false;
-                icon = "globe-asia";
-                text = "User Default";
+                noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking];
+            } else if (script.script.originalScript.startsWith('setFont')) {
+                const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+                text = StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
+                noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia",
+                    "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"];
             }
-            noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking];
-        } else if (script === 'setFont') {
-            const editorView = RichTextMenu.Instance?.TextView?.EditorView;
-            text = StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
-            noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia",
-                "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"];
+        } catch (e) {
+            console.log(e);
         }
 
         // Get items to place into the list
@@ -272,18 +266,12 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
             if (Doc.UserDoc().noviceMode && !noviceList.includes(value)) {
                 return;
             }
-            const click = () => {
-                const s = ScriptField.MakeScript(script + '("' + value + '")');
-                if (s) {
-                    s.script.run().result;
-                }
-            };
             return <div className="list-item" key={`${value}`}
                 style={{
-                    fontFamily: script === 'setFont' ? value : undefined,
+                    fontFamily: script.script.originalScript.startsWith('setFont') ? value : undefined,
                     backgroundColor: value === text ? Colors.LIGHT_BLUE : undefined
                 }}
-                onClick={click}>
+                onClick={() => script.script.run({ value }).result}>
                 {value[0].toUpperCase() + value.slice(1)}
             </div>;
         });
@@ -319,15 +307,12 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
     }
 
     @observable colorPickerClosed: boolean = true;
-    @computed get colorScript() {
-        const script = StrCast(this.rootDoc.script);
-        return ScriptField.MakeScript(script + '(colValue, checkResult)', { colValue: "string", checkResult: "boolean" });
-    }
+    @computed get colorScript() { return ScriptCast(this.rootDoc.script); }
 
     colorPicker = (curColor: string) => {
         const change = (value: ColorState) => {
             const s = this.colorScript;
-            s && undoBatch(() => s.script.run({ colValue: Utils.colorString(value), checkResult: false }).result)();
+            s && undoBatch(() => s.script.run({ value: Utils.colorString(value), _readOnly_: false }).result)();
         };
         const presets = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
             '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
@@ -343,7 +328,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
     @computed get colorButton() {
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const curColor = this.colorScript?.script.run({ colValue: undefined, checkResult: true }).result ?? "transparent";
+        const curColor = this.colorScript?.script.run({ value: undefined, _readOnly_: true }).result ?? "transparent";
 
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
             <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
@@ -439,52 +424,34 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
 
     @computed get editableText() {
         // Script for running the toggle
-        const script: string = StrCast(this.rootDoc.script);
-
-        // Script for checking the outcome of the toggle
-        const checkScript: string = StrCast(this.rootDoc.script) + "('', true)";
-
+        const script = ScriptCast(this.rootDoc.script);
         // Function to run the script
-        const checkResult = ScriptField.MakeScript(checkScript)?.script.run().result;
+        const checkResult = script?.script.run({ value: "", _readOnly_: true }).result;
 
-        const setValue = (value: string, shiftDown?: boolean): boolean => {
-            ScriptField.MakeScript(script + "('" + value + "')")?.script.run();
-            return true;
-        };
+        const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ value, _readOnly_: false }).result;
         return (
             <div className="menuButton editableText">
                 <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"lock"} />
                 <div style={{ width: "calc(100% - .875em)", paddingLeft: "4px" }}>
-                    <EditableView GetValue={() => checkResult} SetValue={setValue} contents={checkResult} />
+                    <EditableView GetValue={() => script?.script.run({ value: "", _readOnly_: true }).result} SetValue={setValue} contents={checkResult} />
                 </div>
             </div>
         );
     }
 
-
     render() {
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}>
+            <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
                 {this.label}
             </div>;
 
         const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color: color, backgroundColor: "transparent" }}>
+            <div className="fontIconBox-label" style={{ color, backgroundColor: "transparent" }}>
                 {this.label}
             </div>;
 
-        const buttonProps: IButtonProps = {
-            type: this.type,
-            rootDoc: this.rootDoc,
-            label: label,
-            backgroundColor: backgroundColor,
-            icon: this.icon,
-            color: color
-        };
-
         const buttonText = StrCast(this.rootDoc.buttonText);
 
         // TODO:glr Add label of button type
@@ -493,7 +460,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
         switch (this.type) {
             case ButtonType.TextButton:
                 button = (
-                    <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}>
+                    <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}>
                         <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                         {buttonText ?
                             <div className="button-text">
@@ -522,7 +489,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                 break;
             case ButtonType.ToolButton:
                 button = (
-                    <div className={`menuButton ${this.type}`} style={{ opacity: 1, backgroundColor: backgroundColor, color: color }}>
+                    <div className={`menuButton ${this.type}`} style={{ opacity: 1, backgroundColor, color }}>
                         <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                         {label}
                     </div>
@@ -534,7 +501,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                 break;
             case ButtonType.ClickButton:
                 button = (
-                    <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor, opacity: 1 }}>
+                    <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1 }}>
                         <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                         {label}
                     </div>
@@ -544,7 +511,7 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
                 const trailsIcon = <img src={`/assets/${"presTrails.png"}`}
                     style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? "0%" : "100%"})` }} />;
                 button = (
-                    <div className={`menuButton ${this.type}`} style={{ color: color, backgroundColor: backgroundColor }}>
+                    <div className={`menuButton ${this.type}`} style={{ color, backgroundColor }}>
                         {this.icon === "pres-trail" ? trailsIcon : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />}
                         {menuLabel}
                         <FontIconBadge collection={Cast(this.rootDoc.watchedDocuments, Doc, null)} />
@@ -564,14 +531,14 @@ export class FontIconBox extends DocComponent<ButtonProps, FontIconDocument>(Fon
 
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function setView(view: string) {
+ScriptingGlobals.add(function setView(view: string) {
     const selected = SelectionManager.Docs().lastElement();
     selected ? selected._viewType = view : console.log("[FontIconBox.tsx] changeView failed");
 });
 
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function setBackgroundColor(color?: string, checkResult?: boolean) {
+ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: boolean) {
     const selected = SelectionManager.Docs().lastElement();
     if (checkResult) {
         return selected?._backgroundColor ?? "transparent";
@@ -580,7 +547,7 @@ Scripting.addGlobal(function setBackgroundColor(color?: string, checkResult?: bo
 });
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function setHeaderColor(color?: string, checkResult?: boolean) {
+ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) {
     if (checkResult) {
         return Doc.SharingDoc().userColor;
     }
@@ -590,7 +557,7 @@ Scripting.addGlobal(function setHeaderColor(color?: string, checkResult?: boolea
 });
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) {
+ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
     const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
     if (checkResult && selected) {
         if (NumCast(selected.Document.z) >= 1) return Colors.MEDIUM_BLUE;
@@ -612,7 +579,7 @@ Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) {
  **/
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function setFont(font: string, checkResult?: boolean) {
+ScriptingGlobals.add(function setFont(font: string, checkResult?: boolean) {
     SelectionManager.Docs().map(doc => doc._fontFamily = font);
     const editorView = RichTextMenu.Instance.TextView?.EditorView;
     if (checkResult) {
@@ -622,7 +589,7 @@ Scripting.addGlobal(function setFont(font: string, checkResult?: boolean) {
     else Doc.UserDoc().fontFamily = font;
 });
 
-Scripting.addGlobal(function getActiveTextInfo(info: "family" | "size" | "color" | "highlight") {
+ScriptingGlobals.add(function getActiveTextInfo(info: "family" | "size" | "color" | "highlight") {
     const editorView = RichTextMenu.Instance.TextView?.EditorView;
     const style = editorView?.state && RichTextMenu.Instance.getActiveFontStylesOnSelection();
     switch (info) {
@@ -633,7 +600,7 @@ Scripting.addGlobal(function getActiveTextInfo(info: "family" | "size" | "color"
     }
 });
 
-Scripting.addGlobal(function setAlignment(align: "left" | "right" | "center", checkResult?: boolean) {
+ScriptingGlobals.add(function setAlignment(align: "left" | "right" | "center", checkResult?: boolean) {
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     if (checkResult) {
         return (editorView ? RichTextMenu.Instance.textAlign : Doc.UserDoc().textAlign) === align ? Colors.MEDIUM_BLUE : "transparent";
@@ -642,7 +609,7 @@ Scripting.addGlobal(function setAlignment(align: "left" | "right" | "center", ch
     else Doc.UserDoc().textAlign = align;
 });
 
-Scripting.addGlobal(function setBulletList(mapStyle: "bullet" | "decimal", checkResult?: boolean) {
+ScriptingGlobals.add(function setBulletList(mapStyle: "bullet" | "decimal", checkResult?: boolean) {
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     if (checkResult) {
         const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle();
@@ -657,8 +624,8 @@ Scripting.addGlobal(function setBulletList(mapStyle: "bullet" | "decimal", check
 });
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function setFontColor(color?: string, checkResult?: boolean) {
-    const editorView = RichTextMenu.Instance.TextView?.EditorView;
+ScriptingGlobals.add(function setFontColor(color?: string, checkResult?: boolean) {
+    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
 
     if (checkResult) {
         return editorView ? RichTextMenu.Instance.fontColor : Doc.UserDoc().fontColor;
@@ -669,7 +636,7 @@ Scripting.addGlobal(function setFontColor(color?: string, checkResult?: boolean)
 });
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function setFontHighlight(color?: string, checkResult?: boolean) {
+ScriptingGlobals.add(function setFontHighlight(color?: string, checkResult?: boolean) {
     const selected = SelectionManager.Docs().lastElement();
     const editorView = RichTextMenu.Instance.TextView?.EditorView;
 
@@ -686,18 +653,18 @@ Scripting.addGlobal(function setFontHighlight(color?: string, checkResult?: bool
 });
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function setFontSize(size: string | number, checkResult?: boolean) {
-    if (typeof size === "number") size = size.toString();
-    if (size && Number(size).toString() === size) size += "px";
-    const editorView = RichTextMenu.Instance.TextView?.EditorView;
+ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: boolean) {
+    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     if (checkResult) {
         return (editorView ? RichTextMenu.Instance.fontSize : StrCast(Doc.UserDoc().fontSize, "10px")).replace("px", "");
     }
+    if (typeof size === "number") size = size.toString();
+    if (size && Number(size).toString() === size) size += "px";
     if (editorView) RichTextMenu.Instance.setFontSize(size);
     else Doc.UserDoc()._fontSize = size;
 });
 
-Scripting.addGlobal(function toggleBold(checkResult?: boolean) {
+ScriptingGlobals.add(function toggleBold(checkResult?: boolean) {
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     if (checkResult) {
         return (editorView ? RichTextMenu.Instance.bold : Doc.UserDoc().fontWeight === "bold") ? Colors.MEDIUM_BLUE : "transparent";
@@ -706,7 +673,7 @@ Scripting.addGlobal(function toggleBold(checkResult?: boolean) {
     else Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === "bold" ? undefined : "bold";
 });
 
-Scripting.addGlobal(function toggleUnderline(checkResult?: boolean) {
+ScriptingGlobals.add(function toggleUnderline(checkResult?: boolean) {
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     if (checkResult) {
         return (editorView ? RichTextMenu.Instance.underline : Doc.UserDoc().textDecoration === "underline") ? Colors.MEDIUM_BLUE : "transparent";
@@ -715,7 +682,7 @@ Scripting.addGlobal(function toggleUnderline(checkResult?: boolean) {
     else Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === "underline" ? undefined : "underline";
 });
 
-Scripting.addGlobal(function toggleItalic(checkResult?: boolean) {
+ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     if (checkResult) {
         return (editorView ? RichTextMenu.Instance.italics : Doc.UserDoc().fontStyle === "italics") ? Colors.MEDIUM_BLUE : "transparent";
@@ -733,7 +700,7 @@ Scripting.addGlobal(function toggleItalic(checkResult?: boolean) {
  *      setStrokeColor
  **/
 
-Scripting.addGlobal(function setActiveInkTool(tool: string, checkResult?: boolean) {
+ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boolean) {
     if (checkResult) {
         return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ?
             Colors.MEDIUM_BLUE : "transparent";
@@ -759,7 +726,7 @@ Scripting.addGlobal(function setActiveInkTool(tool: string, checkResult?: boolea
 });
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function setFillColor(color?: string, checkResult?: boolean) {
+ScriptingGlobals.add(function setFillColor(color?: string, checkResult?: boolean) {
     const selected = SelectionManager.Docs().lastElement();
     if (checkResult) {
         if (selected?.type === DocumentType.INK) {
@@ -771,7 +738,7 @@ Scripting.addGlobal(function setFillColor(color?: string, checkResult?: boolean)
     SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.fillColor = color);
 });
 
-Scripting.addGlobal(function setStrokeWidth(width: number, checkResult?: boolean) {
+ScriptingGlobals.add(function setStrokeWidth(width: number, checkResult?: boolean) {
     if (checkResult) {
         const selected = SelectionManager.Docs().lastElement();
         if (selected?.type === DocumentType.INK) {
@@ -784,7 +751,7 @@ Scripting.addGlobal(function setStrokeWidth(width: number, checkResult?: boolean
 });
 
 // toggle: Set overlay status of selected document
-Scripting.addGlobal(function setStrokeColor(color?: string, checkResult?: boolean) {
+ScriptingGlobals.add(function setStrokeColor(color?: string, checkResult?: boolean) {
     if (checkResult) {
         const selected = SelectionManager.Docs().lastElement();
         if (selected?.type === DocumentType.INK) {
@@ -800,7 +767,7 @@ Scripting.addGlobal(function setStrokeColor(color?: string, checkResult?: boolea
 /** WEB
  *      webSetURL
  **/
-Scripting.addGlobal(function webSetURL(url: string, checkResult?: boolean) {
+ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) {
     const selected = SelectionManager.Views().lastElement();
     if (selected?.rootDoc.type === DocumentType.WEB) {
         if (checkResult) {
@@ -810,24 +777,26 @@ Scripting.addGlobal(function webSetURL(url: string, checkResult?: boolean) {
         //selected.rootDoc.data = new WebField(url);
     }
 });
-Scripting.addGlobal(function webForward() {
-    const selected = SelectionManager.Views().lastElement();
-    if (selected?.rootDoc.type === DocumentType.WEB) {
-        (selected.ComponentView as WebBox).forward();
+ScriptingGlobals.add(function webForward(checkResult?: boolean) {
+    const selected = (SelectionManager.Views().lastElement()?.ComponentView as WebBox);
+    if (checkResult) {
+        return selected?.forward(checkResult) ? undefined : "lightGray";
     }
+    selected?.forward();
 });
-Scripting.addGlobal(function webBack() {
-    const selected = SelectionManager.Views().lastElement();
-    if (selected?.rootDoc.type === DocumentType.WEB) {
-        (selected.ComponentView as WebBox).back();
+ScriptingGlobals.add(function webBack(checkResult?: boolean) {
+    const selected = (SelectionManager.Views().lastElement()?.ComponentView as WebBox);
+    if (checkResult) {
+        return selected?.back(checkResult) ? undefined : "lightGray";
     }
+    selected?.back();
 });
 
 
 /** Schema
  *      toggleSchemaPreview
  **/
-Scripting.addGlobal(function toggleSchemaPreview(checkResult?: boolean) {
+ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) {
     const selected = SelectionManager.Docs().lastElement();
     if (checkResult && selected) {
         const result: boolean = NumCast(selected.schemaPreviewWidth) > 0;
@@ -846,7 +815,7 @@ Scripting.addGlobal(function toggleSchemaPreview(checkResult?: boolean) {
 /** STACK
  *      groupBy
  */
-Scripting.addGlobal(function setGroupBy(key: string, checkResult?: boolean) {
+ScriptingGlobals.add(function setGroupBy(key: string, checkResult?: boolean) {
     SelectionManager.Docs().map(doc => doc._fontFamily = key);
     const editorView = RichTextMenu.Instance.TextView?.EditorView;
     if (checkResult) {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 311d52afa..d8cd8d4c5 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -12,13 +12,11 @@ import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from "
 import { EditorView } from "prosemirror-view";
 import { DateField } from '../../../../fields/DateField';
 import { AclAdmin, AclEdit, AclSelfEdit, DataSym, Doc, DocListCast, DocListCastAsync, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym, AclAugment } from "../../../../fields/Doc";
-import { documentSchema } from '../../../../fields/documentSchemas';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
 import { PrefetchProxy } from '../../../../fields/Proxy';
 import { RichTextField } from "../../../../fields/RichTextField";
 import { RichTextUtils } from '../../../../fields/RichTextUtils';
-import { makeInterface } from "../../../../fields/Schema";
 import { Cast, DateCast, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
 import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util';
 import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../../Utils';
@@ -77,13 +75,10 @@ export interface FormattedTextBoxProps {
 }
 export const GoogleRef = "googleDocId";
 
-type RichTextDocument = makeInterface<[typeof documentSchema]>;
-const RichTextDocument = makeInterface(documentSchema);
-
 type PullHandler = (exportState: Opt<GoogleApiClientUtils.Docs.ImportResult>, dataDoc: Doc) => void;
 
 @observer
-export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProps & FormattedTextBoxProps), RichTextDocument>(RichTextDocument) {
+export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProps & FormattedTextBoxProps)>() {
     public static LayoutString(fieldStr: string) { return FieldView.LayoutString(FormattedTextBox, fieldStr); }
     public static blankState = () => EditorState.create(FormattedTextBox.Instance.config);
     public static Instance: FormattedTextBox;
@@ -250,7 +245,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             this._editorView.updateState(state);
 
             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 * 1000)));
+            //tsel.marks().filter(m => m.type === this._editorView!.state.schema.marks.user_mark).map(m => AudioBox.SetScrubTime(Math.max(0, m.attrs.modified * 1000)));
             const curText = state.doc.textBetween(0, state.doc.content.size, " \n");
             const curTemp = this.layoutDoc.resolvedDataDoc ? Cast(this.layoutDoc[this.props.fieldKey], RichTextField) : undefined;               // the actual text in the text box
             const curProto = Cast(Cast(this.dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null);              // the default text inherited from a prototype
@@ -645,7 +640,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 } else if (!this.rootDoc.isTemplateDoc) {
                     const title = StrCast(this.rootDoc.title);
                     this.rootDoc.title = "text";
-                    this.rootDoc.layout = (this.layoutDoc as Doc).layout as string;
+                    this.rootDoc.layout = this.layoutDoc.layout as string;
                     this.rootDoc.title = this.layoutDoc.isTemplateForField as string;
                     this.rootDoc.isTemplateDoc = false;
                     this.rootDoc.isTemplateForField = "";
@@ -1130,13 +1125,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 },
                 dispatchTransaction: this.dispatchTransaction,
                 nodeViews: {
-                    dashComment(node, view, getPos) { return new DashDocCommentView(node, view, getPos); },
-                    dashDoc(node, view, getPos) { return new DashDocView(node, view, getPos, self); },
-                    dashField(node, view, getPos) { return new DashFieldView(node, view, getPos, self); },
-                    equation(node, view, getPos) { return new EquationView(node, view, getPos, self); },
-                    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); }
+                    dashComment(node: any, view: any, getPos: any) { return new DashDocCommentView(node, view, getPos); },
+                    dashDoc(node: any, view: any, getPos: any) { return new DashDocView(node, view, getPos, self); },
+                    dashField(node: any, view: any, getPos: any) { return new DashFieldView(node, view, getPos, self); },
+                    equation(node: any, view: any, getPos: any) { return new EquationView(node, view, getPos, self); },
+                    summary(node: any, view: any, getPos: any) { return new SummaryView(node, view, getPos); },
+                    ordered_list(node: any, view: any, getPos: any) { return new OrderedListView(); },
+                    footnote(node: any, view: any, getPos: any) { return new FootnoteView(node, view, getPos); }
                 },
                 clipboardTextSerializer: this.clipboardTextSerializer,
                 handlePaste: this.handlePaste,
@@ -1423,7 +1418,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                         this.dataDoc[this.fieldKey + "-translation"] = result1 + "\r\n\r\n" + result[0];
                     }), 1000);
                 });
-            } catch (e) { console.log(e.message); }
+            } catch (e: any) { console.log(e.message); }
             this._lastText = curText;
         }
     }
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 76a5675de..c76eda859 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -106,7 +106,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     bind("Mod-U", toggleEditableMark(schema.marks.underline));
 
     //Commands for lists
-    bind("Ctrl-i", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state, dispatch as any));
+    bind("Ctrl-i", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state as any, dispatch as any));
 
     bind("Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
         /// bcz; Argh!!  replace layotuTEmpalteString with a onTab prop conditionally handles Tab);
@@ -125,7 +125,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
             dispatch(tx3);
         })) { // couldn't sink into an existing list, so wrap in a new one
             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) => {
+            if (!wrapInList(schema.nodes.ordered_list)(newstate.state as any, (tx2: Transaction) => {
                 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]);
@@ -160,14 +160,14 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     });
 
     //Commands to modify BlockType
-    bind("Ctrl->", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit((state) && wrapIn(schema.nodes.blockquote)(state, dispatch as any)));
-    bind("Alt-\\", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.paragraph)(state, dispatch as any));
-    bind("Shift-Ctrl-\\", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.code_block)(state, dispatch as any));
+    bind("Ctrl->", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit((state) && wrapIn(schema.nodes.blockquote)(state as any, dispatch as any)));
+    bind("Alt-\\", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.paragraph)(state as any, dispatch as any));
+    bind("Shift-Ctrl-\\", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.code_block)(state as any, dispatch as any));
 
     bind("Ctrl-m", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && dispatch(state.tr.replaceSelectionWith(schema.nodes.equation.create({ fieldKey: "math" + Utils.GenerateGuid() }))));
 
     for (let i = 1; i <= 6; i++) {
-        bind("Shift-Ctrl-" + i, (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.heading, { level: i })(state, dispatch as any));
+        bind("Shift-Ctrl-" + i, (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && setBlockType(schema.nodes.heading, { level: i })(state as any, dispatch as any));
     }
 
     //Command to create a horizontal break line
@@ -197,13 +197,13 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     bind("Backspace", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
         if (!canEdit(state)) return true;
 
-        if (!deleteSelection(state, (tx: Transaction<Schema<any, any>>) => {
+        if (!deleteSelection(state, (tx: Transaction<S>) => {
             dispatch(updateBullets(tx, schema));
         })) {
-            if (!joinBackward(state, (tx: Transaction<Schema<any, any>>) => {
+            if (!joinBackward(state, (tx: Transaction<S>) => {
                 dispatch(updateBullets(tx, schema));
             })) {
-                if (!selectNodeBackward(state, (tx: Transaction<Schema<any, any>>) => {
+                if (!selectNodeBackward(state, (tx: Transaction<S>) => {
                     dispatch(updateBullets(tx, schema));
                 })) {
                     return false;
@@ -225,14 +225,14 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
         const depth = trange ? liftTarget(trange) : undefined;
         const split = path.length > 5 && !path[path.length - 3].textContent && path[path.length - 6].type !== schema.nodes.list_item;
         if (split && trange && depth !== undefined && depth !== null) {
-            dispatch(state.tr.lift(trange, depth));
+            dispatch(state.tr.lift(trange, depth) as any);
             return true;
         }
 
         const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
         const cr = state.selection.$from.node().textContent.endsWith("\n");
-        if (cr || !newlineInCode(state, dispatch)) {
-            if (!splitListItem(schema.nodes.list_item)(state, (tx2: Transaction) => {
+        if (cr || !newlineInCode(state, dispatch as any)) {
+            if (!splitListItem(schema.nodes.list_item)(state as any, (tx2: Transaction) => {
                 const tx3 = updateBullets(tx2, schema);
                 marks && tx3.ensureMarks([...marks]);
                 marks && tx3.setStoredMarks([...marks]);
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 711136469..bafae84dc 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -34,13 +34,9 @@ export class RichTextRules {
             wrappingInputRule(
                 /^1\.\s$/,
                 schema.nodes.ordered_list,
-                () => {
-                    return ({ mapStyle: "decimal", bulletStyle: 1 });
-                },
-                (match: any, node: any) => {
-                    return node.childCount + node.attrs.order === +match[1];
-                },
-                (type: any) => ({ type: type, attrs: { mapStyle: "decimal", bulletStyle: 1 } })
+                () => ({ mapStyle: "decimal", bulletStyle: 1 }),
+                (match: any, node: any) => node.childCount + node.attrs.order === +match[1],
+                ((type: any) => ({ type: type, attrs: { mapStyle: "decimal", bulletStyle: 1 } })) as any
             ),
 
             // A. create alphabetical ordered list
@@ -55,20 +51,16 @@ export class RichTextRules {
                 (match: any, node: any) => {
                     return node.childCount + node.attrs.order === +match[1];
                 },
-                (type: any) => ({ type: type, attrs: { mapStyle: "multi", bulletStyle: 1 } })
+                ((type: any) => ({ type: type, attrs: { mapStyle: "multi", bulletStyle: 1 } })) as any
             ),
 
             // * + - create bullet list
             wrappingInputRule(/^\s*([-+*])\s$/, schema.nodes.ordered_list,
                 // match => {
-                () => {
-                    return ({ mapStyle: "bullet" });
-                    // return ({ order: +match[1] })
-                },
-                (match: any, node: any) => {
-                    return node.childCount + node.attrs.order === +match[1];
-                },
-                (type: any) => ({ type: type, attrs: { mapStyle: "bullet" } })),
+                () => ({ mapStyle: "bullet" }),  //  ({ order: +match[1] })
+                (match: any, node: any) => node.childCount + node.attrs.order === +match[1],
+                ((type: any) => ({ type: type, attrs: { mapStyle: "bullet" } })) as any
+            ),
 
             // ``` create code block
             textblockTypeInputRule(/^```$/, schema.nodes.code_block),
@@ -221,7 +213,7 @@ export class RichTextRules {
                     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)
-                            tr.selection.anchor - tr.selection.$anchor.nodeBefore!.nodeSize)));
+                            tr.selection.anchor - (tr.selection.$anchor.nodeBefore?.nodeSize || 0))));
                 }),
 
             // activate a style by name using prefix '%<color name>'
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 2370b7e16..2e312ee51 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -6,11 +6,10 @@ import { observer } from "mobx-react";
 import { ColorState, SketchPicker } from "react-color";
 import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
 import { Doc, DocListCast, DocListCastAsync, FieldResult } from "../../../../fields/Doc";
-import { documentSchema } from "../../../../fields/documentSchemas";
 import { InkTool } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
 import { PrefetchProxy } from "../../../../fields/Proxy";
-import { listSpec, makeInterface } from "../../../../fields/Schema";
+import { listSpec } from "../../../../fields/Schema";
 import { ScriptField } from "../../../../fields/ScriptField";
 import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
 import { emptyFunction, returnFalse, returnOne, returnTrue } from '../../../../Utils';
@@ -18,7 +17,7 @@ import { Docs } from "../../../documents/Documents";
 import { DocumentType } from "../../../documents/DocumentTypes";
 import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
 import { DocumentManager } from "../../../util/DocumentManager";
-import { Scripting } from "../../../util/Scripting";
+import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { undoBatch, UndoManager } from "../../../util/UndoManager";
 import { CollectionDockingView } from "../../collections/CollectionDockingView";
@@ -39,11 +38,8 @@ export class PinProps {
     hidePresBox?: boolean;
 }
 
-type PresBoxSchema = makeInterface<[typeof documentSchema]>;
-const PresBoxDocument = makeInterface(documentSchema);
-
 @observer
-export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>(PresBoxDocument) {
+export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); }
 
     /**
@@ -2461,7 +2457,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema>
             </div>;
     }
 }
-Scripting.addGlobal(function lookupPresBoxField(container: Doc, field: string, data: Doc) {
+ScriptingGlobals.add(function lookupPresBoxField(container: Doc, field: string, data: Doc) {
     if (field === 'indexInPres') return DocListCast(container[StrCast(container.presentationFieldKey)]).indexOf(data);
     if (field === 'presCollapsedHeight') return container._viewType === CollectionViewType.Stacking ? 35 : 31;
     if (field === 'presStatus') return container.presStatus;
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 238d025dc..a4ec559f5 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -3,11 +3,10 @@ import { Tooltip } from "@material-ui/core";
 import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
 import { observer } from "mobx-react";
 import { DataSym, Doc, Opt } from "../../../../fields/Doc";
-import { documentSchema } from '../../../../fields/documentSchemas';
 import { Id } from "../../../../fields/FieldSymbols";
-import { createSchema, makeInterface } from '../../../../fields/Schema';
 import { Cast, NumCast, StrCast } from "../../../../fields/Types";
-import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents, emptyPath, returnEmptyDoclist } from "../../../../Utils";
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from "../../../../Utils";
+import { DocUtils } from "../../../documents/Documents";
 import { DocumentType } from "../../../documents/DocumentTypes";
 import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
 import { DocumentManager } from "../../../util/DocumentManager";
@@ -16,36 +15,20 @@ import { Transform } from "../../../util/Transform";
 import { undoBatch } from "../../../util/UndoManager";
 import { ViewBoxBaseComponent } from '../../DocComponent';
 import { EditableView } from "../../EditableView";
+import { Colors } from "../../global/globalEnums";
 import { DocumentView, DocumentViewProps } from "../../nodes/DocumentView";
 import { FieldView, FieldViewProps } from '../../nodes/FieldView';
-import { PresBox } from "./PresBox";
-import { Colors } from "../../global/globalEnums";
 import { StyleProp } from "../../StyleProvider";
+import { PresBox } from "./PresBox";
 import "./PresElementBox.scss";
-import React = require("react");
-import { DocUtils } from "../../../documents/Documents";
 import { PresMovement } from "./PresEnums";
-
-export const presSchema = createSchema({
-    presentationTargetDoc: Doc,
-    presBox: Doc,
-    presZoomButton: "boolean",
-    presNavButton: "boolean",
-    presHideTillShownButton: "boolean",
-    presFadeButton: "boolean",
-    presHideAfterButton: "boolean",
-    presGroupButton: "boolean",
-    presExpandInlineButton: "boolean"
-});
-
-type PresDocument = makeInterface<[typeof presSchema, typeof documentSchema]>;
-const PresDocument = makeInterface(presSchema, documentSchema);
+import React = require("react");
 /**
  * This class models the view a document added to presentation will have in the presentation.
  * It involves some functionality for its buttons and options.
  */
 @observer
-export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDocument>(PresDocument) {
+export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresElementBox, fieldKey); }
     _heightDisposer: IReactionDisposer | undefined;
 
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 3f7f38bdf..1856c5353 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -26,7 +26,6 @@ import { StyleProp } from "../StyleProvider";
 import { AnchorMenu } from "./AnchorMenu";
 import { Annotation } from "./Annotation";
 import "./PDFViewer.scss";
-const pdfjs = require('pdfjs-dist/es5/build/pdf.js');
 import React = require("react");
 const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
 const pdfjsLib = require("pdfjs-dist");
@@ -34,7 +33,7 @@ const _global = (window /* browser */ || global /* node */) as any;
 
 //pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`;
 // The workerSrc property shall be specified.
-pdfjsLib.GlobalWorkerOptions.workerSrc = "https://unpkg.com/pdfjs-dist@2.4.456/build/pdf.worker.min.js";
+pdfjsLib.GlobalWorkerOptions.workerSrc = "https://unpkg.com/pdfjs-dist@2.13.216/build/pdf.worker.js";
 
 interface IViewerProps extends FieldViewProps {
     Document: Doc;
@@ -159,7 +158,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     initialLoad = async () => {
         if (this._pageSizes.length === 0) {
             this._pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages);
-            await Promise.all(this._pageSizes.map<Pdfjs.PDFPromise<any>>((val, i) =>
+            await Promise.all(this._pageSizes.map((val, i) =>
                 this.props.pdf.getPage(i + 1).then(action((page: Pdfjs.PDFPageProxy) => {
                     const page0or180 = page.rotate === 0 || page.rotate === 180;
                     this._pageSizes.splice(i, 1, {
@@ -219,7 +218,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     }
 
     pagesinit = () => {
-        if (this._pdfViewer._setDocumentViewerElement.offsetParent) {
+        if (this._pdfViewer._setDocumentViewerElement?.offsetParent) {
             runInAction(() => this._pdfViewer.currentScaleValue = this._zoomed = 1);
             this.gotoPage(NumCast(this.props.Document._curPage, 1));
         }
@@ -555,7 +554,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         </div>;
     }
     @computed get pdfViewerDiv() {
-        return <div className={"pdfViewerDash-text" + (this.props.pointerEvents !== "none" && this._textSelecting && (this.props.isSelected() || this.props.isContentActive()) ? "-selected" : "")} ref={this._viewer} />;
+        return <div className={"pdfViewerDash-text" + (this.props.pointerEvents !== "none" && this._textSelecting && this.props.isContentActive() ? "-selected" : "")} ref={this._viewer} />;
     }
     @computed get contentScaling() { return this.props.ContentScaling?.() || 1; }
     @computed get standinViews() {
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 09cfb2077..5fe2a5ab1 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -1,28 +1,18 @@
+import { Tooltip } from "@material-ui/core";
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field } from '../../../fields/Doc';
-import { documentSchema } from "../../../fields/documentSchemas";
 import { Id } from '../../../fields/FieldSymbols';
-import { createSchema, makeInterface } from '../../../fields/Schema';
 import { StrCast } from '../../../fields/Types';
+import { DocUtils } from '../../documents/Documents';
 import { DocumentType } from "../../documents/DocumentTypes";
+import { DocumentManager } from '../../util/DocumentManager';
 import { CollectionDockingView } from "../collections/CollectionDockingView";
 import { ViewBoxBaseComponent } from "../DocComponent";
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
 import "./SearchBox.scss";
-import { DocumentManager } from '../../util/DocumentManager';
-import { DocUtils } from '../../documents/Documents';
-import { Tooltip } from "@material-ui/core";
-import { DictationOverlay } from '../DictationOverlay';
-import { CollectionSchemaBooleanCell } from '../collections/collectionSchema/CollectionSchemaCells';
-
-export const searchSchema = createSchema({
-    Document: Doc
-});
 
-type SearchBoxDocument = makeInterface<[typeof documentSchema, typeof searchSchema]>;
-const SearchBoxDocument = makeInterface(documentSchema, searchSchema);
 
 const DAMPENING_FACTOR = 0.9;
 const MAX_ITERATIONS = 25;
@@ -38,7 +28,7 @@ export interface SearchBoxProps extends FieldViewProps {
  * the search panel on the left side of the screen.
  */
 @observer
-export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps, SearchBoxDocument>(SearchBoxDocument) {
+export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); }
     public static Instance: SearchBox;
 
diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx
index 505a8da7e..e0d328c89 100644
--- a/src/client/views/webcam/DashWebRTCVideo.tsx
+++ b/src/client/views/webcam/DashWebRTCVideo.tsx
@@ -12,6 +12,7 @@ import "./DashWebRTCVideo.scss";
 import { hangup, initialize, refreshVideos } from "./WebCamLogic";
 import React = require("react");
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
+import { IconLookup } from "@fortawesome/fontawesome-svg-core";
 
 
 /**
@@ -64,8 +65,8 @@ export class DashWebRTCVideo extends React.Component<CollectionFreeFormDocumentV
                     }}></video>
                 </div>
                 <div className="buttonContainer">
-                    <div className="videoButtons" style={{ background: "red" }} onClick={this.onClickHangUp}><FontAwesomeIcon icon={faPhoneSlash} color="white" /></div>
-                    <div className="videoButtons" style={{ background: "green" }} onClick={this.onClickRefresh}><FontAwesomeIcon icon={faSync} color="white" /></div>
+                    <div className="videoButtons" style={{ background: "red" }} onClick={this.onClickHangUp}><FontAwesomeIcon icon={faPhoneSlash as IconLookup} color="white" /></div>
+                    <div className="videoButtons" style={{ background: "green" }} onClick={this.onClickRefresh}><FontAwesomeIcon icon={faSync as IconLookup} color="white" /></div>
                 </div>
             </div >;
 
diff --git a/src/fields/CursorField.ts b/src/fields/CursorField.ts
index 28467377b..a8a2859d2 100644
--- a/src/fields/CursorField.ts
+++ b/src/fields/CursorField.ts
@@ -50,7 +50,7 @@ export default class CursorField extends ObjectField {
     setPosition(position: CursorPosition) {
         this.data.position = position;
         this.data.metadata.timestamp = Date.now();
-        this[OnUpdate]();
+        this[OnUpdate]?.();
     }
 
     [Copy]() {
diff --git a/src/fields/DateField.ts b/src/fields/DateField.ts
index 48106d978..26f51b2d3 100644
--- a/src/fields/DateField.ts
+++ b/src/fields/DateField.ts
@@ -2,7 +2,7 @@ import { Deserializable } from "../client/util/SerializationHelper";
 import { serializable, date } from "serializr";
 import { ObjectField } from "./ObjectField";
 import { Copy, ToScriptString, ToString } from "./FieldSymbols";
-import { scriptingGlobal, Scripting } from "../client/util/Scripting";
+import { scriptingGlobal, ScriptingGlobals } from "../client/util/ScriptingGlobals";
 
 @scriptingGlobal
 @Deserializable("date")
@@ -35,6 +35,6 @@ export class DateField extends ObjectField {
     }
 }
 
-Scripting.addGlobal(function d(...dateArgs: any[]) {
+ScriptingGlobals.add(function d(...dateArgs: any[]) {
     return new DateField(new (Date as any)(...dateArgs));
 });
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index c15d4b4af..642becb46 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -7,7 +7,7 @@ import { DocServer } from "../client/DocServer";
 import { DocumentType } from "../client/documents/DocumentTypes";
 import { CurrentUserUtils } from "../client/util/CurrentUserUtils";
 import { LinkManager } from "../client/util/LinkManager";
-import { Scripting, scriptingGlobal } from "../client/util/Scripting";
+import { scriptingGlobal, ScriptingGlobals } from "../client/util/ScriptingGlobals";
 import { SelectionManager } from "../client/util/SelectionManager";
 import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper } from "../client/util/SerializationHelper";
 import { UndoManager } from "../client/util/UndoManager";
@@ -216,7 +216,7 @@ export class Doc extends RefField {
         return self.resolvedDataDoc && !self.isTemplateForField ? self :
             Doc.GetProto(Cast(Doc.Layout(self).resolvedDataDoc, Doc, null) || self);
     }
-    @computed get __LAYOUT__() {
+    @computed get __LAYOUT__(): Doc | undefined {
         const templateLayoutDoc = Cast(Doc.LayoutField(this[SelfProxy]), Doc, null);
         if (templateLayoutDoc) {
             let renderFieldKey: any;
@@ -521,7 +521,8 @@ export namespace Doc {
             Doc.SetLayout(alias, Doc.MakeAlias(layout));
         }
         alias.aliasOf = doc;
-        alias.title = ComputedField.MakeFunction(`renameAlias(this, ${Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1})`);
+        alias.aliasNumber = Doc.GetProto(doc).aliasNumber = NumCast(Doc.GetProto(doc).aliasNumber) + 1;
+        alias.title = ComputedField.MakeFunction(`renameAlias(this)`);
         alias.author = Doc.CurrentUserEmail;
 
         Doc.AddDocToList(Doc.GetProto(doc)[DataSym], "aliases", alias);
@@ -1391,33 +1392,33 @@ export namespace Doc {
 
 }
 
-Scripting.addGlobal(function idToDoc(id: string): any { return DocServer.GetCachedRefField(id); });
-Scripting.addGlobal(function renameAlias(doc: any, n: any) { return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, "") + `(${n})`; });
-Scripting.addGlobal(function getProto(doc: any) { return Doc.GetProto(doc); });
-Scripting.addGlobal(function getDocTemplate(doc?: any) { return Doc.getDocTemplate(doc); });
-Scripting.addGlobal(function getAlias(doc: any) { return Doc.MakeAlias(doc); });
-Scripting.addGlobal(function getCopy(doc: any, copyProto: any) { return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto); });
-Scripting.addGlobal(function copyDragFactory(dragFactory: Doc) { return Doc.copyDragFactory(dragFactory); });
-Scripting.addGlobal(function delegateDragFactory(dragFactory: Doc) { return Doc.delegateDragFactory(dragFactory); });
-Scripting.addGlobal(function copyField(field: any) { return Field.Copy(field); });
-Scripting.addGlobal(function docList(field: any) { return DocListCast(field); });
-Scripting.addGlobal(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); });
-Scripting.addGlobal(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); });
-Scripting.addGlobal(function deiconifyView(doc: any) { Doc.deiconifyView(doc); });
-Scripting.addGlobal(function undo() { SelectionManager.DeselectAll(); return UndoManager.Undo(); });
-Scripting.addGlobal(function redo() { SelectionManager.DeselectAll(); return UndoManager.Redo(); });
-Scripting.addGlobal(function DOC(id: string) { console.log("Can't parse a document id in a script"); return "invalid"; });
-Scripting.addGlobal(function assignDoc(doc: Doc, field: string, id: string) { return Doc.assignDocToField(doc, field, id); });
-Scripting.addGlobal(function docCast(doc: FieldResult): any { return DocCastAsync(doc); });
-Scripting.addGlobal(function activePresentationItem() {
+ScriptingGlobals.add(function idToDoc(id: string): any { return DocServer.GetCachedRefField(id); });
+ScriptingGlobals.add(function renameAlias(doc: any) { return StrCast(Doc.GetProto(doc).title).replace(/\([0-9]*\)/, "") + `(${doc.aliasNumber})`; });
+ScriptingGlobals.add(function getProto(doc: any) { return Doc.GetProto(doc); });
+ScriptingGlobals.add(function getDocTemplate(doc?: any) { return Doc.getDocTemplate(doc); });
+ScriptingGlobals.add(function getAlias(doc: any) { return Doc.MakeAlias(doc); });
+ScriptingGlobals.add(function getCopy(doc: any, copyProto: any) { return doc.isTemplateDoc ? Doc.ApplyTemplate(doc) : Doc.MakeCopy(doc, copyProto); });
+ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) { return Doc.copyDragFactory(dragFactory); });
+ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) { return Doc.delegateDragFactory(dragFactory); });
+ScriptingGlobals.add(function copyField(field: any) { return Field.Copy(field); });
+ScriptingGlobals.add(function docList(field: any) { return DocListCast(field); });
+ScriptingGlobals.add(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); });
+ScriptingGlobals.add(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); });
+ScriptingGlobals.add(function deiconifyView(doc: any) { Doc.deiconifyView(doc); });
+ScriptingGlobals.add(function undo() { SelectionManager.DeselectAll(); return UndoManager.Undo(); });
+ScriptingGlobals.add(function redo() { SelectionManager.DeselectAll(); return UndoManager.Redo(); });
+ScriptingGlobals.add(function DOC(id: string) { console.log("Can't parse a document id in a script"); return "invalid"; });
+ScriptingGlobals.add(function assignDoc(doc: Doc, field: string, id: string) { return Doc.assignDocToField(doc, field, id); });
+ScriptingGlobals.add(function docCast(doc: FieldResult): any { return DocCastAsync(doc); });
+ScriptingGlobals.add(function activePresentationItem() {
     const curPres = Doc.UserDoc().activePresentation as Doc;
     return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)];
 });
-Scripting.addGlobal(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
+ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
     const docs = SelectionManager.Views().map(dv => dv.props.Document).
         filter(d => !Doc.AreProtosEqual(d, container) && !d.annotationOn && d.type !== DocumentType.KVP &&
             (!excludeCollections || d.type !== DocumentType.COL || !Cast(d.data, listSpec(Doc), null)));
     return docs.length ? new List(docs) : prevValue;
 });
-Scripting.addGlobal(function setDocFilter(container: Doc, key: string, value: any, modifiers: "match" | "check" | "x" | "remove") { Doc.setDocFilter(container, key, value, modifiers); });
-Scripting.addGlobal(function setDocRangeFilter(container: Doc, key: string, range: number[]) { Doc.setDocRangeFilter(container, key, range); });
+ScriptingGlobals.add(function setDocFilter(container: Doc, key: string, value: any, modifiers: "match" | "check" | "x" | "remove") { Doc.setDocFilter(container, key, value, modifiers); });
+ScriptingGlobals.add(function setDocRangeFilter(container: Doc, key: string, range: number[]) { Doc.setDocRangeFilter(container, key, range); });
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index 560cf3d63..31024e805 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -1,9 +1,9 @@
+import { Bezier } from "bezier-js";
 import { createSimpleSchema, list, object, serializable } from "serializr";
-import { Scripting } from "../client/util/Scripting";
+import { ScriptingGlobals } from "../client/util/ScriptingGlobals";
 import { Deserializable } from "../client/util/SerializationHelper";
 import { Copy, ToScriptString, ToString } from "./FieldSymbols";
 import { ObjectField } from "./ObjectField";
-import { Bezier } from "bezier-js";
 
 // Helps keep track of the current ink tool in use.
 export enum InkTool {
@@ -90,4 +90,4 @@ export class InkField extends ObjectField {
     }
 }
 
-Scripting.addGlobal("InkField", InkField);
\ No newline at end of file
+ScriptingGlobals.add("InkField", InkField);
\ No newline at end of file
diff --git a/src/fields/List.ts b/src/fields/List.ts
index 93a8d1d60..60bf442d4 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -1,7 +1,7 @@
 import { action, observable } from "mobx";
 import { alias, list, serializable } from "serializr";
 import { DocServer } from "../client/DocServer";
-import { Scripting } from "../client/util/Scripting";
+import { ScriptingGlobals } from "../client/util/ScriptingGlobals";
 import { afterDocDeserialize, autoObject, Deserializable } from "../client/util/SerializationHelper";
 import { Field } from "./Doc";
 import { Copy, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update } from "./FieldSymbols";
@@ -327,8 +327,8 @@ class ListImpl<T extends Field> extends ObjectField {
 export type List<T extends Field> = ListImpl<T> & (T | (T extends RefField ? Promise<T> : never))[];
 export const List: { new <T extends Field>(fields?: T[]): List<T> } = ListImpl as any;
 
-Scripting.addGlobal("List", List);
-Scripting.addGlobal(function compareLists(l1: any, l2: any) {
+ScriptingGlobals.add("List", List);
+ScriptingGlobals.add(function compareLists(l1: any, l2: any) {
     const L1 = Cast(l1, listSpec("string"), []);
     const L2 = Cast(l2, listSpec("string"), []);
     return !L1 && !L2 ? true : L1 && L2 && L1.length === L2.length && L2.reduce((p, v) => p && L1.includes(v), true);
diff --git a/src/fields/ObjectField.ts b/src/fields/ObjectField.ts
index 92b2cfa60..daa8a7777 100644
--- a/src/fields/ObjectField.ts
+++ b/src/fields/ObjectField.ts
@@ -1,9 +1,9 @@
 import { RefField } from "./RefField";
 import { OnUpdate, Parent, Copy, ToScriptString, ToString } from "./FieldSymbols";
-import { Scripting } from "../client/util/Scripting";
+import { ScriptingGlobals } from "../client/util/ScriptingGlobals";
 
 export abstract class ObjectField {
-    public [OnUpdate](diff?: any) { }
+    public [OnUpdate]?: (diff?: any) => void;
     public [Parent]?: RefField | ObjectField;
     abstract [Copy](): ObjectField;
 
@@ -17,4 +17,4 @@ export namespace ObjectField {
     }
 }
 
-Scripting.addGlobal(ObjectField);
\ No newline at end of file
+ScriptingGlobals.add(ObjectField);
\ No newline at end of file
diff --git a/src/fields/Proxy.ts b/src/fields/Proxy.ts
index f01b502c9..2c5f38818 100644
--- a/src/fields/Proxy.ts
+++ b/src/fields/Proxy.ts
@@ -6,7 +6,7 @@ import { DocServer } from "../client/DocServer";
 import { RefField } from "./RefField";
 import { ObjectField } from "./ObjectField";
 import { Id, Copy, ToScriptString, ToString } from "./FieldSymbols";
-import { scriptingGlobal } from "../client/util/Scripting";
+import { scriptingGlobal } from "../client/util/ScriptingGlobals";
 import { Plugins } from "./util";
 
 function deserializeProxy(field: any) {
diff --git a/src/fields/RichTextField.ts b/src/fields/RichTextField.ts
index ae5f301d0..d7edd4266 100644
--- a/src/fields/RichTextField.ts
+++ b/src/fields/RichTextField.ts
@@ -1,8 +1,8 @@
-import { ObjectField } from "./ObjectField";
 import { serializable } from "serializr";
+import { scriptingGlobal } from "../client/util/ScriptingGlobals";
 import { Deserializable } from "../client/util/SerializationHelper";
-import { Copy, ToScriptString, ToPlainText, ToString } from "./FieldSymbols";
-import { scriptingGlobal } from "../client/util/Scripting";
+import { Copy, ToScriptString, ToString } from "./FieldSymbols";
+import { ObjectField } from "./ObjectField";
 
 @scriptingGlobal
 @Deserializable("RichTextField")
diff --git a/src/fields/SchemaHeaderField.ts b/src/fields/SchemaHeaderField.ts
index a53fa542e..3b02d0cfe 100644
--- a/src/fields/SchemaHeaderField.ts
+++ b/src/fields/SchemaHeaderField.ts
@@ -2,7 +2,7 @@ import { Deserializable } from "../client/util/SerializationHelper";
 import { serializable, primitive } from "serializr";
 import { ObjectField } from "./ObjectField";
 import { Copy, ToScriptString, ToString, OnUpdate } from "./FieldSymbols";
-import { scriptingGlobal } from "../client/util/Scripting";
+import { scriptingGlobal } from "../client/util/ScriptingGlobals";
 import { ColumnType } from "../client/views/collections/collectionSchema/CollectionSchemaView";
 
 export const PastelSchemaPalette = new Map<string, string>([
@@ -82,32 +82,32 @@ export class SchemaHeaderField extends ObjectField {
 
     setHeading(heading: string) {
         this.heading = heading;
-        this[OnUpdate]();
+        this[OnUpdate]?.();
     }
 
     setColor(color: string) {
         this.color = color;
-        this[OnUpdate]();
+        this[OnUpdate]?.();
     }
 
     setType(type: ColumnType) {
         this.type = type;
-        this[OnUpdate]();
+        this[OnUpdate]?.();
     }
 
     setWidth(width: number) {
         this.width = width;
-        this[OnUpdate]();
+        this[OnUpdate]?.();
     }
 
     setDesc(desc: boolean | undefined) {
         this.desc = desc;
-        this[OnUpdate]();
+        this[OnUpdate]?.();
     }
 
     setCollapsed(collapsed: boolean | undefined) {
         this.collapsed = collapsed;
-        this[OnUpdate]();
+        this[OnUpdate]?.();
     }
 
     [Copy]() {
diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts
index bd93bf5fb..40ca0ce22 100644
--- a/src/fields/ScriptField.ts
+++ b/src/fields/ScriptField.ts
@@ -1,15 +1,16 @@
-import { ObjectField } from "./ObjectField";
-import { CompiledScript, CompileScript, scriptingGlobal, ScriptOptions, CompileError, CompileResult, Scripting } from "../client/util/Scripting";
-import { Copy, ToScriptString, ToString, Parent, SelfProxy } from "./FieldSymbols";
-import { serializable, createSimpleSchema, map, primitive, object, deserialize, PropSchema, custom, SKIP } from "serializr";
-import { Deserializable, autoObject } from "../client/util/SerializationHelper";
-import { Doc, Field, Opt } from "./Doc";
-import { Plugins, setter } from "./util";
 import { computedFn } from "mobx-utils";
+import { createSimpleSchema, custom, map, object, primitive, PropSchema, serializable, SKIP } from "serializr";
+import { CompiledScript, CompileScript } from "../client/util/Scripting";
+import { scriptingGlobal, ScriptingGlobals } from "../client/util/ScriptingGlobals";
+import { autoObject, Deserializable } from "../client/util/SerializationHelper";
+import { numberRange } from "../Utils";
+import { Doc, Field, Opt } from "./Doc";
+import { Copy, ToScriptString, ToString } from "./FieldSymbols";
+import { List } from "./List";
+import { ObjectField } from "./ObjectField";
 import { ProxyField } from "./Proxy";
 import { Cast, NumCast } from "./Types";
-import { List } from "./List";
-import { numberRange } from "../Utils";
+import { Plugins } from "./util";
 
 function optional(propSchema: PropSchema) {
     return custom(value => {
@@ -40,28 +41,9 @@ const scriptSchema = createSimpleSchema({
 });
 
 async function deserializeScript(script: ScriptField) {
-    if (script.script.originalScript === 'copyDragFactory(this.dragFactory)') {
-        return (script as any).script = (ScriptField.GetCopyOfDragFactory ?? (ScriptField.GetCopyOfDragFactory = ScriptField.MakeFunction('copyDragFactory(this.dragFactory)')))?.script;
-    }
-    if (script.script.originalScript === 'links(self)') {
-        return (script as any).script = (ScriptField.LinksSelf ?? (ScriptField.LinksSelf = ComputedField.MakeFunction('links(self)')))?.script;
-    }
-    if (script.script.originalScript === 'openOnRight(copyDragFactory(this.dragFactory))') {
-        return (script as any).script = (ScriptField.OpenOnRight ?? (ScriptField.OpenOnRight = ComputedField.MakeFunction('openOnRight(copyDragFactory(this.dragFactory))')))?.script;
-    }
-    if (script.script.originalScript === 'deiconifyView(self)') {
-        return (script as any).script = (ScriptField.DeiconifyView ?? (ScriptField.DeiconifyView = ComputedField.MakeFunction('deiconifyView(self)')))?.script;
-    }
-    if (script.script.originalScript === 'convertToButtons(dragData)') {
-        return (script as any).script = (ScriptField.ConvertToButtons ?? (ScriptField.ConvertToButtons = ComputedField.MakeFunction('convertToButtons(dragData)', { dragData: "DocumentDragData" })))?.script;
-    }
-    if (script.script.originalScript === 'IsNoviceMode()') {
-        return (script as any).script = (ScriptField.NoviceMode ?? (ScriptField.NoviceMode = ComputedField.MakeFunction('IsNoviceMode()')))?.script;
-    }
-    if (script.script.originalScript === `selectMainMenu(self)`) {
-        return (script as any).script = (ScriptField.SelectMenu ?? (ScriptField.SelectMenu = ComputedField.MakeFunction('selectMainMenu(self)')))?.script;
-    }
     const captures: ProxyField<Doc> = (script as any).captures;
+    const cache = captures ? undefined : ScriptField.GetScriptFieldCache(script.script.originalScript);
+    if (cache) return (script as any).script = cache;
     if (captures) {
         const doc = (await captures.value())!;
         const captured: any = {};
@@ -75,6 +57,7 @@ async function deserializeScript(script: ScriptField) {
         throw new Error("Couldn't compile loaded script");
     }
     (script as any).script = comp;
+    !captures && ScriptField._scriptFieldCache.set(script.script.originalScript, comp);
     if (script.setterscript) {
         const compset = CompileScript(script.setterscript?.originalScript, script.setterscript.options);
         if (!compset.compiled) {
@@ -95,13 +78,9 @@ export class ScriptField extends ObjectField {
     @serializable(autoObject())
     private captures?: ProxyField<Doc>;
 
-    public static GetCopyOfDragFactory: Opt<ScriptField>;
-    public static LinksSelf: Opt<ScriptField>;
-    public static OpenOnRight: Opt<ScriptField>;
-    public static DeiconifyView: Opt<ScriptField>;
-    public static ConvertToButtons: Opt<ScriptField>;
-    public static NoviceMode: Opt<ScriptField>;
-    public static SelectMenu: Opt<ScriptField>;
+    public static _scriptFieldCache: Map<string, Opt<CompiledScript>> = new Map();
+    public static GetScriptFieldCache(field: string) { return this._scriptFieldCache.get(field); }
+
     constructor(script: CompiledScript, setterscript?: CompiledScript) {
         super();
 
@@ -150,7 +129,13 @@ export class ScriptField extends ObjectField {
     }
     public static CompileScript(script: string, params: object = {}, addReturn = false, capturedVariables?: { [name: string]: Field }) {
         const compiled = CompileScript(script, {
-            params: { this: Doc?.name || "Doc", self: Doc?.name || "Doc", _last_: "any", ...params },
+            params: {
+                this: Doc?.name || "Doc",  // this is the doc that executes the script
+                self: Doc?.name || "Doc",  // self is the root doc of the doc that executes the script
+                _last_: "any",             // _last_ is the previous value of a computed field when it is being triggered to re-run.
+                _readOnly_: "boolean",     // _readOnly_ is set when a computed field is executed to indicate that it should not have mobx side-effects.  used for checking the value of a set function (see FontIconBox)
+                ...params
+            },
             typecheck: false,
             editable: true,
             addReturn: addReturn,
@@ -175,7 +160,7 @@ export class ComputedField extends ScriptField {
     _lastComputedResult: any;
     //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._valueOutsideReaction(doc));
-    _valueOutsideReaction = (doc: Doc) => this._lastComputedResult = this.script.run({ this: doc, self: Cast(doc.rootDocument, Doc, null) || doc, _last_: this._lastComputedResult }, console.log).result;
+    _valueOutsideReaction = (doc: Doc) => this._lastComputedResult = this.script.run({ this: doc, self: Cast(doc.rootDocument, Doc, null) || doc, _last_: this._lastComputedResult, _readOnly_: true }, console.log).result;
 
 
     [Copy](): ObjectField {
@@ -201,19 +186,6 @@ export class ComputedField extends ScriptField {
         return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined;
     }
 }
-Scripting.addGlobal(function setIndexVal(list: any[], index: number, value: any) {
-    while (list.length <= index) list.push(undefined);
-    list[index] = value;
-}, "sets the value at a given index of a list", "(list: any[], index: number, value: any)");
-
-Scripting.addGlobal(function getIndexVal(list: any[], index: number) {
-    return list?.reduce((p, x, i) => (i <= index && x !== undefined) || p === undefined ? x : p, undefined as any);
-}, "returns the value at a given index of a list", "(list: any[], index: number)");
-
-Scripting.addGlobal(function makeScript(script: string) {
-    return ScriptField.MakeScript(script);
-}, "returns the value at a given index of a list", "(list: any[], index: number)");
-
 export namespace ComputedField {
     let useComputed = true;
     export function DisableComputedFields() {
@@ -242,4 +214,17 @@ export namespace ComputedField {
             }
         });
     }
-}
\ No newline at end of file
+}
+
+ScriptingGlobals.add(function setIndexVal(list: any[], index: number, value: any) {
+    while (list.length <= index) list.push(undefined);
+    list[index] = value;
+}, "sets the value at a given index of a list", "(list: any[], index: number, value: any)");
+
+ScriptingGlobals.add(function getIndexVal(list: any[], index: number) {
+    return list?.reduce((p, x, i) => (i <= index && x !== undefined) || p === undefined ? x : p, undefined as any);
+}, "returns the value at a given index of a list", "(list: any[], index: number)");
+
+ScriptingGlobals.add(function makeScript(script: string) {
+    return ScriptField.MakeScript(script);
+}, "returns the value at a given index of a list", "(list: any[], index: number)");
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
index 3d784448d..c90f3b6b3 100644
--- a/src/fields/Types.ts
+++ b/src/fields/Types.ts
@@ -3,6 +3,7 @@ import { List } from "./List";
 import { RefField } from "./RefField";
 import { DateField } from "./DateField";
 import { ScriptField } from "./ScriptField";
+import { URLField, WebField, ImageField } from "./URLField";
 
 export type ToType<T extends InterfaceValue> =
     T extends "string" ? string :
@@ -91,6 +92,12 @@ export function DateCast(field: FieldResult) {
 export function ScriptCast(field: FieldResult, defaultVal: ScriptField | null = null) {
     return Cast(field, ScriptField, defaultVal);
 }
+export function WebCast(field: FieldResult, defaultVal: WebField | null = null) {
+    return Cast(field, WebField, defaultVal);
+}
+export function ImageCast(field: FieldResult, defaultVal: ImageField | null = null) {
+    return Cast(field, ImageField, defaultVal);
+}
 
 type WithoutList<T extends Field> = T extends List<infer R> ? (R extends RefField ? (R | Promise<R>)[] : R[]) : T;
 
@@ -104,5 +111,6 @@ export interface PromiseLike<T> {
     then(callback: (field: Opt<T>) => void): void;
 }
 export function PromiseValue<T extends Field>(field: FieldResult<T>): PromiseLike<Opt<T>> {
-    return field instanceof Promise ? field : { then(cb: ((field: Opt<T>) => void)) { return cb(field); } };
+    if (field instanceof Promise) return field as Promise<Opt<T>>;
+    return { then(cb: ((field: Opt<T>) => void)) { return cb(field); } };
 }
\ No newline at end of file
diff --git a/src/fields/URLField.ts b/src/fields/URLField.ts
index 4d3776a2c..1d4bbaed0 100644
--- a/src/fields/URLField.ts
+++ b/src/fields/URLField.ts
@@ -2,7 +2,7 @@ import { Deserializable } from "../client/util/SerializationHelper";
 import { serializable, custom } from "serializr";
 import { ObjectField } from "./ObjectField";
 import { ToScriptString, ToString, Copy } from "./FieldSymbols";
-import { Scripting, scriptingGlobal } from "../client/util/Scripting";
+import { scriptingGlobal } from "../client/util/ScriptingGlobals";
 import { Utils } from "../Utils";
 
 function url() {
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index d732a6e2f..cfcc48608 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -16,7 +16,7 @@ import * as React from "react";
 import { Docs, DocumentOptions, DocUtils } from '../client/documents/Documents';
 import { DocumentType } from "../client/documents/DocumentTypes";
 import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
-import { Scripting } from '../client/util/Scripting';
+import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
 import { SettingsManager, ColorScheme } from '../client/util/SettingsManager';
 import { Transform } from '../client/util/Transform';
 import { UndoManager } from "../client/util/UndoManager";
@@ -39,13 +39,13 @@ import "./AudioUpload.scss";
 import "./ImageUpload.scss";
 import "./MobileInterface.scss";
 
-library.add(faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
+library.add(...[faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
     faTerminal, faToggleOn, fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard,
     faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt,
     faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter,
     faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote,
     faThumbtack, faTree, faTv, faUndoAlt, faBook, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faHome, faLongArrowAltLeft, faBars, faTh, faChevronLeft,
-    faAlignLeft, faAlignRight);
+    faAlignLeft, faAlignRight].map(m => m as any));
 
 
 @observer
@@ -669,19 +669,19 @@ export class MobileInterface extends React.Component {
 
 
 //Global functions for mobile menu
-Scripting.addGlobal(function switchToMobileLibrary() { return MobileInterface.Instance.switchToLibrary(); },
+ScriptingGlobals.add(function switchToMobileLibrary() { return MobileInterface.Instance.switchToLibrary(); },
     "opens the library to navigate through dashboards on Dash Mobile");
-Scripting.addGlobal(function openMobileUploads() { return MobileInterface.Instance.toggleUpload(); },
+ScriptingGlobals.add(function openMobileUploads() { return MobileInterface.Instance.toggleUpload(); },
     "opens the upload files menu for Dash Mobile");
-Scripting.addGlobal(function switchToMobileUploadCollection() { return MobileInterface.Instance.switchToMobileUploads(); },
+ScriptingGlobals.add(function switchToMobileUploadCollection() { return MobileInterface.Instance.switchToMobileUploads(); },
     "opens the mobile uploads collection on Dash Mobile");
-Scripting.addGlobal(function openMobileAudio() { return MobileInterface.Instance.toggleAudio(); },
+ScriptingGlobals.add(function openMobileAudio() { return MobileInterface.Instance.toggleAudio(); },
     "opens the record and dictate menu on Dash Mobile");
-Scripting.addGlobal(function switchToMobilePresentation() { return MobileInterface.Instance.setupDefaultPresentation(); },
+ScriptingGlobals.add(function switchToMobilePresentation() { return MobileInterface.Instance.setupDefaultPresentation(); },
     "opens the presentation on Dash Mobile");
-Scripting.addGlobal(function openMobileSettings() { return SettingsManager.Instance.open(); },
+ScriptingGlobals.add(function openMobileSettings() { return SettingsManager.Instance.open(); },
     "opens settings on Dash Mobile");
 
 // Other global functions for mobile
-Scripting.addGlobal(function switchMobileView(doc: Doc, renderView?: () => JSX.Element, onSwitch?: () => void) { return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch); },
+ScriptingGlobals.add(function switchMobileView(doc: Doc, renderView?: () => JSX.Element, onSwitch?: () => void) { return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch); },
     "changes the active document displayed on the Dash Mobile", "(doc: any)");
\ No newline at end of file
diff --git a/src/scraping/buxton/final/BuxtonImporter.ts b/src/scraping/buxton/final/BuxtonImporter.ts
deleted file mode 100644
index 684c00c0d..000000000
--- a/src/scraping/buxton/final/BuxtonImporter.ts
+++ /dev/null
@@ -1,604 +0,0 @@
-import { readdirSync, writeFile, mkdirSync, createReadStream, createWriteStream, existsSync, statSync } from "fs";
-import * as path from "path";
-import { red, cyan, yellow } from "colors";
-import { Utils } from "../../../Utils";
-import rimraf = require("rimraf");
-import { DashUploadUtils } from "../../../server/DashUploadUtils";
-const StreamZip = require('node-stream-zip');
-const createImageSizeStream = require("image-size-stream");
-import { parseXml } from "libxmljs";
-import { strictEqual } from "assert";
-import { Readable, PassThrough } from "stream";
-import { Directory, serverPathToFile, pathToDirectory } from "../../../server/ApiManagers/UploadManager";
-
-/**
- * This is an arbitrary bundle of data that gets populated
- * in extractFileContents
- */
-interface DocumentContents {
-    body: string;
-    imageData: ImageData[];
-    hyperlinks: string[];
-    tableData: TableData[];
-    longDescription: string;
-}
-
-/**
- * A rough schema for everything that Bill has
- * included for each document
- */
-export interface DeviceDocument {
-    title: string;
-    shortDescription: string;
-    longDescription: string;
-    company: string;
-    year: number;
-    originalPrice?: number;
-    degreesOfFreedom?: number;
-    dimensions?: string;
-    primaryKey: string;
-    secondaryKey: string;
-    attribute: string;
-    __images: ImageData[];
-    additionalMedia: ({ [type: string]: string } | undefined)[];
-    hyperlinks: string[];
-    captions: string[]; // from the table column
-    embeddedFileNames: string[]; // from the table column
-}
-
-/**
- * A layer of abstraction around a single parsing
- * attempt. The error is not a TypeScript error, but
- * rather an invalidly formatted value for a given key.
- */
-export interface AnalysisResult {
-    device?: DeviceDocument;
-    invalid?: { [deviceProperty: string]: string };
-}
-
-/**
- * A mini API that takes in a string and returns
- * either the given T or an error indicating that the
- * transformation was rejected.
- */
-type Transformer<T> = (raw: string) => TransformResult<T>;
-interface TransformResult<T> {
-    transformed?: T;
-    error?: string;
-}
-
-/**
- * Simple bundle counting successful and failed imports
- */
-export interface ImportResults {
-    deviceCount: number;
-    errorCount: number;
-}
-
-/**
- * Definitions for callback functions. Such instances are
- * just invoked by when a single document has been parsed
- * or the entire import is over. As of this writing, these
- * callbacks are supplied by WebSocket.ts and used to inform
- * the client of these events.
- */
-type ResultCallback = (result: AnalysisResult) => void;
-type TerminatorCallback = (result: ImportResults) => void;
-
-/**
- * Defines everything needed to define how a single key should be
- * formatted within the plain body text. The association between
- * keys and their format definitions is stored FormatMap
- */
-interface ValueFormatDefinition<T> {
-    exp: RegExp; // the expression that the key's value should match
-    matchIndex?: number; // defaults to 0, but can be overridden to account for grouping in @param exp
-    transformer?: Transformer<T>; // if desirable, how to transform the Regex match
-    required?: boolean; // defaults to true, confirms that for a whole document to be counted successful,
-    // all of its required values should be present and properly formatted
-}
-
-/**
- * The basic data we extract from each image in the document
- */
-interface ImageData {
-    url: string;
-    nativeWidth: number;
-    nativeHeight: number;
-}
-
-namespace Utilities {
-
-    /**
-     * Numeric 'try parse', fits with the Transformer API
-     * @param raw the serialized number
-     */
-    export function numberValue(raw: string): TransformResult<number> {
-        const transformed = Number(raw);
-        if (isNaN(transformed)) {
-            return { error: `${raw} cannot be parsed to a numeric value.` };
-        }
-        return { transformed };
-    }
-
-    /**
-     * A simple tokenizer that splits along 'and' and commas, and removes duplicates
-     * Helpful mainly for attribute and primary key lists
-     * @param raw the string to tokenize
-     */
-    export function collectUniqueTokens(raw: string): TransformResult<string[]> {
-        const pieces = raw.replace(/,|\s+and\s+/g, " ").split(/\s+/).filter(piece => piece.length);
-        const unique = new Set(pieces.map(token => token.toLowerCase().trim()));
-        return { transformed: Array.from(unique).map(capitalize).sort() };
-    }
-
-    /**
-     * Tries to correct XML text parsing artifact where some sentences lose their separating space,
-     * and others gain excess whitespace
-     * @param raw 
-     */
-    export function correctSentences(raw: string): TransformResult<string> {
-        raw = raw.replace(/\./g, ". ").replace(/\:/g, ": ").replace(/\,/g, ", ").replace(/\?/g, "? ").trimRight();
-        raw = raw.replace(/\s{2,}/g, " ");
-        return { transformed: raw };
-    }
-
-    /**
-     * Simple capitalization
-     * @param word to capitalize
-     */
-    export function capitalize(word: string): string {
-        const clean = word.trim();
-        if (!clean.length) {
-            return word;
-        }
-        return word.charAt(0).toUpperCase() + word.slice(1);
-    }
-
-    /**
-     * Streams the requeted file at the relative path to the
-     * root of the zip, then parses it with a library
-     * @param zip the zip instance data source
-     * @param relativePath the path to a .xml file within the zip to parse
-     */
-    export async function readAndParseXml(zip: any, relativePath: string) {
-        console.log(`Text streaming ${relativePath}`);
-        const contents = await new Promise<string>((resolve, reject) => {
-            let body = "";
-            zip.stream(relativePath, (error: any, stream: any) => {
-                if (error) {
-                    reject(error);
-                }
-                stream.on('data', (chunk: any) => body += chunk.toString());
-                stream.on('end', () => resolve(body));
-            });
-        });
-        return parseXml(contents);
-    }
-}
-
-/**
- * Defines how device values should be formatted. As you can see, the formatting is
- * not super consistent and has changed over time as edge cases have been found, but this
- * at least imposes some constraints, and will notify you if a document doesn't match the specifications
- * in this map.
- */
-const FormatMap = new Map<keyof DeviceDocument, ValueFormatDefinition<any>>([
-    ["title", {
-        exp: /contact\s+(.*)Short Description:/
-    }],
-    ["company", {
-        exp: /Company:\s+([^\|]*)\s+\|/,
-        transformer: (raw: string) => ({ transformed: raw.replace(/\./g, "") })
-    }],
-    ["year", {
-        exp: /Year:\s+([^\|]*)\s+\|/,
-        transformer: (raw: string) => Utilities.numberValue(/[0-9]{4}/.exec(raw)![0])
-    }],
-    ["primaryKey", {
-        exp: /Primary:\s+(.*)(Secondary|Additional):/,
-        transformer: raw => {
-            const { transformed, error } = Utilities.collectUniqueTokens(raw);
-            return transformed ? { transformed: transformed[0] } : { error };
-        }
-    }],
-    ["secondaryKey", {
-        exp: /(Secondary|Additional):\s+(.*)Attributes?:/,
-        transformer: raw => {
-            const { transformed, error } = Utilities.collectUniqueTokens(raw);
-            return transformed ? { transformed: transformed[0] } : { error };
-        },
-        matchIndex: 2
-    }],
-    ["attribute", {
-        exp: /Attributes?:\s+(.*)Links/,
-        transformer: raw => {
-            const { transformed, error } = Utilities.collectUniqueTokens(raw);
-            return transformed ? { transformed: transformed[0] } : { error };
-        },
-    }],
-    ["originalPrice", {
-        exp: /Original Price \(USD\)\:\s+(\$[0-9\,]+\.[0-9]+|NFS)/,
-        transformer: (raw: string) => {
-            raw = raw.replace(/\,/g, "");
-            if (raw === "NFS") {
-                return { transformed: -1 };
-            }
-            return Utilities.numberValue(raw.slice(1));
-        },
-        required: false
-    }],
-    ["degreesOfFreedom", {
-        exp: /Degrees of Freedom:\s+([0-9]+)/,
-        transformer: Utilities.numberValue,
-        required: false
-    }],
-    ["dimensions", {
-        exp: /Dimensions\s+\(L x W x H\):\s+([0-9\.]+\s+x\s+[0-9\.]+\s+x\s+[0-9\.]+\s\([A-Za-z]+\))/,
-        transformer: (raw: string) => {
-            const [length, width, group] = raw.split(" x ");
-            const [height, unit] = group.split(" ");
-            return {
-                transformed: {
-                    dim_length: Number(length),
-                    dim_width: Number(width),
-                    dim_height: Number(height),
-                    dim_unit: unit.replace(/[\(\)]+/g, "")
-                }
-            };
-        },
-        required: false
-    }],
-    ["shortDescription", {
-        exp: /Short Description:\s+(.*)Bill Buxton[’']s Notes/,
-        transformer: Utilities.correctSentences
-    }],
-]);
-
-const sourceDir = path.resolve(__dirname, "source"); // where the Word documents are assumed to be stored
-const assetDir = path.resolve(__dirname, "assets"); // where any additional media content like pdfs will be stored. Each subdirectory of this
-// must follow the enum Directory.<type> naming scheme
-const outDir = path.resolve(__dirname, "json"); // where the JSON output of these device documents will be written
-const imageDir = path.resolve(__dirname, "../../../server/public/files/images/buxton"); // where, in the server, these images will be written
-const successOut = "buxton.json"; // the JSON list representing properly formatted documents
-const failOut = "incomplete.json"; // the JSON list representing improperly formatted documents
-const deviceKeys = Array.from(FormatMap.keys()); // a way to iterate through all keys of the DeviceDocument interface
-
-/**
- * Starts by REMOVING ALL EXISTING BUXTON RESOURCES. This might need to be
- * changed going forward
- * @param emitter the callback when each document is completed
- * @param terminator the callback when the entire import is completed
- */
-export default async function executeImport(emitter: ResultCallback, terminator: TerminatorCallback) {
-    try {
-        // get all Word documents in the source directory
-        const contents = readdirSync(sourceDir);
-        const wordDocuments = contents.filter(file => /.*\.docx?$/.test(file)).map(file => `${sourceDir}/${file}`);
-        // removal takes place here
-        [outDir, imageDir].forEach(dir => {
-            rimraf.sync(dir);
-            mkdirSync(dir);
-        });
-        await transferAssets();
-        return parseFiles(wordDocuments, emitter, terminator);
-    } catch (e) {
-        const message = [
-            "Unable to find a source directory.",
-            "Please ensure that the following directory exists:",
-            `${e.message}`
-        ].join('\n');
-        console.log(red(message));
-        return { error: message };
-    }
-}
-
-/**
- * Builds a mirrored directory structure of all media / asset files
- * within the server's public directory.
- */
-async function transferAssets() {
-    for (const assetType of readdirSync(assetDir)) {
-        const subroot = path.resolve(assetDir, assetType);
-        if (!statSync(subroot).isDirectory()) {
-            continue;
-        }
-        const outputSubroot = serverPathToFile(assetType as Directory, "buxton");
-        if (existsSync(outputSubroot)) {
-            continue;
-        } else {
-            mkdirSync(outputSubroot);
-        }
-        for (const fileName of readdirSync(subroot)) {
-            const readStream = createReadStream(path.resolve(subroot, fileName));
-            const writeStream = createWriteStream(path.resolve(outputSubroot, fileName));
-            await new Promise<void>(resolve => {
-                readStream.pipe(writeStream).on("close", resolve);
-            });
-        }
-    }
-}
-
-/**
- * Parse every Word document in the directory, notifying any callers as needed
- * at each iteration via the emitter.
- * @param wordDocuments the string list of Word document names to parse
- * @param emitter the callback when each document is completed
- * @param terminator the callback when the entire import is completed
- */
-async function parseFiles(wordDocuments: string[], emitter: ResultCallback, terminator: TerminatorCallback): Promise<DeviceDocument[]> {
-    // execute parent-most parse function
-    const results: AnalysisResult[] = [];
-    for (const filePath of wordDocuments) {
-        const fileName = path.basename(filePath).replace("Bill_Notes_", ""); // not strictly needed, but cleaner
-        console.log(cyan(`\nExtracting contents from ${fileName}...`));
-        const result = analyze(fileName, await extractFileContents(filePath));
-        emitter(result);
-        results.push(result);
-    }
-
-    // collect information about errors and successes
-    const masterDevices: DeviceDocument[] = [];
-    const masterErrors: { [key: string]: string }[] = [];
-    results.forEach(({ device, invalid: errors }) => {
-        if (device) {
-            masterDevices.push(device);
-        } else if (errors) {
-            masterErrors.push(errors);
-        }
-    });
-
-    // something went wrong, since errors and successes should sum to total inputs
-    const total = wordDocuments.length;
-    if (masterDevices.length + masterErrors.length !== total) {
-        throw new Error(`Encountered a ${masterDevices.length} to ${masterErrors.length} mismatch in device / error split!`);
-    }
-
-    // write the external JSON representations of this import
-    console.log();
-    await writeOutputFile(successOut, masterDevices, total, true);
-    await writeOutputFile(failOut, masterErrors, total, false);
-    console.log();
-
-    // notify the caller that the import has finished
-    terminator({ deviceCount: masterDevices.length, errorCount: masterErrors.length });
-
-    return masterDevices;
-}
-
-/**
- * XPath definitions for desired XML targets in respective hierarchies.
- * 
- * For table cells, can be read as: "find me anything that looks like <w:tc> in XML, whose
- * parent looks like <w:tr>, whose parent looks like <w:tbl>"
- * 
- * <w:tbl>
- *      <w:tr>
- *           <w:tc>
- * 
- * These are found by trial and error, and using an online XML parser / prettifier
- * to inspect the structure, since the Node XML library does not expose the parsed
- * structure very well for searching, say in the debug console.
- */
-const xPaths = {
-    paragraphs: '//*[name()="w:p"]',
-    tableCells: '//*[name()="w:tbl"]/*[name()="w:tr"]/*[name()="w:tc"]',
-    hyperlinks: '//*[name()="Relationship" and contains(@Type, "hyperlink")]'
-};
-
-interface TableData {
-    fileName: string;
-    caption: string;
-    additionalMedia?: { [type: string]: string };
-}
-
-const SuffixDirectoryMap = new Map<string, Directory>([
-    ["p", Directory.pdfs]
-]);
-
-/**
- * The meat of the script, images and text content are extracted here
- * @param pathToDocument the path to the document relative to the root of the zip
- */
-async function extractFileContents(pathToDocument: string): Promise<DocumentContents> {
-    console.log('Extracting text...');
-    const zip = new StreamZip({ file: pathToDocument, storeEntries: true });
-    await new Promise<void>(resolve => zip.on('ready', resolve));
-
-    // extract the body of the document and, specifically, its captions
-    const document = await Utilities.readAndParseXml(zip, "word/document.xml");
-    // get plain text
-    const body = document.root()?.text() ?? "No body found. Check the import script's XML parser.";
-    const captions: string[] = [];
-    const tableData: TableData[] = [];
-    // preserve paragraph formatting and line breaks that would otherwise get lost in the plain text parsing
-    // of the XML hierarchy
-    const paragraphs = document.find(xPaths.paragraphs).map(node => Utilities.correctSentences(node.text()).transformed!);
-    const start = paragraphs.indexOf(paragraphs.find(el => /Bill Buxton[’']s Notes/.test(el))!) + 1;
-    const end = paragraphs.indexOf("Device Details");
-    const longDescription = paragraphs.slice(start, end).filter(paragraph => paragraph.length).join("\n\n");
-
-    // extract captions from the table cells
-    const tableRowsFlattened = document.find(xPaths.tableCells).map(node => node.text().trim());
-    const { length } = tableRowsFlattened;
-    const numCols = 4;
-    strictEqual(length > numCols, true, "No captions written."); // first row has the headers, not content
-    strictEqual(length % numCols === 0, true, "Improper caption formatting.");
-
-    // break the flat list of strings into groups of numColumns. Thus, each group represents
-    // a row in the table, where the first row has no text content since it's
-    // the image, the second has the file name and the third has the caption (maybe additional columns
-    // have been added or reordered since this was written, but follow the same appraoch)
-    for (let i = numCols; i < tableRowsFlattened.length; i += numCols) {
-        const row = tableRowsFlattened.slice(i, i + numCols);
-        const entry: TableData = { fileName: row[1], caption: row[2] };
-        const key = SuffixDirectoryMap.get(row[3].toLowerCase());
-        if (key) {
-            const media: any = {};
-            media[key] = `${entry.fileName.split(".")[0]}.pdf`;
-            entry.additionalMedia = media;
-        }
-        tableData.push(entry);
-    }
-
-    // extract all hyperlinks embedded in the document
-    const rels = await Utilities.readAndParseXml(zip, "word/_rels/document.xml.rels");
-    const hyperlinks = rels.find(xPaths.hyperlinks).map(el => el.attrs()[2].value());
-    console.log("Text extracted.");
-
-    // write out the images for this document
-    console.log("Beginning image extraction...");
-    const imageData = await writeImages(zip);
-    console.log(`Extracted ${imageData.length} images.`);
-
-    // cleanup
-    zip.close();
-
-    return { body, longDescription, imageData, tableData, hyperlinks };
-}
-
-// zip relative path from root expression / filter used to isolate only media assets
-const imageEntry = /^word\/media\/\w+\.(jpeg|jpg|png|gif)/;
-
-/**
- * Image dimensions and file suffix, 
- */
-interface ImageAttrs {
-    width: number;
-    height: number;
-    type: string;
-}
-
-/**
- * For each image, stream the file, get its size, check if it's an icon
- * (if it is, ignore it)
- * @param zip the zip instance data source
- */
-async function writeImages(zip: any): Promise<ImageData[]> {
-    const allEntries = Object.values<any>(zip.entries()).map(({ name }) => name);
-    const imageEntries = allEntries.filter(name => imageEntry.test(name));
-
-    const imageUrls: ImageData[] = [];
-    const valid: any[] = [];
-
-    const getImageStream = (mediaPath: string) => new Promise<Readable>((resolve, reject) => {
-        zip.stream(mediaPath, (error: any, stream: any) => error ? reject(error) : resolve(stream));
-    });
-
-    for (const mediaPath of imageEntries) {
-        const { width, height, type } = await new Promise<ImageAttrs>(async resolve => {
-            const sizeStream = (createImageSizeStream() as PassThrough).on('size', (dimensions: ImageAttrs) => {
-                readStream.destroy();
-                resolve(dimensions);
-            }).on("error", () => readStream.destroy());
-            const readStream = await getImageStream(mediaPath);
-            readStream.pipe(sizeStream);
-        });
-
-        // if it's not an icon, by this rough heuristic, i.e. is it not square
-        const number = Number(/image(\d+)/.exec(mediaPath)![1]);
-        if (number > 5 || width - height > 10) {
-            valid.push({ width, height, type, mediaPath, number });
-        }
-    }
-
-    valid.sort((a, b) => a.number - b.number);
-
-    const [{ width: first_w, height: first_h }, { width: second_w, height: second_h }] = valid;
-    if (Math.abs(first_w / second_w - first_h / second_h) < 0.01) {
-        const first_size = first_w * first_h;
-        const second_size = second_w * second_h;
-        const target = first_size >= second_size ? 1 : 0;
-        valid.splice(target, 1);
-        console.log(`Heuristically removed image with size ${target ? second_size : first_size}`);
-    }
-
-    // for each valid image, output the _o, _l, _m, and _s files
-    // THIS IS WHERE THE SCRIPT SPENDS MOST OF ITS TIME
-    for (const { type, width, height, mediaPath } of valid) {
-        const generatedFileName = `upload_${Utils.GenerateGuid()}.${type.toLowerCase()}`;
-        await DashUploadUtils.outputResizedImages(() => getImageStream(mediaPath), generatedFileName, imageDir);
-        imageUrls.push({
-            url: `/files/images/buxton/${generatedFileName}`,
-            nativeWidth: width,
-            nativeHeight: height
-        });
-    }
-
-    return imageUrls;
-}
-
-/**
- * Takes the results of extractFileContents, which relative to this is sort of the
- * external media / preliminary text processing, and now tests the given file name to
- * with those value definitions to make sure the body of the document contains all
- * required fields, properly formatted
- * @param fileName the file whose body to inspect
- * @param contents the data already computed / parsed by extractFileContents
- */
-function analyze(fileName: string, contents: DocumentContents): AnalysisResult {
-    const { body, imageData, hyperlinks, tableData, longDescription } = contents;
-    const device: any = {
-        hyperlinks,
-        captions: tableData.map(({ caption }) => caption),
-        embeddedFileNames: tableData.map(({ fileName }) => fileName),
-        additionalMedia: tableData.map(({ additionalMedia }) => additionalMedia),
-        longDescription,
-        __images: imageData
-    };
-    const errors: { [key: string]: string } = { fileName };
-
-    for (const key of deviceKeys) {
-        const { exp, transformer, matchIndex, required } = FormatMap.get(key)!;
-        const matches = exp.exec(body);
-
-        let captured: string;
-        // if we matched and we got the specific match we're after
-        if (matches && (captured = matches[matchIndex ?? 1])) { // matchIndex defaults to 1
-            captured = captured.replace(/\s{2,}/g, " "); // remove excess whitespace
-            // if supplied, apply the required transformation (recall this is specified in FormatMap)
-            if (transformer) {
-                const { error, transformed } = transformer(captured);
-                if (error) {
-                    // we hit a snag trying to transform the valid match
-                    // still counts as a fundamental error
-                    errors[key] = `__ERR__${key.toUpperCase()}__TRANSFORM__: ${error}`;
-                    continue;
-                }
-                captured = transformed;
-            }
-            device[key] = captured;
-        } else if (required ?? true) {
-            // the field was either implicitly or explicitly required, and failed to match the definition in
-            // FormatMap
-            errors[key] = `ERR__${key.toUpperCase()}__: outer match ${matches === null ? "wasn't" : "was"} captured.`;
-            continue;
-        }
-    }
-
-    // print errors - this can be removed
-    const errorKeys = Object.keys(errors);
-    if (errorKeys.length > 1) {
-        console.log(red(`@ ${cyan(fileName.toUpperCase())}...`));
-        errorKeys.forEach(key => key !== "filename" && console.log(red(errors[key])));
-        return { invalid: errors };
-    }
-
-    return { device };
-}
-
-/**
- * A utility function that writes the JSON results for this import out to the desired path
- * @param relativePath where to write the JSON file
- * @param data valid device document objects, or errors
- * @param total used for more informative printing
- * @param success whether or not the caller is writing the successful parses or the failures
- */
-async function writeOutputFile(relativePath: string, data: any[], total: number, success: boolean) {
-    console.log(yellow(`Encountered ${data.length} ${success ? "valid" : "invalid"} documents out of ${total} candidates. Writing ${relativePath}...`));
-    return new Promise<void>((resolve, reject) => {
-        const destination = path.resolve(outDir, relativePath);
-        const contents = JSON.stringify(data, undefined, 4); // format the JSON
-        writeFile(destination, contents, err => err ? reject(err) : resolve());
-    });
-}
\ No newline at end of file
diff --git a/src/server/ActionUtilities.ts b/src/server/ActionUtilities.ts
index d237869ed..bc8fd6f87 100644
--- a/src/server/ActionUtilities.ts
+++ b/src/server/ActionUtilities.ts
@@ -62,7 +62,7 @@ export async function log_execution<T>({ startMessage, endMessage, action, color
     log_helper(`${startMessage}...`, resolvedColor);
     try {
         result = await action();
-    } catch (e) {
+    } catch (e: any) {
         error = e;
     } finally {
         log_helper(typeof endMessage === "string" ? endMessage : endMessage({ result, error }), resolvedColor);
diff --git a/src/server/ApiManagers/PDFManager.ts b/src/server/ApiManagers/PDFManager.ts
index 2b4212588..e419d3ac4 100644
--- a/src/server/ApiManagers/PDFManager.ts
+++ b/src/server/ApiManagers/PDFManager.ts
@@ -2,8 +2,7 @@ import ApiManager, { Registration } from "./ApiManager";
 import { Method } from "../RouteManager";
 import RouteSubscriber from "../RouteSubscriber";
 import { existsSync, createReadStream, createWriteStream } from "fs";
-const pdfjs = require('pdfjs-dist/es5/build/pdf.js');
-import * as Pdfjs from 'pdfjs-dist';
+import * as Pdfjs from 'pdfjs-dist/legacy/build/pdf';
 import { createCanvas } from "canvas";
 const imageSize = require("probe-image-size");
 import * as express from "express";
@@ -84,7 +83,7 @@ async function CreateThumbnail(coreFilename: string, pageNum: number, res: expre
     });
 }
 
-function dispatchThumbnail(res: express.Response, { width, height }: Pdfjs.PDFPageViewport, thumbnailName: string) {
+function dispatchThumbnail(res: express.Response, { width, height }: Pdfjs.PageViewport, thumbnailName: string) {
     res.send({
         path: clientPathToFile(Directory.pdf_thumbnails, thumbnailName),
         width,
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 02f6462aa..bfa07d47a 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -49,14 +49,17 @@ export default class UploadManager extends ApiManager {
             subscription: "/uploadFormData",
             secureHandler: async ({ req, res }) => {
                 const form = new formidable.IncomingForm();
-                form.uploadDir = pathToDirectory(Directory.parsed_files);
                 form.keepExtensions = true;
+                form.uploadDir = pathToDirectory(Directory.parsed_files);
                 return new Promise<void>(resolve => {
                     form.parse(req, async (_err, _fields, files) => {
                         const results: Upload.FileResponse[] = [];
                         for (const key in files) {
-                            const result = await DashUploadUtils.upload(files[key]);
-                            result && !(result.result instanceof Error) && results.push(result);
+                            const f = files[key];
+                            if (!Array.isArray(f)) {
+                                const result = await DashUploadUtils.upload(f);
+                                result && !(result.result instanceof Error) && results.push(result);
+                            }
                         }
                         _success(res, results);
                         resolve();
@@ -192,7 +195,8 @@ export default class UploadManager extends ApiManager {
                         let id: string = "";
                         try {
                             for (const name in files) {
-                                const path_2 = files[name].path;
+                                const f = files[name];
+                                const path_2 = Array.isArray(f) ? "" : f.path;
                                 const zip = new AdmZip(path_2);
                                 zip.getEntries().forEach((entry: any) => {
                                     if (!entry.entryName.startsWith("files/")) return;
@@ -218,7 +222,7 @@ export default class UploadManager extends ApiManager {
                                     id = getId(data.id);
                                     const docs = Object.keys(datadocs).map(key => datadocs[key]);
                                     docs.forEach(mapFn);
-                                    await Promise.all(docs.map((doc: any) => new Promise(res => {
+                                    await Promise.all(docs.map((doc: any) => new Promise<void>(res => {
                                         Database.Instance.replace(doc.id, doc, (err, r) => {
                                             err && console.log(err);
                                             res();
@@ -255,14 +259,15 @@ export default class UploadManager extends ApiManager {
             secureHandler: ({ req, res }) => {
                 const uri = req.body.uri;
                 const filename = req.body.name;
+                const origSuffix = req.body.nosuffix ? SizeSuffix.None : SizeSuffix.Original;
                 if (!uri || !filename) {
                     res.status(401).send("incorrect parameters specified");
                     return;
                 }
-                return imageDataUri.outputFile(uri, serverPathToFile(Directory.images, InjectSize(filename, SizeSuffix.Original))).then((savedName: string) => {
+                return imageDataUri.outputFile(uri, serverPathToFile(Directory.images, InjectSize(filename, origSuffix))).then((savedName: string) => {
                     const ext = extname(savedName).toLowerCase();
                     const { pngs, jpgs } = AcceptableMedia;
-                    const resizers = [
+                    const resizers = !origSuffix ? [{ resizer: sharp().resize(400, undefined, { withoutEnlargement: true }), suffix: "_m" }] : [
                         { 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/UserManager.ts b/src/server/ApiManagers/UserManager.ts
index fe80c6a7c..7be8a1e9f 100644
--- a/src/server/ApiManagers/UserManager.ts
+++ b/src/server/ApiManagers/UserManager.ts
@@ -22,7 +22,7 @@ export default class UserManager extends ApiManager {
             secureHandler: async ({ res }) => {
                 const cursor = await Database.Instance.query({}, { email: 1, linkDatabaseId: 1, sharingDocumentId: 1 }, "users");
                 const results = await cursor.toArray();
-                res.send(results.map(user => ({ email: user.email, linkDatabaseId: user.linkDatabaseId, sharingDocumentId: user.sharingDocumentId })));
+                res.send(results.map((user: any) => ({ email: user.email, linkDatabaseId: user.linkDatabaseId, sharingDocumentId: user.sharingDocumentId })));
             }
         });
 
diff --git a/src/server/DashSession/DashSessionAgent.ts b/src/server/DashSession/DashSessionAgent.ts
index 03ba33fee..1a5934d8f 100644
--- a/src/server/DashSession/DashSessionAgent.ts
+++ b/src/server/DashSession/DashSessionAgent.ts
@@ -214,7 +214,7 @@ export class DashSessionAgent extends AppliedSessionAgent {
             // indicate success or failure
             mainLog(`${error === null ? green("successfully dispatched") : red("failed to dispatch")} ${zipName} to ${cyan(to)}`);
             error && mainLog(red(error.message));
-        } catch (error) {
+        } catch (error: any) {
             mainLog(red("unable to dispatch zipped backup..."));
             mainLog(red(error.message));
         }
diff --git a/src/server/DashSession/Session/agents/monitor.ts b/src/server/DashSession/Session/agents/monitor.ts
index 0fdaf07ff..9cb5ab576 100644
--- a/src/server/DashSession/Session/agents/monitor.ts
+++ b/src/server/DashSession/Session/agents/monitor.ts
@@ -178,7 +178,7 @@ export class Monitor extends IPCMessageReceiver {
             // ensure all necessary and no excess information is specified by the configuration file
             validate(config, configurationSchema, options);
             config = Utilities.preciseAssign({}, defaultConfig, config);
-        } catch (error) {
+        } catch (error: any) {
             if (error instanceof ValidationError) {
                 console.log(red("\nSession configuration failed."));
                 console.log("The given session.config.json configuration file is invalid.");
diff --git a/src/server/DashSession/Session/agents/promisified_ipc_manager.ts b/src/server/DashSession/Session/agents/promisified_ipc_manager.ts
index 95aa686e6..f6c8de521 100644
--- a/src/server/DashSession/Session/agents/promisified_ipc_manager.ts
+++ b/src/server/DashSession/Session/agents/promisified_ipc_manager.ts
@@ -157,7 +157,7 @@ export class PromisifiedIPCManager {
                 if (registered) {
                     results = await Promise.all(registered.map(handler => handler(args)));
                 }
-            } catch (e) {
+            } catch (e: any) {
                 error = e;
             }
             if (!this.isDestroyed && this.target.send) {
diff --git a/src/server/DashSession/Session/agents/server_worker.ts b/src/server/DashSession/Session/agents/server_worker.ts
index 84d35b40e..634b0113d 100644
--- a/src/server/DashSession/Session/agents/server_worker.ts
+++ b/src/server/DashSession/Session/agents/server_worker.ts
@@ -138,7 +138,7 @@ export class ServerWorker extends IPCMessageReceiver {
                         this.isInitialized = true;
                     }
                     this.shouldServerBeResponsive = true;
-                } catch (error) {
+                } catch (error: any) {
                     // if we expect the server to be unavailable, i.e. during compilation,
                     // the listening variable is false, activeExit will return early and the child
                     // process will continue
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index f13580865..552ab57a5 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -16,6 +16,7 @@ import { clientPathToFile, Directory, pathToDirectory, serverPathToFile } from '
 import { resolvedServerUrl } from "./server_Initialization";
 import { AcceptableMedia, Upload } from './SharedMediaTypes';
 import request = require('request-promise');
+import formidable = require('formidable');
 const { exec } = require("child_process");
 const parse = require('pdf-parse');
 const ffmpeg = require("fluent-ffmpeg");
@@ -25,7 +26,8 @@ export enum SizeSuffix {
     Small = "_s",
     Medium = "_m",
     Large = "_l",
-    Original = "_o"
+    Original = "_o",
+    None = ""
 }
 
 export function InjectSize(filename: string, size: SizeSuffix) {
@@ -69,7 +71,7 @@ export namespace DashUploadUtils {
                     else {
                         console.log(`stdout: ${stdout}`);
                         const data = { size: 0, path: videoId + ".mp4", name: videoId, type: "video/mp4" };
-                        const file = { ...data, toJSON: () => data };
+                        const file = { ...data, toJSON: () => ({ ...data, filename: data.path.replace(/.*\//, ""), mtime: null, length: 0, mime: "", toJson: () => undefined as any }) };
                         res(MoveParsedFile(file, Directory.videos));
                     }
                 });
@@ -78,7 +80,7 @@ export namespace DashUploadUtils {
 
     export async function upload(file: File): Promise<Upload.FileResponse> {
         const { type, path, name } = file;
-        const types = type.split("/");
+        const types = type?.split("/") ?? [];
 
         const category = types[0];
         let format = `.${types[1]}`;
@@ -242,7 +244,7 @@ export namespace DashUploadUtils {
         // Use the request library to parse out file level image information in the headers
         const { headers } = (await new Promise<any>((resolve, reject) => {
             request.head(resolvedUrl, (error, res) => error ? reject(error) : resolve(res));
-        }).catch(error => console.error(error)));
+        }).catch(console.error));
         try {
             // Compute the native width and height ofthe image with an npm module
             const { width: nativeWidth, height: nativeHeight } = await requestImageSize(resolvedUrl);
@@ -256,7 +258,7 @@ export namespace DashUploadUtils {
                 filename,
                 ...results
             };
-        } catch (e) {
+        } catch (e: any) {
             console.log(e);
             return e;
         }
@@ -272,7 +274,7 @@ export namespace DashUploadUtils {
      * @param suffix If the file doesn't have a suffix and you want to provide it one
      * to appear in the new location
      */
-    export async function MoveParsedFile(file: File, destination: Directory, suffix: string | undefined = undefined, text?: string): Promise<Upload.FileResponse> {
+    export async function MoveParsedFile(file: formidable.File, destination: Directory, suffix: string | undefined = undefined, text?: string): Promise<Upload.FileResponse> {
         const { path: sourcePath } = file;
         let name = path.basename(sourcePath);
         suffix && (name += suffix);
diff --git a/src/server/Message.ts b/src/server/Message.ts
index 59b24cd82..d87ae5027 100644
--- a/src/server/Message.ts
+++ b/src/server/Message.ts
@@ -1,5 +1,4 @@
 import { Point } from "../pen-gestures/ndollar";
-import { AnalysisResult, ImportResults } from "../scraping/buxton/final/BuxtonImporter";
 import { Utils } from "../Utils";
 
 export class Message<T> {
@@ -82,9 +81,6 @@ export namespace MessageStore {
     export const GetDocument = new Message<string>("Get Document");
     export const DeleteAll = new Message<any>("Delete All");
     export const ConnectionTerminated = new Message<string>("Connection Terminated");
-    export const BeginBuxtonImport = new Message<string>("Begin Buxton Import");
-    export const BuxtonDocumentResult = new Message<AnalysisResult>("Buxton Document Result");
-    export const BuxtonImportComplete = new Message<ImportResults>("Buxton Import Complete");
 
     export const GesturePoints = new Message<GestureContent>("Gesture Points");
     export const MobileInkOverlayTrigger = new Message<MobileInkOverlayContent>("Trigger Mobile Ink Overlay");
diff --git a/src/server/RouteManager.ts b/src/server/RouteManager.ts
index 78b75d6be..aa9bfcfa7 100644
--- a/src/server/RouteManager.ts
+++ b/src/server/RouteManager.ts
@@ -124,7 +124,7 @@ export default class RouteManager {
                 }
                 await tryExecute(secureHandler, { ...core, user });
             } else {
-                req.session!.target = target;
+                //req.session!.target = target;
                 if (publicHandler) {
                     await tryExecute(publicHandler, core);
                     if (!res.headersSent) {
diff --git a/src/server/Search.ts b/src/server/Search.ts
index 68f61deb2..25bd8badf 100644
--- a/src/server/Search.ts
+++ b/src/server/Search.ts
@@ -49,7 +49,7 @@ export namespace Search {
                 },
                 json: true
             });
-        } catch (e) {
+        } catch (e: any) {
             console.log(red("Unable to clear search..."));
             console.log(red(e.message));
         }
diff --git a/src/server/apis/google/GoogleApiServerUtils.ts b/src/server/apis/google/GoogleApiServerUtils.ts
index 64bafe7fb..4453b83bf 100644
--- a/src/server/apis/google/GoogleApiServerUtils.ts
+++ b/src/server/apis/google/GoogleApiServerUtils.ts
@@ -128,7 +128,7 @@ export namespace GoogleApiServerUtils {
      * @param userId the id of the Dash user making the request to the API
      * @returns the relevant 'googleapis' wrapper, if any
      */
-    export async function GetEndpoint(sector: string, userId: string): Promise<Opt<Endpoint>> {
+    export async function GetEndpoint(sector: string, userId: string): Promise<Endpoint | void> {
         return new Promise(async resolve => {
             const auth = await retrieveOAuthClient(userId);
             if (!auth) {
@@ -157,7 +157,7 @@ export namespace GoogleApiServerUtils {
      * npm-installed API wrappers that use authenticated client instances rather than access codes for
      * security.
      */
-    export async function retrieveOAuthClient(userId: string): Promise<OAuth2Client> {
+    export async function retrieveOAuthClient(userId: string): Promise<OAuth2Client | void> {
         return new Promise(async resolve => {
             const { credentials, refreshed } = await retrieveCredentials(userId);
             if (!credentials) {
diff --git a/src/server/authentication/AuthenticationManager.ts b/src/server/authentication/AuthenticationManager.ts
index 3fbd4b3a7..b736f0d35 100644
--- a/src/server/authentication/AuthenticationManager.ts
+++ b/src/server/authentication/AuthenticationManager.ts
@@ -55,7 +55,7 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
 
     const user = new User(model);
 
-    User.findOne({ email }, (err, existingUser) => {
+    User.findOne({ email }, (err: any, existingUser: any) => {
         if (err) { return next(err); }
         if (existingUser) {
             return res.redirect("/login");
@@ -72,9 +72,8 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
 };
 
 const tryRedirectToTarget = (req: Request, res: Response) => {
-    if (req.session && req.session.target) {
-        const target = req.session.target;
-        req.session.target = undefined;
+    const target = (req.session as any)?.target;
+    if (req.session && target) {
         res.redirect(target);
     } else {
         res.redirect("/home");
@@ -88,7 +87,7 @@ const tryRedirectToTarget = (req: Request, res: Response) => {
  */
 export let getLogin = (req: Request, res: Response) => {
     if (req.user) {
-        req.session!.target = undefined;
+        //req.session.target = undefined;
         return res.redirect("/home");
     }
     res.render("login.pug", {
@@ -160,7 +159,7 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio
             });
         },
         function (token: string, done: any) {
-            User.findOne({ email }, function (err, user: DashUserModel) {
+            User.findOne({ email }, function (err: any, user: DashUserModel) {
                 if (!user) {
                     // NO ACCOUNT WITH SUBMITTED EMAIL
                     res.redirect("/forgotPassword");
@@ -202,7 +201,7 @@ export let postForgot = function (req: Request, res: Response, next: NextFunctio
 };
 
 export let getReset = function (req: Request, res: Response) {
-    User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err, user: DashUserModel) {
+    User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err: any, user: DashUserModel) {
         if (!user || err) {
             return res.redirect("/forgotPassword");
         }
@@ -216,7 +215,7 @@ export let getReset = function (req: Request, res: Response) {
 export let postReset = function (req: Request, res: Response) {
     async.waterfall([
         function (done: any) {
-            User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err, user: DashUserModel) {
+            User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err: any, user: DashUserModel) {
                 if (!user || err) {
                     return res.redirect('back');
                 }
diff --git a/src/server/authentication/Passport.ts b/src/server/authentication/Passport.ts
index 9b0069414..d7f891c34 100644
--- a/src/server/authentication/Passport.ts
+++ b/src/server/authentication/Passport.ts
@@ -4,12 +4,12 @@ import { default as User } from './DashUserModel';
 
 const LocalStrategy = passportLocal.Strategy;
 
-passport.serializeUser<any, any>((user, done) => {
-    done(undefined, user.id);
+passport.serializeUser<any, any>((req, user, done) => {
+    done(undefined, (user as any)?.id);
 });
 
 passport.deserializeUser<any, any>((id, done) => {
-    User.findById(id, (err, user) => {
+    User.findById(id, (err: any, user: any) => {
         done(err, user);
     });
 });
diff --git a/src/server/remapUrl.ts b/src/server/remapUrl.ts
index e9f9da25a..b8e17ec66 100644
--- a/src/server/remapUrl.ts
+++ b/src/server/remapUrl.ts
@@ -47,7 +47,7 @@ async function update() {
     await cursor.forEach(updateDoc);
     await Promise.all(updates.map(doc => {
         console.log(doc[0], doc[1]);
-        return new Promise(res => Database.Instance.update(doc[0], doc[1], () => {
+        return new Promise<void>(res => Database.Instance.update(doc[0], doc[1], () => {
             console.log("wrote " + JSON.stringify(doc[1]));
             res();
         }, false));
diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts
index de93b64c3..24cc3b796 100644
--- a/src/server/server_Initialization.ts
+++ b/src/server/server_Initialization.ts
@@ -31,7 +31,7 @@ const compiler = webpack(config);
 /* RouteSetter is a wrapper around the server that prevents the server
    from being exposed. */
 export type RouteSetter = (server: RouteManager) => void;
-export let disconnect: Function;
+//export let disconnect: Function;
 
 export let resolvedPorts: { server: number, socket: number } = { server: 1050, socket: 4321 };
 export let resolvedServerUrl: string;
@@ -40,6 +40,14 @@ export default async function InitializeServer(routeSetter: RouteSetter) {
     const isRelease = determineEnvironment();
     const app = buildWithMiddleware(express());
 
+    const compiler = webpack(config);
+
+    app.use(require("webpack-dev-middleware")(compiler, {
+        publicPath: config.output.publicPath
+    }));
+
+    app.use(require("webpack-hot-middleware")(compiler));
+
     // route table managed by express.  routes are tested sequentially against each of these map rules.  when a match is found, the handler is called to process the request
     app.get(new RegExp(/^\/+$/), (req, res) => res.redirect(req.user ? "/home" : "/login")); // target urls that consist of one or more '/'s with nothing in between
     app.use(express.static(publicDirectory, { setHeaders: res => res.setHeader("Access-Control-Allow-Origin", "*") }));  //all urls that start with dash's public directory: /files/  (e.g., /files/images, /files/audio, etc)
@@ -66,7 +74,7 @@ export default async function InitializeServer(routeSetter: RouteSetter) {
     // a field on one client, that change must be broadcast to all other clients)
     await WebSocket.initialize(isRelease, app);
 
-    disconnect = async () => new Promise<Error>(resolve => server.close(resolve));
+    //disconnect = async () => new Promise<Error>(resolve => server.close(resolve));
     return isRelease;
 }
 
@@ -92,6 +100,13 @@ function buildWithMiddleware(server: express.Express) {
         passport.session(),
         (req: express.Request, res: express.Response, next: express.NextFunction) => {
             res.locals.user = req.user;
+            if ((req.originalUrl.endsWith(".png") /*|| req.originalUrl.endsWith(".js")*/) && req.method === 'GET' && (res as any)._contentLength) {
+                const period = 30000;
+                res.set('Cache-control', `public, max-age=${period}`);
+            } else {
+                // for the other requests set strict no caching parameters
+                res.set('Cache-control', `no-store`);
+            }
             next();
         }
     ].forEach(next => server.use(next));
@@ -178,11 +193,21 @@ function proxyServe(req: any, requrl: string, response: any) {
                                     return `href="${resolvedServerUrl + "/corsProxy/http" + href}"`;
                                 };
                                 const zipToStringDecoder = new (require('string_decoder').StringDecoder)('utf8');
-                                const htmlText = zipToStringDecoder.write(zlib.gunzipSync(htmlBodyMemoryStream.read()).toString('utf8')
-                                    .replace('<head>', '<head> <style>[id ^= "google"] { display: none; } </style>')
-                                    .replace(/href="http([^"]*)"/g, replacer)
-                                    .replace(/target="_blank"/g, ""));
-                                rewrittenHtmlBody = zlib.gzipSync(htmlText);
+                                // const htmlText = zipToStringDecoder.write(zlib.gunzipSync(htmlBodyMemoryStream.read()).toString('utf8')
+                                //     .replace('<head>', '<head> <style>[id ^= "google"] { display: none; } </style>')
+                                //     .replace(/href="http([^"]*)"/g, replacer)
+                                //     .replace(/target="_blank"/g, ""));
+                                // rewrittenHtmlBody = zlib.gzipSync(htmlText);
+                                const bodyStream = htmlBodyMemoryStream.read();
+                                if (bodyStream) {
+                                    const htmlText = zipToStringDecoder.write(zlib.gunzipSync(bodyStream).toString('utf8')
+                                        .replace('<head>', '<head> <style>[id ^= "google"] { display: none; } </style>')
+                                        // .replace(/href="http([^"]*)"/g, replacer)
+                                        .replace(/target="_blank"/g, ""));
+                                    rewrittenHtmlBody = zlib.gzipSync(htmlText);
+                                } else {
+                                    console.log("EMPTY body: href");
+                                }
                             } catch (e) { console.log(e); }
                         }
                     });
diff --git a/src/server/websocket.ts b/src/server/websocket.ts
index 0c79c1bbb..1b7f5919f 100644
--- a/src/server/websocket.ts
+++ b/src/server/websocket.ts
@@ -4,7 +4,6 @@ import { createServer, Server } from "https";
 import { networkInterfaces } from "os";
 import * as sio from 'socket.io';
 import { Socket } from "socket.io";
-import executeImport from "../scraping/buxton/final/BuxtonImporter";
 import { Utils } from "../Utils";
 import { logPort } from './ActionUtilities';
 import { timeMap } from "./ApiManagers/UserManager";
@@ -125,12 +124,6 @@ export namespace WebSocket {
              * as an emitter and a terminator the functions that simply broadcast a result
              * or indicate termination to the client via the web socket
              */
-            Utils.AddServerHandler(socket, MessageStore.BeginBuxtonImport, () => {
-                executeImport(
-                    deviceOrError => Utils.Emit(socket, MessageStore.BuxtonDocumentResult, deviceOrError),
-                    results => Utils.Emit(socket, MessageStore.BuxtonImportComplete, results)
-                );
-            });
 
             disconnect = () => {
                 socket.broadcast.emit("connection_terminated", Date.now());
diff --git a/webpack.config.js b/webpack.config.js
index 56bd428e4..3fd00bcf3 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,6 +1,7 @@
 var path = require('path');
 var webpack = require('webpack');
 const CopyWebpackPlugin = require("copy-webpack-plugin");
+const HtmlWebpackPlugin = require('html-webpack-plugin');
 const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
 
 const plugins = [
@@ -8,11 +9,16 @@ const plugins = [
         from: "deploy",
         to: path.join(__dirname, "build")
     }]),
+    new HtmlWebpackPlugin({
+        title: 'Caching',
+    }),
     new ForkTsCheckerWebpackPlugin({
         tslint: true,
+        //  memoryLimit: 4096,
         useTypescriptIncrementalApi: true
     }),
-    new webpack.optimize.OccurrenceOrderPlugin(),
+    new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'], }),
+    new webpack.ProvidePlugin({ process: 'process/browser', }),
     new webpack.HotModuleReplacementPlugin(),
 ];
 
@@ -45,24 +51,29 @@ module.exports = {
         inkControls: ["./src/mobile/InkControls.tsx", 'webpack-hot-middleware/client?reload=true'],
         mobileInterface: ["./src/mobile/MobileMain.tsx", 'webpack-hot-middleware/client?reload=true'],
     },
-    optimization: {
-        noEmitOnErrors: true
-    },
     devtool: "source-map",
-    node: {
-        fs: 'empty',
-        module: 'empty',
-        dns: 'mock',
-        tls: 'mock',
-        net: 'mock'
-    },
     output: {
         filename: "[name].js",
         path: path.resolve(__dirname, "build"),
-        publicPath: "/"
+        publicPath: "/",
     },
     resolve: {
-        extensions: ['.js', '.ts', '.tsx']
+        extensions: ['.js', '.ts', '.tsx'],
+        fallback: {
+            fs: false,
+            tls: false,
+            net: false,
+            zlib: false,
+            dns: false,
+            crypto: false,
+            assert: false,
+            os: false,
+            path: require.resolve("path-browserify"),
+            http: require.resolve("http-browserify"),
+            https: require.resolve("https-browserify"),
+            stream: require.resolve("stream-browserify"),
+            buffer: require.resolve("buffer")
+        }
     },
     module: {
         rules: [{
@@ -74,6 +85,12 @@ module.exports = {
                 }
             }]
         },
+        {
+            test: /\.m?js/,
+            resolve: {
+                fullySpecified: false
+            }
+        },
         {
             test: /\.(woff|woff2|ttf|eot|otf|svg)$/,
             use: 'file-loader?name=fonts/[name].[ext]!static'
@@ -113,5 +130,5 @@ module.exports = {
     plugins,
     externals: [
         'child_process'
-    ]
+    ],
 };
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 6e61005b3b6dd8deeb3ca43a8c6241d7778a1998 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 24 Mar 2022 09:58:01 -0400
Subject: fixed videoBox play issue.  cleaned up global referencing a bit.

---
 src/client/util/CurrentUserUtils.ts                |  1 +
 src/client/views/MainView.tsx                      |  2 +-
 .../collectionLinear/CollectionLinearView.tsx      | 54 +++++++++++-----------
 .../CollectionMulticolumnView.tsx                  |  2 +-
 src/client/views/nodes/AudioBox.tsx                |  6 +--
 src/client/views/nodes/VideoBox.tsx                |  6 +--
 src/client/views/nodes/WebBox.tsx                  |  2 +-
 7 files changed, 37 insertions(+), 36 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index c0e3f66eb..22ab4beb8 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1501,6 +1501,7 @@ export class CurrentUserUtils {
         return tbox;
     }
 
+    public static get DockedBtns() { return Cast(Doc.UserDoc().dockedBtns, Doc, null); }
     public static get MySearchPanelDoc() { return Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null); }
     public static get ActiveDashboard() { return Cast(Doc.UserDoc().activeDashboard, Doc, null); }
     public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 8c0795881..7cb40f701 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -184,7 +184,7 @@ export class MainView extends React.Component {
         fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
         fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
         fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines,
-        fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt]);
+        fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt, fa.faSearchPlus, fa.faVolumeUp, fa.faVolumeDown]);
         this.initAuthenticationRouters();
     }
 
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 160134b60..1d142d595 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -8,6 +8,7 @@ import { Id } from '../../../../fields/FieldSymbols';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnTrue, Utils } from '../../../../Utils';
 import { DocUtils } from '../../../documents/Documents';
+import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
 import { DocumentManager } from "../../../util/DocumentManager";
 import { DragManager } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
@@ -206,36 +207,35 @@ export class CollectionLinearView extends CollectionSubView() {
                     }}>
                     {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
                 </div>
-                {DocumentLinksButton.StartLink && StrCast(this.layoutDoc.title) === "docked buttons" ? <span className="bottomPopup-background" style={{
-                    pointerEvents: "all"
-                }}
-                    onPointerDown={e => e.stopPropagation()} >
-                    <span className="bottomPopup-text" >
-                        Creating link from: <b>{DocumentLinksButton.AnnotationId ? "Annotation in " : " "} {StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...'}</b>
-                    </span>
-
-                    <Tooltip title={<><div className="dash-tooltip">{"Toggle description pop-up"} </div></>} placement="top">
-                        <span className="bottomPopup-descriptions" onClick={this.changeDescriptionSetting}>
-                            Labels: {LinkDescriptionPopup.showDescriptions ? LinkDescriptionPopup.showDescriptions : "ON"}
+                {!DocumentLinksButton.StartLink || this.layoutDoc !== CurrentUserUtils.DockedBtns ? null :
+                    <span className="bottomPopup-background" style={{ pointerEvents: "all" }}
+                        onPointerDown={e => e.stopPropagation()} >
+                        <span className="bottomPopup-text" >
+                            Creating link from: <b>{DocumentLinksButton.AnnotationId ? "Annotation in " : " "} {StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...'}</b>
                         </span>
-                    </Tooltip>
 
-                    <Tooltip title={<><div className="dash-tooltip">Exit linking mode</div></>} placement="top">
-                        <span className="bottomPopup-exit" onClick={this.exitLongLinks}>
-                            Stop
+                        <Tooltip title={<><div className="dash-tooltip">{"Toggle description pop-up"} </div></>} placement="top">
+                            <span className="bottomPopup-descriptions" onClick={this.changeDescriptionSetting}>
+                                Labels: {LinkDescriptionPopup.showDescriptions ? LinkDescriptionPopup.showDescriptions : "ON"}
+                            </span>
+                        </Tooltip>
+
+                        <Tooltip title={<><div className="dash-tooltip">Exit linking mode</div></>} placement="top">
+                            <span className="bottomPopup-exit" onClick={this.exitLongLinks}>
+                                Stop
+                            </span>
+                        </Tooltip>
+                    </span>}
+                {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== CurrentUserUtils.DockedBtns ? (null) :
+                    <span className="bottomPopup-background">
+                        <span className="bottomPopup-text">
+                            Currently playing:
+                            {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) =>
+                                <span className="audio-title" onPointerDown={() => DocumentManager.Instance.jumpToDocument(clip, true)}>
+                                    {clip.title + (i == CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? "" : ",")}
+                                </span>)}
                         </span>
-                    </Tooltip>
-
-                </span> : null}
-                {CollectionStackedTimeline.CurrentlyPlaying && CollectionStackedTimeline.CurrentlyPlaying.length != 0 && StrCast(this.layoutDoc.title) === "docked buttons" ? <span className="bottomPopup-background">
-                    <span className="bottomPopup-text">
-                        Currently playing: {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) =>
-                            <span className="audio-title" onPointerDown={() => {
-                                DocumentManager.Instance.jumpToDocument(clip, true);
-                            }}>{clip.title + (i == CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? "" : ",")} </span>
-                        )}
-                    </span>
-                </span> : null}
+                    </span>}
 
             </div>
         </div>;
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 2bdf92417..6929a1cd8 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -214,7 +214,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         }
         return this.props.addDocTab(doc, where);
     }
-    getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
+    getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
         return <DocumentView
             Document={layout}
             DataDoc={layout.resolvedDataDoc as Doc}
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index be18cc1de..968dbc54d 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -201,7 +201,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     @action
     removeCurrentlyPlaying = () => {
         if (CollectionStackedTimeline.CurrentlyPlaying) {
-            const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc.doc as Doc);
+            const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc);
             index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1);
         }
     }
@@ -212,8 +212,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         if (!CollectionStackedTimeline.CurrentlyPlaying) {
             CollectionStackedTimeline.CurrentlyPlaying = [];
         }
-        if (CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc.doc as Doc) == -1) {
-            CollectionStackedTimeline.CurrentlyPlaying.push(this.layoutDoc.doc as Doc);
+        if (CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc) == -1) {
+            CollectionStackedTimeline.CurrentlyPlaying.push(this.layoutDoc);
         }
     }
 
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 913123cda..804ba777e 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -477,7 +477,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     @action
     removeCurrentlyPlaying = () => {
         if (CollectionStackedTimeline.CurrentlyPlaying) {
-            const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc.doc as Doc);
+            const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc);
             index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1);
         }
     }
@@ -488,8 +488,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         if (!CollectionStackedTimeline.CurrentlyPlaying) {
             CollectionStackedTimeline.CurrentlyPlaying = [];
         }
-        if (CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc.doc as Doc) == -1) {
-            CollectionStackedTimeline.CurrentlyPlaying.push(this.layoutDoc.doc as Doc);
+        if (CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc) == -1) {
+            CollectionStackedTimeline.CurrentlyPlaying.push(this.layoutDoc);
         }
     }
 
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 7969546c5..2928b6227 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -145,7 +145,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                             NumCast(this.layoutDoc._scrollTop)
                         ).then
                             ((dataUrl: any) => {
-                                VideoBox.convertDataUri(dataUrl, this.layoutDoc[Id] + "-thumb" + (new Date()).getTime(), true).then(
+                                VideoBox.convertDataUri(dataUrl, this.layoutDoc[Id] + "-thumb" + (new Date()).getTime()).then(
                                     returnedfilename => setTimeout(action(() => this.layoutDoc.thumb = new ImageField(returnedfilename)), 500));
                             })
                             .catch(function (error: any) {
-- 
cgit v1.2.3-70-g09d2


From 9a35bde069a28f53b4ecf668a066d7c94e63cce2 Mon Sep 17 00:00:00 2001
From: Aubrey Li <Aubrey-Li>
Date: Thu, 24 Mar 2022 18:09:04 -0400
Subject: change presbox to tree view

---
 src/client/util/CurrentUserUtils.ts | 2 +-
 src/client/views/MainView.tsx       | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index f02c5a5a1..1a3a27950 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -351,7 +351,7 @@ export class CurrentUserUtils {
     }[] {
         if (doc.emptyPresentation === undefined) {
             doc.emptyPresentation = Docs.Create.PresDocument(new List<Doc>(),
-                { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0", system: true, cloneFieldFilter: new List<string>(["system"]) });
+                { title: "Untitled Presentation", _viewType: CollectionViewType.Tree, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0", system: true, cloneFieldFilter: new List<string>(["system"]) });
             ((doc.emptyPresentation as Doc).proto as Doc)["dragFactory-count"] = 0;
         }
         if (doc.emptyCollection === undefined) {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 8c0795881..7f3304de5 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -245,8 +245,8 @@ export class MainView extends React.Component {
             }));
         }
         const pres = Docs.Create.PresDocument(new List<Doc>(),
-            { title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
-        CollectionDockingView.AddSplit(pres, "right");
+            { title: "Untitled Trail", childDontRegisterViews: true, _viewType: CollectionViewType.Tree, _forceActive: true, treeViewOpen: true, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
+        CollectionDockingView.AddSplit(pres, "left");
         this.userDoc.activePresentation = pres;
         Doc.AddDocToList(this.userDoc.myTrails as Doc, "data", pres);
     }
-- 
cgit v1.2.3-70-g09d2


From d5d472521229f006f4a95d5c019276ec0281ac57 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 31 Mar 2022 14:56:03 -0400
Subject: cleaned up addDocTab for properties views to allow toggling of
 targets

---
 src/client/views/DocumentDecorations.tsx            | 16 +++++++++-------
 src/client/views/MainView.tsx                       | 20 ++++++++++++++++----
 src/client/views/PropertiesDocBacklinksSelector.tsx |  4 +---
 src/client/views/PropertiesDocContextSelector.tsx   | 11 +++++++----
 src/client/views/PropertiesView.tsx                 |  7 ++++---
 src/client/views/collections/TabDocView.tsx         |  6 ++----
 6 files changed, 39 insertions(+), 25 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 328b8ab88..8cd646935 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -83,16 +83,18 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         } else if (this._titleControlString.startsWith("#")) {
             const titleFieldKey = this._titleControlString.substring(1);
             UndoManager.RunInBatch(() => titleFieldKey && SelectionManager.Views().forEach(d => {
-                titleFieldKey === "title" && (d.dataDoc["title-custom"] = !this._accumulatedTitle.startsWith("-"));
+                if (titleFieldKey === "title") {
+                    d.dataDoc["title-custom"] = !this._accumulatedTitle.startsWith("-");
+                    if (StrCast(d.rootDoc.title).startsWith("@") && !this._accumulatedTitle.startsWith("@")) {
+                        Doc.RemoveDocFromList(Doc.UserDoc(), "myPublishedDocs", d.rootDoc);
+                    }
+                    if (!StrCast(d.rootDoc.title).startsWith("@") && this._accumulatedTitle.startsWith("@")) {
+                        Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", d.rootDoc);
+                    }
+                }
                 //@ts-ignore
                 const titleField = (+this._accumulatedTitle === this._accumulatedTitle ? +this._accumulatedTitle : this._accumulatedTitle);
-                if (titleFieldKey === "title" && StrCast(d.rootDoc.title).startsWith("@") && !this._accumulatedTitle.startsWith("@")) {
-                    Doc.RemoveDocFromList(Doc.UserDoc(), "myPublishedDocs", SelectionManager.Docs().lastElement());
-                }
                 Doc.SetInPlace(d.rootDoc, titleFieldKey, titleField, true);
-                if (titleFieldKey === "title" && this._accumulatedTitle.startsWith("@")) {
-                    Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", SelectionManager.Docs().lastElement());
-                }
 
                 if (d.rootDoc.syncLayoutFieldWithTitle) {
                     const title = titleField.toString();
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 7cb40f701..5e68832fc 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -329,9 +329,21 @@ export class MainView extends React.Component {
 
     sidebarScreenToLocal = () => new Transform(0, -this.topOfMainDoc, 1);
     mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftScreenOffsetOfMainDocView, 0);
-    addDocTabFunc = (doc: Doc, where: string): boolean => {
-        return where === "close" ? CollectionDockingView.CloseSplit(doc) :
-            doc.dockingConfig ? CurrentUserUtils.openDashboard(Doc.UserDoc(), doc) : CollectionDockingView.AddSplit(doc, "right");
+    addDocTabFunc = (doc: Doc, location: string): boolean => {
+        const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":");
+        const locationParams = locationFields.length > 1 ? locationFields[1] : "";
+        if (doc.dockingConfig) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+        switch (locationFields[0]) {
+            case "dashboard": return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+            case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
+            case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
+            case "lightbox": return LightboxView.AddDocTab(doc, location);
+            case "toggle": return CollectionDockingView.ToggleSplit(doc, locationParams);
+            case "inPlace":
+            case "add":
+            default:
+                return CollectionDockingView.AddSplit(doc, locationParams);
+        }
     }
 
 
@@ -440,7 +452,7 @@ export class MainView extends React.Component {
                         <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.colorScheme === ColorScheme.Dark ? Colors.WHITE : Colors.BLACK} size="sm" />
                     </div>
                     <div className="properties-container" style={{ width: this.propertiesWidth() }}>
-                        {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} width={this.propertiesWidth()} height={this.propertiesHeight()} />}
+                        {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} addDocTab={this.addDocTabFunc} width={this.propertiesWidth()} height={this.propertiesHeight()} />}
                     </div>
                 </div>
             </div>
diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx
index 5ffa93961..ea0d90e04 100644
--- a/src/client/views/PropertiesDocBacklinksSelector.tsx
+++ b/src/client/views/PropertiesDocBacklinksSelector.tsx
@@ -5,8 +5,6 @@ import { Doc, DocListCast } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { Cast, NumCast, StrCast } from "../../fields/Types";
 import { LinkManager } from "../util/LinkManager";
-import { CollectionDockingView } from "./collections/CollectionDockingView";
-import { CollectionViewType } from "./collections/CollectionView";
 import './PropertiesDocContextSelector.scss';
 
 type PropertiesDocBacklinksSelectorProps = {
@@ -34,7 +32,7 @@ export class PropertiesDocBacklinksSelector extends React.Component<PropertiesDo
 
     getOnClick = (col: Doc) => {
         col = Doc.IsPrototype(col) ? Doc.MakeDelegate(col) : col;
-        this.props.addDocTab(col, "add:right");
+        this.props.addDocTab(col, "toggle:right");
     }
 
     render() {
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index 45a8b6bf6..4d803f992 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -6,10 +6,11 @@ import { Id } from "../../fields/FieldSymbols";
 import { Cast, NumCast, StrCast } from "../../fields/Types";
 import { CollectionDockingView } from "./collections/CollectionDockingView";
 import { CollectionViewType } from "./collections/CollectionView";
+import { DocumentView } from "./nodes/DocumentView";
 import './PropertiesDocContextSelector.scss';
 
 type PropertiesDocContextSelectorProps = {
-    Document: Doc,
+    DocView?: DocumentView,
     Stack?: any,
     hideTitle?: boolean,
     addDocTab(doc: Doc, location: string): void
@@ -18,9 +19,11 @@ type PropertiesDocContextSelectorProps = {
 @observer
 export class PropertiesDocContextSelector extends React.Component<PropertiesDocContextSelectorProps> {
     @computed get _docs() {
-        const target = this.props.Document;
+        if (!this.props.DocView) return [];
+        const target = this.props.DocView.props.Document;
+        const targetContext = this.props.DocView.props.ContainingCollectionDoc;
         const aliases = DocListCast(target.aliases);
-        const containerProtos = aliases.filter(alias => alias.context).reduce((set, alias) => set.add(Cast(alias.context, Doc, null)), new Set<Doc>());
+        const containerProtos = aliases.filter(alias => alias.context && alias.context instanceof Doc && Cast(alias.context, Doc, null) !== targetContext).reduce((set, alias) => set.add(Cast(alias.context, Doc, null)), new Set<Doc>());
         const containerSets = Array.from(containerProtos.keys()).map(container => DocListCast(container.aliases));
         const containers = containerSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set<Doc>());
         const doclayoutSets = Array.from(containers.keys()).map(dp => DocListCast(dp.aliases));
@@ -34,7 +37,7 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
             col._panX = NumCast(target.x) + NumCast(target._width) / 2;
             col._panY = NumCast(target.y) + NumCast(target._height) / 2;
         }
-        this.props.addDocTab(col, "add:right");
+        this.props.addDocTab(col, "toggle:right");
     }
 
     render() {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 8e1e9ea15..91cf83619 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -44,6 +44,7 @@ interface PropertiesViewProps {
     width: number;
     height: number;
     styleProvider?: StyleProviderFunc;
+    addDocTab: (doc: Doc, where: string) => boolean;
 }
 
 @observer
@@ -279,11 +280,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     @computed get contexts() {
-        return !this.selectedDoc ? (null) : <PropertiesDocContextSelector Document={this.selectedDoc} hideTitle={true} addDocTab={(doc, where) => CollectionDockingView.AddSplit(doc, "right")} />;
+        return !this.selectedDoc ? (null) : <PropertiesDocContextSelector DocView={this.selectedDocumentView} hideTitle={true} addDocTab={this.props.addDocTab} />;
     }
 
     @computed get links() {
-        return !this.selectedDoc ? (null) : <PropertiesDocBacklinksSelector Document={this.selectedDoc} hideTitle={true} addDocTab={(doc, where) => CollectionDockingView.AddSplit(doc, "right")} />;
+        return !this.selectedDoc ? (null) : <PropertiesDocBacklinksSelector Document={this.selectedDoc} hideTitle={true} addDocTab={this.props.addDocTab} />;
     }
 
     @computed get layoutPreview() {
@@ -1077,7 +1078,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             <div className="propertiesView-contexts-title"
                 onPointerDown={action(() => this.openContexts = !this.openContexts)}
                 style={{ backgroundColor: this.openContexts ? "black" : "" }}>
-                Contexts
+                Other Contexts
                 <div className="propertiesView-contexts-title-icon">
                     <FontAwesomeIcon icon={this.openContexts ? "caret-down" : "caret-right"} size="lg" color="white" />
                 </div>
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index d52746d11..8b5022593 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -298,10 +298,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
             case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
             case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack);
-            case "lightbox": {
-                // TabDocView.PinDoc(doc, { hidePresBox: true });
-                return LightboxView.AddDocTab(doc, location);
-            }
+            case "lightbox": return LightboxView.AddDocTab(doc, location);
+            case "toggle": return CollectionDockingView.ToggleSplit(doc, locationParams, this.stack);
             case "inPlace":
             case "add":
             default:
-- 
cgit v1.2.3-70-g09d2


From af8df1fc1af11318cadf1b71373bef776461a5cc Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 6 Apr 2022 10:42:36 -0400
Subject: uses linkMenu in propertiesWindow now.  updated linkMenu api, changed
 layout, made groups collapsible

---
 src/client/views/MainView.tsx                      |  2 +-
 .../views/PropertiesDocBacklinksSelector.scss      |  5 +++++
 .../views/PropertiesDocBacklinksSelector.tsx       | 25 ++++++++++++++--------
 src/client/views/SidebarAnnos.tsx                  |  2 +-
 src/client/views/linking/LinkMenu.scss             |  9 +++++---
 src/client/views/linking/LinkMenu.tsx              | 10 +++++----
 src/client/views/linking/LinkMenuGroup.tsx         | 11 +++++++---
 src/client/views/linking/LinkMenuItem.scss         | 14 ++++++++----
 src/client/views/linking/LinkMenuItem.tsx          | 15 ++++++++-----
 src/client/views/nodes/LinkDocPreview.tsx          |  2 +-
 10 files changed, 64 insertions(+), 31 deletions(-)
 create mode 100644 src/client/views/PropertiesDocBacklinksSelector.scss

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 5e68832fc..a67cb3014 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -631,7 +631,7 @@ export class MainView extends React.Component {
             <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} />
             {this.topbar}
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
-            {DocumentLinksButton.LinkEditorDocView ? <LinkMenu docView={DocumentLinksButton.LinkEditorDocView} changeFlyout={emptyFunction} /> : (null)}
+            {DocumentLinksButton.LinkEditorDocView ? <LinkMenu docView={DocumentLinksButton.LinkEditorDocView} /> : (null)}
             {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)}
             <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 2001 }} >
                 <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
diff --git a/src/client/views/PropertiesDocBacklinksSelector.scss b/src/client/views/PropertiesDocBacklinksSelector.scss
new file mode 100644
index 000000000..4d2a61c3b
--- /dev/null
+++ b/src/client/views/PropertiesDocBacklinksSelector.scss
@@ -0,0 +1,5 @@
+.propertiesView-contexts-content {
+    .linkMenu {
+        position: relative;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx
index ea0d90e04..082492671 100644
--- a/src/client/views/PropertiesDocBacklinksSelector.tsx
+++ b/src/client/views/PropertiesDocBacklinksSelector.tsx
@@ -2,10 +2,12 @@ import { computed } from "mobx";
 import { observer } from "mobx-react";
 import * as React from "react";
 import { Doc, DocListCast } from "../../fields/Doc";
-import { Id } from "../../fields/FieldSymbols";
-import { Cast, NumCast, StrCast } from "../../fields/Types";
+import { Cast } from "../../fields/Types";
+import { DocumentType } from "../documents/DocumentTypes";
 import { LinkManager } from "../util/LinkManager";
-import './PropertiesDocContextSelector.scss';
+import { SelectionManager } from "../util/SelectionManager";
+import { LinkMenu } from "./linking/LinkMenu";
+import './PropertiesDocBacklinksSelector.scss';
 
 type PropertiesDocBacklinksSelectorProps = {
     Document: Doc,
@@ -22,7 +24,7 @@ export class PropertiesDocBacklinksSelector extends React.Component<PropertiesDo
         const collectedLinks = [] as Doc[];
         links.map(link => {
             const other = LinkManager.getOppositeAnchor(link, linkSource);
-            const otherdoc = !other ? undefined : other.annotationOn ? Cast(other.annotationOn, Doc, null) : other;
+            const otherdoc = !other ? undefined : other.annotationOn && other.type !== DocumentType.RTF ? Cast(other.annotationOn, Doc, null) : other;
             if (otherdoc && !collectedLinks.some(d => Doc.AreProtosEqual(d, otherdoc))) {
                 collectedLinks.push(otherdoc);
             }
@@ -30,15 +32,20 @@ export class PropertiesDocBacklinksSelector extends React.Component<PropertiesDo
         return collectedLinks;
     }
 
-    getOnClick = (col: Doc) => {
-        col = Doc.IsPrototype(col) ? Doc.MakeDelegate(col) : col;
-        this.props.addDocTab(col, "toggle:right");
+    getOnClick = (link: Doc) => {
+        const linkSource = this.props.Document;
+        const other = LinkManager.getOppositeAnchor(link, linkSource);
+        const otherdoc = !other ? undefined : other.annotationOn && other.type !== DocumentType.RTF ? Cast(other.annotationOn, Doc, null) : other;
+
+        if (otherdoc) {
+            this.props.addDocTab(Doc.IsPrototype(otherdoc) ? Doc.MakeDelegate(otherdoc) : otherdoc, "toggle:right");
+        }
     }
 
     render() {
-        return <div>
+        return !SelectionManager.Views().length ? (null) : <div>
             {this.props.hideTitle ? (null) : <p key="contexts">Contexts:</p>}
-            {this._docs.map(doc => <p key={doc[Id]}><a onClick={() => this.getOnClick(doc)}>{StrCast(doc.title)}</a></p>)}
+            <LinkMenu docView={SelectionManager.Views().lastElement()} itemHandler={this.getOnClick} position={{ x: 0 }} />
         </div>;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 62f6e388f..fae385660 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -61,7 +61,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         FormattedTextBox.SelectOnLoad = target[Id];
         FormattedTextBox.DontSelectInitialText = true;
         this.allMetadata.map(tag => target[tag] = tag);
-        DocUtils.MakeLink({ doc: anchor }, { doc: target }, "inline markup", "annotation");
+        DocUtils.MakeLink({ doc: anchor }, { doc: target }, "inline markup");
         this.addDocument(target);
         this._stackRef.current?.focusDocument(target);
     }
diff --git a/src/client/views/linking/LinkMenu.scss b/src/client/views/linking/LinkMenu.scss
index 19c6463d3..77c16a28f 100644
--- a/src/client/views/linking/LinkMenu.scss
+++ b/src/client/views/linking/LinkMenu.scss
@@ -45,8 +45,11 @@
     }
 
     .linkMenu-group-name {
-        padding: 10px;
-
+        padding: 1px;
+        padding-left: 5px;
+        font-size: 10px;
+        font-style: italic;
+        font-weight: bold;
 
         &:hover {
             // p {
@@ -64,7 +67,7 @@
 
         p {
             width: 100%;
-            line-height: 12px;
+            line-height: 1;
             border-radius: 5px;
             text-transform: capitalize;
         }
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index 53fe3f682..bc922be36 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -12,7 +12,8 @@ import React = require("react");
 
 interface Props {
     docView: DocumentView;
-    changeFlyout: () => void;
+    position?: { x?: number, y?: number };
+    itemHandler?: (doc: Doc) => void;
 }
 
 /**
@@ -25,7 +26,7 @@ export class LinkMenu extends React.Component<Props> {
     @observable _linkMenuRef = React.createRef<HTMLDivElement>();
 
     @computed get position() {
-        return ((dv) => ({ x: dv?.left || 0, y: dv?.top || 0, r: dv?.right || 0, b: dv?.bottom || 0 }))(this.props.docView.getBounds());
+        return this.props.position ?? (dv => ({ x: dv?.left || 0, y: (dv?.bottom || 0) + 15 }))(this.props.docView.getBounds());
     }
 
     componentDidMount() { document.addEventListener("pointerdown", this.onPointerDown); }
@@ -48,19 +49,20 @@ export class LinkMenu extends React.Component<Props> {
         const linkItems = Array.from(groups.entries()).map(group =>
             <LinkMenuGroup
                 key={group[0]}
+                itemHandler={this.props.itemHandler}
                 docView={this.props.docView}
                 sourceDoc={this.props.docView.props.Document}
                 group={group[1]}
                 groupType={group[0]}
                 showEditor={action(linkDoc => this._editingLink = linkDoc)} />);
 
-        return linkItems.length ? linkItems : [<p key="">No links have been created yet. Drag the linking button onto another document to create a link.</p>];
+        return linkItems.length ? linkItems : this.props.position ? [<></>] : [<p key="">No links have been created yet. Drag the linking button onto another document to create a link.</p>];
     }
 
     render() {
         const sourceDoc = this.props.docView.props.Document;
         return <div className="linkMenu" ref={this._linkMenuRef}
-            style={{ left: this.position.x, top: this.props.docView.topMost ? undefined : this.position.b + 15, bottom: this.props.docView.topMost ? 20 : undefined }}
+            style={{ left: this.position.x, top: this.props.docView.topMost ? undefined : this.position.y, bottom: this.props.docView.topMost ? 20 : undefined }}
         >
             {this._editingLink ?
                 <div className="linkMenu-listEditor">
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index 03377ad4e..5b1e29e67 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -1,4 +1,5 @@
 import { observer } from "mobx-react";
+import { observable, action } from "mobx";
 import { Doc, StrListCast } from "../../../fields/Doc";
 import { Id } from "../../../fields/FieldSymbols";
 import { Cast } from "../../../fields/Types";
@@ -14,6 +15,7 @@ interface LinkMenuGroupProps {
     groupType: string;
     showEditor: (linkDoc: Doc) => void;
     docView: DocumentView;
+    itemHandler?: (doc: Doc) => void;
 }
 
 @observer
@@ -36,6 +38,8 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
         return color;
     }
 
+    @observable _collapsed = false;
+
     render() {
         const set = new Set<Doc>(this.props.group);
         const groupItems = Array.from(set.keys()).map(linkDoc => {
@@ -43,6 +47,7 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
                 LinkManager.getOppositeAnchor(linkDoc, Cast(linkDoc.anchor2, Doc, null).annotationOn === this.props.sourceDoc ? Cast(linkDoc.anchor2, Doc, null) : Cast(linkDoc.anchor1, Doc, null));
             if (destination && this.props.sourceDoc) {
                 return <LinkMenuItem key={linkDoc[Id]}
+                    itemHandler={this.props.itemHandler}
                     groupType={this.props.groupType}
                     docView={this.props.docView}
                     linkDoc={linkDoc}
@@ -55,12 +60,12 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
 
         return (
             <div className="linkMenu-group" ref={this._menuRef}>
-                <div className="linkMenu-group-name" style={{ background: this.getBackgroundColor() }}>
+                <div className="linkMenu-group-name" onClick={action(() => this._collapsed = !this._collapsed)} style={{ background: this.getBackgroundColor() }}>
                     <p className={this.props.groupType === "*" || this.props.groupType === "" ? "" : "expand-one"}> {this.props.groupType}:</p>
                 </div>
-                <div className="linkMenu-group-wrapper">
+                {this._collapsed ? (null) : <div className="linkMenu-group-wrapper">
                     {groupItems}
-                </div>
+                </div>}
             </div >
         );
     }
diff --git a/src/client/views/linking/LinkMenuItem.scss b/src/client/views/linking/LinkMenuItem.scss
index 90722daf9..8333aa374 100644
--- a/src/client/views/linking/LinkMenuItem.scss
+++ b/src/client/views/linking/LinkMenuItem.scss
@@ -9,8 +9,8 @@
     padding-left: 6.5px;
     padding-right: 2px;
 
-    padding-top: 6.5px;
-    padding-bottom: 6.5px;
+    padding-top: 1px;
+    padding-bottom: 1px;
 
     background-color: white;
 
@@ -18,10 +18,12 @@
     .linkMenu-name {
         position: relative;
         width: auto;
+        align-items: center;
+        display: flex;
 
         .linkMenu-text {
 
-            padding: 4px 2px;
+            // padding: 4px 2px;
             //display: inline;
 
             .linkMenu-source-title {
@@ -37,6 +39,8 @@
             .linkMenu-title-wrapper {
 
                 display: flex;
+                align-items: center;
+                min-height: 20px;
 
                 .destination-icon-wrapper {
                     //border: 0.5px solid rgb(161, 161, 161);
@@ -56,7 +60,8 @@
                 .linkMenu-destination-title {
                     text-decoration: none;
                     color: #4476F7;
-                    font-size: 16px;
+                    font-size: 13px;
+                    line-height: 0.9;
                     padding-bottom: 2px;
                     padding-right: 4px;
                     margin-right: 4px;
@@ -77,6 +82,7 @@
                 font-style: italic;
                 color: rgb(95, 97, 102);
                 font-size: 9px;
+                line-height: 0.9;
                 margin-left: 20px;
                 max-width: 125px;
                 height: auto;
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 96cc6d600..d6400a6b3 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -1,12 +1,12 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
-import { action, observable, runInAction } from 'mobx';
+import { action, observable } from 'mobx';
 import { observer } from "mobx-react";
 import { Doc, DocListCast } from '../../../fields/Doc';
 import { Cast, StrCast } from '../../../fields/Types';
 import { WebField } from '../../../fields/URLField';
-import { emptyFunction, setupMoveUpEvents, returnFalse } from '../../../Utils';
+import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager } from '../../util/DragManager';
@@ -14,11 +14,10 @@ import { Hypothesis } from '../../util/HypothesisUtils';
 import { LinkManager } from '../../util/LinkManager';
 import { undoBatch } from '../../util/UndoManager';
 import { DocumentLinksButton } from '../nodes/DocumentLinksButton';
-import { DocumentView, DocumentViewSharedProps } from '../nodes/DocumentView';
+import { DocumentView } from '../nodes/DocumentView';
 import { LinkDocPreview } from '../nodes/LinkDocPreview';
 import './LinkMenuItem.scss';
 import React = require("react");
-import { setup } from 'mocha';
 
 
 interface LinkMenuItemProps {
@@ -29,6 +28,7 @@ interface LinkMenuItemProps {
     destinationDoc: Doc;
     showEditor: (linkDoc: Doc) => void;
     menuRef: React.Ref<HTMLDivElement>;
+    itemHandler?: (doc: Doc) => void;
 }
 
 // drag links and drop link targets (aliasing them if needed)
@@ -98,7 +98,12 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
             emptyFunction,
             () => {
                 DocumentLinksButton.ClearLinkEditor();
-                LinkManager.FollowLink(this.props.linkDoc, this.props.sourceDoc, this.props.docView.props, false);
+                if (this.props.itemHandler) {
+
+                    this.props.itemHandler?.(this.props.linkDoc);
+                } else {
+                    LinkManager.FollowLink(this.props.linkDoc, this.props.sourceDoc, this.props.docView.props, false);
+                }
             });
     }
 
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 7637d39be..f5f773595 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -46,7 +46,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
         if (anchor1 && anchor2) {
             linkTarget = Doc.AreProtosEqual(anchor1, this._linkSrc) || Doc.AreProtosEqual(anchor1?.annotationOn as Doc, this._linkSrc) ? anchor2 : anchor1;
         }
-        if (linkTarget?.annotationOn) {
+        if (linkTarget?.annotationOn && linkTarget?.type !== DocumentType.RTF) { // want to show annotation context document if annotation is not text
             linkTarget && DocCastAsync(linkTarget.annotationOn).then(action(anno => this._targetDoc = anno));
         } else {
             this._targetDoc = linkTarget;
-- 
cgit v1.2.3-70-g09d2


From ab0e1ac6f5b21f2e10bdce428c882482a0f159b4 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Tue, 12 Apr 2022 18:09:03 -0400
Subject: ink to text for groupings

---
 package-lock.json                                  |  79 ++++++++-
 package.json                                       |   1 +
 src/client/documents/Documents.ts                  |   5 +-
 src/client/views/GestureOverlay.tsx                |  16 +-
 src/client/views/InkTranscription.scss             |   5 +
 src/client/views/InkTranscription.tsx              | 188 +++++++++++++++++++++
 src/client/views/MainView.tsx                      |   4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  19 ++-
 .../collectionFreeForm/MarqueeOptionsMenu.tsx      |  11 --
 .../collections/collectionFreeForm/MarqueeView.tsx |  16 +-
 src/client/views/nodes/EquationBox.scss            |  36 ++++
 src/client/views/nodes/EquationBox.tsx             |  91 +++++++++-
 src/fields/InkField.ts                             |   3 +-
 src/pen-gestures/GestureUtils.ts                   |   3 +-
 src/typings/index.d.ts                             |   1 +
 15 files changed, 444 insertions(+), 34 deletions(-)
 create mode 100644 src/client/views/InkTranscription.scss
 create mode 100644 src/client/views/InkTranscription.tsx

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

diff --git a/package-lock.json b/package-lock.json
index b1431e55b..1f8b1b8f6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4579,6 +4579,16 @@
             "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
             "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM="
         },
+        "clipboard": {
+            "version": "1.7.1",
+            "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-1.7.1.tgz",
+            "integrity": "sha1-Ng1taUbpmnof7zleQrqStem1oWs=",
+            "requires": {
+                "good-listener": "^1.2.2",
+                "select": "^1.1.2",
+                "tiny-emitter": "^2.0.0"
+            }
+        },
         "cliss": {
             "version": "0.0.2",
             "resolved": "https://registry.npmjs.org/cliss/-/cliss-0.0.2.tgz",
@@ -5307,6 +5317,11 @@
                 }
             }
         },
+        "crypto-js": {
+            "version": "3.3.0",
+            "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.3.0.tgz",
+            "integrity": "sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q=="
+        },
         "crypto-random-string": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
@@ -5885,6 +5900,11 @@
             "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
             "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
         },
+        "delegate": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+            "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+        },
         "delegates": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
@@ -7846,6 +7866,14 @@
                 "jquery": "*"
             }
         },
+        "good-listener": {
+            "version": "1.2.2",
+            "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+            "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
+            "requires": {
+                "delegate": "^3.1.2"
+            }
+        },
         "google-auth-library": {
             "version": "4.2.6",
             "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-4.2.6.tgz",
@@ -8462,6 +8490,29 @@
             "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
             "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk="
         },
+        "iink-js": {
+            "version": "1.5.4",
+            "resolved": "https://registry.npmjs.org/iink-js/-/iink-js-1.5.4.tgz",
+            "integrity": "sha512-WcjMmyXbSo4GY56AVVTKRyxlguh2KAGFyoH24+Zmm87ZWII12or2uLiovAyOK4IE+VVz7m0U5RMuv8i/RV/3Nw==",
+            "requires": {
+                "@babel/runtime": "^7.9.2",
+                "clipboard": "^1.7.1",
+                "crypto-js": "^3.3.0",
+                "d3-selection": "^1.4.1",
+                "json-css": "^1.5.6",
+                "lodash.merge": "^4.6.2",
+                "loglevel": "^1.6.8",
+                "perfect-scrollbar": "^1.5.0",
+                "uuid-js": "^0.7.5"
+            },
+            "dependencies": {
+                "d3-selection": {
+                    "version": "1.4.2",
+                    "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.2.tgz",
+                    "integrity": "sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg=="
+                }
+            }
+        },
         "image-data-uri": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/image-data-uri/-/image-data-uri-2.0.1.tgz",
@@ -9524,6 +9575,11 @@
             "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
             "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
         },
+        "json-css": {
+            "version": "1.5.6",
+            "resolved": "https://registry.npmjs.org/json-css/-/json-css-1.5.6.tgz",
+            "integrity": "sha1-65ZPg0ouTqobwvaY/12wB6JsfAA="
+        },
         "json-parse-better-errors": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
@@ -9996,8 +10052,7 @@
         "loglevel": {
             "version": "1.6.8",
             "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz",
-            "integrity": "sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==",
-            "dev": true
+            "integrity": "sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA=="
         },
         "loglevelnext": {
             "version": "1.0.5",
@@ -14793,6 +14848,11 @@
             "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
             "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
         },
+        "perfect-scrollbar": {
+            "version": "1.5.5",
+            "resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.5.5.tgz",
+            "integrity": "sha512-dzalfutyP3e/FOpdlhVryN4AJ5XDVauVWxybSkLZmakFE2sS3y3pc4JnSprw8tGmHvkaG5Edr5T7LBTZ+WWU2g=="
+        },
         "performance-now": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
@@ -16791,6 +16851,11 @@
             "resolved": "https://registry.npmjs.org/section-iterator/-/section-iterator-2.0.0.tgz",
             "integrity": "sha1-v0RNev7rlK1Dw5rS+yYVFifMuio="
         },
+        "select": {
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+            "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
+        },
         "select-hose": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@@ -18208,6 +18273,11 @@
             "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
             "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8="
         },
+        "tiny-emitter": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+            "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+        },
         "tiny-inflate": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
@@ -19185,6 +19255,11 @@
             "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
             "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
         },
+        "uuid-js": {
+            "version": "0.7.5",
+            "resolved": "https://registry.npmjs.org/uuid-js/-/uuid-js-0.7.5.tgz",
+            "integrity": "sha1-bIhtAqU9LUDc8l2RoXC0p7JblNA="
+        },
         "valid-url": {
             "version": "1.0.9",
             "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz",
diff --git a/package.json b/package.json
index 2c3e641dd..c8455c8a3 100644
--- a/package.json
+++ b/package.json
@@ -196,6 +196,7 @@
         "https": "^1.0.0",
         "https-browserify": "^1.0.0",
         "i": "^0.3.7",
+        "iink-js": "^1.5.4",
         "image-data-uri": "^2.0.1",
         "image-size": "^0.7.5",
         "image-size-stream": "^1.1.0",
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 9bcd23aa0..884cc781d 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -4,7 +4,7 @@ import { DateField } from "../../fields/DateField";
 import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Initializing, Opt, updateCachedAcls, WidthSym } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { HtmlField } from "../../fields/HtmlField";
-import { InkField } from "../../fields/InkField";
+import { InkField, PointData } from "../../fields/InkField";
 import { List } from "../../fields/List";
 import { ProxyField } from "../../fields/Proxy";
 import { RichTextField } from "../../fields/RichTextField";
@@ -738,7 +738,7 @@ export namespace Docs {
             return linkDoc;
         }
 
-        export function InkDocument(color: string, tool: string, strokeWidth: number, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) {
+        export function InkDocument(color: string, tool: string, strokeWidth: number, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: PointData[], options: DocumentOptions = {}) {
             const I = new Doc();
             I[Initializing] = true;
             I.type = DocumentType.INK;
@@ -1161,6 +1161,7 @@ export namespace DocUtils {
             created = Docs.Create.AudioDocument((field).url.href, resolved);
             layout = AudioBox.LayoutString;
         } else if (field instanceof InkField) {
+            console.log("Documents " + field.inkData)
             created = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), (field).inkData, resolved);
             layout = InkingStroke.LayoutString;
         } else if (field instanceof List && field[0] instanceof Doc) {
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 50dca0a99..16a9cfaeb 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -41,6 +41,7 @@ export class GestureOverlay extends Touchable {
     @observable private _menuY: number = -300;
     @observable private _pointerY?: number;
     @observable private _points: { X: number, Y: number }[] = [];
+    @observable private _timeStamps: number[] = [];
     @observable private _strokes: InkData[] = [];
     @observable private _palette?: JSX.Element;
     @observable private _clipboardDoc?: JSX.Element;
@@ -59,6 +60,7 @@ export class GestureOverlay extends Touchable {
     private pointerIdentifier?: number;
     private _hands: Map<number, React.Touch[]> = new Map<number, React.Touch[]>();
     private _holdTimer: NodeJS.Timeout | undefined;
+    private _strokeStartTime: number = 0;
 
     protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
 
@@ -418,6 +420,7 @@ export class GestureOverlay extends Touchable {
 
             this._strokes = [];
             this._points = [];
+            this._timeStamps = [];
             this._possibilities = [];
             document.removeEventListener("touchend", this.handleHandUp);
         }
@@ -493,6 +496,8 @@ export class GestureOverlay extends Touchable {
     onPointerDown = (e: React.PointerEvent) => {
         if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
             this._points.push({ X: e.clientX, Y: e.clientY });
+            this._timeStamps.push(0);
+            this._strokeStartTime = new Date().getTime();
             setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
             // if (CurrentUserUtils.SelectedTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)");
         }
@@ -501,6 +506,7 @@ export class GestureOverlay extends Touchable {
     @action
     onPointerMove = (e: PointerEvent) => {
         this._points.push({ X: e.clientX, Y: e.clientY });
+        this._timeStamps.push(new Date().getTime() - this._strokeStartTime);
 
         if (this._points.length > 1) {
             const B = this.svgBounds;
@@ -547,6 +553,7 @@ export class GestureOverlay extends Touchable {
             const points = this._points.map(p => ({ X: p.X - B.left, Y: p.Y - B.top }));
             //push first points to so interactionUtil knows pointer is up
             this._points.push({ X: this._points[0].X, Y: this._points[0].Y });
+            this._timeStamps.push(this._timeStamps[0]);
 
             const initialPoint = this._points[0.];
             const xInGlass = initialPoint.X > (this._thumbX ?? Number.MAX_SAFE_INTEGER) && initialPoint.X < (this._thumbX ?? Number.MAX_SAFE_INTEGER) + (this.height);
@@ -558,6 +565,7 @@ export class GestureOverlay extends Touchable {
                     case ToolglassTools.InkToText:
                         this._strokes.push(new Array(...this._points));
                         this._points = [];
+                        this._timeStamps = [];
                         CognitiveServices.Inking.Appliers.InterpretStrokes(this._strokes).then((results) => {
                             const wordResults = results.filter((r: any) => r.category === "line");
                             const possibilities: string[] = [];
@@ -581,6 +589,7 @@ export class GestureOverlay extends Touchable {
                     case ToolglassTools.IgnoreGesture:
                         this.dispatchGesture(GestureUtils.Gestures.Stroke);
                         this._points = [];
+                        this._timeStamps = [];
                         break;
                 }
             }
@@ -589,6 +598,7 @@ export class GestureOverlay extends Touchable {
                 this.makePolygon(this.InkShape, false);
                 this.dispatchGesture(GestureUtils.Gestures.Stroke);
                 this._points = [];
+                this._timeStamps = [];
                 if (!CollectionFreeFormViewChrome.Instance?._keepPrimitiveMode) {
                     this.InkShape = "";
                     Doc.UserDoc().activeInkTool = InkTool.None;
@@ -635,9 +645,11 @@ export class GestureOverlay extends Touchable {
                     this.dispatchGesture(GestureUtils.Gestures.Stroke);
                 }
                 this._points = [];
+                this._timeStamps = [];
             }
         } else {
             this._points = [];
+            this._timeStamps = [];
         }
         CollectionFreeFormViewChrome.Instance?.primCreated();
     }
@@ -686,6 +698,7 @@ export class GestureOverlay extends Touchable {
             }
         }
         this._points = [];
+        this._timeStamps = [];
         switch (shape) {
             //must push an extra point in the end so InteractionUtils knows pointer is up.
             //must be (points[0].X,points[0]-1)
@@ -808,7 +821,8 @@ export class GestureOverlay extends Touchable {
                         points: stroke ?? this._points,
                         gesture: gesture as any,
                         bounds: this.getBounds(stroke ?? this._points),
-                        text: data
+                        text: data,
+                        times: this._timeStamps
                     }
                 }
             )
diff --git a/src/client/views/InkTranscription.scss b/src/client/views/InkTranscription.scss
new file mode 100644
index 000000000..bbb0a1afa
--- /dev/null
+++ b/src/client/views/InkTranscription.scss
@@ -0,0 +1,5 @@
+.ink-transcription {
+    .error-msg {
+        display: none !important;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
new file mode 100644
index 000000000..950f622dd
--- /dev/null
+++ b/src/client/views/InkTranscription.tsx
@@ -0,0 +1,188 @@
+import { timeStamp } from 'console';
+import * as iink from 'iink-js';
+import { action, observable } from 'mobx';
+import * as React from 'react';
+import { Doc } from '../../fields/Doc';
+import { InkData, InkField } from "../../fields/InkField";
+import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { Docs } from "../documents/Documents";
+import './InkTranscription.scss';
+
+export class InkTranscription extends React.Component {
+    static Instance: InkTranscription;
+
+    @observable _mathRegister: any;
+    @observable _mathRef: any;
+    @observable _textRegister: any;
+    @observable _textRef: any;
+    private addDocument?: (doc: Doc | Doc[]) => boolean;
+    private bounds: { x: number, y: number, width: number, height: number } = { x: 0, y: 0, width: 0, height: 0 };
+
+    constructor(props: Readonly<{}>) {
+        super(props);
+
+        InkTranscription.Instance = this;
+    }
+
+    componentWillUnmount() {
+        this._mathRef.removeEventListener('exported', (e: any) => this.exportInk(e, this._mathRef));
+        this._textRef.removeEventListener('exported', (e: any) => this.exportInk(e, this._textRef));
+
+        this._mathRef.removeEventListener('changed', (e: any) => this.callExport(this._mathRef));
+        this._textRef.removeEventListener('changed', (e: any) => this.callExport(this._textRef));
+    }
+
+    @action
+    setMathRef = (r: any) => {
+        if (!this._mathRegister) {
+            this._mathRegister = r ? iink.register(r, {
+                recognitionParams: {
+                    type: 'MATH',
+                    protocol: 'WEBSOCKET',
+                    server: {
+                        host: 'cloud.myscript.com',
+                        applicationKey: '7277ec34-0c2e-4ee1-9757-ccb657e3f89f',
+                        hmacKey: 'f5cb18f2-1f95-4ddb-96ac-3f7c888dffc1',
+                        websocket: {
+                            pingEnabled: false,
+                            autoReconnect: true
+                        }
+                    },
+                    iink: {
+                        math: {
+                            mimeTypes: ['application/x-latex', 'application/vnd.myscript.jiix']
+                        },
+                        export: {
+                            jiix: {
+                                strokes: true
+                            }
+                        }
+                    }
+                },
+                triggers: {
+                    exportContent: 'DEMAND'
+                }
+            }) : null;
+        }
+
+        r.addEventListener('exported', (e: any) => this.exportInk(e, this._mathRef));
+        r.addEventListener('changed', (e: any) => this.callExport(this._mathRef));
+
+        return this._mathRef = r;
+    }
+
+    @action
+    setTextRef = (r: any) => {
+        if (!this._textRegister) {
+            this._textRegister = r ? iink.register(r, {
+                recognitionParams: {
+                    type: 'TEXT',
+                    protocol: 'WEBSOCKET',
+                    server: {
+                        host: 'cloud.myscript.com',
+                        applicationKey: '7277ec34-0c2e-4ee1-9757-ccb657e3f89f',
+                        hmacKey: 'f5cb18f2-1f95-4ddb-96ac-3f7c888dffc1',
+                        websocket: {
+                            pingEnabled: false,
+                            autoReconnect: true
+                        }
+                    },
+                    iink: {
+                        text: {
+                            mimeTypes: ['text/plain']
+                        },
+                        export: {
+                            jiix: {
+                                strokes: true
+                            }
+                        }
+                    }
+                },
+                triggers: {
+                    exportContent: 'DEMAND'
+                }
+            }) : null;
+        }
+
+        r.addEventListener('exported', (e: any) => this.exportInk(e, this._textRef));
+        r.addEventListener('changed', (e: any) => this.callExport(this._textRef));
+
+        return this._textRef = r;
+    }
+
+    transcribeInk = (inkdocs: Doc[], math: boolean, bounds: { x: number, y: number, width: number, height: number }, addDocument?: (doc: Doc | Doc[]) => boolean) => {
+        const strokes: InkData[] = [];
+        inkdocs.filter(i => Cast(i.data, InkField)).forEach(i => {
+            // TODO: interpolate missing times stamps
+            const d = Cast(i.data, InkField, null);
+            const left = Math.min(...d?.inkData.map(pd => pd.X) ?? [0]);
+            const top = Math.min(...d?.inkData.map(pd => pd.Y) ?? [0]);
+            strokes.push(d.inkData.map(pd => ({ X: pd.X + NumCast(i.x) - left, Y: pd.Y + NumCast(i.y) - top, time: pd.time })));
+        });
+
+        this.addDocument = addDocument;
+        this.bounds = bounds;
+
+        const pointerData = { "events": strokes.map(stroke => this.inkJSON(stroke)) };
+        // console.log(JSON.stringify(pointerData));
+        const processGestures = false;
+
+        if (math) {
+            this._mathRef.editor.pointerEvents(pointerData, processGestures);
+        }
+        else {
+            this._textRef.editor.pointerEvents(pointerData, processGestures);
+        }
+    }
+
+    callExport(ref: any) {
+        if (ref.editor.canExport) {
+            ref.editor.export_();
+        }
+    }
+
+    inkJSON = (stroke: InkData) => {
+        return {
+            "pointerType": "PEN",
+            "pointerId": 1,
+            "x": stroke.map(point => point.X),
+            "y": stroke.map(point => point.Y),
+            "t": stroke.map(point => point.time),
+            "p": new Array(stroke.length).fill(1.0)
+        };
+    }
+
+    exportInk = (e: any, ref: any) => {
+        const exports = e.detail.exports;
+        if (exports) {
+            if (exports['application/x-latex']) {
+                const latex = exports['application/x-latex'];
+                console.log(latex);
+
+                this.addDocument?.(Docs.Create.EquationDocument({ title: latex, x: this.bounds.width, y: 0, _width: this.bounds.width, _height: this.bounds.height }));
+            }
+            else if (exports['text/plain']) {
+                const text = exports['text/plain'];
+                console.log(text);
+                this.addDocument?.(Docs.Create.TextDocument(text, { title: text, x: this.bounds.width, y: 0, _width: this.bounds.width, _height: this.bounds.height }));
+            }
+
+            ref.editor.clear();
+        }
+    }
+
+    render() {
+        return (
+            <div className="ink-transcription">
+                <div className='math-editor'
+                    ref={this.setMathRef}
+                    touch-action="none">
+                </div>
+                <div className='text-editor'
+                    ref={this.setTextRef}
+                    touch-action="none">
+                </div>
+            </div>
+        )
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 8c0795881..84c1037dd 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -44,6 +44,7 @@ import { GestureOverlay } from './GestureOverlay';
 import { DASHBOARD_SELECTOR_HEIGHT, LEFT_MENU_WIDTH } from './global/globalCssVariables.scss';
 import { Colors } from './global/globalEnums';
 import { KeyManager } from './GlobalKeyHandler';
+import { InkTranscription } from './InkTranscription';
 import { LightboxView } from './LightboxView';
 import { LinkMenu } from './linking/LinkMenu';
 import "./MainView.scss";
@@ -184,7 +185,7 @@ export class MainView extends React.Component {
         fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
         fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
         fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines,
-        fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt]);
+        fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt, fa.faSquareRootAlt]);
         this.initAuthenticationRouters();
     }
 
@@ -636,6 +637,7 @@ export class MainView extends React.Component {
             <OverlayView />
             <TimelineMenu />
             <RichTextMenu />
+            <InkTranscription />
             {this.snapLines}
             <div className="mainView-webRef" ref={this.makeWebRef} />
             <LightboxView PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} />
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index e2ea81392..7d60387de 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -50,6 +50,7 @@ import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCurso
 import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
+import { InkTranscription } from "../../InkTranscription";
 
 export const panZoomSchema = createSchema({
     _panX: "number",
@@ -492,7 +493,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             case GestureUtils.Gestures.Stroke:
                 const points = ge.points;
                 const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
-                const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points,
+                const times = ge.times;
+                const strokes: PointData[] = !times || times.length == 0 ? points : points.map((pt, i) => { return { X: pt.X, Y: pt.Y, time: times[i] } });
+                const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), strokes,
                     { title: "ink stroke", x: B.x - ActiveInkWidth() / 2, y: B.y - ActiveInkWidth() / 2, _width: B.width + ActiveInkWidth(), _height: B.height + ActiveInkWidth() });
                 this.addDocument(inkDoc);
                 e.stopPropagation();
@@ -1477,6 +1480,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         appearanceItems.push({ description: `${this.fitToContent ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
         this.props.ContainingCollectionView &&
             appearanceItems.push({ description: "Ungroup collection", event: this.promoteCollection, icon: "table" });
+
+        this.props.Document._isGroup && this.childDocs.filter(s => s.type === DocumentType.INK).length > 0 && appearanceItems.push({ description: "Ink to text", event: () => this.transcribeStrokes(false), icon: "font" });
+
+        this.props.Document._isGroup && this.childDocs.filter(s => s.type === DocumentType.INK).length > 0 && appearanceItems.push({ description: "Ink to math", event: () => this.transcribeStrokes(true), icon: "square-root-alt" });
+
         !Doc.UserDoc().noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null;
         !appearance && ContextMenu.Instance.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" });
 
@@ -1536,6 +1544,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         input.click();
     }
 
+    @undoBatch
+    @action
+    transcribeStrokes = (math: boolean) => {
+        if (this.props.Document._isGroup) {
+            const inkdocs = this.childDocs.filter(s => s.type === DocumentType.INK);
+            InkTranscription.Instance.transcribeInk(inkdocs, math, { x: NumCast(this.layoutDoc.x) ?? 0, y: NumCast(this.layoutDoc.y) ?? 0, width: NumCast(this.layoutDoc._width) ?? 0, height: NumCast(this.layoutDoc._height) ?? 0 }, this.addDocument);
+        }
+    }
+
     @action
     setupDragLines = (snapToDraggedDoc: boolean = false) => {
         const activeDocs = this.getActiveDocuments();
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 1f59f9732..8a8b528f6 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -14,7 +14,6 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
     public createCollection: (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean) => void = unimplementedFunction;
     public delete: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
     public summarize: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
-    public inkToText: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
     public showMarquee: () => void = unimplementedFunction;
     public hideMarquee: () => void = unimplementedFunction;
     public pinWithView: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
@@ -64,16 +63,6 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
                 </button>
             </Tooltip>,
         ];
-        if (false && !SelectionManager.Views().some(v => v.props.Document.type !== DocumentType.INK)) {
-            buttons.push(
-                <Tooltip key="inkToText" title={<div className="dash-tooltip">Change to Text</div>} placement="bottom">
-                    <button
-                        className="antimodeMenu-button"
-                        onPointerDown={this.inkToText}>
-                        <FontAwesomeIcon icon="font" size="lg" />
-                    </button>
-                </Tooltip>);
-        }
         return this.getElement(buttons);
     }
 }
\ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index b10b0912f..a828cd981 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,14 +1,15 @@
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
-import { AclAugment, AclAdmin, AclEdit, DataSym, Doc, Opt } from "../../../../fields/Doc";
+import { AclAdmin, AclAugment, AclEdit, DataSym, Doc, Opt } from "../../../../fields/Doc";
 import { Id } from "../../../../fields/FieldSymbols";
 import { InkData, InkField, InkTool } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
 import { RichTextField } from "../../../../fields/RichTextField";
 import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
 import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
+import { ImageField } from "../../../../fields/URLField";
 import { GetEffectiveAcl } from "../../../../fields/util";
-import { Utils, intersectRect, returnFalse } from "../../../../Utils";
+import { intersectRect, returnFalse, Utils } from "../../../../Utils";
 import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
 import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents";
 import { DocumentType } from "../../../documents/DocumentTypes";
@@ -21,18 +22,17 @@ import { ContextMenu } from "../../ContextMenu";
 import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
 import { PresBox } from "../../nodes/trails/PresBox";
 import { PresMovement } from "../../nodes/trails/PresEnums";
+import { VideoBox } from "../../nodes/VideoBox";
+import { pasteImageBitmap } from "../../nodes/WebBoxRenderer";
 import { PreviewCursor } from "../../PreviewCursor";
+import { StyleLayers } from "../../StyleProvider";
 import { CollectionDockingView } from "../CollectionDockingView";
 import { SubCollectionViewProps } from "../CollectionSubView";
 import { CollectionView } from "../CollectionView";
+import { TreeView } from "../TreeView";
 import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
 import "./MarqueeView.scss";
 import React = require("react");
-import { StyleLayers } from "../../StyleProvider";
-import { TreeView } from "../TreeView";
-import { VideoBox } from "../../nodes/VideoBox";
-import { ImageField, WebField } from "../../../../fields/URLField";
-import { pasteImageBitmap } from "../../nodes/WebBoxRenderer";
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -265,7 +265,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             MarqueeOptionsMenu.Instance.createCollection = this.collection;
             MarqueeOptionsMenu.Instance.delete = this.delete;
             MarqueeOptionsMenu.Instance.summarize = this.summary;
-            MarqueeOptionsMenu.Instance.inkToText = this.syntaxHighlight;
+            // MarqueeOptionsMenu.Instance.inkToText = this.syntaxHighlight;
             MarqueeOptionsMenu.Instance.showMarquee = this.showMarquee;
             MarqueeOptionsMenu.Instance.hideMarquee = this.hideMarquee;
             MarqueeOptionsMenu.Instance.jumpTo(e.clientX, e.clientY);
diff --git a/src/client/views/nodes/EquationBox.scss b/src/client/views/nodes/EquationBox.scss
index 6c9d53d10..229a1a485 100644
--- a/src/client/views/nodes/EquationBox.scss
+++ b/src/client/views/nodes/EquationBox.scss
@@ -1,3 +1,39 @@
+@import "../global/globalCssVariables.scss";
+
 .equationBox-cont {
     transform-origin: top left;
+    overflow: visible;
+    width: 100%;
+    height: 100%;
+}
+
+.button {
+    position: absolute;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    top: 0;
+    right: 0;
+    width: 20px;
+    height: 20px;
+    border-radius: 5px;
+    background: $dark-gray;
+    color: white;
+
+    svg {
+        width: 12px;
+        height: 12px;
+    }
+}
+
+.ink-editor {
+    top: 20px;
+    min-width: 500px;
+    min-height: 300px;
+    background: $light-gray;
+    pointer-events: all;
+
+    button {
+        float: right;
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index c170f9867..c0c4fab09 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -1,5 +1,7 @@
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import EquationEditor from 'equation-editor-react';
-import { action, reaction } from 'mobx';
+import * as iink from 'iink-js';
+import { action, observable, reaction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { WidthSym } from '../../../fields/Doc';
@@ -12,12 +14,16 @@ import { LightboxView } from '../LightboxView';
 import './EquationBox.scss';
 import { FieldView, FieldViewProps } from './FieldView';
 
-
 @observer
 export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(EquationBox, fieldKey); }
     public static SelectOnLoad: string = "";
+
     _ref: React.RefObject<EquationEditor> = React.createRef();
+    @observable _inkOpen = false;
+    @observable _inkEditor: any;
+    @observable _inkRef: any;
+
     componentDidMount() {
         if (EquationBox.SelectOnLoad === this.rootDoc[Id] && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
             this.props.select(false);
@@ -39,7 +45,11 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 }
             }, { fireImmediately: true });
     }
-    plot: any;
+
+    componentWillUnmount() {
+        this._inkRef.removeEventListener('exported', this.exportInk);
+    }
+
     @action
     keyPressed = (e: KeyboardEvent) => {
         const _height = Number(getComputedStyle(this._ref.current!.element.current).height.replace("px", ""));
@@ -65,6 +75,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
         if (e.key === "Backspace" && !this.dataDoc.text) this.props.removeDocument?.(this.rootDoc);
     }
+
     onChange = (str: string) => {
         this.dataDoc.text = str;
         const style = this._ref.current && getComputedStyle(this._ref.current.element.current);
@@ -75,6 +86,58 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.layoutDoc._height = Math.max(25, _height);
         }
     }
+
+    @action
+    toggleInk = (e: React.PointerEvent) => {
+        e.stopPropagation();
+
+        this._inkOpen = !this._inkOpen;
+
+        if (!this._inkEditor) {
+            this._inkEditor = this._inkRef ? iink.register(this._inkRef, {
+                recognitionParams: {
+                    type: 'MATH',
+                    protocol: 'WEBSOCKET',
+                    server: {
+                        host: 'cloud.myscript.com',
+                        applicationKey: '7277ec34-0c2e-4ee1-9757-ccb657e3f89f',
+                        hmacKey: 'f5cb18f2-1f95-4ddb-96ac-3f7c888dffc1',
+                    },
+                    iink: {
+                        math: {
+                            mimeTypes: ['application/x-latex', 'application/vnd.myscript.jiix']
+                        },
+                        export: {
+                            jiix: {
+                                strokes: true
+                            }
+                        }
+                    }
+                }
+            }) : null;
+
+            this._inkRef.addEventListener('exported', this.exportInk)
+        }
+    }
+
+    convertInk = (e: React.MouseEvent) => {
+        this._inkRef.editor.convert();
+    }
+
+    clearInk = (e: React.MouseEvent) => {
+        this._inkRef.editor.clear();
+        this.onChange("");
+    }
+
+    exportInk = (e: any) => {
+        const exports = e.detail.exports;
+        if (exports && exports['application/x-latex']) {
+            this.onChange(exports['application/x-latex']);
+            console.log(JSON.parse(exports['application/vnd.myscript.jiix']).expressions[0].items[0]);
+        }
+    }
+
+
     render() {
         TraceMobx();
         const scale = (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
@@ -82,18 +145,34 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
             onPointerDown={e => !e.ctrlKey && e.stopPropagation()}
             style={{
                 transform: `scale(${scale})`,
-                width: `${100 / scale}%`,
+                width: `calc(${100 / scale}% + 25px)`,
                 height: `${100 / scale}%`,
                 pointerEvents: !this.props.isSelected() ? "none" : undefined,
             }}
-            onKeyDown={e => e.stopPropagation()}
-        >
+            onKeyDown={e => e.stopPropagation()}>
+
             <EquationEditor ref={this._ref}
                 value={this.dataDoc.text || "x"}
                 spaceBehavesLikeTab={true}
                 onChange={this.onChange}
                 autoCommands="pi theta sqrt sum prod alpha beta gamma rho"
                 autoOperatorNames="sin cos tan" />
+
+            <div className="button"
+                style={{ display: this.props.isContentActive() ? "flex" : "none" }}
+                onPointerDown={this.toggleInk}>
+                <FontAwesomeIcon icon="pencil-alt" />
+            </div>
+
+            <div className='ink-editor'
+                ref={action((r: any) => this._inkRef = r)}
+                id="editor" onPointerDown={(e) => e.stopPropagation()}
+                touch-action="none"
+                style={{ display: this._inkOpen && this.props.isContentActive() ? "block" : "none" }}>
+                <button onClick={this.convertInk}>convert</button>
+                <button onClick={this.clearInk}>clear</button>
+            </div>
+
         </div>);
     }
 }
\ No newline at end of file
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index 31024e805..48271d3d3 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -19,6 +19,7 @@ export enum InkTool {
 export interface PointData {
     X: number;
     Y: number;
+    time?: number;
 }
 
 export type Segment = Array<Bezier>;
@@ -83,7 +84,7 @@ export class InkField extends ObjectField {
     }
 
     [ToScriptString]() {
-        return "new InkField([" + this.inkData.map(i => `{X: ${i.X}, Y: ${i.Y}} `) + "])";
+        return "new InkField([" + this.inkData.map(i => `{X: ${i.X}, Y: ${i.Y}, time: ${i.time || 0}} `) + "])";
     }
     [ToString]() {
         return "InkField";
diff --git a/src/pen-gestures/GestureUtils.ts b/src/pen-gestures/GestureUtils.ts
index 65f2bf80c..cf59cb3c6 100644
--- a/src/pen-gestures/GestureUtils.ts
+++ b/src/pen-gestures/GestureUtils.ts
@@ -8,7 +8,8 @@ export namespace GestureUtils {
             readonly gesture: Gestures,
             readonly points: PointData[],
             readonly bounds: Rect,
-            readonly text?: any
+            readonly text?: any,
+            readonly times?: number[]
         ) { }
     }
 
diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts
index 068ac2159..bc7727426 100644
--- a/src/typings/index.d.ts
+++ b/src/typings/index.d.ts
@@ -8,6 +8,7 @@ declare module 'webrtc-adapter';
 declare module 'bezier-curve';
 declare module 'fit-curve';
 declare module 'react-audio-waveform';
+declare module 'iink-js';
 
 declare module 'reveal';
 declare module 'react-reveal';
-- 
cgit v1.2.3-70-g09d2


From 98567999b9882ed093bb0d750504a1bd10aa9a94 Mon Sep 17 00:00:00 2001
From: Aubrey Li <Aubrey-Li>
Date: Tue, 12 Apr 2022 19:21:53 -0400
Subject: revert presbox & treeview onclick focus

---
 package-lock.json                             | 399 +-------------------------
 src/client/util/CurrentUserUtils.ts           |   2 +-
 src/client/util/DocumentManager.ts            |   4 +-
 src/client/views/MainView.tsx                 |   2 +-
 src/client/views/collections/TreeView.tsx     |  94 +++++-
 src/client/views/nodes/button/FontIconBox.tsx |   1 +
 6 files changed, 99 insertions(+), 403 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index 0bfe79f5a..63d1b9d1e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -158,15 +158,9 @@
             }
         },
         "@babel/runtime-corejs3": {
-<<<<<<< HEAD
             "version": "7.17.7",
             "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.7.tgz",
             "integrity": "sha512-TvliGJjhxis5m7xIMvlXH/xG8Oa/LK0SCUCyfKD6nLi42n5fB4WibDJ0g9trmmBB6hwpMNx+Lzbxy9/4gpMaVw==",
-=======
-            "version": "7.17.2",
-            "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.2.tgz",
-            "integrity": "sha512-NcKtr2epxfIrNM4VOmPKO46TvDMCBhgi2CrSHaEarrz+Plk2K5r9QemmOFTGpZaoKnWoGH5MO+CzeRsih/Fcgg==",
->>>>>>> master
             "requires": {
                 "core-js-pure": "^3.20.2",
                 "regenerator-runtime": "^0.13.4"
@@ -229,15 +223,9 @@
             }
         },
         "@discoveryjs/json-ext": {
-<<<<<<< HEAD
             "version": "0.5.7",
             "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
             "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw=="
-=======
-            "version": "0.5.6",
-            "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz",
-            "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA=="
->>>>>>> master
         },
         "@emotion/babel-plugin": {
             "version": "11.7.2",
@@ -272,15 +260,9 @@
                     "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw=="
                 },
                 "@babel/runtime": {
-<<<<<<< HEAD
                     "version": "7.17.7",
                     "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz",
                     "integrity": "sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==",
-=======
-                    "version": "7.17.2",
-                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
-                    "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
->>>>>>> master
                     "requires": {
                         "regenerator-runtime": "^0.13.4"
                     }
@@ -317,15 +299,9 @@
                     "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
                 },
                 "csstype": {
-<<<<<<< HEAD
                     "version": "3.0.11",
                     "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
                     "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
-=======
-                    "version": "3.0.10",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
-                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
->>>>>>> master
                 },
                 "escape-string-regexp": {
                     "version": "4.0.0",
@@ -422,15 +398,9 @@
                     "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
                 },
                 "csstype": {
-<<<<<<< HEAD
                     "version": "3.0.11",
                     "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
                     "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
-=======
-                    "version": "3.0.10",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
-                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
->>>>>>> master
                 },
                 "stylis": {
                     "version": "4.0.13",
@@ -458,39 +428,23 @@
             "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw=="
         },
         "@emotion/react": {
-<<<<<<< HEAD
             "version": "11.8.2",
             "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.8.2.tgz",
             "integrity": "sha512-+1bcHBaNJv5nkIIgnGKVsie3otS0wF9f1T1hteF3WeVvMNQEtfZ4YyFpnphGoot3ilU/wWMgP2SgIDuHLE/wAA==",
-=======
-            "version": "11.8.1",
-            "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.8.1.tgz",
-            "integrity": "sha512-XGaie4nRxmtP1BZYBXqC5JGqMYF2KRKKI7vjqNvQxyRpekVAZhb6QqrElmZCAYXH1L90lAelADSVZC4PFsrJ8Q==",
->>>>>>> master
             "requires": {
                 "@babel/runtime": "^7.13.10",
                 "@emotion/babel-plugin": "^11.7.1",
                 "@emotion/cache": "^11.7.1",
                 "@emotion/serialize": "^1.0.2",
-<<<<<<< HEAD
-=======
-                "@emotion/sheet": "^1.1.0",
->>>>>>> master
                 "@emotion/utils": "^1.1.0",
                 "@emotion/weak-memoize": "^0.2.5",
                 "hoist-non-react-statics": "^3.3.1"
             },
             "dependencies": {
                 "@babel/runtime": {
-<<<<<<< HEAD
                     "version": "7.17.7",
                     "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz",
                     "integrity": "sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==",
-=======
-                    "version": "7.17.2",
-                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
-                    "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
->>>>>>> master
                     "requires": {
                         "regenerator-runtime": "^0.13.4"
                     }
@@ -530,15 +484,9 @@
                     "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
                 },
                 "csstype": {
-<<<<<<< HEAD
                     "version": "3.0.11",
                     "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
                     "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
-=======
-                    "version": "3.0.10",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
-                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
->>>>>>> master
                 },
                 "stylis": {
                     "version": "4.0.13",
@@ -577,15 +525,9 @@
             },
             "dependencies": {
                 "@babel/runtime": {
-<<<<<<< HEAD
                     "version": "7.17.7",
                     "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz",
                     "integrity": "sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==",
-=======
-                    "version": "7.17.2",
-                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
-                    "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
->>>>>>> master
                     "requires": {
                         "regenerator-runtime": "^0.13.4"
                     }
@@ -616,15 +558,9 @@
                     "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
                 },
                 "csstype": {
-<<<<<<< HEAD
                     "version": "3.0.11",
                     "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
                     "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
-=======
-                    "version": "3.0.10",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
-                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
->>>>>>> master
                 }
             }
         },
@@ -707,15 +643,9 @@
             }
         },
         "@fortawesome/react-fontawesome": {
-<<<<<<< HEAD
             "version": "0.1.18",
             "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.18.tgz",
             "integrity": "sha512-RwLIB4TZw0M9gvy5u+TusAA0afbwM4JQIimNH/j3ygd6aIvYPQLqXMhC9ErY26J23rDPyDZldIfPq/HpTTJ/tQ==",
-=======
-            "version": "0.1.17",
-            "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.17.tgz",
-            "integrity": "sha512-dX43Z5IvMaW7fwzU8farosYjKNGfRb2HB/DgjVBHeJZ/NSnuuaujPPx0YOdcAq+n3mqn70tyCde2HM1mqbhiuw==",
->>>>>>> master
             "requires": {
                 "prop-types": "^15.8.1"
             },
@@ -786,15 +716,9 @@
             }
         },
         "@hig/theme-data": {
-<<<<<<< HEAD
             "version": "2.24.0",
             "resolved": "https://registry.npmjs.org/@hig/theme-data/-/theme-data-2.24.0.tgz",
             "integrity": "sha512-/71T3kezuom7z60Bxyxwxo0oHqb2B59jCbdWrgRgUYorK0vO6zjFmQvRmPSgxq2i1bQxQLx12iuzW9cM494pMQ==",
-=======
-            "version": "2.23.1",
-            "resolved": "https://registry.npmjs.org/@hig/theme-data/-/theme-data-2.23.1.tgz",
-            "integrity": "sha512-Yca1iCIs8U1dpkLW1VNiKOJW9fCJdb70f1dc61+R+HO1QJ8jMBu5eOpmQE1NKotJmcazhsXpo6xC11yV1crE4w==",
->>>>>>> master
             "requires": {
                 "tinycolor2": "^1.4.1"
             }
@@ -867,30 +791,11 @@
                         "readable-stream": "^3.6.0"
                     }
                 },
-<<<<<<< HEAD
-=======
-                "chownr": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
-                    "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
-                },
->>>>>>> master
                 "emoji-regex": {
                     "version": "8.0.0",
                     "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
                     "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
                 },
-<<<<<<< HEAD
-=======
-                "fs-minipass": {
-                    "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
-                    "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
-                    "requires": {
-                        "minipass": "^3.0.0"
-                    }
-                },
->>>>>>> master
                 "gauge": {
                     "version": "3.0.2",
                     "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
@@ -935,31 +840,6 @@
                         }
                     }
                 },
-<<<<<<< HEAD
-=======
-                "minipass": {
-                    "version": "3.1.6",
-                    "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz",
-                    "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==",
-                    "requires": {
-                        "yallist": "^4.0.0"
-                    }
-                },
-                "minizlib": {
-                    "version": "2.1.2",
-                    "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
-                    "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
-                    "requires": {
-                        "minipass": "^3.0.0",
-                        "yallist": "^4.0.0"
-                    }
-                },
-                "mkdirp": {
-                    "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
-                    "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
-                },
->>>>>>> master
                 "node-fetch": {
                     "version": "2.6.7",
                     "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
@@ -968,17 +848,6 @@
                         "whatwg-url": "^5.0.0"
                     }
                 },
-<<<<<<< HEAD
-=======
-                "nopt": {
-                    "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
-                    "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
-                    "requires": {
-                        "abbrev": "1"
-                    }
-                },
->>>>>>> master
                 "npmlog": {
                     "version": "5.0.1",
                     "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
@@ -1016,22 +885,6 @@
                         "ansi-regex": "^5.0.1"
                     }
                 },
-<<<<<<< HEAD
-=======
-                "tar": {
-                    "version": "6.1.11",
-                    "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
-                    "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
-                    "requires": {
-                        "chownr": "^2.0.0",
-                        "fs-minipass": "^2.0.0",
-                        "minipass": "^3.0.0",
-                        "minizlib": "^2.1.1",
-                        "mkdirp": "^1.0.3",
-                        "yallist": "^4.0.0"
-                    }
-                },
->>>>>>> master
                 "tr46": {
                     "version": "0.0.3",
                     "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
@@ -1078,15 +931,9 @@
             },
             "dependencies": {
                 "csstype": {
-<<<<<<< HEAD
                     "version": "3.0.11",
                     "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
                     "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
-=======
-                    "version": "3.0.10",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
-                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
->>>>>>> master
                 },
                 "dom-helpers": {
                     "version": "5.2.1",
@@ -1200,15 +1047,9 @@
             },
             "dependencies": {
                 "@babel/runtime": {
-<<<<<<< HEAD
                     "version": "7.17.7",
                     "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz",
                     "integrity": "sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==",
-=======
-                    "version": "7.17.2",
-                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
-                    "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
->>>>>>> master
                     "requires": {
                         "regenerator-runtime": "^0.13.4"
                     }
@@ -1228,15 +1069,6 @@
             "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
             "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="
         },
-<<<<<<< HEAD
-=======
-        "@socket.io/component-emitter": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz",
-            "integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==",
-            "dev": true
-        },
->>>>>>> master
         "@szmarczak/http-timer": {
             "version": "5.0.1",
             "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz",
@@ -1383,19 +1215,11 @@
             }
         },
         "@types/bson": {
-<<<<<<< HEAD
             "version": "4.0.2",
             "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.2.tgz",
             "integrity": "sha512-+uWmsejEHfmSjyyM/LkrP0orfE2m5Mx9Xel4tXNeqi1ldK5XMQcDsFkBmLDtuyKUbxj2jGDo0H240fbCRJZo7Q==",
             "requires": {
                 "@types/node": "*"
-=======
-            "version": "4.2.0",
-            "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.2.0.tgz",
-            "integrity": "sha512-ELCPqAdroMdcuxqwMgUpifQyRoTpyYCNr1V9xKyF40VsBobsj+BbWNRvwGchMgBPGqkw655ypkjj2MEF5ywVwg==",
-            "requires": {
-                "bson": "*"
->>>>>>> master
             }
         },
         "@types/cacheable-request": {
@@ -1445,15 +1269,9 @@
             "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
         },
         "@types/connect": {
-<<<<<<< HEAD
             "version": "3.4.35",
             "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
             "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
-=======
-            "version": "3.4.33",
-            "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
-            "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
->>>>>>> master
             "dev": true,
             "requires": {
                 "@types/node": "*"
@@ -1728,15 +1546,9 @@
             }
         },
         "@types/json-schema": {
-<<<<<<< HEAD
-            "version": "7.0.10",
-            "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.10.tgz",
-            "integrity": "sha512-BLO9bBq59vW3fxCpD4o0N4U+DXsvwvIcl+jofw0frQo/GrBFC+/jRZj1E7kgp6dvTyNmA4y6JCV5Id/r3mNP5A=="
-=======
             "version": "7.0.9",
             "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
             "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ=="
->>>>>>> master
         },
         "@types/keygrip": {
             "version": "1.0.2",
@@ -1745,15 +1557,9 @@
             "dev": true
         },
         "@types/keyv": {
-<<<<<<< HEAD
-            "version": "3.1.4",
-            "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
-            "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
-=======
             "version": "3.1.3",
             "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz",
             "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==",
->>>>>>> master
             "requires": {
                 "@types/node": "*"
             }
@@ -1768,15 +1574,9 @@
             }
         },
         "@types/lodash": {
-<<<<<<< HEAD
-            "version": "4.14.180",
-            "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.180.tgz",
-            "integrity": "sha512-XOKXa1KIxtNXgASAnwj7cnttJxS4fksBRywK/9LzRV5YxrF80BXZIGeQSuoESQ/VkUj30Ae0+YcuHc15wJCB2g==",
-=======
             "version": "4.14.179",
             "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.179.tgz",
             "integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w==",
->>>>>>> master
             "dev": true
         },
         "@types/mime": {
@@ -1810,10 +1610,7 @@
             "version": "3.6.20",
             "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz",
             "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==",
-<<<<<<< HEAD
             "dev": true,
-=======
->>>>>>> master
             "requires": {
                 "@types/bson": "*",
                 "@types/node": "*"
@@ -1929,15 +1726,9 @@
             }
         },
         "@types/prop-types": {
-<<<<<<< HEAD
-            "version": "15.7.3",
-            "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
-            "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw=="
-=======
             "version": "15.7.4",
             "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
             "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ=="
->>>>>>> master
         },
         "@types/prosemirror-commands": {
             "version": "1.0.4",
@@ -2076,15 +1867,9 @@
             }
         },
         "@types/react": {
-<<<<<<< HEAD
-            "version": "16.14.24",
-            "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.24.tgz",
-            "integrity": "sha512-e7U2WC8XQP/xfR7bwhOhNFZKPTfW1ph+MiqtudKb8tSV8RyCsovQx2sNVtKoOryjxFKpHPPC/yNiGfdeVM5Gyw==",
-=======
             "version": "16.14.23",
             "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.23.tgz",
             "integrity": "sha512-WngBZLuSkP4IAgPi0HOsGCHo6dn3CcuLQnCfC17VbA7YBgipZiZoTOhObwl/93DsFW0Y2a/ZXeonpW4DxirEJg==",
->>>>>>> master
             "requires": {
                 "@types/prop-types": "*",
                 "@types/scheduler": "*",
@@ -2092,15 +1877,9 @@
             },
             "dependencies": {
                 "csstype": {
-<<<<<<< HEAD
-                    "version": "3.0.11",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
-                    "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
-=======
                     "version": "3.0.10",
                     "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
                     "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
->>>>>>> master
                 }
             }
         },
@@ -2178,15 +1957,9 @@
             }
         },
         "@types/react-redux": {
-<<<<<<< HEAD
-            "version": "7.1.23",
-            "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.23.tgz",
-            "integrity": "sha512-D02o3FPfqQlfu2WeEYwh3x2otYd2Dk1o8wAfsA0B1C2AJEFxE663Ozu7JzuWbznGgW248NaOF6wsqCGNq9d3qw==",
-=======
             "version": "7.1.22",
             "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.22.tgz",
             "integrity": "sha512-GxIA1kM7ClU73I6wg9IRTVwSO9GS+SAKZKe0Enj+82HMU6aoESFU2HNAdNi3+J53IaOHPiUfT3kSG4L828joDQ==",
->>>>>>> master
             "requires": {
                 "@types/hoist-non-react-statics": "^3.3.0",
                 "@types/react": "*",
@@ -2209,10 +1982,6 @@
             "version": "6.8.9",
             "resolved": "https://registry.npmjs.org/@types/react-table/-/react-table-6.8.9.tgz",
             "integrity": "sha512-fVQXjy/EYDbgraScgjDONA291McKqGrw0R0NeK639fx2bS4T19TnXMjg3FjOPlkI3qYTQtFTPADlRYysaQIMpA==",
-<<<<<<< HEAD
-            "dev": true,
-=======
->>>>>>> master
             "requires": {
                 "@types/react": "*"
             }
@@ -2383,15 +2152,9 @@
             "integrity": "sha512-6JqTgijtfXcTJik8NtiNxr2L90ex6ElM00qilOGeUcrEsJLOdzLJSIkXHUYS+KPAYQYtRJQKD6XaXds3HjS+gg=="
         },
         "@types/tough-cookie": {
-<<<<<<< HEAD
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz",
-            "integrity": "sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A=="
-=======
             "version": "4.0.1",
             "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz",
             "integrity": "sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg=="
->>>>>>> master
         },
         "@types/typescript": {
             "version": "2.0.0",
@@ -2769,18 +2532,6 @@
             },
             "dependencies": {
                 "mime-db": {
-<<<<<<< HEAD
-                    "version": "1.52.0",
-                    "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
-                    "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
-                },
-                "mime-types": {
-                    "version": "2.1.35",
-                    "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
-                    "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
-                    "requires": {
-                        "mime-db": "1.52.0"
-=======
                     "version": "1.51.0",
                     "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
                     "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g=="
@@ -2791,7 +2542,6 @@
                     "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
                     "requires": {
                         "mime-db": "1.51.0"
->>>>>>> master
                     }
                 }
             }
@@ -3245,15 +2995,9 @@
             "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
         },
         "asn1": {
-<<<<<<< HEAD
-            "version": "0.2.4",
-            "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
-            "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
-=======
             "version": "0.2.6",
             "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
             "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
->>>>>>> master
             "requires": {
                 "safer-buffer": "~2.1.0"
             }
@@ -3334,15 +3078,9 @@
             "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
         },
         "aws4": {
-<<<<<<< HEAD
-            "version": "1.9.1",
-            "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
-            "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
-=======
             "version": "1.11.0",
             "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
             "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA=="
->>>>>>> master
         },
         "axios": {
             "version": "0.19.2",
@@ -3492,15 +3230,9 @@
             },
             "dependencies": {
                 "core-js": {
-<<<<<<< HEAD
-                    "version": "2.6.11",
-                    "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
-                    "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg=="
-=======
                     "version": "2.6.12",
                     "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
                     "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
->>>>>>> master
                 },
                 "regenerator-runtime": {
                     "version": "0.11.1",
@@ -3661,18 +3393,6 @@
             "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
             "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw=="
         },
-<<<<<<< HEAD
-        "bindings": {
-            "version": "1.5.0",
-            "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
-            "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
-            "optional": true,
-            "requires": {
-                "file-uri-to-path": "1.0.0"
-            }
-        },
-=======
->>>>>>> master
         "bl": {
             "version": "4.0.2",
             "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz",
@@ -3859,21 +3579,12 @@
             "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw=="
         },
         "browserslist": {
-<<<<<<< HEAD
-            "version": "4.20.2",
-            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.2.tgz",
-            "integrity": "sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==",
-            "requires": {
-                "caniuse-lite": "^1.0.30001317",
-                "electron-to-chromium": "^1.4.84",
-=======
             "version": "4.19.3",
             "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.3.tgz",
             "integrity": "sha512-XK3X4xtKJ+Txj8G5c30B4gsm71s69lqXlkYui4s6EkKxuv49qjYlY6oVd+IFJ73d4YymtM3+djvvt/R/iJwwDg==",
             "requires": {
                 "caniuse-lite": "^1.0.30001312",
                 "electron-to-chromium": "^1.4.71",
->>>>>>> master
                 "escalade": "^3.1.1",
                 "node-releases": "^2.0.2",
                 "picocolors": "^1.0.0"
@@ -4119,15 +3830,9 @@
             "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
         },
         "caniuse-lite": {
-<<<<<<< HEAD
-            "version": "1.0.30001317",
-            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001317.tgz",
-            "integrity": "sha512-xIZLh8gBm4dqNX0gkzrBeyI86J2eCjWzYAs40q88smG844YIrN4tVQl/RhquHvKEKImWWFIVh1Lxe5n1G/N+GQ=="
-=======
             "version": "1.0.30001312",
             "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz",
             "integrity": "sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ=="
->>>>>>> master
         },
         "canvas": {
             "version": "2.9.0",
@@ -4236,16 +3941,6 @@
                 "upath": "^1.1.1"
             },
             "dependencies": {
-<<<<<<< HEAD
-                "fsevents": {
-                    "version": "1.2.13",
-                    "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
-                    "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
-                    "optional": true,
-                    "requires": {
-                        "bindings": "^1.5.0",
-                        "nan": "^2.12.1"
-=======
                 "bindings": {
                     "version": "1.5.0",
                     "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
@@ -4737,7 +4432,6 @@
                             "bundled": true,
                             "optional": true
                         }
->>>>>>> master
                     }
                 }
             }
@@ -5739,7 +5433,6 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
-<<<<<<< HEAD
         "d": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
@@ -5750,8 +5443,6 @@
                 "type": "^1.0.1"
             }
         },
-=======
->>>>>>> master
         "d3-array": {
             "version": "1.2.4",
             "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
@@ -11529,12 +11220,7 @@
             "dependencies": {
                 "JSONStream": {
                     "version": "1.3.5",
-<<<<<<< HEAD
-                    "resolved": "",
-                    "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
-=======
                     "bundled": true,
->>>>>>> master
                     "requires": {
                         "jsonparse": "^1.2.0",
                         "through": ">=2.2.7 <3"
@@ -12981,12 +12667,7 @@
                 },
                 "jsonparse": {
                     "version": "1.3.1",
-<<<<<<< HEAD
-                    "resolved": "",
-                    "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA="
-=======
                     "bundled": true
->>>>>>> master
                 },
                 "jsprim": {
                     "version": "1.4.2",
@@ -14371,12 +14052,7 @@
                 },
                 "strict-uri-encode": {
                     "version": "2.0.0",
-<<<<<<< HEAD
-                    "resolved": "",
-                    "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
-=======
                     "bundled": true
->>>>>>> master
                 },
                 "string-width": {
                     "version": "2.1.1",
@@ -14409,24 +14085,14 @@
                 },
                 "string_decoder": {
                     "version": "1.3.0",
-<<<<<<< HEAD
-                    "resolved": "",
-                    "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
-=======
                     "bundled": true,
->>>>>>> master
                     "requires": {
                         "safe-buffer": "~5.2.0"
                     },
                     "dependencies": {
                         "safe-buffer": {
                             "version": "5.2.0",
-<<<<<<< HEAD
-                            "resolved": "",
-                            "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
-=======
                             "bundled": true
->>>>>>> master
                         }
                     }
                 },
@@ -16830,11 +16496,6 @@
             }
         },
         "react-table": {
-<<<<<<< HEAD
-            "version": "7.7.0",
-            "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.7.0.tgz",
-            "integrity": "sha512-jBlj70iBwOTvvImsU9t01LjFjy4sXEtclBovl3mTiqjz23Reu0DKnRza4zlLtOPACx6j2/7MrQIthIK1Wi+LIA=="
-=======
             "version": "6.11.5",
             "resolved": "https://registry.npmjs.org/react-table/-/react-table-6.11.5.tgz",
             "integrity": "sha512-LM+AS9v//7Y7lAlgTWW/cW6Sn5VOb3EsSkKQfQTzOW8FngB1FUskLLNEVkAYsTX9LjOWR3QlGjykJqCE6eXT/g==",
@@ -16843,7 +16504,6 @@
                 "classnames": "^2.2.5",
                 "react-is": "^16.8.1"
             }
->>>>>>> master
         },
         "react-themeable": {
             "version": "1.1.0",
@@ -17531,37 +17191,6 @@
                 "object-assign": "^4.1.1"
             }
         },
-        "schema-utils": {
-            "version": "3.1.1",
-            "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
-            "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
-            "dev": true,
-            "requires": {
-                "@types/json-schema": "^7.0.8",
-                "ajv": "^6.12.5",
-                "ajv-keywords": "^3.5.2"
-            },
-            "dependencies": {
-                "ajv": {
-                    "version": "6.12.6",
-                    "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
-                    "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
-                    "dev": true,
-                    "requires": {
-                        "fast-deep-equal": "^3.1.1",
-                        "fast-json-stable-stringify": "^2.0.0",
-                        "json-schema-traverse": "^0.4.1",
-                        "uri-js": "^4.2.2"
-                    }
-                },
-                "ajv-keywords": {
-                    "version": "3.5.2",
-                    "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
-                    "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
-                    "dev": true
-                }
-            }
-        },
         "scss-loader": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/scss-loader/-/scss-loader-0.0.1.tgz",
@@ -18933,8 +18562,6 @@
                 "terser": "^5.7.2"
             },
             "dependencies": {
-<<<<<<< HEAD
-=======
                 "ajv": {
                     "version": "6.12.6",
                     "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -18964,7 +18591,6 @@
                         "ajv-keywords": "^3.5.2"
                     }
                 },
->>>>>>> master
                 "serialize-javascript": {
                     "version": "6.0.0",
                     "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
@@ -20182,15 +19808,9 @@
             "dev": true
         },
         "webpack": {
-<<<<<<< HEAD
-            "version": "5.70.0",
-            "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.70.0.tgz",
-            "integrity": "sha512-ZMWWy8CeuTTjCxbeaQI21xSswseF2oNOwc70QSKNePvmxE7XW36i7vpBMYZFAUHPwQiEbNGCEYIOOlyRbdGmxw==",
-=======
             "version": "5.69.1",
             "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.69.1.tgz",
             "integrity": "sha512-+VyvOSJXZMT2V5vLzOnDuMz5GxEqLk7hKWQ56YxPW/PQRUuKimPqmEIJOx8jHYeyo65pKbapbW464mvsKbaj4A==",
->>>>>>> master
             "dev": true,
             "requires": {
                 "@types/eslint-scope": "^3.7.3",
@@ -20202,7 +19822,7 @@
                 "acorn-import-assertions": "^1.7.6",
                 "browserslist": "^4.14.5",
                 "chrome-trace-event": "^1.0.2",
-                "enhanced-resolve": "^5.9.2",
+                "enhanced-resolve": "^5.8.3",
                 "es-module-lexer": "^0.9.0",
                 "eslint-scope": "5.1.1",
                 "events": "^3.2.0",
@@ -20224,13 +19844,6 @@
                     "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
                     "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
                     "dev": true
-<<<<<<< HEAD
-                },
-                "enhanced-resolve": {
-                    "version": "5.9.2",
-                    "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz",
-                    "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==",
-=======
                 },
                 "ajv": {
                     "version": "6.12.6",
@@ -20251,10 +19864,9 @@
                     "dev": true
                 },
                 "enhanced-resolve": {
-                    "version": "5.9.1",
-                    "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.1.tgz",
-                    "integrity": "sha512-jdyZMwCQ5Oj4c5+BTnkxPgDZO/BJzh/ADDmKebayyzNwjVX1AFCeGkOfxNx0mHi2+8BKC5VxUYiw3TIvoT7vhw==",
->>>>>>> master
+                    "version": "5.9.2",
+                    "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz",
+                    "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==",
                     "dev": true,
                     "requires": {
                         "graceful-fs": "^4.2.4",
@@ -20272,8 +19884,6 @@
                     "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
                     "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
                     "dev": true
-<<<<<<< HEAD
-=======
                 },
                 "schema-utils": {
                     "version": "3.1.1",
@@ -20285,7 +19895,6 @@
                         "ajv": "^6.12.5",
                         "ajv-keywords": "^3.5.2"
                     }
->>>>>>> master
                 },
                 "tapable": {
                     "version": "2.2.1",
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b62b9b3ac..af731ce9f 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -350,7 +350,7 @@ export class CurrentUserUtils {
     }[] {
         if (doc.emptyPresentation === undefined) {
             doc.emptyPresentation = Docs.Create.PresDocument(new List<Doc>(),
-                { title: "Untitled Presentation", _viewType: CollectionViewType.Tree, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0", system: true, cloneFieldFilter: new List<string>(["system"]) });
+                { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0", system: true, cloneFieldFilter: new List<string>(["system"]) });
             ((doc.emptyPresentation as Doc).proto as Doc)["dragFactory-count"] = 0;
         }
         if (doc.emptyCollection === undefined) {
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index ad6d90bc3..93d0c6936 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -168,8 +168,8 @@ export class DocumentManager {
         originalTarget = originalTarget ?? targetDoc;
         const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView;
         const docView = getFirstDocView(targetDoc, originatingDoc);
-        const wasHidden = targetDoc.hidden;  // 
-        if (wasHidden) runInAction(() => targetDoc.hidden = false);  // if the target is hidden, un-hide it here.
+        const wasHidden = targetDoc?.hidden;  // 
+        if (wasHidden && targetDoc) runInAction(() => targetDoc.hidden = false);  // if the target is hidden, un-hide it here.
         const focusAndFinish = (didFocus: boolean) => {
             if (originatingDoc?.isPushpin) {
                 if (!didFocus && !wasHidden) {  // don't toggle the hidden state if the doc was already un-hidden as part of this document traversal
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index dcb678046..3bf81d374 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -245,7 +245,7 @@ export class MainView extends React.Component {
             }));
         }
         const pres = Docs.Create.PresDocument(new List<Doc>(),
-            { title: "Untitled Trail", childDontRegisterViews: true, _viewType: CollectionViewType.Tree, _forceActive: true, treeViewOpen: true, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
+            { title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
         CollectionDockingView.AddSplit(pres, "left");
         this.userDoc.activePresentation = pres;
         Doc.AddDocToList(this.userDoc.myTrails as Doc, "data", pres);
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index ff5c4bab1..751ae631b 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -1,5 +1,5 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, WidthSym, StrListCast } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
@@ -13,7 +13,7 @@ import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from "../../documents/DocumentTypes";
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
-import { DocumentManager } from '../../util/DocumentManager';
+import { DocumentManager, DocFocusOrOpen } from '../../util/DocumentManager';
 import { DragManager, dropActionType } from "../../util/DragManager";
 import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
@@ -31,6 +31,8 @@ import { CollectionTreeView } from './CollectionTreeView';
 import { CollectionView, CollectionViewType } from './CollectionView';
 import "./TreeView.scss";
 import React = require("react");
+import { PresMovement } from '../nodes/trails/PresEnums';
+import { PresBox } from '../nodes/trails';
 
 export interface TreeViewProps {
     treeView: CollectionTreeView;
@@ -112,13 +114,24 @@ export class TreeView extends React.Component<TreeViewProps> {
     @computed get dataDoc() { return this.doc[DataSym]; }
     @computed get layoutDoc() { return Doc.Layout(this.doc); }
     @computed get fieldKey() { return Doc.LayoutFieldKey(this.doc); }
-    @computed get childDocs() { return this.childDocList(this.fieldKey); }
+    // @computed get childDocs() { return this.childDocList(this.fieldKey); }
+    @computed get childDocs() { return DocListCast(this.dataDoc[this.fieldKey]); }
     @computed get childLinks() { return this.childDocList("links"); }
     @computed get childAliases() { return this.childDocList("aliases"); }
     @computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); }
     @computed get selected() { return SelectionManager.IsSelected(this._docRef); }
     //      SelectionManager.Views().lastElement()?.props.Document === this.props.document; }
 
+    @observable _presTimer!: NodeJS.Timeout;
+    @observable _presKeyEventsActive: boolean = false;
+
+    @observable _selectedArray: ObservableMap = new ObservableMap<Doc, any>();
+    // the selected item's index
+    @computed get itemIndex() { return NumCast(this.doc._itemIndex); }
+    // the item that's active 
+    @computed get activeItem() { return Cast(this.childDocs[NumCast(this.doc._itemIndex)], Doc, null); }
+    @computed get targetDoc() { return Cast(this.activeItem?.presentationTargetDoc, Doc, null); }
+
     childDocList(field: string) {
         const layout = Cast(Doc.LayoutField(this.doc), Doc, null);
         return (this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
@@ -546,7 +559,80 @@ export class TreeView extends React.Component<TreeViewProps> {
         const icons = StrListCast(this.doc.childContextMenuIcons);
         return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
     }
-    onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
+
+    // TODO: currently doc focus works, but can't seem to edit title
+    // onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
+    onChildClick = () => {
+        return this.props.onChildClick?.() ?? (ScriptField.MakeFunction(`DocFocusOrOpen(self)`)! || this._editTitleScript?.())
+    }
+
+    //Regular click zoom to document on canvas
+    @action
+    selectElement = async (doc: Doc) => {
+        const context = Cast(doc.context, Doc, null);
+        this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem);
+        if (doc.presPinView || doc.presentationTargetDoc === this.layoutDoc.presCollection) setTimeout(() => this.updateCurrentPresentation(context), 0);
+        else this.updateCurrentPresentation(context);
+
+        if (this.activeItem.setPosition &&
+            this.activeItem.y !== undefined &&
+            this.activeItem.x !== undefined &&
+            this.targetDoc.x !== undefined &&
+            this.targetDoc.y !== undefined) {
+            const timer = (ms: number) => new Promise(res => this._presTimer = setTimeout(res, ms));
+            const time = 10;
+            const ydiff = NumCast(this.activeItem.y) - NumCast(this.targetDoc.y);
+            const xdiff = NumCast(this.activeItem.x) - NumCast(this.targetDoc.x);
+
+            for (let i = 0; i < time; i++) {
+                this.targetDoc.x = NumCast(this.targetDoc.x) + xdiff / time;
+                this.targetDoc.y = NumCast(this.targetDoc.y) + ydiff / time;
+                await timer(0.1);
+            }
+        }
+    }
+
+    static keyEventsWrapper = (e: KeyboardEvent) => {
+        PresBox.Instance.keyEvents(e);
+    }
+
+    @action
+    updateCurrentPresentation = (pres?: Doc) => {
+        if (pres) Doc.UserDoc().activePresentation = pres;
+        else Doc.UserDoc().activePresentation = this.doc;
+        document.removeEventListener("keydown", PresBox.keyEventsWrapper, true);
+        document.addEventListener("keydown", PresBox.keyEventsWrapper, true);
+        this._presKeyEventsActive = true;
+        // PresBox.Instance = this;
+    }
+
+    //The function that is called when a document is clicked or reached through next or back.
+    //it'll also execute the necessary actions if presentation is playing.
+    public gotoDocument = action((index: number, from?: Doc, group?: boolean) => {
+        Doc.UnBrushAllDocs();
+        if (index >= 0 && index < this.childDocs.length) {
+            this.doc._itemIndex = index;
+            const activeItem: Doc = this.activeItem;
+            const targetDoc: Doc = this.targetDoc;
+
+            if (targetDoc) {
+                targetDoc && runInAction(() => {
+                    if (activeItem.presMovement === PresMovement.Jump) targetDoc.focusSpeed = 0;
+                    else targetDoc.focusSpeed = activeItem.presTransition ? activeItem.presTransition : 500;
+                });
+                setTimeout(() => targetDoc.focusSpeed = 500, this.activeItem.presTransition ? NumCast(this.activeItem.presTransition) + 10 : 510);
+            }
+            if (targetDoc?.lastFrame !== undefined) {
+                targetDoc._currentFrame = 0;
+            }
+            if (!group) this._selectedArray.clear();
+            this.childDocs[index] && this._selectedArray.set(this.childDocs[index], undefined); //TODO: needs more work on selected arrayUpdate selected array
+            // if (this.layoutDoc._viewType === "stacking" && !group) this.navigateToElement(this.childDocs[index]); //Handles movement to element only when presTrail is list
+            // this.onHideDocument(); //Handles hide after/before
+        }
+    });
+
+
 
     onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
 
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index ca13590de..0c960a95b 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -229,6 +229,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
 
         const script = ScriptCast(this.rootDoc.script);
+        if (!script) { return null };
 
         let noviceList: string[] = [];
         let text: string | undefined;
-- 
cgit v1.2.3-70-g09d2


From 605137223d8afa0eaf8818de5b556601fca8441f Mon Sep 17 00:00:00 2001
From: Michael Foiani <sotech117@michaels-mbp-3.devices.brown.edu>
Date: Thu, 14 Apr 2022 17:23:49 -0400
Subject: Create smooth record and playback of tracking.

---
 src/client/util/DragManager.ts                     |  2 +-
 src/client/views/MainView.scss                     |  5 ++++
 src/client/views/MainView.tsx                      |  1 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 32 ++++++++++++++++++++++
 4 files changed, 39 insertions(+), 1 deletion(-)

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

diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 8d4b98d88..db9986a41 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -216,7 +216,7 @@ export namespace DragManager {
     ) {
         // stop an 'accidental' on-click drag that may have occured if the user is in presenting mode
         // note: dragData.dropAction is only undefined when the element itself being dragged without being selected
-        if (Doc.UserDoc()?.isPresenting && dragData.dropAction === undefined) return false;
+        if (Doc.UserDoc()?.presentationMode === 'recording' && dragData.dropAction === undefined) return false;
 
         const addAudioTag = (dropDoc: any) => {
             dropDoc && !dropDoc.creationDate && (dropDoc.creationDate = new DateField);
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index 15cd2c144..3799b6b13 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -29,6 +29,11 @@
     left: calc(100% + 5px);
     z-index: 1;
     pointer-events: none;
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    justify-content: space-between;
+    gap: 10px;
 }
 
 .mainView-snapLines {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 8c0795881..b98c80068 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -528,6 +528,7 @@ export class MainView extends React.Component {
                     searchFilterDocs={returnEmptyDoclist}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined} />
+                {['watching', 'recording'].includes(String(this.userDoc?.presentationMode) ?? '') ? <div style={{ border: '.5rem solid green', padding: '5px' }}>{this.userDoc?.presentationMode}</div>: <></>}
             </div>;
     }
     @computed get snapLines() {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index e2ea81392..75855f5d9 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -50,6 +50,7 @@ import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCurso
 import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
+import e = require("connect-flash");
 
 export const panZoomSchema = createSchema({
     _panX: "number",
@@ -117,6 +118,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @observable _keyframeEditing = false;
     @observable ChildDrag: DocumentView | undefined; // child document view being dragged.  needed to update drop areas of groups when a group item is dragged.
 
+    @observable storedMovements: object[] = []; // stores the movement if in presenting mode
+
     @computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); }
     @computed get backgroundEvents() { return this.props.layerProvider?.(this.layoutDoc) === false && SnappingManager.GetIsDragging(); }
     @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && this.props.isContentActive(); }
@@ -956,8 +959,37 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         }
     }
 
+    followMovements = (): void => {
+        // need the first for subtraction
+        let first = null;
+
+        this.storedMovements.forEach(movement => {
+            if (first === null) first = movement.time;
+
+            // set the pan to what was stored
+            setTimeout(() => {
+                this.Document._panX = movement.panX;
+                this.Document._panY = movement.panY;
+            }, movement.time - first)
+        })
+
+        // for now, clear the movements
+        this.storedMovements = []
+    }
+
     @action
     setPan(panX: number, panY: number, panTime: number = 0, clamp: boolean = false) {
+        // if not presenting, just retrace the movements
+        if (Doc.UserDoc()?.presentationMode === 'watching') {
+            this.followMovements()
+            return;
+        }
+
+        if (Doc.UserDoc()?.presentationMode === 'recording') {
+            // store as many movments as possible
+            this.storedMovements.push({time: new Date().getTime(), panX, panY})
+        }
+
         if (!this.isAnnotationOverlay && clamp) {
             // this section wraps the pan position, horizontally and/or vertically whenever the content is panned out of the viewing bounds
             const docs = this.childLayoutPairs.map(pair => pair.layout).filter(doc => doc instanceof Doc);
-- 
cgit v1.2.3-70-g09d2


From cb59842fcb79af7ef72443e08c6a479c3f0af343 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 25 Apr 2022 20:20:48 -0400
Subject:  changed freeformview's with engines to not allow children to be
 edited using setHeight prop since engine layouts are temporary and shouldn't
 edit the doc.  fixed tags to work with pivot view. cleaned up pile views to
 be less hacky..

---
 src/client/documents/Documents.ts                  | 27 +++++++++++++---------
 src/client/views/MainView.tsx                      |  1 +
 src/client/views/SidebarAnnos.tsx                  |  2 +-
 .../views/collections/CollectionPileView.tsx       |  4 ++--
 .../views/collections/CollectionStackingView.tsx   |  6 ++---
 src/client/views/collections/CollectionSubView.tsx |  2 +-
 .../views/collections/CollectionTreeView.tsx       |  2 +-
 src/client/views/collections/TreeView.tsx          |  1 +
 .../CollectionFreeFormLayoutEngines.tsx            | 26 +++++++++++++--------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  4 +++-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  5 +---
 src/client/views/nodes/DocumentContentsView.tsx    |  2 +-
 src/client/views/nodes/DocumentView.tsx            | 17 +++++++-------
 src/client/views/nodes/FieldView.tsx               |  2 +-
 src/client/views/nodes/LinkDocPreview.tsx          |  1 +
 src/client/views/nodes/WebBox.tsx                  |  2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  2 +-
 src/client/views/pdf/PDFViewer.tsx                 |  2 +-
 src/fields/Doc.ts                                  |  2 +-
 19 files changed, 62 insertions(+), 48 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 49e53a214..a19530c92 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -14,7 +14,7 @@ import { Cast, NumCast, StrCast } from "../../fields/Types";
 import { AudioField, ImageField, MapField, PdfField, VideoField, WebField, YoutubeField } from "../../fields/URLField";
 import { SharingPermissions } from "../../fields/util";
 import { Upload } from "../../server/SharedMediaTypes";
-import { OmitKeys, Utils } from "../../Utils";
+import { OmitKeys, Utils, aggregateBounds } from "../../Utils";
 import { YoutubeBox } from "../apis/youtube/YoutubeBox";
 import { DocServer } from "../DocServer";
 import { Networking } from "../Network";
@@ -1352,26 +1352,31 @@ export namespace DocUtils {
         if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") doc.deiconifyLayout = layoutKey.replace("layout_", "");
     }
 
-    export function pileup(docList: Doc[], x?: number, y?: number, create: boolean = true) {
+    export function pileup(docList: Doc[], x?: number, y?: number, size: number = 55, create: boolean = true) {
         let w = 0, h = 0;
         runInAction(() => {
             docList.forEach(d => {
                 DocUtils.iconify(d);
-                w = Math.max(d[WidthSym](), w);
-                h = Math.max(d[HeightSym](), h);
+                w = Math.max(NumCast(d._width), w);
+                h = Math.max(NumCast(d._height), h);
             });
-            h = Math.max(h, w * 4 / 3); // converting to an icon does not update the height right away.  so this is a fallback hack to try to do something reasonable
             docList.forEach((d, i) => {
-                d.x = Math.cos(Math.PI * 2 * i / docList.length) * 10 - w / 2;
-                d.y = Math.sin(Math.PI * 2 * i / docList.length) * 10 - h / 2;
+                d.x = Math.cos(Math.PI * 2 * i / docList.length) * size;
+                d.y = Math.sin(Math.PI * 2 * i / docList.length) * size;
+                d._timecodeToShow = undefined;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+            });
+            const aggBounds = aggregateBounds(docList.map(d => ({ x: NumCast(d.x), y: NumCast(d.y), width: NumCast(d._width), height: NumCast(d._height) })), 0, 0);
+            docList.forEach((d, i) => {
+                d.x = NumCast(d.x) - ((aggBounds.r + aggBounds.x) / 2);
+                d.y = NumCast(d.y) - ((aggBounds.b + aggBounds.y) / 2);
                 d._timecodeToShow = undefined;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
             });
         });
         if (create) {
-            const newCollection = Docs.Create.PileDocument(docList, { title: "pileup", x: (x || 0) - 55, y: (y || 0) - 55, _width: 110, _height: 100, });
-            newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - 55;
-            newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - 55;
-            newCollection._width = newCollection._height = 110;
+            const newCollection = Docs.Create.PileDocument(docList, { title: "pileup", x: (x || 0) - size, y: (y || 0) - size, _width: size * 2, _height: size * 2, });
+            newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - size;
+            newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - size;
+            newCollection._width = newCollection._height = size * 2;
             newCollection._jitterRotation = 10;
             return newCollection;
         }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index a67cb3014..68b4710ed 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -297,6 +297,7 @@ export class MainView extends React.Component {
             searchFilterDocs={returnEmptyDoclist}
             ContainingCollectionView={undefined}
             ContainingCollectionDoc={undefined}
+            suppressSetHeight={true}
             renderDepth={-1}
         />;
     }
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index fae385660..43a02d029 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -88,7 +88,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     removeDocument = (doc: Doc | Doc[]) => this.props.removeDocument(doc, this.sidebarKey);
     docFilters = () => [...StrListCast(this.props.layoutDoc._docFilters), ...StrListCast(this.props.layoutDoc[this.filtersKey])];
     showTitle = () => "title";
-    setHeightCallback = (height: number) => this.props.setHeight(height + this.filtersHeight());
+    setHeightCallback = (height: number) => this.props.setHeight?.(height + this.filtersHeight());
     render() {
         const renderTag = (tag: string) => {
             const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`${tag}:${tag}:check`);
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index 8832b0f4a..4489601db 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -74,13 +74,13 @@ export class CollectionPileView extends CollectionSubView() {
             this.rootDoc.y = NumCast(this.rootDoc.y) + this.layoutDoc[HeightSym]() / 2 - NumCast(this.layoutDoc._starburstPileHeight, defaultSize) / 2;
             this.layoutDoc._width = NumCast(this.layoutDoc._starburstPileWidth, defaultSize);
             this.layoutDoc._height = NumCast(this.layoutDoc._starburstPileHeight, defaultSize);
-            DocUtils.pileup(this.childDocs, undefined, undefined, false);
+            DocUtils.pileup(this.childDocs, undefined, undefined, NumCast(this.layoutDoc._width) / 2, false);
             this.layoutDoc._panX = 0;
             this.layoutDoc._panY = -10;
             this.props.Document._pileLayoutEngine = 'pass';
         } else {
             const defaultSize = 25;
-            !this.layoutDoc._starburstRadius && (this.layoutDoc._starburstRadius = 500);
+            !this.layoutDoc._starburstRadius && (this.layoutDoc._starburstRadius = 250);
             !this.layoutDoc._starburstDocScale && (this.layoutDoc._starburstDocScale = 2.5);
             if (this.layoutEngine() === 'pass') {
                 this.rootDoc.x = NumCast(this.rootDoc.x) + this.layoutDoc[WidthSym]() / 2 - defaultSize / 2;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 8634ea139..509005b45 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -141,7 +141,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             () => this.layoutDoc._columnHeaders = new List()
         );
         this._autoHeightDisposer = reaction(() => this.layoutDoc._autoHeight,
-            autoHeight => autoHeight && this.props.setHeight(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
+            autoHeight => autoHeight && this.props.setHeight?.(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
                 this.headerMargin + (this.isStackingView ?
                     Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))) :
                     this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0)))));
@@ -408,7 +408,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                                 Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
                                     Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))));
                             if (!LightboxView.IsLightboxDocView(this.props.docViewPath())) {
-                                this.props.setHeight(height);
+                                this.props.setHeight?.(height);
                             }
                         }
                     }));
@@ -458,7 +458,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                     this.observer = new _global.ResizeObserver(action((entries: any) => {
                         if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
                             const height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0);
-                            this.props.setHeight(this.headerMargin + height);
+                            this.props.setHeight?.(this.headerMargin + height);
                         }
                     }));
                     this.observer.observe(ref);
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 5bdd0c0ac..30e0adf24 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -453,7 +453,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                 if (completed) completed(set);
                 else {
                     if (isFreeformView && generatedDocuments.length > 1) {
-                        addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!);
+                        addDocument(DocUtils.pileup(generatedDocuments, options.x!, options.y!)!,);
                     } else {
                         generatedDocuments.forEach(addDocument);
                     }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 76da96174..41970eb96 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -88,7 +88,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             const titleHeight = !this._titleRef ? this.marginTop() : Number(getComputedStyle(this._titleRef).height.replace("px", ""));
             const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), this.marginBot());
             this.layoutDoc._autoHeightMargins = bodyHeight;
-            this.props.setHeight(bodyHeight + titleHeight);
+            this.props.setHeight?.(bodyHeight + titleHeight);
         }
     }
     unobserveHeight = (ref: any) => {
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 6213850d8..b9710b3f5 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -30,6 +30,7 @@ import { CollectionTreeView } from './CollectionTreeView';
 import { CollectionView, CollectionViewType } from './CollectionView';
 import "./TreeView.scss";
 import React = require("react");
+import { KeyValueBox } from '../nodes/KeyValueBox';
 
 export interface TreeViewProps {
     treeView: CollectionTreeView;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 598960af7..16c7df311 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -111,17 +111,17 @@ export function computerStarburstLayout(
     viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
     engineProps: any
 ) {
-    const mustFit = pivotDoc[WidthSym]() !== panelDim[0];
+    const mustFit = pivotDoc[WidthSym]() !== panelDim[0]; // if a panel size is set that's not the same as the pivot doc's size, then assume this is in a panel for a content fitting view (like a grid) in which case everything must be scaled to stay within the panel
     const docMap = new Map<string, PoolData>();
-    const burstRadius = mustFit ? panelDim : [NumCast(pivotDoc._starburstRadius, panelDim[0]), NumCast(pivotDoc._starburstRadius, panelDim[1])];
-    const docSize = mustFit ? panelDim[0] * .33 : 75; // assume a icon sized at 75
-    const scaleDim = [burstRadius[0] + docSize, burstRadius[1] + docSize];
+    const docSize = mustFit ? panelDim[0] * .33 : 75; // assume an icon sized at 75
+    const burstRadius = mustFit ? panelDim : [NumCast(pivotDoc._starburstRadius, panelDim[0]) - docSize, NumCast(pivotDoc._starburstRadius, panelDim[1]) - docSize];
+    const scaleDim = [burstRadius[0] * 2 + docSize, burstRadius[1] * 2 + docSize];
     childPairs.forEach(({ layout, data }, i) => {
-        const docSize = layout.layoutKey === "layout_icon" ? mustFit ? panelDim[0] * .33 : 75 : 400; // assume a icon sized at 10750
+        const docSize = layout.layoutKey === "layout_icon" ? (mustFit ? panelDim[0] * .33 : 75) : 400; // assume a icon sized at 75
         const deg = i / childPairs.length * Math.PI * 2;
         docMap.set(layout[Id], {
-            x: Math.cos(deg) * (burstRadius[0] / 3) - docSize / 2,
-            y: Math.sin(deg) * (burstRadius[1] / 3) - docSize * layout[HeightSym]() / layout[WidthSym]() / 2,
+            x: Math.cos(deg) * burstRadius[0] - docSize / 2,
+            y: Math.sin(deg) * burstRadius[1] - docSize * layout[HeightSym]() / layout[WidthSym]() / 2,
             width: docSize,//layout[WidthSym](),
             height: docSize * layout[HeightSym]() / layout[WidthSym](),
             zIndex: NumCast(layout.zIndex),
@@ -129,7 +129,7 @@ export function computerStarburstLayout(
             replica: ""
         });
     });
-    const divider = { type: "div", color: "transparent", x: -burstRadius[0] / 3, y: 0, width: 15, height: 15, payload: undefined };
+    const divider = { type: "div", color: "transparent", x: -burstRadius[0], y: 0, width: 15, height: 15, payload: undefined };
     return normalizeResults(scaleDim, 12, docMap, poolData, viewDefsToJSX, [], 0, [divider]);
 }
 
@@ -267,7 +267,13 @@ export function computePivotLayout(
     });
 
     const dividers = sortedPivotKeys.map((key, i) =>
-        ({ type: "div", color: "lightGray", x: i * pivotAxisWidth * (numCols * expander + gap) - pivotAxisWidth * (expander - 1) / 2, y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander, height: maxColHeight, payload: pivotColumnGroups.get(key)!.filters }));
+    ({
+        type: "div", color: "lightGray",
+        x: i * pivotAxisWidth * (numCols * expander + gap) - pivotAxisWidth * (expander - 1) / 2,
+        y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander,
+        height: maxColHeight,
+        payload: pivotColumnGroups.get(key)!.filters
+    }));
     groupNames.push(...dividers);
     return normalizeResults(panelDim, max_text, docMap, poolData, viewDefsToJSX, groupNames, 0, []);
 }
@@ -404,7 +410,7 @@ function normalizeResults(
     const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as ViewDefBounds);
     const docEles = Array.from(docMap.entries()).map(ele => ele[1]);
     const aggBounds = aggregateBounds(extras.concat(grpEles.concat(docEles.map(de => ({ ...de, type: "doc", payload: "" })))).filter(e => e.zIndex !== -99), 0, 0);
-    aggBounds.r = Math.max(minWidth, aggBounds.r - aggBounds.x);
+    aggBounds.r = aggBounds.x + Math.max(minWidth, aggBounds.r - aggBounds.x);
     const wscale = panelDim[0] / (aggBounds.r - aggBounds.x);
     let scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] ? (panelDim[1]) / (aggBounds.b - aggBounds.y) : wscale;
     if (Number.isNaN(scale)) scale = 1;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index eaef1b49c..a14405a0b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1158,6 +1158,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             replica={entry.replica}
             renderIndex={renderIndex}
             dataTransition={entry.transition}
+            suppressSetHeight={this.layoutEngine ? true : false}
             renderCutoffProvider={this.renderCutoffProvider}
             ContainingCollectionView={this.props.CollectionView}
             ContainingCollectionDoc={this.props.Document}
@@ -1300,10 +1301,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return [] as ViewDefResult[];
     }
 
+    @computed get layoutEngine() { return this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine); }
     @computed get doInternalLayoutComputation() {
         TraceMobx();
         const newPool = new Map<string, PoolData>();
-        switch (this.props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine)) {
+        switch (this.layoutEngine) {
             case "pass": return { newPool, computedElementData: this.doEngineLayout(newPool, computerPassLayout) };
             case "timeline": return { newPool, computedElementData: this.doEngineLayout(newPool, computeTimelineLayout) };
             case "pivot": return { newPool, computedElementData: this.doEngineLayout(newPool, computePivotLayout) };
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 3e35039e3..6097425e1 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -17,9 +17,6 @@ import { StyleProp } from "../StyleProvider";
 import "./CollectionFreeFormDocumentView.scss";
 import { DocumentView, DocumentViewProps } from "./DocumentView";
 import React = require("react");
-import { DocumentType } from "../../documents/DocumentTypes";
-import { collectionSchema } from "../../../fields/documentSchemas";
-import { CollectionViewType } from "../collections/CollectionView";
 
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined;
@@ -174,7 +171,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             style={{
                 outline: this.Highlight ? "orange solid 2px" : "",
                 width: this.panelWidth(),
-                height: !this.ShowTitle && this.layoutDoc.autoHeight && this.rootDoc.type === DocumentType.RTF ? undefined : this.panelHeight(),
+                height: this.panelHeight(),
                 transform: this.transform,
                 transition: this.props.dataTransition ? this.props.dataTransition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.dataTransition),
                 zIndex: this.ZInd,
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 005133eb0..d4e8ffc7f 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -112,7 +112,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
     isSelected: (outsideReaction: boolean) => boolean,
     select: (ctrl: boolean) => void,
     scaling?: () => number,
-    setHeight: (height: number) => void,
+    setHeight?: (height: number) => void,
     layoutKey: string,
 }> {
     @computed get layout(): string {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 504a3afb6..20eabe6a9 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -114,6 +114,7 @@ export interface DocumentViewSharedProps {
     fitContentsToDoc?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitToBox property on a Document
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
+    suppressSetHeight?: boolean;
     thumbShown?: () => boolean;
     isHovering?: () => boolean;
     setContentView?: (view: DocComponentView) => any;
@@ -842,11 +843,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
     contentScaling = () => this.ContentScale;
     onClickFunc = () => this.onClickHandler;
-    setHeight = (height: number) => {
-        if (this.props.renderDepth !== -1) {
-            this.layoutDoc._height = height;
-        }
-    }
+    setHeight = (height: number) => this.layoutDoc._height = height;
     setContentView = action((view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view);
     isContentActive = (outsideReaction?: boolean) => {
         return this.props.isContentActive() === false ? false : (
@@ -890,7 +887,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 setContentView={this.setContentView}
                 scaling={this.contentScaling}
                 PanelHeight={this.panelHeight}
-                setHeight={this.setHeight}
+                setHeight={!this.props.suppressSetHeight ? this.setHeight : undefined}
                 isContentActive={this.isContentActive}
                 ScreenToLocalTransform={this.screenToLocal}
                 rootSelected={this.rootSelected}
@@ -1207,7 +1204,11 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         return this.docView?._componentView?.reverseNativeScaling?.() ? 0 :
             returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, this.props.freezeDimensions));
     }
-    @computed get shouldNotScale() { return (this.fitWidth && !this.nativeWidth) || this.props.treeViewDoc || [CollectionViewType.Docking].includes(this.Document._viewType as any); }
+    @computed get shouldNotScale() {
+        return (this.fitWidth && !this.nativeWidth) ||
+            this.props.ContainingCollectionView?.collectionViewType === CollectionViewType.Time ||
+            this.props.treeViewDoc || [CollectionViewType.Docking].includes(this.Document._viewType as any);
+    }
     @computed get effectiveNativeWidth() { return this.shouldNotScale ? 0 : (this.nativeWidth || NumCast(this.layoutDoc.width)); }
     @computed get effectiveNativeHeight() { return this.shouldNotScale ? 0 : (this.nativeHeight || NumCast(this.layoutDoc.height)); }
     @computed get nativeScaling() {
@@ -1324,7 +1325,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
                         position: this.props.Document.isInkMask ? "absolute" : undefined,
                         transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`,
                         width: isButton || isPresTreeElement ? "100%" : xshift() ?? `${100 * (this.props.PanelWidth() - this.Xshift * 2) / this.props.PanelWidth()}%`,
-                        height: (!this.props.ignoreAutoHeight && this.layoutDoc.autoHeight && this.layoutDoc.type === DocumentType.RTF) || isButton || this.props.forceAutoHeight ? undefined : yshift() ?? (this.fitWidth ? `${this.panelHeight}px` :
+                        height: isButton || this.props.forceAutoHeight ? undefined : yshift() ?? (this.fitWidth ? `${this.panelHeight}px` :
                             `${100 * this.effectiveNativeHeight / this.effectiveNativeWidth * this.props.PanelWidth() / this.props.PanelHeight()}%`),
                     }}>
                     <DocumentViewInternal {...this.props}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 943b9f153..ae9acfe3f 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -22,7 +22,7 @@ export interface FieldViewProps extends DocumentViewSharedProps {
     isDocumentActive?: () => boolean;
     isSelected: (outsideReaction?: boolean) => boolean;
     scaling?: () => number;
-    setHeight: (height: number) => void;
+    setHeight?: (height: number) => void;
 
     // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
     pointerEvents?: string;
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index eca2e3cfd..55f6d6059 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -195,6 +195,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                             ContainingCollectionDoc={undefined}
                             ContainingCollectionView={undefined}
                             renderDepth={-1}
+                            suppressSetHeight={true}
                             PanelWidth={this.width}
                             PanelHeight={this.height}
                             focus={DocUtils.DefaultFocus}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 0fd193977..eeb7b925b 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -164,7 +164,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             autoHeight => {
                 if (autoHeight) {
                     this.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]);
-                    this.props.setHeight(NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]) * (this.props.scaling?.() || 1));
+                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]) * (this.props.scaling?.() || 1));
                 }
             });
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index dc1c67bea..710270be4 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1524,7 +1524,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         if (children) {
             const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins);
             const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight);
-            if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) {  // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
+            if (this.props.setHeight && scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) {  // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
                 const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight;
                 if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) {
                     setScrollHeight();
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 9aaa6e90f..92a69c02b 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -123,7 +123,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             autoHeight => {
                 if (autoHeight) {
                     this.props.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]);
-                    this.props.setHeight(NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]) * (this.props.scaling?.() || 1));
+                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]) * (this.props.scaling?.() || 1));
                 }
             });
 
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 15df673f3..63af8401c 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1108,7 +1108,7 @@ export namespace Doc {
         if (typeof value === "string") {
             value = value.replace(`,${Utils.noRecursionHack}`, "");
         }
-        const fieldVal = doc[key];
+        const fieldVal = key === "#" ? (StrCast(doc.tags).includes(":#" + value + ":") ? StrCast(doc.tags) : undefined) : doc[key];
         if (Cast(fieldVal, listSpec("string"), []).length) {
             const vals = Cast(fieldVal, listSpec("string"), []);
             const docs = vals.some(v => (v as any) instanceof Doc);
-- 
cgit v1.2.3-70-g09d2


From 08217336445cf2b3cd3efbe97e3c83525f02bf1b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 26 Apr 2022 22:34:21 -0400
Subject: added image cropping. made treeView icons show up for file system and
 dashboard, and made them persist invisibly to fix triggering on hover. 
 changed tree view filesystem and dashboard to show expanded icons on hover.

---
 src/client/views/MainView.tsx                      |  2 +-
 src/client/views/MarqueeAnnotator.tsx              | 27 ++++++++++++-
 src/client/views/PropertiesView.tsx                |  4 +-
 src/client/views/StyleProvider.scss                |  2 +-
 src/client/views/collections/TreeView.scss         | 23 +++++++++--
 src/client/views/collections/TreeView.tsx          | 16 ++++----
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 19 +++++----
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/client/views/nodes/ImageBox.tsx                | 45 ++++++++++++++++++++++
 .../views/nodes/formattedText/FormattedTextBox.tsx |  3 +-
 src/client/views/pdf/AnchorMenu.tsx                | 16 +++++++-
 11 files changed, 132 insertions(+), 27 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 68b4710ed..b73074899 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -362,7 +362,7 @@ export class MainView extends React.Component {
                         pinToPres={emptyFunction}
                         docViewPath={returnEmptyDoclist}
                         layerProvider={undefined}
-                        styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards ? DashboardStyleProvider : DefaultStyleProvider}
+                        styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards || this._sidebarContent.proto === Doc.UserDoc().myFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
                         rootSelected={returnTrue}
                         removeDocument={returnFalse}
                         ScreenToLocalTransform={this.mainContainerXf}
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 563261dec..a6b012bd6 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -5,7 +5,7 @@ import { Id } from "../../fields/FieldSymbols";
 import { List } from "../../fields/List";
 import { NumCast } from "../../fields/Types";
 import { GetEffectiveAcl } from "../../fields/util";
-import { Utils } from "../../Utils";
+import { unimplementedFunction, Utils } from "../../Utils";
 import { Docs } from "../documents/Documents";
 import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { DragManager } from "../util/DragManager";
@@ -33,6 +33,7 @@ export interface MarqueeAnnotatorProps {
     getPageFromScroll?: (top: number) => number;
     finishMarquee: (x?: number, y?: number, PointerEvent?: PointerEvent) => void;
     anchorMenuClick?: () => undefined | ((anchor: Doc) => void);
+    anchorMenuCrop?: (anchor: Doc | undefined, addCrop: boolean) => Doc | undefined;
 }
 @observer
 export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
@@ -63,6 +64,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         doc.addEventListener("pointermove", this.onSelectMove);
         doc.addEventListener("pointerup", this.onSelectEnd);
 
+        AnchorMenu.Instance.OnCrop = (e: PointerEvent) => this.props.anchorMenuCrop?.(this.highlight("rgba(173, 216, 230, 0.75)", true), true);
         AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight("rgba(173, 216, 230, 0.75)", true));
         AnchorMenu.Instance.Highlight = this.highlight;
         AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => this.highlight("rgba(173, 216, 230, 0.75)", true, savedAnnotations);
@@ -93,6 +95,29 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
                 }
             });
         });
+        /**
+         * This function is used by the AnchorMenu to create an anchor highlight and a new linked text annotation.  
+         * It also initiates a Drag/Drop interaction to place the text annotation.
+         */
+        AnchorMenu.Instance.StartCropDrag = !this.props.anchorMenuCrop ? unimplementedFunction : action((e: PointerEvent, ele: HTMLElement) => {
+            e.preventDefault();
+            e.stopPropagation();
+            var cropRegion: Doc | undefined;
+            const sourceAnchorCreator = () => {
+                cropRegion = this.highlight("rgba(173, 216, 230, 0.75)", true); // hyperlink color
+                cropRegion && this.props.addDocument(cropRegion);
+                return cropRegion;
+            };
+            const targetCreator = (annotationOn: Doc | undefined) => this.props.anchorMenuCrop!(cropRegion, false)!;
+            DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
+                dragComplete: e => {
+                    if (!e.aborted && e.linkDocument) {
+                        Doc.GetProto(e.linkDocument).linkRelationship = "cropped image";
+                        Doc.GetProto(e.linkDocument).title = "crop: " + this.props.docView.rootDoc.title;
+                    }
+                }
+            });
+        });
     }
     componentWillUnmount() {
         const doc = (this.props.iframe?.()?.contentDocument ?? document);
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 91cf83619..b63395c76 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1360,12 +1360,12 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
                     {this.optionsSubMenu}
 
+                    {this.fieldsSubMenu}
+
                     {this.sharingSubMenu}
 
                     {isNovice ? null : this.filtersSubMenu}
 
-                    {isNovice ? null : this.fieldsSubMenu}
-
                     {isNovice ? null : this.layoutSubMenu}
                 </div>;
             }
diff --git a/src/client/views/StyleProvider.scss b/src/client/views/StyleProvider.scss
index f26ed1f2d..8929954c8 100644
--- a/src/client/views/StyleProvider.scss
+++ b/src/client/views/StyleProvider.scss
@@ -25,5 +25,5 @@
 }
 
 .styleProvider-treeView-icon {
-    display: none;
+    opacity: 0;
 }
\ No newline at end of file
diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss
index 7c75810d3..4707ebb80 100644
--- a/src/client/views/collections/TreeView.scss
+++ b/src/client/views/collections/TreeView.scss
@@ -21,7 +21,9 @@
     }
 
     .treeView-bulletIcons {
-        width: $TREE_BULLET_WIDTH;
+       // width: $TREE_BULLET_WIDTH; 
+        width: 100%;
+        height: 100%;
 
         .treeView-expandIcon {
             display: none;
@@ -37,10 +39,17 @@
 
         &:hover {
             .treeView-expandIcon {
-                display: unset;
+                display: unset; 
             }
         }
     }
+    .treeView-bulletIcons:hover img {
+        left: 14px;
+        position: absolute;
+        transform-origin: center left;
+        transform: scale(6);
+        pointer-events:none;
+    }
 
     .bullet {
         position: relative;
@@ -60,6 +69,10 @@
     pointer-events: all;
 }
 
+.bullet:hover {
+    z-index: 100;
+}
+
 .treeView-openRight {
     display: none;
     height: 17px;
@@ -123,7 +136,9 @@
         }
 
         >svg {
-            display: none;
+            //display: none;
+            opacity: 0;
+            pointer-events: none;
         }
     }
 }
@@ -150,6 +165,8 @@
         >svg,
         .styleProvider-treeView-icon {
             display: inherit;
+            opacity: unset;
+            pointer-events: unset;
         }
     }
 }
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index b9710b3f5..9108acfcf 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -101,8 +101,9 @@ export class TreeView extends React.Component<TreeViewProps> {
     get displayName() { return "TreeView(" + this.props.document.title + ")"; }  // this makes mobx trace() statements more descriptive
     get defaultExpandedView() {
         return this.doc.viewType === CollectionViewType.Docking ? this.fieldKey :
-            this.props.treeView.fileSysMode ? (this.doc.isFolder ? this.fieldKey : "layout") :
-                this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.UserDoc().noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields");
+            this.props.treeView.dashboardMode ? this.fieldKey :
+                this.props.treeView.fileSysMode ? (this.doc.isFolder ? this.fieldKey : "aliases") :
+                    this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.UserDoc().noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields");
     }
 
     @computed get doc() { return this.props.document; }
@@ -491,15 +492,12 @@ export class TreeView extends React.Component<TreeViewProps> {
     }
 
     @computed get validExpandViewTypes() {
-        if (this.props.treeView.dashboardMode && Doc.UserDoc().noviceMode) {
-            return [this.doc.viewType === CollectionViewType.Docking ? this.fieldKey : "layout"];
-        }
-        const annos = () => DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : "";
-        const links = () => DocListCast(this.doc.links).length ? "links" : "";
-        const data = () => this.childDocs ? this.fieldKey : "";
+        const annos = () => DocListCast(this.doc[this.fieldKey + "-annotations"]).length && !this.props.treeView.dashboardMode ? "annotations" : "";
+        const links = () => DocListCast(this.doc.links).length && !this.props.treeView.dashboardMode ? "links" : "";
+        const data = () => this.childDocs || this.props.treeView.dashboardMode ? this.fieldKey : "";
         const aliases = () => this.props.treeView.dashboardMode ? "" : "aliases";
         const fields = () => Doc.UserDoc().noviceMode ? "" : "fields";
-        const layout = this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"];
+        const layout = (this.props.treeView.dashboardMode && Doc.UserDoc().noviceMode) || this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"];
         return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()].filter(m => m);
     }
     @action
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index bdae5a4f8..f0b3d70a0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -936,8 +936,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (deltaScale * invTransform.Scale > 20) {
             deltaScale = 20 / invTransform.Scale;
         }
-        if (deltaScale * invTransform.Scale < 1 && this.isAnnotationOverlay) {
-            deltaScale = 1 / invTransform.Scale;
+        if (deltaScale * invTransform.Scale < NumCast(this.rootDoc._viewScaleMin, 1) && this.isAnnotationOverlay) {
+            deltaScale = NumCast(this.rootDoc._viewScaleMin, 1) / invTransform.Scale;
         }
 
         const localTransform = this.getLocalTransform().inverse().scaleAbout(deltaScale, x, y);
@@ -989,8 +989,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || CurrentUserUtils.OverlayDocs.includes(this.Document)) {
             this._viewTransition = panTime;
             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));
+            const minScale = NumCast(this.rootDoc._viewScaleMin, 1);
+            const minPanX = NumCast(this.rootDoc._panXMin, 0);
+            const minPanY = NumCast(this.rootDoc._panYMin, 0);
+            const newPanX = Math.min(
+                minPanX + (1 - minScale / scale) * NumCast(this.rootDoc._panXMax, this.nativeWidth), Math.max(minPanX, panX));
+            const newPanY = Math.min((this.props.Document.scrollHeight !== undefined ? NumCast(this.Document.scrollHeight) :
+                minPanY + (1 - minScale / scale) * NumCast(this.rootDoc._panYMax, this.nativeHeight)), Math.max(minPanY, panY));
             !this.Document._verticalScroll && (this.Document._panX = this.isAnnotationOverlay ? newPanX : panX);
             !this.Document._horizontalScroll && (this.Document._panY = this.isAnnotationOverlay ? newPanY : panY);
         }
@@ -1056,9 +1061,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 HistoryUtil.pushState(state);
             }
         }
-        if (SelectionManager.Views().length !== 1 || SelectionManager.Views()[0].Document !== doc) {
-            SelectionManager.DeselectAll();
-        }
+        // if (SelectionManager.Views().length !== 1 || SelectionManager.Views()[0].Document !== doc) {
+        //     SelectionManager.DeselectAll();
+        // }
         if (this.props.Document.scrollHeight || this.props.Document.scrollTop !== undefined) {
             this.props.focus(doc, options);
         } else {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 20eabe6a9..9d738180b 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1084,7 +1084,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         TraceMobx();
         const thumb = ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url.href.replace(".png", "_m.png");
         const isButton = this.props.Document.type === DocumentType.FONTICON;
-        if (!(this.props.Document instanceof Doc) || GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate || this.hidden) return null;
+        if (!(this.props.Document instanceof Doc) || GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate || (this.hidden && !this.props.treeViewDoc)) return null;
         return this.docContents ??
             <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
                 id={this.props.Document[Id]}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index a1de944d8..aadad5ffa 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -14,6 +14,8 @@ import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
+import { Docs, DocUtils } from '../../documents/Documents';
+import { DocumentType } from '../../documents/DocumentTypes';
 import { Networking } from '../../Network';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { DragManager } from '../../util/DragManager';
@@ -127,6 +129,48 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this.layoutDoc._height = w;
     });
 
+    crop = (region: Doc | undefined, addCrop?: boolean) => {
+        if (!region) return;
+        const cropping = Doc.MakeCopy(region, true);
+        Doc.GetProto(region).lockedPosition = true;
+        Doc.GetProto(region).title = "region:" + this.rootDoc.title;
+        Doc.GetProto(region).isPushpin = true;
+        this.addDocument(region);
+        const anchx = NumCast(cropping.x);
+        const anchy = NumCast(cropping.y);
+        const anchw = NumCast(cropping._width);
+        const anchh = NumCast(cropping._height);
+        const viewScale = NumCast(this.rootDoc[this.fieldKey + "-nativeWidth"]) / anchw;
+        cropping.title = "crop: " + this.rootDoc.title;
+        cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
+        cropping.y = NumCast(this.rootDoc.y);
+        cropping._width = anchw * (this.props.scaling?.() || 1);
+        cropping._height = anchh * (this.props.scaling?.() || 1);
+        cropping.isLinkButton = undefined;
+        const croppingProto = Doc.GetProto(cropping);
+        croppingProto.annotationOn = undefined;
+        croppingProto.isPrototype = true;
+        croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
+        croppingProto.type = DocumentType.IMG;
+        croppingProto.layout = ImageBox.LayoutString("data")
+        croppingProto.data = ObjectField.MakeCopy(this.rootDoc[this.fieldKey] as ObjectField);
+        croppingProto["data-nativeWidth"] = anchw;
+        croppingProto["data-nativeHeight"] = anchh;
+        croppingProto.viewScale = viewScale;
+        croppingProto.viewScaleMin = viewScale;
+        croppingProto.panX = anchx / viewScale;
+        croppingProto.panY = anchy / viewScale;
+        croppingProto.panXMin = (anchx) / viewScale;
+        croppingProto.panXMax = (anchw) / viewScale;
+        croppingProto.panYMin = (anchy) / viewScale;
+        croppingProto.panYMax = (anchh) / viewScale;
+        if (addCrop) {
+            DocUtils.MakeLink({ doc: region }, { doc: cropping }, "cropped image", "");
+        }
+        this.props.bringToFront(cropping);
+        return cropping;
+    }
+
     specificContextMenu = (e: React.MouseEvent): void => {
         const field = Cast(this.dataDoc[this.fieldKey], ImageField);
         if (field) {
@@ -367,6 +411,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     savedAnnotations={this._savedAnnotations}
                     annotationLayer={this._annotationLayer.current}
                     mainCont={this._mainCont.current}
+                    anchorMenuCrop={this.crop}
                 />}
         </div >);
     }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 710270be4..41e2fc266 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -19,7 +19,7 @@ import { RichTextField } from "../../../../fields/RichTextField";
 import { RichTextUtils } from '../../../../fields/RichTextUtils';
 import { Cast, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
 import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util';
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils';
 import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils';
 import { DocServer } from "../../../DocServer";
 import { Docs, DocUtils } from '../../../documents/Documents';
@@ -219,6 +219,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             return undefined;
         });
         AnchorMenu.Instance.onMakeAnchor = this.getAnchor;
+        AnchorMenu.Instance.StartCropDrag = unimplementedFunction;
         /**
          * This function is used by the PDFmenu to create an anchor highlight and a new linked text annotation.  
          * It also initiates a Drag/Drop interaction to place the text annotation.
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index ad3afb775..29d068817 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -46,8 +46,10 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
 
     public onMakeAnchor: () => Opt<Doc> = () => undefined; // Method to get anchor from text search
 
+    public OnCrop: (e: PointerEvent) => void = unimplementedFunction;
     public OnClick: (e: PointerEvent) => void = unimplementedFunction;
     public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
+    public StartCropDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
     public Highlight: (color: string, isPushpin: boolean) => Opt<Doc> = (color: string, isPushpin: boolean) => undefined;
     public GetAnchor: (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => Opt<Doc> = () => undefined;
     public Delete: () => void = unimplementedFunction;
@@ -79,6 +81,13 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
         }, returnFalse, e => this.OnClick?.(e));
     }
 
+    cropDown = (e: React.PointerEvent) => {
+        setupMoveUpEvents(this, e, (e: PointerEvent) => {
+            this.StartCropDrag(e, this._commentCont.current!);
+            return true;
+        }, returnFalse, e => this.OnCrop?.(e));
+    }
+
     @action
     highlightClicked = (e: React.MouseEvent) => {
         if (!this.Highlight(this.highlightColor, false) && this.Pinned) {
@@ -161,7 +170,12 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
                         <FontAwesomeIcon style={{ position: "absolute", transform: "scale(0.5)", transformOrigin: "top left", top: 12, left: 12 }} icon={"link"} size="lg" />
                     </button>
                 </Tooltip>,
-                <LinkPopup key="popup" showPopup={this._showLinkPopup} linkFrom={this.onMakeAnchor} />
+                <LinkPopup key="popup" showPopup={this._showLinkPopup} linkFrom={this.onMakeAnchor} />,
+                AnchorMenu.Instance.StartCropDrag === unimplementedFunction ? <></> : <Tooltip key="crop" title={<div className="dash-tooltip">{"Click/Drag to create cropped image"}</div>}>
+                    <button className="antimodeMenu-button annotate" onPointerDown={this.cropDown} style={{ cursor: "grab" }}>
+                        <FontAwesomeIcon icon="image" size="lg" />
+                    </button>
+                </Tooltip>,
             ] : [
                 <Tooltip key="trash" title={<div className="dash-tooltip">{"Remove Link Anchor"}</div>}>
                     <button className="antimodeMenu-button" onPointerDown={this.Delete}>
-- 
cgit v1.2.3-70-g09d2


From 36261eac67ae6f872c9be2c9766d21ac24698f8e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 28 Apr 2022 01:37:27 -0400
Subject: fixed linkEditor to work when invoked from documentButtonsBar and
 properties sidebar is up.

---
 src/client/views/MainView.tsx                       |  2 +-
 src/client/views/PropertiesDocBacklinksSelector.tsx |  3 ++-
 src/client/views/linking/LinkMenu.tsx               | 20 ++++++++++++++------
 src/client/views/linking/LinkMenuGroup.tsx          |  2 ++
 src/client/views/linking/LinkMenuItem.tsx           |  6 +++---
 src/client/views/nodes/DocumentLinksButton.tsx      | 10 ++--------
 6 files changed, 24 insertions(+), 19 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index b73074899..c794c73db 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -632,7 +632,7 @@ export class MainView extends React.Component {
             <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} />
             {this.topbar}
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
-            {DocumentLinksButton.LinkEditorDocView ? <LinkMenu docView={DocumentLinksButton.LinkEditorDocView} /> : (null)}
+            {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => DocumentLinksButton.LinkEditorDocView = undefined)} docView={DocumentLinksButton.LinkEditorDocView} /> : (null)}
             {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)}
             <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 2001 }} >
                 <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx
index dce3e35e8..4ead8eaf0 100644
--- a/src/client/views/PropertiesDocBacklinksSelector.tsx
+++ b/src/client/views/PropertiesDocBacklinksSelector.tsx
@@ -3,6 +3,7 @@ import { observer } from "mobx-react";
 import * as React from "react";
 import { Doc, DocListCast } from "../../fields/Doc";
 import { Cast } from "../../fields/Types";
+import { emptyFunction } from "../../Utils";
 import { DocumentType } from "../documents/DocumentTypes";
 import { LinkManager } from "../util/LinkManager";
 import { SelectionManager } from "../util/SelectionManager";
@@ -46,7 +47,7 @@ export class PropertiesDocBacklinksSelector extends React.Component<PropertiesDo
     render() {
         return !SelectionManager.Views().length ? (null) : <div>
             {this.props.hideTitle ? (null) : <p key="contexts">Contexts:</p>}
-            <LinkMenu docView={SelectionManager.Views().lastElement()} itemHandler={this.getOnClick} position={{ x: 0 }} />
+            <LinkMenu docView={SelectionManager.Views().lastElement()} clearLinkEditor={emptyFunction} itemHandler={this.getOnClick} position={{ x: 0 }} />
         </div>;
     }
 }
\ No newline at end of file
diff --git a/src/client/views/linking/LinkMenu.tsx b/src/client/views/linking/LinkMenu.tsx
index bc922be36..a564c59d3 100644
--- a/src/client/views/linking/LinkMenu.tsx
+++ b/src/client/views/linking/LinkMenu.tsx
@@ -1,9 +1,10 @@
-import { action, computed, observable } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
 import { observer } from "mobx-react";
 import { Doc } from "../../../fields/Doc";
 import { LinkManager } from "../../util/LinkManager";
+import { SelectionManager } from "../../util/SelectionManager";
 import { DocumentLinksButton } from "../nodes/DocumentLinksButton";
-import { DocumentView, DocumentViewSharedProps } from "../nodes/DocumentView";
+import { DocumentView } from "../nodes/DocumentView";
 import { LinkDocPreview } from "../nodes/LinkDocPreview";
 import { LinkEditor } from "./LinkEditor";
 import './LinkMenu.scss';
@@ -14,6 +15,7 @@ interface Props {
     docView: DocumentView;
     position?: { x?: number, y?: number };
     itemHandler?: (doc: Doc) => void;
+    clearLinkEditor: () => void;
 }
 
 /**
@@ -21,7 +23,7 @@ interface Props {
  */
 @observer
 export class LinkMenu extends React.Component<Props> {
-    private _editorRef = React.createRef<HTMLDivElement>();
+    _editorRef = React.createRef<HTMLDivElement>();
     @observable _editingLink?: Doc;
     @observable _linkMenuRef = React.createRef<HTMLDivElement>();
 
@@ -29,16 +31,21 @@ export class LinkMenu extends React.Component<Props> {
         return this.props.position ?? (dv => ({ x: dv?.left || 0, y: (dv?.bottom || 0) + 15 }))(this.props.docView.getBounds());
     }
 
+    clear = action(() => {
+        this.props.clearLinkEditor();
+        this._editingLink = undefined;
+    });
+
     componentDidMount() { document.addEventListener("pointerdown", this.onPointerDown); }
     componentWillUnmount() { document.removeEventListener("pointerdown", this.onPointerDown); }
 
-    onPointerDown = (e: PointerEvent) => {
+    onPointerDown = action((e: PointerEvent) => {
         LinkDocPreview.Clear();
         if (!this._linkMenuRef.current?.contains(e.target as any) &&
             !this._editorRef.current?.contains(e.target as any)) {
-            DocumentLinksButton.ClearLinkEditor();
+            this.clear();
         }
-    }
+    });
 
     /**
      * maps each link to a JSX element to be rendered
@@ -54,6 +61,7 @@ export class LinkMenu extends React.Component<Props> {
                 sourceDoc={this.props.docView.props.Document}
                 group={group[1]}
                 groupType={group[0]}
+                clearLinkEditor={this.clear}
                 showEditor={action(linkDoc => this._editingLink = linkDoc)} />);
 
         return linkItems.length ? linkItems : this.props.position ? [<></>] : [<p key="">No links have been created yet. Drag the linking button onto another document to create a link.</p>];
diff --git a/src/client/views/linking/LinkMenuGroup.tsx b/src/client/views/linking/LinkMenuGroup.tsx
index 5b1e29e67..fa6a2f506 100644
--- a/src/client/views/linking/LinkMenuGroup.tsx
+++ b/src/client/views/linking/LinkMenuGroup.tsx
@@ -13,6 +13,7 @@ interface LinkMenuGroupProps {
     sourceDoc: Doc;
     group: Doc[];
     groupType: string;
+    clearLinkEditor: () => void;
     showEditor: (linkDoc: Doc) => void;
     docView: DocumentView;
     itemHandler?: (doc: Doc) => void;
@@ -53,6 +54,7 @@ export class LinkMenuGroup extends React.Component<LinkMenuGroupProps> {
                     linkDoc={linkDoc}
                     sourceDoc={this.props.sourceDoc}
                     destinationDoc={destination}
+                    clearLinkEditor={this.props.clearLinkEditor}
                     showEditor={this.props.showEditor}
                     menuRef={this._menuRef} />;
             }
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index d6400a6b3..3ddcf803d 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -13,7 +13,6 @@ import { DragManager } from '../../util/DragManager';
 import { Hypothesis } from '../../util/HypothesisUtils';
 import { LinkManager } from '../../util/LinkManager';
 import { undoBatch } from '../../util/UndoManager';
-import { DocumentLinksButton } from '../nodes/DocumentLinksButton';
 import { DocumentView } from '../nodes/DocumentView';
 import { LinkDocPreview } from '../nodes/LinkDocPreview';
 import './LinkMenuItem.scss';
@@ -26,6 +25,7 @@ interface LinkMenuItemProps {
     docView: DocumentView;
     sourceDoc: Doc;
     destinationDoc: Doc;
+    clearLinkEditor: () => void;
     showEditor: (linkDoc: Doc) => void;
     menuRef: React.Ref<HTMLDivElement>;
     itemHandler?: (doc: Doc) => void;
@@ -92,12 +92,12 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
                 const eleClone: any = this._drag.current!.cloneNode(true);
                 eleClone.style.transform = `translate(${e.x}px, ${e.y}px)`;
                 StartLinkTargetsDrag(eleClone, this.props.docView, e.x, e.y, this.props.sourceDoc, [this.props.linkDoc]);
-                DocumentLinksButton.ClearLinkEditor();
+                this.props.clearLinkEditor();
                 return true;
             },
             emptyFunction,
             () => {
-                DocumentLinksButton.ClearLinkEditor();
+                this.props.clearLinkEditor();
                 if (this.props.itemHandler) {
 
                     this.props.itemHandler?.(this.props.linkDoc);
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index 46537df7e..9b79db319 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -1,21 +1,17 @@
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { Tooltip } from "@material-ui/core";
-import { action, computed, observable, runInAction, trace } from "mobx";
+import { action, computed, observable, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, Opt } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
-import { ScriptField } from "../../../fields/ScriptField";
 import { StrCast } from "../../../fields/Types";
 import { TraceMobx } from "../../../fields/util";
 import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../Utils";
-import { DocServer } from "../../DocServer";
-import { Docs, DocUtils } from "../../documents/Documents";
+import { DocUtils } from "../../documents/Documents";
 import { DragManager } from "../../util/DragManager";
 import { Hypothesis } from "../../util/HypothesisUtils";
 import { LinkManager } from "../../util/LinkManager";
 import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { Colors } from "../global/globalEnums";
-import { LightboxView } from "../LightboxView";
 import './DocumentLinksButton.scss';
 import { DocumentView } from "./DocumentView";
 import { LinkDescriptionPopup } from "./LinkDescriptionPopup";
@@ -46,8 +42,6 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp
     @observable public static invisibleWebDoc: Opt<Doc>;
     public static invisibleWebRef = React.createRef<HTMLDivElement>();
 
-    @action public static ClearLinkEditor() { DocumentLinksButton.LinkEditorDocView = undefined; }
-
     @action @undoBatch
     onLinkButtonMoved = (e: PointerEvent) => {
         if (this.props.InMenu && this.props.StartLink) {
-- 
cgit v1.2.3-70-g09d2


From 416d9a1ef143cffed341fdb390a245b82c4b4137 Mon Sep 17 00:00:00 2001
From: Jenny Yu <jennyyu212@outlook.com>
Date: Thu, 28 Apr 2022 17:55:49 -0400
Subject: feat: hiding recording

---
 src/client/views/MainView.tsx                      | 1220 +++++++++---------
 .../views/nodes/RecordingBox/RecordingView.tsx     |   41 -
 src/client/views/nodes/button/FontIconBox.tsx      | 1300 ++++++++++----------
 src/client/views/nodes/trails/PresElementBox.tsx   |   55 +-
 4 files changed, 1306 insertions(+), 1310 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 8e9be1eeb..7ee9f5b70 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -68,310 +68,342 @@ const _global = (window /* browser */ || global /* node */) as any;
 
 @observer
 export class MainView extends React.Component {
-    public static Instance: MainView;
-    public static Live: boolean = false;
-    private _docBtnRef = React.createRef<HTMLDivElement>();
-    @observable public LastButton: Opt<Doc>;
-    @observable private _windowWidth: number = 0;
-    @observable private _windowHeight: number = 0;
-    @observable private _dashUIWidth: number = 0;   // width of entire main dashboard region including left menu buttons and properties panel (but not including the dashboard selector button row)
-    @observable private _dashUIHeight: number = 0;  // height of entire main dashboard region including top menu buttons
-    @observable private _panelContent: string = "none";
-    @observable private _sidebarContent: any = this.userDoc?.sidebar;
-    @observable private _leftMenuFlyoutWidth: number = 0;
-
-    @computed private get dashboardTabHeight() { return 27; } //  27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js
-    @computed private get topOfDashUI() { return Number(DASHBOARD_SELECTOR_HEIGHT.replace("px", "")); }
-    @computed private get topOfMainDoc() { return this.topOfDashUI + this.topMenuHeight(); }
-    @computed private get topOfMainDocContent() { return this.topOfMainDoc + this.dashboardTabHeight; }
-    @computed private get leftScreenOffsetOfMainDocView() { return this.leftMenuWidth() - 2; }
-    @computed private get userDoc() { return Doc.UserDoc(); }
-    @computed private get colorScheme() { return StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme); }
-    @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; }
-    @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs?.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
-
-    topMenuHeight = () => 35;
-    topMenuWidth = returnZero; // value is ignored ...
-    leftMenuWidth = () => Number(LEFT_MENU_WIDTH.replace("px", ""));
-    leftMenuHeight = () => this._dashUIHeight;
-    leftMenuFlyoutWidth = () => this._leftMenuFlyoutWidth;
-    leftMenuFlyoutHeight = () => this._dashUIHeight;
-    propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, CurrentUserUtils.propertiesWidth || 0));
-    propertiesHeight = () => this._dashUIHeight;
-    mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth();
-    mainDocViewHeight = () => this._dashUIHeight;
-
-    componentDidMount() {
-        document.getElementById("root")?.addEventListener("scroll", e => ((ele) => ele.scrollLeft = ele.scrollTop = 0)(document.getElementById("root")!));
-        const ele = document.getElementById("loader");
-        const prog = document.getElementById("dash-progress");
-        if (ele && prog) {
-            // remove from DOM
-            setTimeout(() => {
-                clearTimeout();
-                prog.style.transition = "1s";
-                prog.style.width = "100%";
-            }, 0);
-            setTimeout(() => ele.outerHTML = '', 1000);
-        }
-        this._sidebarContent.proto = undefined;
-        if (!MainView.Live) {
-            DocServer.setPlaygroundFields(["dataTransition", "treeViewOpen", "autoHeight", "showSidebar", "sidebarWidthPercent", "viewTransition",
-                "panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "text-scrollHeight", "text-height", "hideMinimap",
-                "viewScale", "scrollTop", "hidden", "curPage", "viewType", "chromeHidden", "nativeWidth"]); // can play with these fields on someone else's
-        }
-        DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg)));
-
-        const tag = document.createElement('script');
-        tag.src = "https://www.youtube.com/iframe_api";
-        const firstScriptTag = document.getElementsByTagName('script')[0];
-        firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag);
-        window.removeEventListener("keydown", KeyManager.Instance.handle);
-        window.addEventListener("keydown", KeyManager.Instance.handle);
-        window.removeEventListener("keyup", KeyManager.Instance.unhandle);
-        window.addEventListener("keyup", KeyManager.Instance.unhandle);
-        window.addEventListener("paste", KeyManager.Instance.paste as any);
-        document.addEventListener("dash", (e: any) => {  // event used by chrome plugin to tell Dash which document to focus on
-            const id = FormattedTextBox.GetDocFromUrl(e.detail);
-            DocServer.GetRefField(id).then(doc => (doc instanceof Doc) ? DocumentManager.Instance.jumpToDocument(doc, false, undefined) : (null));
-        });
-        document.addEventListener("linkAnnotationToDash", Hypothesis.linkListener);
-        this.initEventListeners();
-    }
-
-    componentWillUnMount() {
-        window.removeEventListener("keyup", KeyManager.Instance.unhandle);
-        window.removeEventListener("keydown", KeyManager.Instance.handle);
-        window.removeEventListener("pointerdown", this.globalPointerDown);
-        window.removeEventListener("paste", KeyManager.Instance.paste as any);
-        document.removeEventListener("linkAnnotationToDash", Hypothesis.linkListener);
-    }
-
-    constructor(props: Readonly<{}>) {
-        super(props);
-        MainView.Instance = this;
-        CurrentUserUtils._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 !== "/home") {
-            const pathname = window.location.pathname.substr(1).split("/");
-            if (pathname.length > 1 && pathname[0] === "doc") {
-                CurrentUserUtils.MainDocId = pathname[1];
-                !this.userDoc && DocServer.GetRefField(pathname[1]).then(action(field => field instanceof Doc && (CurrentUserUtils.GuestTarget = field)));
+      public static Instance: MainView;
+      public static Live: boolean = false;
+      private _docBtnRef = React.createRef<HTMLDivElement>();
+      @observable public LastButton: Opt<Doc>;
+      @observable private _windowWidth: number = 0;
+      @observable private _windowHeight: number = 0;
+      @observable private _dashUIWidth: number = 0;   // width of entire main dashboard region including left menu buttons and properties panel (but not including the dashboard selector button row)
+      @observable private _dashUIHeight: number = 0;  // height of entire main dashboard region including top menu buttons
+      @observable private _panelContent: string = "none";
+      @observable private _sidebarContent: any = this.userDoc?.sidebar;
+      @observable private _leftMenuFlyoutWidth: number = 0;
+
+      @computed private get dashboardTabHeight() { return 27; } //  27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js
+      @computed private get topOfDashUI() { return Number(DASHBOARD_SELECTOR_HEIGHT.replace("px", "")); }
+      @computed private get topOfMainDoc() { return this.topOfDashUI + this.topMenuHeight(); }
+      @computed private get topOfMainDocContent() { return this.topOfMainDoc + this.dashboardTabHeight; }
+      @computed private get leftScreenOffsetOfMainDocView() { return this.leftMenuWidth() - 2; }
+      @computed private get userDoc() { return Doc.UserDoc(); }
+      @computed private get colorScheme() { return StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme); }
+      @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; }
+      @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs?.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
+
+      topMenuHeight = () => 35;
+      topMenuWidth = returnZero; // value is ignored ...
+      leftMenuWidth = () => Number(LEFT_MENU_WIDTH.replace("px", ""));
+      leftMenuHeight = () => this._dashUIHeight;
+      leftMenuFlyoutWidth = () => this._leftMenuFlyoutWidth;
+      leftMenuFlyoutHeight = () => this._dashUIHeight;
+      propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, CurrentUserUtils.propertiesWidth || 0));
+      propertiesHeight = () => this._dashUIHeight;
+      mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth();
+      mainDocViewHeight = () => this._dashUIHeight;
+
+      componentDidMount() {
+            document.getElementById("root")?.addEventListener("scroll", e => ((ele) => ele.scrollLeft = ele.scrollTop = 0)(document.getElementById("root")!));
+            const ele = document.getElementById("loader");
+            const prog = document.getElementById("dash-progress");
+            if (ele && prog) {
+                  // remove from DOM
+                  setTimeout(() => {
+                        clearTimeout();
+                        prog.style.transition = "1s";
+                        prog.style.width = "100%";
+                  }, 0);
+                  setTimeout(() => ele.outerHTML = '', 1000);
             }
-        }
-
-        library.add(...[fa.faEdit, fa.faTrash, fa.faTrashAlt, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faCalendar,
-        fa.faSquare, far.faSquare as any, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faMapMarker, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock,
-        fa.faLock, fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointLeft, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard,
-        fa.faQuestion, fa.faTasks, fa.faPalette, fa.faAngleLeft, fa.faAngleRight, fa.faBell, fa.faCamera, fa.faExpand, fa.faCaretDown, fa.faCaretLeft, fa.faCaretRight,
-        fa.faCaretSquareDown, fa.faCaretSquareRight, fa.faArrowsAltH, fa.faPlus, fa.faMinus, fa.faTerminal, fa.faToggleOn, fa.faFile, fa.faLocationArrow,
-        fa.faSearch, fa.faFileDownload, fa.faFileUpload, fa.faStop, fa.faCalculator, fa.faWindowMaximize, fa.faAddressCard, fa.faQuestionCircle, fa.faArrowLeft,
-        fa.faArrowRight, fa.faArrowDown, fa.faArrowUp, fa.faBolt, fa.faBullseye, fa.faCaretUp, fa.faCat, fa.faCheck, fa.faChevronRight, fa.faChevronLeft, fa.faChevronDown, fa.faChevronUp,
-        fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt,
-        fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer,
-        fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV,
-        fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes,
-        fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
-        fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
-        fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any,
-        fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle as any,
-        fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer as any, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp,
-        fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
-        fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo,
-        fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
-        fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
-        fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines,
-        fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt, fa.faSearchPlus, fa.faVolumeUp, fa.faVolumeDown]);
-        this.initAuthenticationRouters();
-    }
-
-    globalPointerDown = action((e: PointerEvent) => {
-        AudioBox.Enabled = true;
-        const targets = document.elementsFromPoint(e.x, e.y);
-        if (targets.length) {
-            const targClass = targets[0].className.toString();
-            !targClass.includes("contextMenu") && ContextMenu.Instance.closeMenu();
-            !["timeline-menu-desc", "timeline-menu-item", "timeline-menu-input"].includes(targClass) && TimelineMenu.Instance.closeMenu();
-        }
-    });
-
-    initEventListeners = () => {
-        window.addEventListener("drop", e => e.preventDefault(), false);  // prevent default behavior of navigating to a new web page
-        window.addEventListener("dragover", e => e.preventDefault(), false);
-        // document.addEventListener("pointermove", action(e => SearchBox.Instance._undoBackground = UndoManager.batchCounter ? "#000000a8" : undefined));
-        document.addEventListener("pointerdown", this.globalPointerDown);
-        document.addEventListener("click", (e: MouseEvent) => {
-            if (!e.cancelBubble) {
-                const pathstr = (e as any)?.path?.map((p: any) => p.classList?.toString()).join();
-                if (pathstr?.includes("libraryFlyout")) {
-                    SelectionManager.DeselectAll();
-                }
+            this._sidebarContent.proto = undefined;
+            if (!MainView.Live) {
+                  DocServer.setPlaygroundFields(["dataTransition", "treeViewOpen", "autoHeight", "showSidebar", "sidebarWidthPercent", "viewTransition",
+                        "panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "text-scrollHeight", "text-height", "hideMinimap",
+                        "viewScale", "scrollTop", "hidden", "curPage", "viewType", "chromeHidden", "nativeWidth"]); // can play with these fields on someone else's
             }
-        }, false);
-        document.oncontextmenu = () => false;
-    }
-
-    initAuthenticationRouters = async () => {
-        // Load the user's active dashboard, or create a new one if initial session after signup
-        const received = CurrentUserUtils.MainDocId;
-        if (received && !this.userDoc) {
-            reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()), { fireImmediately: true });
-        } else {
-            if (received && CurrentUserUtils._urlState.sharing) {
-                reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized,
-                    initialized => initialized && received && DocServer.GetRefField(received).then(docField => {
-                        if (docField instanceof Doc && docField._viewType !== CollectionViewType.Docking) {
-                            CollectionDockingView.AddSplit(docField, "right");
+            DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg)));
+
+            const tag = document.createElement('script');
+            tag.src = "https://www.youtube.com/iframe_api";
+            const firstScriptTag = document.getElementsByTagName('script')[0];
+            firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag);
+            window.removeEventListener("keydown", KeyManager.Instance.handle);
+            window.addEventListener("keydown", KeyManager.Instance.handle);
+            window.removeEventListener("keyup", KeyManager.Instance.unhandle);
+            window.addEventListener("keyup", KeyManager.Instance.unhandle);
+            window.addEventListener("paste", KeyManager.Instance.paste as any);
+            document.addEventListener("dash", (e: any) => {  // event used by chrome plugin to tell Dash which document to focus on
+                  const id = FormattedTextBox.GetDocFromUrl(e.detail);
+                  DocServer.GetRefField(id).then(doc => (doc instanceof Doc) ? DocumentManager.Instance.jumpToDocument(doc, false, undefined) : (null));
+            });
+            document.addEventListener("linkAnnotationToDash", Hypothesis.linkListener);
+            this.initEventListeners();
+      }
+
+      componentWillUnMount() {
+            window.removeEventListener("keyup", KeyManager.Instance.unhandle);
+            window.removeEventListener("keydown", KeyManager.Instance.handle);
+            window.removeEventListener("pointerdown", this.globalPointerDown);
+            window.removeEventListener("paste", KeyManager.Instance.paste as any);
+            document.removeEventListener("linkAnnotationToDash", Hypothesis.linkListener);
+      }
+
+      constructor(props: Readonly<{}>) {
+            super(props);
+            MainView.Instance = this;
+            CurrentUserUtils._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 !== "/home") {
+                  const pathname = window.location.pathname.substr(1).split("/");
+                  if (pathname.length > 1 && pathname[0] === "doc") {
+                        CurrentUserUtils.MainDocId = pathname[1];
+                        !this.userDoc && DocServer.GetRefField(pathname[1]).then(action(field => field instanceof Doc && (CurrentUserUtils.GuestTarget = field)));
+                  }
+            }
+
+            library.add(...[fa.faEdit, fa.faTrash, fa.faTrashAlt, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faCalendar, fa.faVideoSlash, fa.faVideo,
+            fa.faSquare, far.faSquare as any, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faMapMarker, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock,
+            fa.faLock, fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointLeft, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard,
+            fa.faQuestion, fa.faTasks, fa.faPalette, fa.faAngleLeft, fa.faAngleRight, fa.faBell, fa.faCamera, fa.faExpand, fa.faCaretDown, fa.faCaretLeft, fa.faCaretRight,
+            fa.faCaretSquareDown, fa.faCaretSquareRight, fa.faArrowsAltH, fa.faPlus, fa.faMinus, fa.faTerminal, fa.faToggleOn, fa.faFile, fa.faLocationArrow,
+            fa.faSearch, fa.faFileDownload, fa.faFileUpload, fa.faStop, fa.faCalculator, fa.faWindowMaximize, fa.faAddressCard, fa.faQuestionCircle, fa.faArrowLeft,
+            fa.faArrowRight, fa.faArrowDown, fa.faArrowUp, fa.faBolt, fa.faBullseye, fa.faCaretUp, fa.faCat, fa.faCheck, fa.faChevronRight, fa.faChevronLeft, fa.faChevronDown, fa.faChevronUp,
+            fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt,
+            fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer,
+            fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV,
+            fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes,
+            fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
+            fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
+            fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any,
+            fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle as any,
+            fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer as any, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp,
+            fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
+            fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo,
+            fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
+            fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
+            fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines,
+            fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt, fa.faSearchPlus, fa.faVolumeUp, fa.faVolumeDown]);
+            this.initAuthenticationRouters();
+      }
+
+      globalPointerDown = action((e: PointerEvent) => {
+            AudioBox.Enabled = true;
+            const targets = document.elementsFromPoint(e.x, e.y);
+            if (targets.length) {
+                  const targClass = targets[0].className.toString();
+                  !targClass.includes("contextMenu") && ContextMenu.Instance.closeMenu();
+                  !["timeline-menu-desc", "timeline-menu-item", "timeline-menu-input"].includes(targClass) && TimelineMenu.Instance.closeMenu();
+            }
+      });
+
+      initEventListeners = () => {
+            window.addEventListener("drop", e => e.preventDefault(), false);  // prevent default behavior of navigating to a new web page
+            window.addEventListener("dragover", e => e.preventDefault(), false);
+            // document.addEventListener("pointermove", action(e => SearchBox.Instance._undoBackground = UndoManager.batchCounter ? "#000000a8" : undefined));
+            document.addEventListener("pointerdown", this.globalPointerDown);
+            document.addEventListener("click", (e: MouseEvent) => {
+                  if (!e.cancelBubble) {
+                        const pathstr = (e as any)?.path?.map((p: any) => p.classList?.toString()).join();
+                        if (pathstr?.includes("libraryFlyout")) {
+                              SelectionManager.DeselectAll();
                         }
-                    }),
-                );
+                  }
+            }, false);
+            document.oncontextmenu = () => false;
+      }
+
+      initAuthenticationRouters = async () => {
+            // Load the user's active dashboard, or create a new one if initial session after signup
+            const received = CurrentUserUtils.MainDocId;
+            if (received && !this.userDoc) {
+                  reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()), { fireImmediately: true });
+            } else {
+                  if (received && CurrentUserUtils._urlState.sharing) {
+                        reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized,
+                              initialized => initialized && received && DocServer.GetRefField(received).then(docField => {
+                                    if (docField instanceof Doc && docField._viewType !== CollectionViewType.Docking) {
+                                          CollectionDockingView.AddSplit(docField, "right");
+                                    }
+                              }),
+                        );
+                  }
+                  const activeDash = PromiseValue(this.userDoc.activeDashboard);
+                  activeDash.then(dash => {
+                        if (dash instanceof Doc) CurrentUserUtils.openDashboard(this.userDoc, dash);
+                        else CurrentUserUtils.createNewDashboard(this.userDoc);
+                  });
             }
-            const activeDash = PromiseValue(this.userDoc.activeDashboard);
-            activeDash.then(dash => {
-                if (dash instanceof Doc) CurrentUserUtils.openDashboard(this.userDoc, dash);
-                else CurrentUserUtils.createNewDashboard(this.userDoc);
-            });
-        }
-    }
-
-    @action
-    createNewPresentation = async () => {
-        if (!await this.userDoc.myTrails) {
-            this.userDoc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], {
-                title: "TRAILS", childDontRegisterViews: true, _height: 100, _forceActive: true, boxShadow: "0 0", _lockedPosition: true, treeViewOpen: true, system: true
-            }));
-        }
-        const pres = Docs.Create.PresDocument(new List<Doc>(),
-            { title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
-        CollectionDockingView.AddSplit(pres, "right");
-        this.userDoc.activePresentation = pres;
-        Doc.AddDocToList(this.userDoc.myTrails as Doc, "data", pres);
-    }
-
-    @action
-    createNewFolder = async () => {
-        if (!await this.userDoc.myFilesystem) {
-            this.userDoc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
-            const newFolder = ScriptField.MakeFunction(`createNewFolder()`, { scriptContext: "any" })!;
-            const newFolderButton: Doc = Docs.Create.FontIconDocument({
-                onClick: newFolder, _forceActive: true, toolTip: "New folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder",
-                btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true
-            });
-            this.userDoc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([this.userDoc.myFileOrphans as Doc], {
-                title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100,
-                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, ignoreClick: true,
-                isFolder: true, treeViewType: "fileSystem", childHideLinkButton: true,
-                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true,
-                explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
-            }));
-        }
-        const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true });
-        Doc.AddDocToList(this.userDoc.myFilesystem as Doc, "data", folder);
-    }
-
-    @computed get mainDocView() {
-        return <DocumentView key="main"
-            Document={this.mainContainer!}
-            DataDoc={undefined}
-            addDocument={undefined}
-            addDocTab={this.addDocTabFunc}
-            pinToPres={emptyFunction}
-            docViewPath={returnEmptyDoclist}
-            layerProvider={undefined}
-            styleProvider={undefined}
-            rootSelected={returnTrue}
-            isContentActive={returnTrue}
-            removeDocument={undefined}
-            ScreenToLocalTransform={Transform.Identity}
-            PanelWidth={this.mainDocViewWidth}
-            PanelHeight={this.mainDocViewHeight}
-            focus={DocUtils.DefaultFocus}
-            whenChildContentsActiveChanged={emptyFunction}
-            bringToFront={emptyFunction}
-            docFilters={returnEmptyFilter}
-            docRangeFilters={returnEmptyFilter}
-            searchFilterDocs={returnEmptyDoclist}
-            ContainingCollectionView={undefined}
-            ContainingCollectionDoc={undefined}
-            suppressSetHeight={true}
-            renderDepth={-1}
-        />;
-    }
-
-    @computed get dockingContent() {
-        return <div key="docking" className={`mainView-dockingContent${this._leftMenuFlyoutWidth ? "-flyout" : ""}`} onDrop={e => { e.stopPropagation(); e.preventDefault(); }}
-            style={{
-                minWidth: `calc(100% - ${this._leftMenuFlyoutWidth + this.leftMenuWidth() + this.propertiesWidth()}px)`,
-                transform: LightboxView.LightboxDoc ? "scale(0.0001)" : undefined,
-            }}>
-            {!this.mainContainer ? (null) : this.mainDocView}
-        </div>;
-    }
-
-    @action
-    onPropertiesPointerDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e,
-            action(e => (CurrentUserUtils.propertiesWidth = Math.max(0, this._dashUIWidth - e.clientX)) ? false : false),
-            action(() => CurrentUserUtils.propertiesWidth < 5 && (CurrentUserUtils.propertiesWidth = 0)),
-            action(() => CurrentUserUtils.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._dashUIWidth - 50, 250) : 0), false);
-    }
-
-    @action
-    onFlyoutPointerDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e,
-            action(e => (this._leftMenuFlyoutWidth = Math.max(e.clientX - 58, 0)) ? false : false),
-            () => this._leftMenuFlyoutWidth < 5 && this.closeFlyout(),
-            this.closeFlyout);
-    }
-
-    sidebarScreenToLocal = () => new Transform(0, -this.topOfMainDoc, 1);
-    mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftScreenOffsetOfMainDocView, 0);
-    addDocTabFunc = (doc: Doc, location: string): boolean => {
-        const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":");
-        const locationParams = locationFields.length > 1 ? locationFields[1] : "";
-        if (doc.dockingConfig) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
-        switch (locationFields[0]) {
-            case "dashboard": return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
-            case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
-            case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
-            case "lightbox": return LightboxView.AddDocTab(doc, location);
-            case "toggle": return CollectionDockingView.ToggleSplit(doc, locationParams);
-            case "inPlace":
-            case "add":
-            default:
-                return CollectionDockingView.AddSplit(doc, locationParams);
-        }
-    }
-
-
-    @computed get flyout() {
-        return !this._leftMenuFlyoutWidth ? <div key="flyout" className={`mainView-libraryFlyout-out`}>
-            {this.docButtons}
-        </div> :
-            <div key="libFlyout" className="mainView-libraryFlyout" style={{ minWidth: this._leftMenuFlyoutWidth, width: this._leftMenuFlyoutWidth }} >
-                <div className="mainView-contentArea" >
-                    <DocumentView
-                        Document={this._sidebarContent.proto || this._sidebarContent}
+      }
+
+      @action
+      createNewPresentation = async () => {
+            if (!await this.userDoc.myTrails) {
+                  this.userDoc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], {
+                        title: "TRAILS", childDontRegisterViews: true, _height: 100, _forceActive: true, boxShadow: "0 0", _lockedPosition: true, treeViewOpen: true, system: true
+                  }));
+            }
+            const pres = Docs.Create.PresDocument(new List<Doc>(),
+                  { title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
+            CollectionDockingView.AddSplit(pres, "right");
+            this.userDoc.activePresentation = pres;
+            Doc.AddDocToList(this.userDoc.myTrails as Doc, "data", pres);
+      }
+
+      @action
+      createNewFolder = async () => {
+            if (!await this.userDoc.myFilesystem) {
+                  this.userDoc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
+                  const newFolder = ScriptField.MakeFunction(`createNewFolder()`, { scriptContext: "any" })!;
+                  const newFolderButton: Doc = Docs.Create.FontIconDocument({
+                        onClick: newFolder, _forceActive: true, toolTip: "New folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder",
+                        btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true
+                  });
+                  this.userDoc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([this.userDoc.myFileOrphans as Doc], {
+                        title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100,
+                        treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
+                        treeViewTruncateTitleWidth: 150, ignoreClick: true,
+                        isFolder: true, treeViewType: "fileSystem", childHideLinkButton: true,
+                        _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true,
+                        explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
+                  }));
+            }
+            const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true });
+            Doc.AddDocToList(this.userDoc.myFilesystem as Doc, "data", folder);
+      }
+
+      @computed get mainDocView() {
+            return <DocumentView key="main"
+                  Document={this.mainContainer!}
+                  DataDoc={undefined}
+                  addDocument={undefined}
+                  addDocTab={this.addDocTabFunc}
+                  pinToPres={emptyFunction}
+                  docViewPath={returnEmptyDoclist}
+                  layerProvider={undefined}
+                  styleProvider={undefined}
+                  rootSelected={returnTrue}
+                  isContentActive={returnTrue}
+                  removeDocument={undefined}
+                  ScreenToLocalTransform={Transform.Identity}
+                  PanelWidth={this.mainDocViewWidth}
+                  PanelHeight={this.mainDocViewHeight}
+                  focus={DocUtils.DefaultFocus}
+                  whenChildContentsActiveChanged={emptyFunction}
+                  bringToFront={emptyFunction}
+                  docFilters={returnEmptyFilter}
+                  docRangeFilters={returnEmptyFilter}
+                  searchFilterDocs={returnEmptyDoclist}
+                  ContainingCollectionView={undefined}
+                  ContainingCollectionDoc={undefined}
+                  suppressSetHeight={true}
+                  renderDepth={-1}
+            />;
+      }
+
+      @computed get dockingContent() {
+            return <div key="docking" className={`mainView-dockingContent${this._leftMenuFlyoutWidth ? "-flyout" : ""}`} onDrop={e => { e.stopPropagation(); e.preventDefault(); }}
+                  style={{
+                        minWidth: `calc(100% - ${this._leftMenuFlyoutWidth + this.leftMenuWidth() + this.propertiesWidth()}px)`,
+                        transform: LightboxView.LightboxDoc ? "scale(0.0001)" : undefined,
+                  }}>
+                  {!this.mainContainer ? (null) : this.mainDocView}
+            </div>;
+      }
+
+      @action
+      onPropertiesPointerDown = (e: React.PointerEvent) => {
+            setupMoveUpEvents(this, e,
+                  action(e => (CurrentUserUtils.propertiesWidth = Math.max(0, this._dashUIWidth - e.clientX)) ? false : false),
+                  action(() => CurrentUserUtils.propertiesWidth < 5 && (CurrentUserUtils.propertiesWidth = 0)),
+                  action(() => CurrentUserUtils.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._dashUIWidth - 50, 250) : 0), false);
+      }
+
+      @action
+      onFlyoutPointerDown = (e: React.PointerEvent) => {
+            setupMoveUpEvents(this, e,
+                  action(e => (this._leftMenuFlyoutWidth = Math.max(e.clientX - 58, 0)) ? false : false),
+                  () => this._leftMenuFlyoutWidth < 5 && this.closeFlyout(),
+                  this.closeFlyout);
+      }
+
+      sidebarScreenToLocal = () => new Transform(0, -this.topOfMainDoc, 1);
+      mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftScreenOffsetOfMainDocView, 0);
+      addDocTabFunc = (doc: Doc, location: string): boolean => {
+            const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":");
+            const locationParams = locationFields.length > 1 ? locationFields[1] : "";
+            if (doc.dockingConfig) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+            switch (locationFields[0]) {
+                  case "dashboard": return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+                  case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
+                  case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
+                  case "lightbox": return LightboxView.AddDocTab(doc, location);
+                  case "toggle": return CollectionDockingView.ToggleSplit(doc, locationParams);
+                  case "inPlace":
+                  case "add":
+                  default:
+                        return CollectionDockingView.AddSplit(doc, locationParams);
+            }
+      }
+
+
+      @computed get flyout() {
+            return !this._leftMenuFlyoutWidth ? <div key="flyout" className={`mainView-libraryFlyout-out`}>
+                  {this.docButtons}
+            </div> :
+                  <div key="libFlyout" className="mainView-libraryFlyout" style={{ minWidth: this._leftMenuFlyoutWidth, width: this._leftMenuFlyoutWidth }} >
+                        <div className="mainView-contentArea" >
+                              <DocumentView
+                                    Document={this._sidebarContent.proto || this._sidebarContent}
+                                    DataDoc={undefined}
+                                    addDocument={undefined}
+                                    addDocTab={this.addDocTabFunc}
+                                    pinToPres={emptyFunction}
+                                    docViewPath={returnEmptyDoclist}
+                                    layerProvider={undefined}
+                                    styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards || this._sidebarContent.proto === Doc.UserDoc().myFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
+                                    rootSelected={returnTrue}
+                                    removeDocument={returnFalse}
+                                    ScreenToLocalTransform={this.mainContainerXf}
+                                    PanelWidth={this.leftMenuFlyoutWidth}
+                                    PanelHeight={this.leftMenuFlyoutHeight}
+                                    renderDepth={0}
+                                    isContentActive={returnTrue}
+                                    scriptContext={CollectionDockingView.Instance?.props.Document}
+                                    focus={DocUtils.DefaultFocus}
+                                    whenChildContentsActiveChanged={emptyFunction}
+                                    bringToFront={emptyFunction}
+                                    docFilters={returnEmptyFilter}
+                                    docRangeFilters={returnEmptyFilter}
+                                    searchFilterDocs={returnEmptyDoclist}
+                                    ContainingCollectionView={undefined}
+                                    ContainingCollectionDoc={undefined}
+                              />
+                        </div>
+                        {this.docButtons}
+                  </div>;
+      }
+
+      @computed get leftMenuPanel() {
+            return <div key="menu" className="mainView-leftMenuPanel">
+                  <DocumentView
+                        Document={Doc.UserDoc().menuStack as Doc}
                         DataDoc={undefined}
                         addDocument={undefined}
                         addDocTab={this.addDocTabFunc}
                         pinToPres={emptyFunction}
-                        docViewPath={returnEmptyDoclist}
-                        layerProvider={undefined}
-                        styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards || this._sidebarContent.proto === Doc.UserDoc().myFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
                         rootSelected={returnTrue}
                         removeDocument={returnFalse}
-                        ScreenToLocalTransform={this.mainContainerXf}
-                        PanelWidth={this.leftMenuFlyoutWidth}
-                        PanelHeight={this.leftMenuFlyoutHeight}
+                        ScreenToLocalTransform={this.sidebarScreenToLocal}
+                        PanelWidth={this.leftMenuWidth}
+                        PanelHeight={this.leftMenuHeight}
                         renderDepth={0}
-                        isContentActive={returnTrue}
-                        scriptContext={CollectionDockingView.Instance?.props.Document}
+                        docViewPath={returnEmptyDoclist}
                         focus={DocUtils.DefaultFocus}
+                        styleProvider={DefaultStyleProvider}
+                        layerProvider={undefined}
+                        isContentActive={returnTrue}
                         whenChildContentsActiveChanged={emptyFunction}
                         bringToFront={emptyFunction}
                         docFilters={returnEmptyFilter}
@@ -379,333 +411,301 @@ export class MainView extends React.Component {
                         searchFilterDocs={returnEmptyDoclist}
                         ContainingCollectionView={undefined}
                         ContainingCollectionDoc={undefined}
-                    />
-                </div>
-                {this.docButtons}
+                        scriptContext={this}
+                  />
             </div>;
-    }
-
-    @computed get leftMenuPanel() {
-        return <div key="menu" className="mainView-leftMenuPanel">
-            <DocumentView
-                Document={Doc.UserDoc().menuStack as Doc}
-                DataDoc={undefined}
-                addDocument={undefined}
-                addDocTab={this.addDocTabFunc}
-                pinToPres={emptyFunction}
-                rootSelected={returnTrue}
-                removeDocument={returnFalse}
-                ScreenToLocalTransform={this.sidebarScreenToLocal}
-                PanelWidth={this.leftMenuWidth}
-                PanelHeight={this.leftMenuHeight}
-                renderDepth={0}
-                docViewPath={returnEmptyDoclist}
-                focus={DocUtils.DefaultFocus}
-                styleProvider={DefaultStyleProvider}
-                layerProvider={undefined}
-                isContentActive={returnTrue}
-                whenChildContentsActiveChanged={emptyFunction}
-                bringToFront={emptyFunction}
-                docFilters={returnEmptyFilter}
-                docRangeFilters={returnEmptyFilter}
-                searchFilterDocs={returnEmptyDoclist}
-                ContainingCollectionView={undefined}
-                ContainingCollectionDoc={undefined}
-                scriptContext={this}
-            />
-        </div>;
-    }
-
-    @action
-    selectMenu = (button: Doc) => {
-        const title = StrCast(Doc.GetProto(button).title);
-        const willOpen = !this._leftMenuFlyoutWidth || this._panelContent !== title;
-        this.closeFlyout();
-        if (willOpen) {
-            switch (this._panelContent = title) {
-                case "Settings":
-                    SettingsManager.Instance.open();
-                    break;
-                case "Help":
-                    break;
-                default:
-                    this.expandFlyout(button);
+      }
+
+      @action
+      selectMenu = (button: Doc) => {
+            const title = StrCast(Doc.GetProto(button).title);
+            const willOpen = !this._leftMenuFlyoutWidth || this._panelContent !== title;
+            this.closeFlyout();
+            if (willOpen) {
+                  switch (this._panelContent = title) {
+                        case "Settings":
+                              SettingsManager.Instance.open();
+                              break;
+                        case "Help":
+                              break;
+                        default:
+                              this.expandFlyout(button);
+                  }
             }
-        }
-        return true;
-    }
-
-    @computed get mainInnerContent() {
-        const leftMenuFlyoutWidth = this._leftMenuFlyoutWidth + this.leftMenuWidth();
-        const width = this.propertiesWidth() + leftMenuFlyoutWidth;
-        return <>
-            {this.leftMenuPanel}
-            <div key="inner" className={`mainView-innerContent${this.colorScheme}`}>
-                {this.flyout}
-                <div className="mainView-libraryHandle" style={{ left: leftMenuFlyoutWidth - 10 /* ~half width of handle */, display: !this._leftMenuFlyoutWidth ? "none" : undefined }} onPointerDown={this.onFlyoutPointerDown} >
-                    <FontAwesomeIcon icon="chevron-left" color={this.colorScheme === ColorScheme.Dark ? "white" : "black"} style={{ opacity: "50%" }} size="sm" />
-                </div>
-                <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)` }}>
-
-                    {this.dockingContent}
-
-                    <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}>
-                        <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.colorScheme === ColorScheme.Dark ? Colors.WHITE : Colors.BLACK} size="sm" />
-                    </div>
-                    <div className="properties-container" style={{ width: this.propertiesWidth() }}>
-                        {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} addDocTab={this.addDocTabFunc} width={this.propertiesWidth()} height={this.propertiesHeight()} />}
-                    </div>
-                </div>
-            </div>
-        </>;
-    }
-
-    @computed get mainDashboardArea() {
-        return !this.userDoc ? (null) :
-            <div className="mainView-dashboardArea" ref={r => {
-                r && new _global.ResizeObserver(action(() => {
-                    this._dashUIWidth = r.getBoundingClientRect().width;
-                    this._dashUIHeight = r.getBoundingClientRect().height;
-                })).observe(r);
-            }} style={{
-                color: this.colorScheme === ColorScheme.Dark ? "rgb(205,205,205)" : "black",
-                height: `calc(100% - ${this.topOfDashUI + this.topMenuHeight()}px)`,
-                width: "100%",
-            }} >
-                {this.mainInnerContent}
-            </div>;
-    }
-
-
-    expandFlyout = action((button: Doc) => {
-        // bcz: What's going on here!?  
-        // Chrome(not firefox) seems to have a bug when the flyout expands and there's a zoomed freeform tab.  All of the div below the CollectionFreeFormView's main div
-        // generate the wrong value from getClientRectangle() -- specifically they return an 'x' that is the flyout's width greater than it should be.
-        // interactively adjusting the flyout fixes the problem.  So does programmatically changing the value after a timeout to something *fractionally* different (ie, 1.5, not 1);)
-        this._leftMenuFlyoutWidth = (this._leftMenuFlyoutWidth || 250);
-        setTimeout(action(() => this._leftMenuFlyoutWidth += 0.5), 0);
-
-        this._sidebarContent.proto = button.target as any;
-        this.LastButton = button;
-    });
-
-    closeFlyout = action(() => {
-        this.LastButton = undefined;
-        this._panelContent = "none";
-        this._sidebarContent.proto = undefined;
-        this._leftMenuFlyoutWidth = 0;
-    });
-
-    remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true);
-    moveButtonDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => this.remButtonDoc(doc) && addDocument(doc);
-    addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true);
-
-    buttonBarXf = () => {
-        if (!this._docBtnRef.current) return Transform.Identity();
-        const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current);
-        return new Transform(-translateX, -translateY, 1 / scale);
-    }
-
-    @computed get docButtons() {
-        return !(this.userDoc.dockedBtns instanceof Doc) ? (null) :
-            <div className="mainView-docButtons" ref={this._docBtnRef} style={{ height: !this.userDoc.dockedBtns.linearViewIsExpanded ? "42px" : undefined }} >
-                <CollectionLinearView
-                    Document={this.userDoc.dockedBtns}
-                    DataDoc={undefined}
-                    fieldKey={"data"}
-                    dropAction={"alias"}
-                    setHeight={returnFalse}
-                    styleProvider={DefaultStyleProvider}
-                    layerProvider={undefined}
-                    rootSelected={returnTrue}
-                    bringToFront={emptyFunction}
-                    select={emptyFunction}
-                    isAnyChildContentActive={returnFalse}
-                    isContentActive={emptyFunction}
-                    isSelected={returnFalse}
-                    docViewPath={returnEmptyDoclist}
-                    moveDocument={this.moveButtonDoc}
-                    CollectionView={undefined}
-                    addDocument={this.addButtonDoc}
-                    addDocTab={this.addDocTabFunc}
-                    pinToPres={emptyFunction}
-                    removeDocument={this.remButtonDoc}
-                    ScreenToLocalTransform={this.buttonBarXf}
-                    PanelWidth={this.leftMenuFlyoutWidth}
-                    PanelHeight={this.leftMenuFlyoutHeight}
-                    renderDepth={0}
-                    focus={DocUtils.DefaultFocus}
-                    whenChildContentsActiveChanged={emptyFunction}
-                    docFilters={returnEmptyFilter}
-                    docRangeFilters={returnEmptyFilter}
-                    searchFilterDocs={returnEmptyDoclist}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined} />
-                {['watching', 'recording'].includes(String(this.userDoc?.presentationMode) ?? '') ? <div style={{ border: '.5rem solid green', padding: '5px' }}>{this.userDoc?.presentationMode}</div>: <></>}
+            return true;
+      }
+
+      @computed get mainInnerContent() {
+            const leftMenuFlyoutWidth = this._leftMenuFlyoutWidth + this.leftMenuWidth();
+            const width = this.propertiesWidth() + leftMenuFlyoutWidth;
+            return <>
+                  {this.leftMenuPanel}
+                  <div key="inner" className={`mainView-innerContent${this.colorScheme}`}>
+                        {this.flyout}
+                        <div className="mainView-libraryHandle" style={{ left: leftMenuFlyoutWidth - 10 /* ~half width of handle */, display: !this._leftMenuFlyoutWidth ? "none" : undefined }} onPointerDown={this.onFlyoutPointerDown} >
+                              <FontAwesomeIcon icon="chevron-left" color={this.colorScheme === ColorScheme.Dark ? "white" : "black"} style={{ opacity: "50%" }} size="sm" />
+                        </div>
+                        <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)` }}>
+
+                              {this.dockingContent}
+
+                              <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}>
+                                    <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.colorScheme === ColorScheme.Dark ? Colors.WHITE : Colors.BLACK} size="sm" />
+                              </div>
+                              <div className="properties-container" style={{ width: this.propertiesWidth() }}>
+                                    {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} addDocTab={this.addDocTabFunc} width={this.propertiesWidth()} height={this.propertiesHeight()} />}
+                              </div>
+                        </div>
+                  </div>
+            </>;
+      }
+
+      @computed get mainDashboardArea() {
+            return !this.userDoc ? (null) :
+                  <div className="mainView-dashboardArea" ref={r => {
+                        r && new _global.ResizeObserver(action(() => {
+                              this._dashUIWidth = r.getBoundingClientRect().width;
+                              this._dashUIHeight = r.getBoundingClientRect().height;
+                        })).observe(r);
+                  }} style={{
+                        color: this.colorScheme === ColorScheme.Dark ? "rgb(205,205,205)" : "black",
+                        height: `calc(100% - ${this.topOfDashUI + this.topMenuHeight()}px)`,
+                        width: "100%",
+                  }} >
+                        {this.mainInnerContent}
+                  </div>;
+      }
+
+
+      expandFlyout = action((button: Doc) => {
+            // bcz: What's going on here!?  
+            // Chrome(not firefox) seems to have a bug when the flyout expands and there's a zoomed freeform tab.  All of the div below the CollectionFreeFormView's main div
+            // generate the wrong value from getClientRectangle() -- specifically they return an 'x' that is the flyout's width greater than it should be.
+            // interactively adjusting the flyout fixes the problem.  So does programmatically changing the value after a timeout to something *fractionally* different (ie, 1.5, not 1);)
+            this._leftMenuFlyoutWidth = (this._leftMenuFlyoutWidth || 250);
+            setTimeout(action(() => this._leftMenuFlyoutWidth += 0.5), 0);
+
+            this._sidebarContent.proto = button.target as any;
+            this.LastButton = button;
+      });
+
+      closeFlyout = action(() => {
+            this.LastButton = undefined;
+            this._panelContent = "none";
+            this._sidebarContent.proto = undefined;
+            this._leftMenuFlyoutWidth = 0;
+      });
+
+      remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true);
+      moveButtonDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => this.remButtonDoc(doc) && addDocument(doc);
+      addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true);
+
+      buttonBarXf = () => {
+            if (!this._docBtnRef.current) return Transform.Identity();
+            const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current);
+            return new Transform(-translateX, -translateY, 1 / scale);
+      }
+
+      @computed get docButtons() {
+            return !(this.userDoc.dockedBtns instanceof Doc) ? (null) :
+                  <div className="mainView-docButtons" ref={this._docBtnRef} style={{ height: !this.userDoc.dockedBtns.linearViewIsExpanded ? "42px" : undefined }} >
+                        <CollectionLinearView
+                              Document={this.userDoc.dockedBtns}
+                              DataDoc={undefined}
+                              fieldKey={"data"}
+                              dropAction={"alias"}
+                              setHeight={returnFalse}
+                              styleProvider={DefaultStyleProvider}
+                              layerProvider={undefined}
+                              rootSelected={returnTrue}
+                              bringToFront={emptyFunction}
+                              select={emptyFunction}
+                              isAnyChildContentActive={returnFalse}
+                              isContentActive={emptyFunction}
+                              isSelected={returnFalse}
+                              docViewPath={returnEmptyDoclist}
+                              moveDocument={this.moveButtonDoc}
+                              CollectionView={undefined}
+                              addDocument={this.addButtonDoc}
+                              addDocTab={this.addDocTabFunc}
+                              pinToPres={emptyFunction}
+                              removeDocument={this.remButtonDoc}
+                              ScreenToLocalTransform={this.buttonBarXf}
+                              PanelWidth={this.leftMenuFlyoutWidth}
+                              PanelHeight={this.leftMenuFlyoutHeight}
+                              renderDepth={0}
+                              focus={DocUtils.DefaultFocus}
+                              whenChildContentsActiveChanged={emptyFunction}
+                              docFilters={returnEmptyFilter}
+                              docRangeFilters={returnEmptyFilter}
+                              searchFilterDocs={returnEmptyDoclist}
+                              ContainingCollectionView={undefined}
+                              ContainingCollectionDoc={undefined} />
+                        {['watching', 'recording'].includes(String(this.userDoc?.presentationMode) ?? '') ? <div style={{ border: '.5rem solid green', padding: '5px' }}>{this.userDoc?.presentationMode}</div> : <></>}
+                  </div>;
+      }
+      @computed get snapLines() {
+            return !this.userDoc.showSnapLines ? (null) : <div className="mainView-snapLines">
+                  <svg style={{ width: "100%", height: "100%" }}>
+                        {SnappingManager.horizSnapLines().map(l => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
+                        {SnappingManager.vertSnapLines().map(l => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
+                  </svg>
             </div>;
-    }
-    @computed get snapLines() {
-        return !this.userDoc.showSnapLines ? (null) : <div className="mainView-snapLines">
-            <svg style={{ width: "100%", height: "100%" }}>
-                {SnappingManager.horizSnapLines().map(l => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
-                {SnappingManager.vertSnapLines().map(l => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
-            </svg>
-        </div>;
-    }
-
-    @computed get inkResources() {
-        return <svg width={0} height={0}>
-            <defs>
-                <filter id="inkSelectionHalo">
-                    <feColorMatrix type="matrix"
-                        result="color"
-                        values="1 0 0 0 0
+      }
+
+      @computed get inkResources() {
+            return <svg width={0} height={0}>
+                  <defs>
+                        <filter id="inkSelectionHalo">
+                              <feColorMatrix type="matrix"
+                                    result="color"
+                                    values="1 0 0 0 0
                 0 0 0 0 0
                 0 0 0 0 0
                 0 0 0 1 0">
-                    </feColorMatrix>
-                    <feGaussianBlur in="color" stdDeviation="4" result="blur"></feGaussianBlur>
-                    <feOffset in="blur" dx="0" dy="0" result="offset"></feOffset>
-                    <feMerge>
-                        <feMergeNode in="bg"></feMergeNode>
-                        <feMergeNode in="offset"></feMergeNode>
-                        <feMergeNode in="SourceGraphic"></feMergeNode>
-                    </feMerge>
-                </filter>
-            </defs>
-        </svg>;
-    }
-
-    @computed get topbar() {
-        TraceMobx();
-        return <div className="mainView-topbar">
-            <TopBar />
-        </div>;
-    }
-
-    @computed get invisibleWebBox() { // see note under the makeLink method in HypothesisUtils.ts
-        return !DocumentLinksButton.invisibleWebDoc ? null :
-            <div className="mainView-invisibleWebRef" ref={DocumentLinksButton.invisibleWebRef}>
-                <WebBox
-                    fieldKey={"data"}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined}
-                    Document={DocumentLinksButton.invisibleWebDoc}
-                    dropAction={"move"}
-                    layerProvider={undefined}
-                    styleProvider={undefined}
-                    isSelected={returnFalse}
-                    select={returnFalse}
-                    setHeight={returnFalse}
-                    rootSelected={returnFalse}
-                    renderDepth={0}
-                    addDocTab={returnFalse}
-                    pinToPres={returnFalse}
-                    ScreenToLocalTransform={Transform.Identity}
-                    bringToFront={returnFalse}
-                    isContentActive={emptyFunction}
-                    whenChildContentsActiveChanged={returnFalse}
-                    focus={returnFalse}
-                    docViewPath={returnEmptyDoclist}
-                    PanelWidth={() => 500}
-                    PanelHeight={() => 800}
-                    docFilters={returnEmptyFilter}
-                    docRangeFilters={returnEmptyFilter}
-                    searchFilterDocs={returnEmptyDoclist}
-                />
+                              </feColorMatrix>
+                              <feGaussianBlur in="color" stdDeviation="4" result="blur"></feGaussianBlur>
+                              <feOffset in="blur" dx="0" dy="0" result="offset"></feOffset>
+                              <feMerge>
+                                    <feMergeNode in="bg"></feMergeNode>
+                                    <feMergeNode in="offset"></feMergeNode>
+                                    <feMergeNode in="SourceGraphic"></feMergeNode>
+                              </feMerge>
+                        </filter>
+                  </defs>
+            </svg>;
+      }
+
+      @computed get topbar() {
+            TraceMobx();
+            return <div className="mainView-topbar">
+                  <TopBar />
             </div>;
-    }
-
-    render() {
-        return (<div className={`mainView-container${this.colorScheme}`}
-            onScroll={() => ((ele) => ele.scrollTop = ele.scrollLeft = 0)(document.getElementById("root")!)}
-            ref={r => {
-                r && new _global.ResizeObserver(action(() => { this._windowWidth = r.getBoundingClientRect().width; this._windowHeight = r.getBoundingClientRect().height; })).observe(r);
-            }}>
-            {this.inkResources}
-            <DictationOverlay />
-            <SharingManager />
-            <SettingsManager />
-            <CaptureManager />
-            <GroupManager />
-            <GoogleAuthenticationManager />
-            <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} />
-            <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} />
-            {this.topbar}
-            {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
-            {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => DocumentLinksButton.LinkEditorDocView = undefined)} docView={DocumentLinksButton.LinkEditorDocView} /> : (null)}
-            {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)}
-            <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 2001 }} >
-                <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
-            </div>
-            <GestureOverlay >
-                {this.mainDashboardArea}
-            </GestureOverlay>
-            <PreviewCursor />
-            <TaskCompletionBox />
-            <ContextMenu />
-            <RadialMenu />
-            <AnchorMenu />
-            <MarqueeOptionsMenu />
-            <OverlayView />
-            <TimelineMenu />
-            <RichTextMenu />
-            {this.snapLines}
-            <div className="mainView-webRef" ref={this.makeWebRef} />
-            <LightboxView PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} />
-        </div >);
-    }
-
-    makeWebRef = (ele: HTMLDivElement) => {
-        reaction(() => DocumentLinksButton.invisibleWebDoc,
-            invisibleDoc => {
-                ReactDOM.unmountComponentAtNode(ele);
-                invisibleDoc && ReactDOM.render(<span title="Drag as document" className="invisible-webbox" >
-                    <div className="mainView-webRef" ref={DocumentLinksButton.invisibleWebRef}>
+      }
+
+      @computed get invisibleWebBox() { // see note under the makeLink method in HypothesisUtils.ts
+            return !DocumentLinksButton.invisibleWebDoc ? null :
+                  <div className="mainView-invisibleWebRef" ref={DocumentLinksButton.invisibleWebRef}>
                         <WebBox
-                            fieldKey={"data"}
-                            ContainingCollectionView={undefined}
-                            ContainingCollectionDoc={undefined}
-                            Document={invisibleDoc}
-                            dropAction={"move"}
-                            isSelected={returnFalse}
-                            docViewPath={returnEmptyDoclist}
-                            select={returnFalse}
-                            rootSelected={returnFalse}
-                            renderDepth={0}
-                            setHeight={returnFalse}
-                            layerProvider={undefined}
-                            styleProvider={undefined}
-                            addDocTab={returnFalse}
-                            pinToPres={returnFalse}
-                            ScreenToLocalTransform={Transform.Identity}
-                            bringToFront={returnFalse}
-                            isContentActive={emptyFunction}
-                            whenChildContentsActiveChanged={returnFalse}
-                            focus={returnFalse}
-                            PanelWidth={() => 500}
-                            PanelHeight={() => 800}
-                            docFilters={returnEmptyFilter}
-                            docRangeFilters={returnEmptyFilter}
-                            searchFilterDocs={returnEmptyDoclist}
+                              fieldKey={"data"}
+                              ContainingCollectionView={undefined}
+                              ContainingCollectionDoc={undefined}
+                              Document={DocumentLinksButton.invisibleWebDoc}
+                              dropAction={"move"}
+                              layerProvider={undefined}
+                              styleProvider={undefined}
+                              isSelected={returnFalse}
+                              select={returnFalse}
+                              setHeight={returnFalse}
+                              rootSelected={returnFalse}
+                              renderDepth={0}
+                              addDocTab={returnFalse}
+                              pinToPres={returnFalse}
+                              ScreenToLocalTransform={Transform.Identity}
+                              bringToFront={returnFalse}
+                              isContentActive={emptyFunction}
+                              whenChildContentsActiveChanged={returnFalse}
+                              focus={returnFalse}
+                              docViewPath={returnEmptyDoclist}
+                              PanelWidth={() => 500}
+                              PanelHeight={() => 800}
+                              docFilters={returnEmptyFilter}
+                              docRangeFilters={returnEmptyFilter}
+                              searchFilterDocs={returnEmptyDoclist}
                         />
-                    </div>;
-                </span>, ele);
-
-                let success = false;
-                const onSuccess = () => {
-                    success = true;
-                    clearTimeout(interval);
-                    document.removeEventListener("editSuccess", onSuccess);
-                };
-
-                // For some reason, Hypothes.is annotations don't load until a click is registered on the page,
-                // so we keep simulating clicks until annotations have loaded and editing is successful
-                const interval = setInterval(() => !success && simulateMouseClick(ele, 50, 50, 50, 50), 500);
-                setTimeout(() => !success && clearInterval(interval), 10000); // give up if no success after 10s
-                document.addEventListener("editSuccess", onSuccess);
-            });
-    }
+                  </div>;
+      }
+
+      render() {
+            return (<div className={`mainView-container${this.colorScheme}`}
+                  onScroll={() => ((ele) => ele.scrollTop = ele.scrollLeft = 0)(document.getElementById("root")!)}
+                  ref={r => {
+                        r && new _global.ResizeObserver(action(() => { this._windowWidth = r.getBoundingClientRect().width; this._windowHeight = r.getBoundingClientRect().height; })).observe(r);
+                  }}>
+                  {this.inkResources}
+                  <DictationOverlay />
+                  <SharingManager />
+                  <SettingsManager />
+                  <CaptureManager />
+                  <GroupManager />
+                  <GoogleAuthenticationManager />
+                  <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} />
+                  <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} />
+                  {this.topbar}
+                  {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
+                  {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => DocumentLinksButton.LinkEditorDocView = undefined)} docView={DocumentLinksButton.LinkEditorDocView} /> : (null)}
+                  {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)}
+                  <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 2001 }} >
+                        <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
+                  </div>
+                  <GestureOverlay >
+                        {this.mainDashboardArea}
+                  </GestureOverlay>
+                  <PreviewCursor />
+                  <TaskCompletionBox />
+                  <ContextMenu />
+                  <RadialMenu />
+                  <AnchorMenu />
+                  <MarqueeOptionsMenu />
+                  <OverlayView />
+                  <TimelineMenu />
+                  <RichTextMenu />
+                  {this.snapLines}
+                  <div className="mainView-webRef" ref={this.makeWebRef} />
+                  <LightboxView PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} />
+            </div >);
+      }
+
+      makeWebRef = (ele: HTMLDivElement) => {
+            reaction(() => DocumentLinksButton.invisibleWebDoc,
+                  invisibleDoc => {
+                        ReactDOM.unmountComponentAtNode(ele);
+                        invisibleDoc && ReactDOM.render(<span title="Drag as document" className="invisible-webbox" >
+                              <div className="mainView-webRef" ref={DocumentLinksButton.invisibleWebRef}>
+                                    <WebBox
+                                          fieldKey={"data"}
+                                          ContainingCollectionView={undefined}
+                                          ContainingCollectionDoc={undefined}
+                                          Document={invisibleDoc}
+                                          dropAction={"move"}
+                                          isSelected={returnFalse}
+                                          docViewPath={returnEmptyDoclist}
+                                          select={returnFalse}
+                                          rootSelected={returnFalse}
+                                          renderDepth={0}
+                                          setHeight={returnFalse}
+                                          layerProvider={undefined}
+                                          styleProvider={undefined}
+                                          addDocTab={returnFalse}
+                                          pinToPres={returnFalse}
+                                          ScreenToLocalTransform={Transform.Identity}
+                                          bringToFront={returnFalse}
+                                          isContentActive={emptyFunction}
+                                          whenChildContentsActiveChanged={returnFalse}
+                                          focus={returnFalse}
+                                          PanelWidth={() => 500}
+                                          PanelHeight={() => 800}
+                                          docFilters={returnEmptyFilter}
+                                          docRangeFilters={returnEmptyFilter}
+                                          searchFilterDocs={returnEmptyDoclist}
+                                    />
+                              </div>;
+                        </span>, ele);
+
+                        let success = false;
+                        const onSuccess = () => {
+                              success = true;
+                              clearTimeout(interval);
+                              document.removeEventListener("editSuccess", onSuccess);
+                        };
+
+                        // For some reason, Hypothes.is annotations don't load until a click is registered on the page,
+                        // so we keep simulating clicks until annotations have loaded and editing is successful
+                        const interval = setInterval(() => !success && simulateMouseClick(ele, 50, 50, 50, 50), 500);
+                        setTimeout(() => !success && clearInterval(interval), 10000); // give up if no success after 10s
+                        document.addEventListener("editSuccess", onSuccess);
+                  });
+      }
 }
 
 ScriptingGlobals.add(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); });
\ No newline at end of file
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
index 6aed07c60..a2a08f5b8 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -67,23 +67,13 @@ export function RecordingView(props: IRecordingViewProps) {
             if (finished) {
                   props.setDuration(recordingTimer * 100)
                   let allVideoChunks: any = []
-                  console.log(videos)
                   videos.forEach((vid) => {
                         console.log(vid.videoChunks)
                         allVideoChunks = allVideoChunks.concat(vid.videoChunks)
                   })
 
-                  console.log(allVideoChunks)
                   const videoFile = new File(allVideoChunks, "video.mkv", { type: allVideoChunks[0].type, lastModified: Date.now() });
 
-                  // const uploadVideo = async () => {
-                  //       const [{ result }] = await Networking.UploadFilesToServer(videoFile);
-                  //       console.log("upload result", result);
-                  //       if (!(result instanceof Error)) {
-                  //             setResult(result)
-                  //       }
-                  // }
-
                   Networking.UploadFilesToServer(videoFile)
                         .then((data) => {
                               const result = data[0].result
@@ -93,13 +83,9 @@ export function RecordingView(props: IRecordingViewProps) {
                                     alert("video conversion failed");
                               }
                         })
-                  // uploadVideo()
 
                   //     this.dataDoc[this.fieldKey + "-duration"] = (new Date().getTime() - this.recordingStart!) / 1000;
 
-
-
-                  // change to one recording box
             }
 
 
@@ -190,7 +176,6 @@ export function RecordingView(props: IRecordingViewProps) {
             // recording paused
             videoRecorder.current.onpause = (event: any) => {
                   // append the current portion to the video pieces
-                  console.log(videoChunks)
                   setVideos(videos => [...videos, { videoChunks: videoChunks, endTime: recordingTimerRef.current }])
 
                   // reset the temporary chunks
@@ -199,7 +184,6 @@ export function RecordingView(props: IRecordingViewProps) {
             }
 
             videoRecorder.current.onresume = async (event: any) => {
-                  console.log(event)
                   await startShowingStream();
                   setRecording(true);
             }
@@ -226,7 +210,6 @@ export function RecordingView(props: IRecordingViewProps) {
       }
 
       const startOrResume = () => {
-            console.log('[RecordingView.tsx] startOrResume')
             if (!videoRecorder.current || videoRecorder.current.state === "inactive") {
                   record();
             } else if (videoRecorder.current.state === "paused") {
@@ -234,22 +217,6 @@ export function RecordingView(props: IRecordingViewProps) {
             }
       }
 
-      // const playSegment = (idx: number) => {
-      //       console.log(idx)
-      //       let currentChunks = videos[idx].chunks
-      //       console.log(currentChunks)
-
-      //       const blob = new Blob(currentChunks, {
-      //             type: 'video/webm'
-      //       })
-      //       const blobUrl = URL.createObjectURL(blob)
-      //       console.log(blobUrl)
-
-      //       videoElementRef.current!.srcObject = null
-      //       videoElementRef.current!.src = blobUrl
-      //       videoElementRef.current!.muted = false
-      // }
-
       const clearPrevious = () => {
             const numVideos = videos.length
             setRecordingTimer(numVideos == 1 ? 0 : videos[numVideos - 2].endTime)
@@ -284,14 +251,6 @@ export function RecordingView(props: IRecordingViewProps) {
             return toTwoDigit(minutes) + " : " + toTwoDigit(seconds);
       }
 
-
-
-
-      useEffect(() => {
-            console.log(videos.map((elt) => elt.endTime / MAXTIME * 100))
-            console.log(videos)
-      }, [videos])
-
       return (
             <div className="recording-container">
                   <div className="video-wrapper">
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index ca13590de..71b165618 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -30,540 +30,540 @@ import { WebBox } from '../WebBox';
 import { FontIconBadge } from './FontIconBadge';
 import './FontIconBox.scss';
 const FontIconSchema = createSchema({
-    icon: "string",
+      icon: "string",
 });
 
 export enum ButtonType {
-    TextButton = "textBtn",
-    MenuButton = "menuBtn",
-    DropdownList = "drpdownList",
-    DropdownButton = "drpdownBtn",
-    ClickButton = "clickBtn",
-    DoubleButton = "dblBtn",
-    ToggleButton = "tglBtn",
-    ColorButton = "colorBtn",
-    ToolButton = "toolBtn",
-    NumberButton = "numBtn",
-    EditableText = "editableText"
+      TextButton = "textBtn",
+      MenuButton = "menuBtn",
+      DropdownList = "drpdownList",
+      DropdownButton = "drpdownBtn",
+      ClickButton = "clickBtn",
+      DoubleButton = "dblBtn",
+      ToggleButton = "tglBtn",
+      ColorButton = "colorBtn",
+      ToolButton = "toolBtn",
+      NumberButton = "numBtn",
+      EditableText = "editableText"
 }
 
 export enum NumButtonType {
-    Slider = "slider",
-    DropdownOptions = "list",
-    Inline = "inline"
+      Slider = "slider",
+      DropdownOptions = "list",
+      Inline = "inline"
 }
 
 export interface ButtonProps extends FieldViewProps {
-    type?: ButtonType;
+      type?: ButtonType;
 }
 @observer
 export class FontIconBox extends DocComponent<ButtonProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FontIconBox, fieldKey); }
-    showTemplate = (): void => {
-        const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null);
-        dragFactory && this.props.addDocTab(dragFactory, "add:right");
-    }
-    dragAsTemplate = (): void => { this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)'); };
-    useAsPrototype = (): void => { this.layoutDoc.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)'); };
-
-    specificContextMenu = (): void => {
-        if (!Doc.UserDoc().noviceMode) {
-            const cm = ContextMenu.Instance;
-            cm.addItem({ description: "Show Template", event: this.showTemplate, icon: "tag" });
-            cm.addItem({ description: "Use as Render Template", event: this.dragAsTemplate, icon: "tag" });
-            cm.addItem({ description: "Use as Prototype", event: this.useAsPrototype, icon: "tag" });
-        }
-    }
-
-    // Determining UI Specs
-    @observable private label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title));
-    @observable private icon = StrCast(this.dataDoc.icon, "user") as any;
-    @observable private dropdown: boolean = BoolCast(this.rootDoc.dropDownOpen);
-    @observable private buttonList: string[] = StrListCast(this.rootDoc.btnList);
-    @observable private type = StrCast(this.rootDoc.btnType);
-
-    /**
-     * Types of buttons in dash:
-     * - Main menu button (LHS)
-     * - Tool button
-     * - Expandable button (CollectionLinearView)
-     * - Button inside of CollectionLinearView vs. outside of CollectionLinearView
-     * - Action button
-     * - Dropdown button 
-     * - Color button
-     * - Dropdown list
-     * - Number button
-    **/
-
-    _batch: UndoManager.Batch | undefined = undefined;
-    /**
-     * Number button
-     */
-    @computed get numberButton() {
-        const numBtnType: string = StrCast(this.rootDoc.numBtnType);
-        const numScript = ScriptCast(this.rootDoc.script);
-        const setValue = (value: number) => numScript?.script.run({ value, _readOnly_: false });
-
-        // Script for checking the outcome of the toggle
-        const checkResult: number = numScript?.script.run({ value: 0, _readOnly_: true }).result || 0;
-
-        if (numBtnType === NumButtonType.Slider) {
-            const dropdown =
-                <div
-                    className="menuButton-dropdownBox"
-                    onPointerDown={e => e.stopPropagation()}
-                >
-                    <input type="range" step="1" min={NumCast(this.rootDoc.numBtnMin, 0)} max={NumCast(this.rootDoc.numBtnMax, 100)} value={checkResult}
-                        className={"menu-slider"} id="slider"
-                        onPointerDown={() => this._batch = UndoManager.StartBatch("presDuration")}
-                        onPointerUp={() => this._batch?.end()}
-                        onChange={e => { e.stopPropagation(); setValue(Number(e.target.value)); }}
-                    />
-                </div>;
-            return (
-                <div
-                    className={`menuButton ${this.type} ${numBtnType}`}
-                    onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}
-                >
-                    {checkResult}
-                    {this.rootDoc.dropDownOpen ? dropdown : null}
-                </div>
-            );
-        } else if (numBtnType === NumButtonType.DropdownOptions) {
-            const items: number[] = [];
-            for (let i = 0; i < 100; i++) {
-                if (i % 2 === 0) {
-                    items.push(i);
-                }
+      public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FontIconBox, fieldKey); }
+      showTemplate = (): void => {
+            const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null);
+            dragFactory && this.props.addDocTab(dragFactory, "add:right");
+      }
+      dragAsTemplate = (): void => { this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)'); };
+      useAsPrototype = (): void => { this.layoutDoc.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)'); };
+
+      specificContextMenu = (): void => {
+            if (!Doc.UserDoc().noviceMode) {
+                  const cm = ContextMenu.Instance;
+                  cm.addItem({ description: "Show Template", event: this.showTemplate, icon: "tag" });
+                  cm.addItem({ description: "Use as Render Template", event: this.dragAsTemplate, icon: "tag" });
+                  cm.addItem({ description: "Use as Prototype", event: this.useAsPrototype, icon: "tag" });
             }
-            const list = items.map((value) => {
-                return <div className="list-item" key={`${value}`}
-                    style={{
-                        backgroundColor: value === checkResult ? Colors.LIGHT_BLUE : undefined
-                    }}
-                    onClick={() => setValue(value)}>
-                    {value}
-                </div>;
-            });
-            return (
-                <div
-                    className={`menuButton ${this.type} ${numBtnType}`}
-                >
-                    <div className={`button`} onClick={action((e) => setValue(Number(checkResult) - 1))}>
-                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"minus"} />
-                    </div>
-                    <div
-                        className={`button ${'number'}`}
-                        onPointerDown={(e) => {
-                            e.stopPropagation();
-                            e.preventDefault();
-                        }}
-                        onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}
-                    >
-                        <input
-                            style={{ width: 30 }}
-                            className="button-input"
-                            type="number"
-                            value={checkResult}
-                            onChange={action((e) => setValue(Number(e.target.value)))}
-                        />
-                    </div>
-                    <div className={`button`} onClick={action((e) => setValue(Number(checkResult) + 1))}>
-                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"plus"} />
-                    </div>
-
-                    {this.rootDoc.dropDownOpen ?
+      }
+
+      // Determining UI Specs
+      @observable private label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title));
+      @observable private icon = StrCast(this.dataDoc.icon, "user") as any;
+      @observable private dropdown: boolean = BoolCast(this.rootDoc.dropDownOpen);
+      @observable private buttonList: string[] = StrListCast(this.rootDoc.btnList);
+      @observable private type = StrCast(this.rootDoc.btnType);
+
+      /**
+       * Types of buttons in dash:
+       * - Main menu button (LHS)
+       * - Tool button
+       * - Expandable button (CollectionLinearView)
+       * - Button inside of CollectionLinearView vs. outside of CollectionLinearView
+       * - Action button
+       * - Dropdown button 
+       * - Color button
+       * - Dropdown list
+       * - Number button
+      **/
+
+      _batch: UndoManager.Batch | undefined = undefined;
+      /**
+       * Number button
+       */
+      @computed get numberButton() {
+            const numBtnType: string = StrCast(this.rootDoc.numBtnType);
+            const numScript = ScriptCast(this.rootDoc.script);
+            const setValue = (value: number) => numScript?.script.run({ value, _readOnly_: false });
+
+            // Script for checking the outcome of the toggle
+            const checkResult: number = numScript?.script.run({ value: 0, _readOnly_: true }).result || 0;
+
+            if (numBtnType === NumButtonType.Slider) {
+                  const dropdown =
+                        <div
+                              className="menuButton-dropdownBox"
+                              onPointerDown={e => e.stopPropagation()}
+                        >
+                              <input type="range" step="1" min={NumCast(this.rootDoc.numBtnMin, 0)} max={NumCast(this.rootDoc.numBtnMax, 100)} value={checkResult}
+                                    className={"menu-slider"} id="slider"
+                                    onPointerDown={() => this._batch = UndoManager.StartBatch("presDuration")}
+                                    onPointerUp={() => this._batch?.end()}
+                                    onChange={e => { e.stopPropagation(); setValue(Number(e.target.value)); }}
+                              />
+                        </div>;
+                  return (
+                        <div
+                              className={`menuButton ${this.type} ${numBtnType}`}
+                              onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}
+                        >
+                              {checkResult}
+                              {this.rootDoc.dropDownOpen ? dropdown : null}
+                        </div>
+                  );
+            } else if (numBtnType === NumButtonType.DropdownOptions) {
+                  const items: number[] = [];
+                  for (let i = 0; i < 100; i++) {
+                        if (i % 2 === 0) {
+                              items.push(i);
+                        }
+                  }
+                  const list = items.map((value) => {
+                        return <div className="list-item" key={`${value}`}
+                              style={{
+                                    backgroundColor: value === checkResult ? Colors.LIGHT_BLUE : undefined
+                              }}
+                              onClick={() => setValue(value)}>
+                              {value}
+                        </div>;
+                  });
+                  return (
+                        <div
+                              className={`menuButton ${this.type} ${numBtnType}`}
+                        >
+                              <div className={`button`} onClick={action((e) => setValue(Number(checkResult) - 1))}>
+                                    <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"minus"} />
+                              </div>
+                              <div
+                                    className={`button ${'number'}`}
+                                    onPointerDown={(e) => {
+                                          e.stopPropagation();
+                                          e.preventDefault();
+                                    }}
+                                    onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}
+                              >
+                                    <input
+                                          style={{ width: 30 }}
+                                          className="button-input"
+                                          type="number"
+                                          value={checkResult}
+                                          onChange={action((e) => setValue(Number(e.target.value)))}
+                                    />
+                              </div>
+                              <div className={`button`} onClick={action((e) => setValue(Number(checkResult) + 1))}>
+                                    <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"plus"} />
+                              </div>
+
+                              {this.rootDoc.dropDownOpen ?
+                                    <div>
+                                          <div className="menuButton-dropdownList"
+                                                style={{ left: "25%" }}>
+                                                {list}
+                                          </div>
+                                          <div className="dropbox-background" onClick={(e) => { e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} />
+                                    </div> : null}
+
+                        </div>
+                  );
+            } else {
+                  return (
                         <div>
-                            <div className="menuButton-dropdownList"
-                                style={{ left: "25%" }}>
-                                {list}
-                            </div>
-                            <div className="dropbox-background" onClick={(e) => { e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} />
-                        </div> : null}
-
-                </div>
-            );
-        } else {
-            return (
-                <div>
 
-                </div>
-            );
-        }
-
-
-    }
-
-    /**
-     * Dropdown button
-     */
-    @computed get dropdownButton() {
-        const active: string = StrCast(this.rootDoc.dropDownOpen);
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        return (
-            <div className={`menuButton ${this.type} ${active}`}
-                style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
-                onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}>
-                <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
-                {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
-                <div
-                    className="menuButton-dropdown"
-                    style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
-                    <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" />
-                </div>
-                {this.rootDoc.dropDownOpen ?
-                    <div className="menuButton-dropdownBox">
-                        {/* DROPDOWN BOX CONTENTS */}
-                    </div> : null}
-            </div>
-        );
-    }
-
-    /**
-     * Dropdown list
-     */
-    @computed get dropdownListButton() {
-        const active: string = StrCast(this.rootDoc.dropDownOpen);
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-
-        const script = ScriptCast(this.rootDoc.script);
-
-        let noviceList: string[] = [];
-        let text: string | undefined;
-        let dropdown = true;
-        let icon: IconProp = "caret-down";
-        try {
-            if (script.script.originalScript.startsWith('setView')) {
-                const selected = SelectionManager.Docs().lastElement();
-                if (selected) {
-                    if (StrCast(selected.type) === DocumentType.COL) {
-                        text = StrCast(selected._viewType);
-                    } else {
-                        dropdown = false;
-                        text = selected.type === DocumentType.RTF ? "Text" : StrCast(selected.type);
-                        icon = Doc.toIcon(selected);
-                    }
-                } else {
-                    dropdown = false;
-                    icon = "globe-asia";
-                    text = "User Default";
-                }
-                noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking];
-            } else if (script.script.originalScript.startsWith('setFont')) {
-                const editorView = RichTextMenu.Instance?.TextView?.EditorView;
-                text = StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
-                noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia",
-                    "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"];
-            }
-        } catch (e) {
-            console.log(e);
-        }
-
-        // Get items to place into the list
-        const list = this.buttonList.map((value) => {
-            if (Doc.UserDoc().noviceMode && !noviceList.includes(value)) {
-                return;
+                        </div>
+                  );
             }
-            return <div className="list-item" key={`${value}`}
-                style={{
-                    fontFamily: script.script.originalScript.startsWith('setFont') ? value : undefined,
-                    backgroundColor: value === text ? Colors.LIGHT_BLUE : undefined
-                }}
-                onClick={() => script.script.run({ value }).result}>
-                {value[0].toUpperCase() + value.slice(1)}
-            </div>;
-        });
 
-        const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}>
-                {this.label}
-            </div>;
 
-        return (
-            <div className={`menuButton ${this.type} ${active}`}
-                style={{ backgroundColor: this.rootDoc.dropDownOpen ? Colors.MEDIUM_BLUE : backgroundColor, color: color, display: dropdown ? undefined : "flex" }}
-                onClick={dropdown ? () => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen : undefined}>
-                {dropdown ? (null) : <FontAwesomeIcon style={{ marginLeft: 5 }} className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />}
-                <div className="menuButton-dropdown-header">
-                    {text && text[0].toUpperCase() + text.slice(1)}
-                </div>
-                {label}
-                {!dropdown ? (null) : <div className="menuButton-dropDown">
-                    <FontAwesomeIcon icon={icon} color={color} size="sm" />
-                </div>}
-                {this.rootDoc.dropDownOpen ?
-                    <div>
-                        <div className="menuButton-dropdownList"
-                            style={{ left: 0 }}>
-                            {list}
-                        </div>
-                        <div className="dropbox-background" onClick={(e) => { e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} />
-                    </div>
-                    : null}
-            </div>
-        );
-    }
-
-    @observable colorPickerClosed: boolean = true;
-    @computed get colorScript() { return ScriptCast(this.rootDoc.script); }
-
-    colorPicker = (curColor: string) => {
-        const change = (value: ColorState) => {
-            const s = this.colorScript;
-            s && undoBatch(() => s.script.run({ value: Utils.colorString(value), _readOnly_: false }).result)();
-        };
-        const presets = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
-            '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
-            '#FFFFFF', '#f1efeb', "transparent"];
-        return <SketchPicker
-            onChange={change}
-            color={curColor}
-            presetColors={presets} />;
-    }
-    /**
-     * Color button
-     */
-    @computed get colorButton() {
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const curColor = this.colorScript?.script.run({ value: undefined, _readOnly_: true }).result ?? "transparent";
-
-        const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
-                {this.label}
-            </div>;
+      }
 
-        const dropdownCaret = <div
-            className="menuButton-dropDown"
-            style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
-            <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" />
-        </div>;
-        setTimeout(() => this.colorPicker(curColor)); // cause an update to the color picker rendered in MainView
-        return (
-            <div className={`menuButton ${this.type} ${this.colorPickerClosed}`}
-                style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
-                onClick={action(() => this.colorPickerClosed = !this.colorPickerClosed)}
-                onPointerDown={e => e.stopPropagation()}>
-                <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
-                <div className="colorButton-color" style={{ backgroundColor: curColor }} />
-                {label}
-                {/* {dropdownCaret} */}
-                {this.colorPickerClosed ? (null) :
-                    <div>
-                        <div className="menuButton-dropdownBox"
-                            onPointerDown={e => e.stopPropagation()}
-                            onClick={e => e.stopPropagation()}>
-                            {this.colorPicker(curColor)}
+      /**
+       * Dropdown button
+       */
+      @computed get dropdownButton() {
+            const active: string = StrCast(this.rootDoc.dropDownOpen);
+            const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+            const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
+            return (
+                  <div className={`menuButton ${this.type} ${active}`}
+                        style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
+                        onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}>
+                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+                        {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
+                        <div
+                              className="menuButton-dropdown"
+                              style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
+                              <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" />
                         </div>
-                        <div className="dropbox-background" onClick={action((e) => {
-                            e.stopPropagation(); this.colorPickerClosed = true;
-                        })} />
-                    </div>}
-            </div>
-        );
-    }
-
-    @computed get toggleButton() {
-        // Determine the type of toggle button
-        const switchToggle: boolean = BoolCast(this.rootDoc.switchToggle);
-        const buttonText: string = StrCast(this.rootDoc.buttonText);
-        // Colors
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-
-        // Button label
-        const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
-                {this.label}
-            </div>;
+                        {this.rootDoc.dropDownOpen ?
+                              <div className="menuButton-dropdownBox">
+                                    {/* DROPDOWN BOX CONTENTS */}
+                              </div> : null}
+                  </div>
+            );
+      }
+
+      /**
+       * Dropdown list
+       */
+      @computed get dropdownListButton() {
+            const active: string = StrCast(this.rootDoc.dropDownOpen);
+            const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+            const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
+
+            const script = ScriptCast(this.rootDoc.script);
+
+            let noviceList: string[] = [];
+            let text: string | undefined;
+            let dropdown = true;
+            let icon: IconProp = "caret-down";
+            try {
+                  if (script.script.originalScript.startsWith('setView')) {
+                        const selected = SelectionManager.Docs().lastElement();
+                        if (selected) {
+                              if (StrCast(selected.type) === DocumentType.COL) {
+                                    text = StrCast(selected._viewType);
+                              } else {
+                                    dropdown = false;
+                                    text = selected.type === DocumentType.RTF ? "Text" : StrCast(selected.type);
+                                    icon = Doc.toIcon(selected);
+                              }
+                        } else {
+                              dropdown = false;
+                              icon = "globe-asia";
+                              text = "User Default";
+                        }
+                        noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking];
+                  } else if (script.script.originalScript.startsWith('setFont')) {
+                        const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+                        text = StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
+                        noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia",
+                              "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"];
+                  }
+            } catch (e) {
+                  console.log(e);
+            }
+
+            // Get items to place into the list
+            const list = this.buttonList.map((value) => {
+                  if (Doc.UserDoc().noviceMode && !noviceList.includes(value)) {
+                        return;
+                  }
+                  return <div className="list-item" key={`${value}`}
+                        style={{
+                              fontFamily: script.script.originalScript.startsWith('setFont') ? value : undefined,
+                              backgroundColor: value === text ? Colors.LIGHT_BLUE : undefined
+                        }}
+                        onClick={() => script.script.run({ value }).result}>
+                        {value[0].toUpperCase() + value.slice(1)}
+                  </div>;
+            });
+
+            const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
+                  <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}>
+                        {this.label}
+                  </div>;
 
-        if (switchToggle) {
             return (
-                <div className={`menuButton ${this.type} ${'switch'}`}>
-                    {buttonText ? buttonText : null}
-                    <label className="switch">
-                        <input type="checkbox"
-                            checked={backgroundColor === Colors.MEDIUM_BLUE}
-                        />
-                        <span className="slider round"></span>
-                    </label>
-                </div>
+                  <div className={`menuButton ${this.type} ${active}`}
+                        style={{ backgroundColor: this.rootDoc.dropDownOpen ? Colors.MEDIUM_BLUE : backgroundColor, color: color, display: dropdown ? undefined : "flex" }}
+                        onClick={dropdown ? () => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen : undefined}>
+                        {dropdown ? (null) : <FontAwesomeIcon style={{ marginLeft: 5 }} className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />}
+                        <div className="menuButton-dropdown-header">
+                              {text && text[0].toUpperCase() + text.slice(1)}
+                        </div>
+                        {label}
+                        {!dropdown ? (null) : <div className="menuButton-dropDown">
+                              <FontAwesomeIcon icon={icon} color={color} size="sm" />
+                        </div>}
+                        {this.rootDoc.dropDownOpen ?
+                              <div>
+                                    <div className="menuButton-dropdownList"
+                                          style={{ left: 0 }}>
+                                          {list}
+                                    </div>
+                                    <div className="dropbox-background" onClick={(e) => { e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} />
+                              </div>
+                              : null}
+                  </div>
             );
-        } else {
+      }
+
+      @observable colorPickerClosed: boolean = true;
+      @computed get colorScript() { return ScriptCast(this.rootDoc.script); }
+
+      colorPicker = (curColor: string) => {
+            const change = (value: ColorState) => {
+                  const s = this.colorScript;
+                  s && undoBatch(() => s.script.run({ value: Utils.colorString(value), _readOnly_: false }).result)();
+            };
+            const presets = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
+                  '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
+                  '#FFFFFF', '#f1efeb', "transparent"];
+            return <SketchPicker
+                  onChange={change}
+                  color={curColor}
+                  presetColors={presets} />;
+      }
+      /**
+       * Color button
+       */
+      @computed get colorButton() {
+            const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+            const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
+            const curColor = this.colorScript?.script.run({ value: undefined, _readOnly_: true }).result ?? "transparent";
+
+            const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
+                  <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
+                        {this.label}
+                  </div>;
+
+            const dropdownCaret = <div
+                  className="menuButton-dropDown"
+                  style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
+                  <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" />
+            </div>;
+            setTimeout(() => this.colorPicker(curColor)); // cause an update to the color picker rendered in MainView
             return (
-                <div className={`menuButton ${this.type}`}
-                    style={{ opacity: 1, backgroundColor, color }}>
-                    <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
-                    {label}
-                </div>
+                  <div className={`menuButton ${this.type} ${this.colorPickerClosed}`}
+                        style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
+                        onClick={action(() => this.colorPickerClosed = !this.colorPickerClosed)}
+                        onPointerDown={e => e.stopPropagation()}>
+                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+                        <div className="colorButton-color" style={{ backgroundColor: curColor }} />
+                        {label}
+                        {/* {dropdownCaret} */}
+                        {this.colorPickerClosed ? (null) :
+                              <div>
+                                    <div className="menuButton-dropdownBox"
+                                          onPointerDown={e => e.stopPropagation()}
+                                          onClick={e => e.stopPropagation()}>
+                                          {this.colorPicker(curColor)}
+                                    </div>
+                                    <div className="dropbox-background" onClick={action((e) => {
+                                          e.stopPropagation(); this.colorPickerClosed = true;
+                                    })} />
+                              </div>}
+                  </div>
             );
-        }
-    }
-
-
-
-    /**
-     * Default
-     */
-    @computed get defaultButton() {
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const active: string = StrCast(this.rootDoc.dropDownOpen);
-        return (
-            <div className={`menuButton ${this.type}`} onContextMenu={this.specificContextMenu}
-                style={{ backgroundColor: "transparent", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}>
-                <div className="menuButton-wrap">
-                    <FontAwesomeIcon className={`menuButton-icon-${this.type}`} icon={this.icon} color={"black"} size={"sm"} />
-                    {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
-                </div>
-            </div>
-        );
-    }
-
-    @computed get editableText() {
-        // Script for running the toggle
-        const script = ScriptCast(this.rootDoc.script);
-        // Function to run the script
-        const checkResult = script?.script.run({ value: "", _readOnly_: true }).result;
-
-        const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ value, _readOnly_: false }).result;
-        return (
-            <div className="menuButton editableText">
-                <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"lock"} />
-                <div style={{ width: "calc(100% - .875em)", paddingLeft: "4px" }}>
-                    <EditableView GetValue={() => script?.script.run({ value: "", _readOnly_: true }).result} SetValue={setValue} contents={checkResult} />
-                </div>
-            </div>
-        );
-    }
-
-    render() {
-        const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-        const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
-                {this.label}
-            </div>;
+      }
+
+      @computed get toggleButton() {
+            // Determine the type of toggle button
+            const switchToggle: boolean = BoolCast(this.rootDoc.switchToggle);
+            const buttonText: string = StrCast(this.rootDoc.buttonText);
+            // Colors
+            const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+            const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
+
+            // Button label
+            const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
+                  <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
+                        {this.label}
+                  </div>;
+
+            if (switchToggle) {
+                  return (
+                        <div className={`menuButton ${this.type} ${'switch'}`}>
+                              {buttonText ? buttonText : null}
+                              <label className="switch">
+                                    <input type="checkbox"
+                                          checked={backgroundColor === Colors.MEDIUM_BLUE}
+                                    />
+                                    <span className="slider round"></span>
+                              </label>
+                        </div>
+                  );
+            } else {
+                  return (
+                        <div className={`menuButton ${this.type}`}
+                              style={{ opacity: 1, backgroundColor, color }}>
+                              <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+                              {label}
+                        </div>
+                  );
+            }
+      }
 
-        const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color, backgroundColor: "transparent" }}>
-                {this.label}
-            </div>;
 
-        const buttonText = StrCast(this.rootDoc.buttonText);
 
-        // TODO:glr Add label of button type
-        let button = this.defaultButton;
+      /**
+       * Default
+       */
+      @computed get defaultButton() {
+            const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+            const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
+            const active: string = StrCast(this.rootDoc.dropDownOpen);
+            return (
+                  <div className={`menuButton ${this.type}`} onContextMenu={this.specificContextMenu}
+                        style={{ backgroundColor: "transparent", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}>
+                        <div className="menuButton-wrap">
+                              <FontAwesomeIcon className={`menuButton-icon-${this.type}`} icon={this.icon} color={"black"} size={"sm"} />
+                              {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
+                        </div>
+                  </div>
+            );
+      }
 
-        switch (this.type) {
-            case ButtonType.TextButton:
-                button = (
-                    <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}>
-                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
-                        {buttonText ?
-                            <div className="button-text">
-                                {buttonText}
-                            </div>
-                            : null}
-                        {label}
-                    </div>
-                );
-                // button = <TextButton {...buttonProps}></TextButton>
-                break;
-            case ButtonType.EditableText:
-                button = this.editableText;
-                break;
-            case ButtonType.NumberButton:
-                button = this.numberButton;
-                break;
-            case ButtonType.DropdownButton:
-                button = this.dropdownButton;
-                break;
-            case ButtonType.DropdownList:
-                button = this.dropdownListButton;
-                break;
-            case ButtonType.ColorButton:
-                button = this.colorButton;
-                break;
-            case ButtonType.ToolButton:
-                button = (
-                    <div className={`menuButton ${this.type}`} style={{ opacity: 1, backgroundColor, color }}>
-                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
-                        {label}
-                    </div>
-                );
-                break;
-            case ButtonType.ToggleButton:
-                button = this.toggleButton;
-                // button = <ToggleButton {...buttonProps}></ToggleButton>
-                break;
-            case ButtonType.ClickButton:
-                button = (
-                    <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1 }}>
-                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
-                        {label}
-                    </div>
-                );
-                break;
-            case ButtonType.MenuButton:
-                const trailsIcon = <img src={`/assets/${"presTrails.png"}`}
-                    style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? "0%" : "100%"})` }} />;
-                button = (
-                    <div className={`menuButton ${this.type}`} style={{ color, backgroundColor }}>
-                        {this.icon === "pres-trail" ? trailsIcon : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />}
-                        {menuLabel}
-                        <FontIconBadge collection={Cast(this.rootDoc.watchedDocuments, Doc, null)} />
-                    </div >
-                );
-                break;
-            default:
-                break;
-        }
-
-        return !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? button :
-            <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>
-                {button}
-            </Tooltip>;
-    }
+      @computed get editableText() {
+            // Script for running the toggle
+            const script = ScriptCast(this.rootDoc.script);
+            // Function to run the script
+            const checkResult = script?.script.run({ value: "", _readOnly_: true }).result;
+
+            const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ value, _readOnly_: false }).result;
+            return (
+                  <div className="menuButton editableText">
+                        <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"lock"} />
+                        <div style={{ width: "calc(100% - .875em)", paddingLeft: "4px" }}>
+                              <EditableView GetValue={() => script?.script.run({ value: "", _readOnly_: true }).result} SetValue={setValue} contents={checkResult} />
+                        </div>
+                  </div>
+            );
+      }
+
+      render() {
+            const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+            const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
+            const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
+                  <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
+                        {this.label}
+                  </div>;
+
+            const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) :
+                  <div className="fontIconBox-label" style={{ color, backgroundColor: "transparent" }}>
+                        {this.label}
+                  </div>;
+
+            const buttonText = StrCast(this.rootDoc.buttonText);
+
+            // TODO:glr Add label of button type
+            let button = this.defaultButton;
+
+            switch (this.type) {
+                  case ButtonType.TextButton:
+                        button = (
+                              <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}>
+                                    <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+                                    {buttonText ?
+                                          <div className="button-text">
+                                                {buttonText}
+                                          </div>
+                                          : null}
+                                    {label}
+                              </div>
+                        );
+                        // button = <TextButton {...buttonProps}></TextButton>
+                        break;
+                  case ButtonType.EditableText:
+                        button = this.editableText;
+                        break;
+                  case ButtonType.NumberButton:
+                        button = this.numberButton;
+                        break;
+                  case ButtonType.DropdownButton:
+                        button = this.dropdownButton;
+                        break;
+                  case ButtonType.DropdownList:
+                        button = this.dropdownListButton;
+                        break;
+                  case ButtonType.ColorButton:
+                        button = this.colorButton;
+                        break;
+                  case ButtonType.ToolButton:
+                        button = (
+                              <div className={`menuButton ${this.type}`} style={{ opacity: 1, backgroundColor, color }}>
+                                    <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+                                    {label}
+                              </div>
+                        );
+                        break;
+                  case ButtonType.ToggleButton:
+                        button = this.toggleButton;
+                        // button = <ToggleButton {...buttonProps}></ToggleButton>
+                        break;
+                  case ButtonType.ClickButton:
+                        button = (
+                              <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1 }}>
+                                    <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
+                                    {label}
+                              </div>
+                        );
+                        break;
+                  case ButtonType.MenuButton:
+                        const trailsIcon = <img src={`/assets/${"presTrails.png"}`}
+                              style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? "0%" : "100%"})` }} />;
+                        button = (
+                              <div className={`menuButton ${this.type}`} style={{ color, backgroundColor }}>
+                                    {this.icon === "pres-trail" ? trailsIcon : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />}
+                                    {menuLabel}
+                                    <FontIconBadge collection={Cast(this.rootDoc.watchedDocuments, Doc, null)} />
+                              </div >
+                        );
+                        break;
+                  default:
+                        break;
+            }
+
+            return !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? button :
+                  <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>
+                        {button}
+                  </Tooltip>;
+      }
 }
 
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setView(view: string) {
-    const selected = SelectionManager.Docs().lastElement();
-    selected ? selected._viewType = view : console.log("[FontIconBox.tsx] changeView failed");
+      const selected = SelectionManager.Docs().lastElement();
+      selected ? selected._viewType = view : console.log("[FontIconBox.tsx] changeView failed");
 });
 
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: boolean) {
-    const selected = SelectionManager.Docs().lastElement();
-    if (checkResult) {
-        return selected?._backgroundColor ?? "transparent";
-    }
-    if (selected) selected._backgroundColor = color;
+      const selected = SelectionManager.Docs().lastElement();
+      if (checkResult) {
+            return selected?._backgroundColor ?? "transparent";
+      }
+      if (selected) selected._backgroundColor = color;
 });
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) {
-    if (checkResult) {
-        return Doc.SharingDoc().userColor;
-    }
-    Doc.SharingDoc().userColor = undefined;
-    Doc.GetProto(Doc.SharingDoc()).userColor = color;
-    Doc.UserDoc().showTitle = color === "transparent" ? undefined : StrCast(Doc.UserDoc().showTitle, "creationDate");
+      if (checkResult) {
+            return Doc.SharingDoc().userColor;
+      }
+      Doc.SharingDoc().userColor = undefined;
+      Doc.GetProto(Doc.SharingDoc()).userColor = color;
+      Doc.UserDoc().showTitle = color === "transparent" ? undefined : StrCast(Doc.UserDoc().showTitle, "creationDate");
 });
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
-    const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
-    if (checkResult && selected) {
-        if (NumCast(selected.Document.z) >= 1) return Colors.MEDIUM_BLUE;
-        return "transparent";
-    }
-    selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("[FontIconBox.tsx] toggleOverlay failed");
+      const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+      if (checkResult && selected) {
+            if (NumCast(selected.Document.z) >= 1) return Colors.MEDIUM_BLUE;
+            return "transparent";
+      }
+      selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("[FontIconBox.tsx] toggleOverlay failed");
 });
 
 /** TEXT
@@ -580,115 +580,115 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setFont(font: string, checkResult?: boolean) {
-    SelectionManager.Docs().map(doc => doc._fontFamily = font);
-    const editorView = RichTextMenu.Instance.TextView?.EditorView;
-    if (checkResult) {
-        return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
-    }
-    if (editorView) RichTextMenu.Instance.setFontFamily(font);
-    else Doc.UserDoc().fontFamily = font;
+      SelectionManager.Docs().map(doc => doc._fontFamily = font);
+      const editorView = RichTextMenu.Instance.TextView?.EditorView;
+      if (checkResult) {
+            return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
+      }
+      if (editorView) RichTextMenu.Instance.setFontFamily(font);
+      else Doc.UserDoc().fontFamily = font;
 });
 
 ScriptingGlobals.add(function getActiveTextInfo(info: "family" | "size" | "color" | "highlight") {
-    const editorView = RichTextMenu.Instance.TextView?.EditorView;
-    const style = editorView?.state && RichTextMenu.Instance.getActiveFontStylesOnSelection();
-    switch (info) {
-        case "family": return style?.activeFamilies[0];
-        case "size": return style?.activeSizes[0];
-        case "color": return style?.activeColors[0];
-        case "highlight": return style?.activeHighlights[0];
-    }
+      const editorView = RichTextMenu.Instance.TextView?.EditorView;
+      const style = editorView?.state && RichTextMenu.Instance.getActiveFontStylesOnSelection();
+      switch (info) {
+            case "family": return style?.activeFamilies[0];
+            case "size": return style?.activeSizes[0];
+            case "color": return style?.activeColors[0];
+            case "highlight": return style?.activeHighlights[0];
+      }
 });
 
 ScriptingGlobals.add(function setAlignment(align: "left" | "right" | "center", checkResult?: boolean) {
-    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
-    if (checkResult) {
-        return (editorView ? RichTextMenu.Instance.textAlign : Doc.UserDoc().textAlign) === align ? Colors.MEDIUM_BLUE : "transparent";
-    }
-    if (editorView?.state) RichTextMenu.Instance.align(editorView, editorView.dispatch, align);
-    else Doc.UserDoc().textAlign = align;
+      const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+      if (checkResult) {
+            return (editorView ? RichTextMenu.Instance.textAlign : Doc.UserDoc().textAlign) === align ? Colors.MEDIUM_BLUE : "transparent";
+      }
+      if (editorView?.state) RichTextMenu.Instance.align(editorView, editorView.dispatch, align);
+      else Doc.UserDoc().textAlign = align;
 });
 
 ScriptingGlobals.add(function setBulletList(mapStyle: "bullet" | "decimal", checkResult?: boolean) {
-    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
-    if (checkResult) {
-        const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle();
-        if (active === mapStyle) return Colors.MEDIUM_BLUE;
-        return "transparent";
-    }
-    if (editorView) {
-        const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle();
-        editorView?.state && RichTextMenu.Instance.changeListType(
-            editorView.state.schema.nodes.ordered_list.create({ mapStyle: active === mapStyle ? "" : mapStyle }));
-    }
+      const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+      if (checkResult) {
+            const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle();
+            if (active === mapStyle) return Colors.MEDIUM_BLUE;
+            return "transparent";
+      }
+      if (editorView) {
+            const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle();
+            editorView?.state && RichTextMenu.Instance.changeListType(
+                  editorView.state.schema.nodes.ordered_list.create({ mapStyle: active === mapStyle ? "" : mapStyle }));
+      }
 });
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setFontColor(color?: string, checkResult?: boolean) {
-    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+      const editorView = RichTextMenu.Instance?.TextView?.EditorView;
 
-    if (checkResult) {
-        return editorView ? RichTextMenu.Instance.fontColor : Doc.UserDoc().fontColor;
-    }
+      if (checkResult) {
+            return editorView ? RichTextMenu.Instance.fontColor : Doc.UserDoc().fontColor;
+      }
 
-    if (editorView) color && RichTextMenu.Instance.setColor(color, editorView, editorView?.dispatch);
-    else Doc.UserDoc().fontColor = color;
+      if (editorView) color && RichTextMenu.Instance.setColor(color, editorView, editorView?.dispatch);
+      else Doc.UserDoc().fontColor = color;
 });
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setFontHighlight(color?: string, checkResult?: boolean) {
-    const selected = SelectionManager.Docs().lastElement();
-    const editorView = RichTextMenu.Instance.TextView?.EditorView;
-
-    if (checkResult) {
-        return (selected ?? Doc.UserDoc())._fontHighlight;
-    }
-    if (selected) {
-        selected._fontColor = color;
-        if (color) {
-            editorView?.state && RichTextMenu.Instance.setHighlight(color, editorView, editorView?.dispatch);
-        }
-    }
-    Doc.UserDoc()._fontHighlight = color;
+      const selected = SelectionManager.Docs().lastElement();
+      const editorView = RichTextMenu.Instance.TextView?.EditorView;
+
+      if (checkResult) {
+            return (selected ?? Doc.UserDoc())._fontHighlight;
+      }
+      if (selected) {
+            selected._fontColor = color;
+            if (color) {
+                  editorView?.state && RichTextMenu.Instance.setHighlight(color, editorView, editorView?.dispatch);
+            }
+      }
+      Doc.UserDoc()._fontHighlight = color;
 });
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: boolean) {
-    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
-    if (checkResult) {
-        return (editorView ? RichTextMenu.Instance.fontSize : StrCast(Doc.UserDoc().fontSize, "10px")).replace("px", "");
-    }
-    if (typeof size === "number") size = size.toString();
-    if (size && Number(size).toString() === size) size += "px";
-    if (editorView) RichTextMenu.Instance.setFontSize(size);
-    else Doc.UserDoc()._fontSize = size;
+      const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+      if (checkResult) {
+            return (editorView ? RichTextMenu.Instance.fontSize : StrCast(Doc.UserDoc().fontSize, "10px")).replace("px", "");
+      }
+      if (typeof size === "number") size = size.toString();
+      if (size && Number(size).toString() === size) size += "px";
+      if (editorView) RichTextMenu.Instance.setFontSize(size);
+      else Doc.UserDoc()._fontSize = size;
 });
 
 ScriptingGlobals.add(function toggleBold(checkResult?: boolean) {
-    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
-    if (checkResult) {
-        return (editorView ? RichTextMenu.Instance.bold : Doc.UserDoc().fontWeight === "bold") ? Colors.MEDIUM_BLUE : "transparent";
-    }
-    if (editorView) RichTextMenu.Instance?.toggleBold();
-    else Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === "bold" ? undefined : "bold";
+      const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+      if (checkResult) {
+            return (editorView ? RichTextMenu.Instance.bold : Doc.UserDoc().fontWeight === "bold") ? Colors.MEDIUM_BLUE : "transparent";
+      }
+      if (editorView) RichTextMenu.Instance?.toggleBold();
+      else Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === "bold" ? undefined : "bold";
 });
 
 ScriptingGlobals.add(function toggleUnderline(checkResult?: boolean) {
-    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
-    if (checkResult) {
-        return (editorView ? RichTextMenu.Instance.underline : Doc.UserDoc().textDecoration === "underline") ? Colors.MEDIUM_BLUE : "transparent";
-    }
-    if (editorView) RichTextMenu.Instance?.toggleUnderline();
-    else Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === "underline" ? undefined : "underline";
+      const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+      if (checkResult) {
+            return (editorView ? RichTextMenu.Instance.underline : Doc.UserDoc().textDecoration === "underline") ? Colors.MEDIUM_BLUE : "transparent";
+      }
+      if (editorView) RichTextMenu.Instance?.toggleUnderline();
+      else Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === "underline" ? undefined : "underline";
 });
 
 ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
-    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
-    if (checkResult) {
-        return (editorView ? RichTextMenu.Instance.italics : Doc.UserDoc().fontStyle === "italics") ? Colors.MEDIUM_BLUE : "transparent";
-    }
-    if (editorView) RichTextMenu.Instance?.toggleItalics();
-    else Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === "italics" ? undefined : "italics";
+      const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+      if (checkResult) {
+            return (editorView ? RichTextMenu.Instance.italics : Doc.UserDoc().fontStyle === "italics") ? Colors.MEDIUM_BLUE : "transparent";
+      }
+      if (editorView) RichTextMenu.Instance?.toggleItalics();
+      else Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === "italics" ? undefined : "italics";
 });
 
 
@@ -701,66 +701,66 @@ ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
  **/
 
 ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boolean) {
-    if (checkResult) {
-        return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ?
-            Colors.MEDIUM_BLUE : "transparent";
-    }
-    if (["circle", "square", "line"].includes(tool)) {
-        if (GestureOverlay.Instance.InkShape === tool) {
-            Doc.UserDoc().activeInkTool = InkTool.None;
-            GestureOverlay.Instance.InkShape = InkTool.None;
-        } else {
-            Doc.UserDoc().activeInkTool = InkTool.Pen;
-            GestureOverlay.Instance.InkShape = tool;
-        }
-    } else if (tool) { // pen or eraser
-        if (Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance.InkShape) {
+      if (checkResult) {
+            return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ?
+                  Colors.MEDIUM_BLUE : "transparent";
+      }
+      if (["circle", "square", "line"].includes(tool)) {
+            if (GestureOverlay.Instance.InkShape === tool) {
+                  Doc.UserDoc().activeInkTool = InkTool.None;
+                  GestureOverlay.Instance.InkShape = InkTool.None;
+            } else {
+                  Doc.UserDoc().activeInkTool = InkTool.Pen;
+                  GestureOverlay.Instance.InkShape = tool;
+            }
+      } else if (tool) { // pen or eraser
+            if (Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance.InkShape) {
+                  Doc.UserDoc().activeInkTool = InkTool.None;
+            } else {
+                  Doc.UserDoc().activeInkTool = tool;
+                  GestureOverlay.Instance.InkShape = "";
+            }
+      } else {
             Doc.UserDoc().activeInkTool = InkTool.None;
-        } else {
-            Doc.UserDoc().activeInkTool = tool;
-            GestureOverlay.Instance.InkShape = "";
-        }
-    } else {
-        Doc.UserDoc().activeInkTool = InkTool.None;
-    }
+      }
 });
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setFillColor(color?: string, checkResult?: boolean) {
-    const selected = SelectionManager.Docs().lastElement();
-    if (checkResult) {
-        if (selected?.type === DocumentType.INK) {
-            return StrCast(selected.fillColor);
-        }
-        return ActiveFillColor();
-    }
-    SetActiveFillColor(StrCast(color));
-    SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.fillColor = color);
+      const selected = SelectionManager.Docs().lastElement();
+      if (checkResult) {
+            if (selected?.type === DocumentType.INK) {
+                  return StrCast(selected.fillColor);
+            }
+            return ActiveFillColor();
+      }
+      SetActiveFillColor(StrCast(color));
+      SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.fillColor = color);
 });
 
 ScriptingGlobals.add(function setStrokeWidth(width: number, checkResult?: boolean) {
-    if (checkResult) {
-        const selected = SelectionManager.Docs().lastElement();
-        if (selected?.type === DocumentType.INK) {
-            return NumCast(selected.strokeWidth);
-        }
-        return ActiveInkWidth();
-    }
-    SetActiveInkWidth(width.toString());
-    SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.strokeWidth = Number(width));
+      if (checkResult) {
+            const selected = SelectionManager.Docs().lastElement();
+            if (selected?.type === DocumentType.INK) {
+                  return NumCast(selected.strokeWidth);
+            }
+            return ActiveInkWidth();
+      }
+      SetActiveInkWidth(width.toString());
+      SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.strokeWidth = Number(width));
 });
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function setStrokeColor(color?: string, checkResult?: boolean) {
-    if (checkResult) {
-        const selected = SelectionManager.Docs().lastElement();
-        if (selected?.type === DocumentType.INK) {
-            return StrCast(selected.color);
-        }
-        return ActiveInkColor();
-    }
-    SetActiveInkColor(StrCast(color));
-    SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.color = String(color));
+      if (checkResult) {
+            const selected = SelectionManager.Docs().lastElement();
+            if (selected?.type === DocumentType.INK) {
+                  return StrCast(selected.color);
+            }
+            return ActiveInkColor();
+      }
+      SetActiveInkColor(StrCast(color));
+      SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.color = String(color));
 });
 
 
@@ -768,28 +768,28 @@ ScriptingGlobals.add(function setStrokeColor(color?: string, checkResult?: boole
  *      webSetURL
  **/
 ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) {
-    const selected = SelectionManager.Views().lastElement();
-    if (selected?.rootDoc.type === DocumentType.WEB) {
-        if (checkResult) {
-            return StrCast(selected.rootDoc.data, Cast(selected.rootDoc.data, WebField, null)?.url?.href);
-        }
-        (selected.ComponentView as WebBox).submitURL(url);
-        //selected.rootDoc.data = new WebField(url);
-    }
+      const selected = SelectionManager.Views().lastElement();
+      if (selected?.rootDoc.type === DocumentType.WEB) {
+            if (checkResult) {
+                  return StrCast(selected.rootDoc.data, Cast(selected.rootDoc.data, WebField, null)?.url?.href);
+            }
+            (selected.ComponentView as WebBox).submitURL(url);
+            //selected.rootDoc.data = new WebField(url);
+      }
 });
 ScriptingGlobals.add(function webForward(checkResult?: boolean) {
-    const selected = (SelectionManager.Views().lastElement()?.ComponentView as WebBox);
-    if (checkResult) {
-        return selected?.forward(checkResult) ? undefined : "lightGray";
-    }
-    selected?.forward();
+      const selected = (SelectionManager.Views().lastElement()?.ComponentView as WebBox);
+      if (checkResult) {
+            return selected?.forward(checkResult) ? undefined : "lightGray";
+      }
+      selected?.forward();
 });
 ScriptingGlobals.add(function webBack(checkResult?: boolean) {
-    const selected = (SelectionManager.Views().lastElement()?.ComponentView as WebBox);
-    if (checkResult) {
-        return selected?.back(checkResult) ? undefined : "lightGray";
-    }
-    selected?.back();
+      const selected = (SelectionManager.Views().lastElement()?.ComponentView as WebBox);
+      if (checkResult) {
+            return selected?.back(checkResult) ? undefined : "lightGray";
+      }
+      selected?.back();
 });
 
 
@@ -797,30 +797,30 @@ ScriptingGlobals.add(function webBack(checkResult?: boolean) {
  *      toggleSchemaPreview
  **/
 ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) {
-    const selected = SelectionManager.Docs().lastElement();
-    if (checkResult && selected) {
-        const result: boolean = NumCast(selected.schemaPreviewWidth) > 0;
-        if (result) return Colors.MEDIUM_BLUE;
-        else return "transparent";
-    }
-    else if (selected) {
-        if (NumCast(selected.schemaPreviewWidth) > 0) {
-            selected.schemaPreviewWidth = 200;
-        } else {
-            selected.schemaPreviewWidth = 0;
-        }
-    }
+      const selected = SelectionManager.Docs().lastElement();
+      if (checkResult && selected) {
+            const result: boolean = NumCast(selected.schemaPreviewWidth) > 0;
+            if (result) return Colors.MEDIUM_BLUE;
+            else return "transparent";
+      }
+      else if (selected) {
+            if (NumCast(selected.schemaPreviewWidth) > 0) {
+                  selected.schemaPreviewWidth = 200;
+            } else {
+                  selected.schemaPreviewWidth = 0;
+            }
+      }
 });
 
 /** STACK
  *      groupBy
  */
 ScriptingGlobals.add(function setGroupBy(key: string, checkResult?: boolean) {
-    SelectionManager.Docs().map(doc => doc._fontFamily = key);
-    const editorView = RichTextMenu.Instance.TextView?.EditorView;
-    if (checkResult) {
-        return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
-    }
-    if (editorView) RichTextMenu.Instance.setFontFamily(key);
-    else Doc.UserDoc().fontFamily = key;
+      SelectionManager.Docs().map(doc => doc._fontFamily = key);
+      const editorView = RichTextMenu.Instance.TextView?.EditorView;
+      if (checkResult) {
+            return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily);
+      }
+      if (editorView) RichTextMenu.Instance.setFontFamily(key);
+      else Doc.UserDoc().fontFamily = key;
 });
\ No newline at end of file
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index f4f33482e..bc1cea505 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -43,6 +43,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
       @computed get presBox() { return Cast(this.lookupField("presBox"), Doc, null); }
       @computed get targetDoc() { return Cast(this.rootDoc.presentationTargetDoc, Doc, null) || this.rootDoc; }
 
+
       @observable isShowingVideo = false;
       @action setIsShowingVideo(shown: boolean) {
             this.isShowingVideo = shown
@@ -271,10 +272,31 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
       }
 
+      @computed get recordingIsInOverlay() {
+            let isInOverlay = false
+            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
+                  if (doc.slides === this.rootDoc) {
+                        isInOverlay = true
+                        return
+                  }
+            })
+            return isInOverlay
+      }
+
       @undoBatch
       @action
-      startRecording = (targetDoc: Doc, activeItem: Doc) => {
+      hideRecording = (targetDoc: Doc, activeItem: Doc) => {
+            console.log("hide recording")
+            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
+                  if (doc.slides === activeItem) {
+                        Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
+                  }
+            })
+      }
 
+      @undoBatch
+      @action
+      startRecording = (targetDoc: Doc, activeItem: Doc) => {
             // Remove every recording that already exists in overlay view
             DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
                   if (doc.slides !== null) {
@@ -284,21 +306,24 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
             if (activeItem.recording) {
                   // if we already have an existing recording
+                  console.log("recording exists")
                   Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, Cast(activeItem.recording, Doc, null));
 
             } else {
+                  console.log("creating new recording")
                   // if we dont have any recording
                   const recording = Docs.Create.WebCamDocument("", { _width: 400, _height: 200, title: "recording", system: true, cloneFieldFilter: new List<string>(["system"]) });
+
                   // attach the recording to the slide, and attach the slide to the recording
                   recording.slides = activeItem
                   activeItem.recording = recording
+
                   // TODO: Figure out exactly where we want the video to appear
                   const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
                   recording.x = pt[0];
                   recording.y = pt[1];
                   Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, recording);
             }
-
       }
 
       @computed
@@ -375,13 +400,25 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                                 onClick={() => this.updateView(targetDoc, activeItem)}
                                                 style={{ fontWeight: 700, display: activeItem.presPinView ? "flex" : "none" }}>V</div>
                                     </Tooltip>
-                                    <Tooltip title={<><div className="dash-tooltip">{"Start recording"}</div></>}>
-                                          <div className="slideButton"
-                                                onClick={() => this.startRecording(targetDoc, activeItem)}
-                                                style={{ fontWeight: 700 }}>
-                                                <FontAwesomeIcon icon={"video"} onPointerDown={e => e.stopPropagation()} />
-                                          </div>
-                                    </Tooltip>
+
+                                    {this.recordingIsInOverlay ?
+                                          <Tooltip title={<><div className="dash-tooltip">{"Hide Recording"}</div></>}>
+                                                <div className="slideButton"
+                                                      onClick={() => this.hideRecording(targetDoc, activeItem)}
+                                                      style={{ fontWeight: 700 }}>
+                                                      <FontAwesomeIcon icon={"video-slash"} onPointerDown={e => e.stopPropagation()} />
+                                                </div>
+                                          </Tooltip> :
+                                          <Tooltip title={<><div className="dash-tooltip">{"Start recording"}</div></>}>
+                                                <div className="slideButton"
+                                                      onClick={() => this.startRecording(targetDoc, activeItem)}
+                                                      style={{ fontWeight: 700 }}>
+                                                      <FontAwesomeIcon icon={"video"} onPointerDown={e => e.stopPropagation()} />
+                                                </div>
+                                          </Tooltip>
+                                    }
+
+
                                     {this.indexInPres === 0 ? (null) : <Tooltip title={<><div className="dash-tooltip">{activeItem.groupWithUp ? "Ungroup" : "Group with up"}</div></>}>
                                           <div className="slideButton"
                                                 onClick={() => activeItem.groupWithUp = !activeItem.groupWithUp}
-- 
cgit v1.2.3-70-g09d2


From 4d130a85618f4e88cf8fdb4763e1a21bb71e689b Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Sun, 1 May 2022 22:18:34 -0400
Subject: put videoBox ui into componentUI function

---
 src/client/views/MainView.tsx        |  2 +-
 src/client/views/nodes/VideoBox.scss | 72 ++++++++++++++++--------------------
 src/client/views/nodes/VideoBox.tsx  | 26 +++++++++++--
 3 files changed, 54 insertions(+), 46 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index c794c73db..15ccd7b31 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -184,7 +184,7 @@ export class MainView extends React.Component {
         fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
         fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
         fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines,
-        fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt, fa.faSearchPlus, fa.faVolumeUp, fa.faVolumeDown]);
+        fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt, fa.faSearchPlus, fa.faVolumeUp, fa.faVolumeMute]);
         this.initAuthenticationRouters();
     }
 
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index f47a71469..08d0bb035 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -75,54 +75,53 @@
 //     pointer-events: all;
 // }
 
+.videoBox-ui-wrapper {
+    width: 0;
+    height: 0;
+}
+
 .videoBox-ui {
     position: absolute;
     flex-direction: row;
     align-items: center;
     justify-content: center;
     display: flex;
-    width: 100%;
-    visibility: none;
-    opacity: 0;
+    // visibility: none;
+    // opacity: 0;
     background-color: $dark-gray;
     color: white;
     border-radius: 100px;
-    top: calc(100% - 20px);
-    left: 50%;
-    transform: translate(-50%, -100%);
+    z-index: 2001;
+    min-width: 300px;
     
     transition: top 0.5s, width 0.5s, opacity 0.2s, visibility 0s;
-    height: 120px;
-    padding: 0 20px;
+    height: 50px;
+    padding: 0 10px 0 7px;
 
     .timecode-controls {
         display: flex;
         flex-direction: row;
         align-items: center;
         justify-content: center;
-        margin: 0 5px;
+        margin: 0 2px;
         flex-grow: 2;
-        font-size: 32px;
-
-        .timecode {
-            margin: 0 5px;
-        }
+        font-size: 14px;
 
         .timeline-slider {
-            margin: 0 20px 0 20px;
+            margin: 5px;
             flex-grow: 2;
         }
     }
 
     .toolbar-slider.volume, .toolbar-slider.zoom {
-        width: 100px;
+        width: 50px;
     }
 
     .videobox-button {
-        margin: 5px;
+        margin: 2px;
         cursor: pointer;
-        width: 70px;
-        height: 70px;
+        width: 30px;
+        height: 30px;
         border-radius: 50%;
         background: $dark-gray;
         display: flex;
@@ -134,8 +133,8 @@
         }
 
         svg {
-            width: 40px;
-            height: 40px;
+            width: 17px;
+            height: 17px;
         }
     }
 }
@@ -156,23 +155,15 @@
     }
 }
 
-.videoBox:hover {
-    .videoBox-ui {
-        visibility: visible;
-        opacity: 1;
-        z-index: 10000;
-    }
-}
-
 .videoBox-content-fullScreen, .videoBox-content-fullScreen-interactive {
     display: flex;
     justify-content: center;
-    align-items: center;
+    align-items: flex-end;
 
-    &:hover {
-        .videoBox-ui {
-            opacity: 0;
-        }
+    .videoBox-ui {
+        opacity: 0;
+        bottom: 50px;
+        width: 80%;
     }
 
     .videoBox-ui:hover {
@@ -187,7 +178,6 @@ video::-webkit-media-controls {
 input[type="range"] {
     -webkit-appearance: none;
     background: none;
-    margin: 10px;
 }
 
 input[type="range"]:focus {
@@ -196,21 +186,21 @@ input[type="range"]:focus {
 
 input[type="range"]::-webkit-slider-runnable-track {
     width: 100%;
-    height: 20px;
+    height: 10px;
     cursor: pointer;
     box-shadow: 0;
     background: $light-gray;
-    border-radius: 20px;
+    border-radius: 10px;
 }
 
 input[type="range"]::-webkit-slider-thumb {
     box-shadow: 0;
     border: 0;
-    height: 26px;
-    width: 26px;
-    border-radius: 20px;
+    height: 12px;
+    width: 12px;
+    border-radius: 10px;
     background: $medium-blue;
     cursor: pointer;
     -webkit-appearance: none;
-    margin-top: -3px;
+    margin-top: -1px;
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index e57cb1abe..80ad63e72 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -383,7 +383,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return !field ? <div key="loading">Loading</div> :
             <div className="videoBox-contentContainer" key="container" style={{ mixBlendMode: "multiply" }}>
                 <div className={classname} ref={this.setContentRef} onPointerDown={(e) => this._fullScreen && e.stopPropagation()}>
-                    {this.uIButtons}
+                    {/* {this._fullScreen && this.componentUI(0, 0)} */}
+                    {this._fullScreen && <div className="videoBox-ui">
+                        {this.UIButtons}
+                    </div>}
                     <video key="video" autoPlay={this._screenCapture} ref={this.setVideoRef} style={this._fullScreen ? this.fullScreenSize() : {}}
                         onCanPlay={this.videoLoad}
                         controls={VideoBox._nativeControls}
@@ -692,9 +695,24 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
 
     // renders video controls
-    @computed get uIButtons() {
+    componentUI = (boundsLeft: number, boundsTop: number) => {
+        const bounds = this.props.docViewPath().lastElement().getBounds();
+        const left = bounds?.left || 0;
+        const right = bounds?.right || 0;
+        const top = bounds?.top || 0;
+        const height = (bounds?.bottom || 0) - top;
+        const yPos = height * this.heightPercent / 100 - 60 + top;
+        const width = Math.max(right - left - 20, 300);
+        return this._fullScreen ? null : <div className="videoBox-ui-wrapper" style={{ clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)` }}>
+            <div className="videoBox-ui" style={{ left: left + 10, top: yPos, width: width }}>
+                {this.UIButtons}
+            </div>
+        </div>
+    }
+
+    @computed get UIButtons() {
         const curTime = NumCast(this.layoutDoc._currentTimecode) - (this.timeline?.clipStart || 0);
-        return <div className="videoBox-ui" style={this._fullScreen || this.heightPercent === 100 ? { fontSize: "40px", minWidth: "80%" } : {}}>
+        return <>
             <div className="videobox-button"
                 title={this._playing ? "play" : "pause"}
                 onPointerDown={this.onPlayDown}>
@@ -762,7 +780,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                         onChange={(e: React.ChangeEvent<HTMLInputElement>) => { this.zoom(Number(e.target.value)); }}
                     />
                 </>}
-        </div>;
+        </>
     }
 
     // renders CollectionStackedTimeline
-- 
cgit v1.2.3-70-g09d2


From f072a9f2c1ad69eb8ae4242fa5d2e18bbd94f6ef Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 6 May 2022 11:45:44 -0400
Subject: changed following links to open up icons all the way down to the
 target document. allow StartLink to work with webBox w/ text selection. 
 fixed web clippings to have a cursor and grab keyDown events.

---
 src/client/util/DocumentManager.ts                 | 42 +++++++++++++++-------
 .../util/Import & Export/DirectoryImportBox.tsx    |  2 +-
 src/client/util/LinkManager.ts                     | 11 +++---
 src/client/util/SharingManager.tsx                 | 30 ++++++++--------
 src/client/views/DocumentDecorations.tsx           | 13 ++++---
 src/client/views/MainView.tsx                      |  2 +-
 src/client/views/collections/TabDocView.tsx        |  2 +-
 src/client/views/collections/TreeView.tsx          |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../collectionLinear/CollectionLinearView.tsx      |  2 +-
 .../collectionSchema/CollectionSchemaCells.tsx     |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  3 +-
 src/client/views/nodes/LinkDescriptionPopup.tsx    |  1 +
 src/client/views/nodes/PDFBox.tsx                  |  1 +
 src/client/views/nodes/WebBox.scss                 |  3 ++
 src/client/views/nodes/WebBox.tsx                  | 11 +++---
 src/client/views/nodes/trails/PresBox.tsx          |  4 +--
 src/client/views/search/SearchBox.tsx              |  2 +-
 18 files changed, 84 insertions(+), 51 deletions(-)

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

diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index b6c28d2fe..aeddc3249 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -156,7 +156,7 @@ export class DocumentManager {
         targetDoc: Doc,        // document to display
         willZoom: boolean,     // whether to zoom doc to take up most of screen
         createViewFunc = DocumentManager.addView, // how to create a view of the doc if it doesn't exist
-        docContext?: Doc,  // context to load that should contain the target
+        docContext: Doc[],  // context to load that should contain the target
         linkDoc?: Doc,   // link that's being followed
         closeContextIfNotFound: boolean = false, // after opening a context where the document should be, this determines whether the context should be closed if the Doc isn't actually there
         originatingDoc: Opt<Doc> = undefined, // doc that initiated the display of the target odoc
@@ -190,31 +190,40 @@ export class DocumentManager {
             finished?.();
         };
         const annoContainerView = (!wasHidden || resolvedTarget !== annotatedDoc) && annotatedDoc && getFirstDocView(annotatedDoc);
-        const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined;
-        const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext : undefined;
+        const contextDocs = docContext.length ? await DocListCastAsync(docContext[0].data) : undefined;
+        const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext.lastElement() : undefined;
         const targetDocContext = contextDoc || annotatedDoc;
         const targetDocContextView = (targetDocContext && getFirstDocView(targetDocContext)) ||
             (wasHidden && annoContainerView);// if we have an annotation container and the target was hidden, then try again because we just un-hid the document above
         const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView;
-        if ((!docView && targetDoc.type !== DocumentType.MARKER) && annoContainerView) {
+        if (annoContainerView) {
             if (annoContainerView.props.Document.layoutKey === "layout_icon") {
-                annoContainerView.iconify(() => this.jumpToDocument(
-                    targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc,
-                    finished, originalTarget, noSelect, presZoom));
+                annoContainerView.iconify(() => annoContainerView.focus(targetDoc, {
+                    originalTarget, willZoom, scale: presZoom, afterFocus: (didFocus: boolean) =>
+                        new Promise<ViewAdjustment>(res => {
+                            focusAndFinish(true);
+                            res(ViewAdjustment.doNothing);
+                        })
+                }));
                 return;
-            } else {
+            } else if (!docView && targetDoc.type !== DocumentType.MARKER) {
                 annoContainerView.focus(targetDoc);  // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below
             }
         }
         if (focusView) {
             !noSelect && Doc.linkFollowHighlight(focusView.rootDoc); //TODO:glr make this a setting in PresBox
-            focusView.focus(targetDoc, {
+            const doFocus = (forceDidFocus: boolean) => focusView.focus(originalTarget ?? targetDoc, {
                 originalTarget, willZoom, scale: presZoom, afterFocus: (didFocus: boolean) =>
                     new Promise<ViewAdjustment>(res => {
-                        focusAndFinish(didFocus);
+                        focusAndFinish(forceDidFocus || didFocus);
                         res(ViewAdjustment.doNothing);
                     })
             });
+            if (focusView.props.Document.layoutKey === "layout_icon") {
+                focusView.iconify(() => doFocus(true));
+            } else {
+                doFocus(false);
+            }
         } else {
             if (!targetDocContext) { // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default
                 createViewFunc(Doc.BrushDoc(targetDoc), finished); // bcz: should we use this?: Doc.MakeAlias(targetDoc)));
@@ -228,7 +237,7 @@ export class DocumentManager {
                             targetDocContext._viewTransition = undefined;
                             if (targetDocContext.layoutKey === "layout_icon") {
                                 targetDocContextView.iconify(() => this.jumpToDocument(
-                                    targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc,
+                                    resolvedTarget ?? targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc,
                                     finished, originalTarget, noSelect, presZoom));
                             }
                             return ViewAdjustment.doNothing;
@@ -263,7 +272,16 @@ export class DocumentManager {
                         };
                         setTimeout(() => findView(0), 0);
                     }
-                } else {  // there's no context view so we need to create one first and try again when that finishes
+                } else {
+                    if (docContext.length && docContext[0]?.layoutKey === "layout_icon") {
+                        const docContextView = this.getFirstDocumentView(docContext[0]);
+                        if (docContextView) {
+                            return docContextView.iconify(() => this.jumpToDocument(
+                                targetDoc, willZoom, createViewFunc, docContext.slice(1, docContext.length), linkDoc, closeContextIfNotFound, originatingDoc,
+                                finished, originalTarget, noSelect, presZoom));
+                        }
+                    }
+                    // there's no context view so we need to create one first and try again when that finishes
                     const finishFunc = () => this.jumpToDocument(targetDoc, true, createViewFunc, docContext, linkDoc, true /* if we don't find the target, we want to get rid of the context just created */, undefined, finished, originalTarget);
                     createViewFunc(targetDocContext, // after creating the context, this calls the finish function that will retry looking for the target
                         finishFunc);
diff --git a/src/client/util/Import & Export/DirectoryImportBox.tsx b/src/client/util/Import & Export/DirectoryImportBox.tsx
index 39e9251a5..37571ae01 100644
--- a/src/client/util/Import & Export/DirectoryImportBox.tsx	
+++ b/src/client/util/Import & Export/DirectoryImportBox.tsx	
@@ -161,7 +161,7 @@ export class DirectoryImportBox extends React.Component<FieldViewProps> {
             await GooglePhotos.Export.CollectionToAlbum({ collection: importContainer });
             Doc.AddDocToList(Doc.GetProto(parent.props.Document), "data", importContainer);
             !this.persistent && this.props.removeDocument && this.props.removeDocument(doc);
-            DocumentManager.Instance.jumpToDocument(importContainer, true);
+            DocumentManager.Instance.jumpToDocument(importContainer, true, undefined, []);
         }
 
         runInAction(() => {
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index 9445533dc..b28662a57 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,3 +1,4 @@
+import { validationResult } from "express-validator/check";
 import { action, observable, observe } from "mobx";
 import { computedFn } from "mobx-utils";
 import { DirectLinksSym, Doc, DocListCast, Field, Opt } from "../../fields/Doc";
@@ -241,10 +242,12 @@ export class LinkManager {
                     } else {
                         const containerAnnoDoc = Cast(target.annotationOn, Doc, null);
                         const containerDoc = containerAnnoDoc || target;
-                        const containerDocContext = Cast(containerDoc?.context, Doc, null);
-                        const targetContext = LightboxView.LightboxDoc ? containerAnnoDoc || containerDocContext : containerDocContext;
-                        const targetNavContext = !Doc.AreProtosEqual(targetContext, currentContext) ? targetContext : undefined;
-                        DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "lightbox"), finished), targetNavContext, linkDoc, undefined, sourceDoc, allFinished);
+                        var containerDocContext = containerDoc?.context ? [Cast(containerDoc?.context, Doc, null)] : [] as Doc[];
+                        while (containerDocContext.length && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && containerDocContext[0].context) {
+                            containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
+                        }
+                        const targetContexts = LightboxView.LightboxDoc ? [containerAnnoDoc || containerDocContext[0]] : containerDocContext;
+                        DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "lightbox"), finished), targetContexts, linkDoc, undefined, sourceDoc, allFinished);
                     }
                 } else {
                     allFinished();
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 6d7f7e8df..11450309a 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -370,11 +370,11 @@ export class SharingManager extends React.Component<{}> {
         if (!uniform) dropdownValues.unshift("-multiple-");
         if (override) dropdownValues.unshift("None");
         return dropdownValues.filter(permission => !Doc.UserDoc().noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
-            (
-                <option key={permission} value={permission}>
-                    {permission}
-                </option>
-            )
+        (
+            <option key={permission} value={permission}>
+                {permission}
+            </option>
+        )
         );
     }
 
@@ -388,7 +388,7 @@ export class SharingManager extends React.Component<{}> {
                 onClick={() => {
                     let context: Opt<CollectionView>;
                     if (this.targetDoc && this.targetDocView && docs.length === 1 && (context = this.targetDocView.props.ContainingCollectionView)) {
-                        DocumentManager.Instance.jumpToDocument(this.targetDoc, true, undefined, context.props.Document);
+                        DocumentManager.Instance.jumpToDocument(this.targetDoc, true, undefined, [context.props.Document]);
                     }
                 }}
                 onPointerEnter={action(() => {
@@ -548,10 +548,10 @@ export class SharingManager extends React.Component<{}> {
                                 {this.sharingOptions(uniform)}
                             </select>
                         ) : (
-                                <div className={"permissions-dropdown"}>
-                                    {permissions}
-                                </div>
-                            )}
+                            <div className={"permissions-dropdown"}>
+                                {permissions}
+                            </div>
+                        )}
                     </div>
                 </div>
             );
@@ -572,7 +572,7 @@ export class SharingManager extends React.Component<{}> {
                         <div className="edit-actions">
                             <div className={"permissions-dropdown"}>
                                 Owner
-                        </div>
+                            </div>
                         </div>
                     </div>
                 ) : null,
@@ -622,10 +622,10 @@ export class SharingManager extends React.Component<{}> {
                                 {this.sharingOptions(uniform, group.title === "Override")}
                             </select>
                         ) : (
-                                <div className={"permissions-dropdown"}>
-                                    {permissions}
-                                </div>
-                            )}
+                            <div className={"permissions-dropdown"}>
+                                {permissions}
+                            </div>
+                        )}
                     </div>
                 </div>
             );
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index eb2bcc18c..936946df0 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -113,7 +113,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         }
     }
 
-    titleEntered = (e: React.KeyboardEvent) => e.key === "Enter" && (e.target as any).blur();
+    titleEntered = (e: React.KeyboardEvent) => {
+        if (e.key === "Enter") {
+            e.stopPropagation();
+            (e.target as any).blur();
+        }
+    }
 
     @action onTitleDown = (e: React.PointerEvent): void => {
         setupMoveUpEvents(this, e, e => this.onBackgroundMove(true, e), (e) => { }, action((e) => {
@@ -288,7 +293,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         if (fixedAspect && (resizeHdl === "documentDecorations-bottomRightResizer" || resizeHdl === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles
             const project = (p: number[], a: number[], b: number[]) => {
                 const atob = [b[0] - a[0], b[1] - a[1]];
-                const atop = [p[0] - a[0], p[1] - a[1]]; first
+                const atop = [p[0] - a[0], p[1] - a[1]];
                 const len = atob[0] * atob[0] + atob[1] * atob[1];
                 let dot = atop[0] * atob[0] + atop[1] * atob[1];
                 const t = dot / len;
@@ -501,7 +506,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                     value={this._accumulatedTitle}
                     onBlur={e => this.titleBlur()}
                     onChange={action(e => this._accumulatedTitle = e.target.value)}
-                    onKeyPress={this.titleEntered} /> :
+                    onKeyDown={this.titleEntered} /> :
                 <div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown} >
                     <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${this.selectionTitle}`}</span>
                 </div>;
@@ -521,7 +526,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
         const useRotation = seldoc.ComponentView instanceof InkingStroke || seldoc.ComponentView instanceof ImageBox;
         const resizerScheme = colorScheme ? "documentDecorations-resizer" + colorScheme : "";
 
-        const rotation = NumCast(seldoc.rootDoc._jitterRotation)
+        const rotation = NumCast(seldoc.rootDoc._jitterRotation);
         const rdeg = rotation / 180 * Math.PI;
 
         return (<div className={`documentDecorations${colorScheme}`}>
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index c794c73db..db51c54f8 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -133,7 +133,7 @@ export class MainView extends React.Component {
         window.addEventListener("paste", KeyManager.Instance.paste as any);
         document.addEventListener("dash", (e: any) => {  // event used by chrome plugin to tell Dash which document to focus on
             const id = FormattedTextBox.GetDocFromUrl(e.detail);
-            DocServer.GetRefField(id).then(doc => (doc instanceof Doc) ? DocumentManager.Instance.jumpToDocument(doc, false, undefined) : (null));
+            DocServer.GetRefField(id).then(doc => (doc instanceof Doc) ? DocumentManager.Instance.jumpToDocument(doc, false, undefined, []) : (null));
         });
         document.addEventListener("linkAnnotationToDash", Hypothesis.linkListener);
         this.initEventListeners();
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 78b125e07..8dfc7edc7 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -256,7 +256,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 }
                 PresBox.Instance?._selectedArray.clear();
                 pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
-                DocumentManager.Instance.jumpToDocument(doc, false, undefined);
+                DocumentManager.Instance.jumpToDocument(doc, false, undefined, []);
                 batch.end();
             });
         }
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 72480b094..164021358 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -72,7 +72,7 @@ export enum TreeSort {
     Down = "down",
     Zindex = "z",
     None = "none"
-};
+}
 /**
  * Renders a treeView of a collection of documents
  * 
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 5d1e1892c..5b3a7db41 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1423,7 +1423,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     }
 
     replaceCanvases = (oldDiv: HTMLElement, newDiv: HTMLElement) => {
-        if (oldDiv.childNodes) {
+        if (oldDiv.childNodes && newDiv) {
             for (let i = 0; i < oldDiv.childNodes.length; i++) {
                 this.replaceCanvases(oldDiv.childNodes[i] as HTMLElement, newDiv.childNodes[i] as HTMLElement);
             }
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index e3d4f86c1..c615bfb8e 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -231,7 +231,7 @@ export class CollectionLinearView extends CollectionSubView() {
                         <span className="bottomPopup-text">
                             Currently playing:
                             {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) =>
-                                <span className="audio-title" onPointerDown={() => DocumentManager.Instance.jumpToDocument(clip, true)}>
+                                <span className="audio-title" onPointerDown={() => DocumentManager.Instance.jumpToDocument(clip, true, undefined, [])}>
                                     {clip.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? "" : ",")}
                                 </span>)}
                         </span>
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
index c2bb3b3ac..adcd9e1e3 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
@@ -199,7 +199,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
             const aliasdoc = await SearchUtil.GetAliasesOfDocument(this._rowDataDoc);
             const targetContext = aliasdoc.length <= 0 ? undefined : Cast(aliasdoc[0].context, Doc, null);
             // Jump to the this document
-            DocumentManager.Instance.jumpToDocument(this._rowDoc, false, emptyFunction, targetContext,
+            DocumentManager.Instance.jumpToDocument(this._rowDoc, false, emptyFunction, targetContext ? [targetContext] : [],
                 undefined, undefined, undefined, () => this.props.setPreviewDoc(this._rowDoc));
         }
     }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 8570f6fff..02ea87103 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -663,7 +663,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             }
             if (de.complete.annoDragData || this.rootDoc !== linkdrag.linkSourceDoc.context) {
                 const dropDoc = de.complete.annoDragData?.dropDocument ?? this._componentView?.getAnchor?.() ?? this.props.Document;
-                de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, undefined, undefined, undefined, undefined, [de.x, de.y]);
+                de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, undefined, undefined, undefined, undefined, [de.x, de.y - 50]);
             }
         }
     }
@@ -857,6 +857,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     }
     @observable _retryThumb = 1;
     thumbShown = () => !this.props.isSelected() && LightboxView.LightboxDoc !== this.rootDoc && this.thumb &&
+        !Doc.AreProtosEqual(DocumentLinksButton.StartLink, this.rootDoc) &&
         !this._componentView?.isAnyChildContentActive?.() ? true : false
     @computed get contents() {
         TraceMobx();
diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx
index a9d33f161..ccac66996 100644
--- a/src/client/views/nodes/LinkDescriptionPopup.tsx
+++ b/src/client/views/nodes/LinkDescriptionPopup.tsx
@@ -54,6 +54,7 @@ export class LinkDescriptionPopup extends React.Component<{}> {
                 top: LinkDescriptionPopup.popupY ? LinkDescriptionPopup.popupY : 350,
             }}>
             <input className="linkDescriptionPopup-input"
+                onKeyDown={e => e.stopPropagation()}
                 onKeyPress={e => e.key === "Enter" && this.onDismiss(true)}
                 placeholder={"(Optional) Enter link description..."}
                 onChange={(e) => this.descriptionChanged(e)}>
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 69662d53a..48465976a 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -75,6 +75,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
     updateIcon = () => {
+        return; // currently we render pdf icons as text labels
         const docViewContent = this.props.docViewPath().lastElement().ContentDiv!;
         const newDiv = docViewContent.cloneNode(true) as HTMLDivElement;
         newDiv.style.width = (this.layoutDoc[WidthSym]()).toString();
diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss
index b037e7220..d8dd074a5 100644
--- a/src/client/views/nodes/WebBox.scss
+++ b/src/client/views/nodes/WebBox.scss
@@ -156,6 +156,9 @@
             position: absolute;
             top: 0;
             left: 0;
+            cursor: text;
+            padding: 15px;
+            height: 100%
         }
 
         .webBox-cont {
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 80961c92f..f441e5d36 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -207,8 +207,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
     @action
-    createTextAnnotation = (sel: Selection, selRange: Range) => {
-        if (this._mainCont.current) {
+    createTextAnnotation = (sel: Selection, selRange: Range | undefined) => {
+        if (this._mainCont.current && selRange) {
             const clientRects = selRange.getClientRects();
             for (let i = 0; i < clientRects.length; i++) {
                 const rect = clientRects.item(i);
@@ -235,7 +235,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     menuControls = () => this.urlEditor; // controls to be added to the top bar when a document of this type is selected
 
     scrollFocus = (doc: Doc, smooth: boolean) => {
-        this.props.select(true);
+        this.props.select(false); // hack to preven the webBox from being replaced with a thumbnail that currently doesn't show the the anchors
         if (StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl), !smooth);
         if (DocListCast(this.props.Document[this.fieldKey + "-sidebar"]).includes(doc) && !this.SidebarShown) {
             this.toggleSidebar(!smooth);
@@ -252,7 +252,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         }
         this._initialScroll = NumCast(this.layoutDoc._scrollTop);
-        return 0;
+        return undefined;
     }
 
     getAnchor = () => {
@@ -279,7 +279,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             const scale = (this.props.scaling?.() || 1) * mainContBounds.scale;
             const sel = this._iframe.contentWindow.getSelection();
             if (sel) {
-                this._textAnnotationCreator = () => this.createTextAnnotation(sel, sel.getRangeAt(0));
+                this._textAnnotationCreator = () => this.createTextAnnotation(sel, !sel.isCollapsed ? sel.getRangeAt(0) : undefined);
                 AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX,
                     e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale);
             }
@@ -650,6 +650,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get content() {
         const interactive = !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents !== "none" && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting;
         return <div className={"webBox-cont" + (interactive ? "-interactive" : "")}
+            onKeyDown={e => e.stopPropagation()}
             style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) || `100%` : "100%", }}>
             {this.urlContent}
         </div>;
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 30ad43562..820d7ef96 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -389,11 +389,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             LightboxView.SetLightboxDoc(targetDoc);
         } else if (curDoc.presMovement === PresMovement.Pan && targetDoc) {
             LightboxView.SetLightboxDoc(undefined);
-            await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext, undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right
+            await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, [srcContext], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right
         } else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) {
             LightboxView.SetLightboxDoc(undefined);
             //awaiting jump so that new scale can be found, since jumping is async     
-            await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext, undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right    
+            await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, [srcContext], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right    
         }
         // After navigating to the document, if it is added as a presPinView then it will
         // adjust the pan and scale to that of the pinView when it was added.
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 9257cb75e..3d36efbc4 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -365,7 +365,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
      * or opening it in a new tab.
      */
     selectElement = async (doc: Doc, finishFunc: () => void) => {
-        await DocumentManager.Instance.jumpToDocument(doc, true, undefined, undefined, undefined, undefined, undefined, finishFunc);
+        await DocumentManager.Instance.jumpToDocument(doc, true, undefined, [], undefined, undefined, undefined, finishFunc);
     }
 
     /**
-- 
cgit v1.2.3-70-g09d2


From 57c1b8235b26a1b6a315922b4dc2926e1e597674 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 13 May 2022 18:17:10 -0400
Subject: removed layerProvider functionality.

---
 src/client/documents/Documents.ts                  |  3 +-
 src/client/views/DocComponent.tsx                  |  4 --
 src/client/views/GestureOverlay.tsx                |  1 -
 src/client/views/InkingStroke.tsx                  |  2 +-
 src/client/views/LightboxView.tsx                  |  1 -
 src/client/views/MainView.tsx                      |  6 ---
 src/client/views/OverlayView.tsx                   |  1 -
 src/client/views/Palette.tsx                       |  1 -
 src/client/views/PropertiesView.tsx                |  2 -
 src/client/views/StyleProvider.tsx                 | 52 +++-------------------
 src/client/views/TemplateMenu.tsx                  |  1 -
 src/client/views/collections/CollectionMenu.tsx    |  1 -
 .../collections/CollectionStackedTimeline.tsx      |  2 +-
 .../views/collections/CollectionStackingView.tsx   |  2 -
 .../views/collections/CollectionTreeView.tsx       |  1 -
 src/client/views/collections/CollectionView.tsx    |  2 +-
 src/client/views/collections/TabDocView.tsx        | 19 +-------
 src/client/views/collections/TreeView.tsx          |  2 -
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 24 +++++-----
 .../collections/collectionFreeForm/MarqueeView.tsx | 10 ++---
 .../collectionLinear/CollectionLinearView.tsx      |  1 -
 .../CollectionMulticolumnView.tsx                  |  1 -
 .../CollectionMultirowView.tsx                     |  1 -
 .../collectionSchema/CollectionSchemaView.tsx      |  1 -
 .../collections/collectionSchema/SchemaTable.tsx   |  1 -
 src/client/views/linking/LinkPopup.tsx             |  3 +-
 src/client/views/nodes/AudioBox.tsx                |  2 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |  2 -
 src/client/views/nodes/DocumentView.tsx            |  5 +--
 src/client/views/nodes/FilterBox.tsx               |  1 -
 src/client/views/nodes/ImageBox.tsx                |  2 +-
 src/client/views/nodes/KeyValuePair.tsx            |  1 -
 src/client/views/nodes/LinkAnchorBox.tsx           |  1 -
 src/client/views/nodes/LinkDocPreview.tsx          |  1 -
 src/client/views/nodes/VideoBox.tsx                |  2 +-
 src/client/views/nodes/WebBox.tsx                  |  6 +--
 .../views/nodes/formattedText/DashDocView.tsx      |  1 -
 .../views/nodes/formattedText/FormattedTextBox.tsx |  2 +-
 src/client/views/nodes/trails/PresBox.tsx          |  2 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |  1 -
 src/client/views/pdf/Annotation.tsx                |  2 +-
 src/client/views/search/SearchBox.tsx              |  2 +-
 src/fields/Doc.ts                                  |  2 +-
 src/mobile/AudioUpload.tsx                         |  1 -
 src/mobile/MobileInterface.tsx                     |  1 -
 src/server/server_Initialization.ts                |  2 +-
 46 files changed, 41 insertions(+), 143 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index d9c2e0d8b..797a129c4 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -122,7 +122,6 @@ export class DocumentOptions {
     _scrollTop?: number; // scroll location for pdfs
     _noAutoscroll?: boolean;// whether collections autoscroll when this item is dragged
     _chromeHidden?: boolean; // whether the editing chrome for a document is hidden
-    _layerTags?: List<string>; // layer tags a document has (used for tab filtering "layers" in document tab)
     _searchDoc?: boolean; // is this a search document (used to change UI for search results in schema view)
     _forceActive?: boolean; // flag to handle pointer events when not selected (or otherwise active)
     _stayInCollection?: boolean;// whether the document should remain in its collection when someone tries to drag and drop it elsewhere
@@ -426,7 +425,7 @@ export namespace Docs {
                     childDontRegisterViews: true, _isLinkButton: true, _height: 150, description: "", showCaption: "description",
                     backgroundColor: "lightblue", // lightblue is default color for linking dot and link documents text comment area
                     links: "@links(self)",
-                    _removeDropProperties: new List(["_layerTags", "isLinkButton"]),
+                    _removeDropProperties: new List(["isLinkButton"]),
                 }
             }],
             [DocumentType.LINKDB, {
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 79aaf2158..9c176a4fd 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -44,7 +44,6 @@ interface ViewBoxBaseProps {
     ContainingCollectionDoc: Opt<Doc>;
     DocumentView?: () => DocumentView;
     fieldKey: string;
-    layerProvider?: (doc: Doc, assign?: boolean) => boolean;
     isSelected: (outsideReaction?: boolean) => boolean;
     isContentActive: () => boolean | undefined;
     renderDepth: number;
@@ -85,7 +84,6 @@ export interface ViewBoxAnnotatableProps {
     DataDoc?: Doc;
     fieldKey: string;
     filterAddDocument?: (doc: Doc[]) => boolean;  // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
-    layerProvider?: (doc: Doc, assign?: boolean) => boolean;
     isContentActive: () => boolean | undefined;
     select: (isCtrlPressed: boolean) => void;
     whenChildContentsActiveChanged: (isActive: boolean) => void;
@@ -209,13 +207,11 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                             if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
                             doc.context = this.props.Document;
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
-                            this.props.layerProvider?.(doc, true);
                             Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
                         });
                     }
                     else {
                         added.filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))).map(doc => {  // only make a pushpin if we have acl's to edit the document
-                            this.props.layerProvider?.(doc, true);
                             //DocUtils.LeavePushpin(doc);
                             doc._stayInCollection = undefined;
                             doc.context = this.props.Document;
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 50dca0a99..482b62479 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -881,7 +881,6 @@ export class GestureOverlay extends Touchable {
                 isContentActive={returnFalse}
                 renderDepth={0}
                 styleProvider={returnEmptyString}
-                layerProvider={undefined}
                 docViewPath={returnEmptyDoclist}
                 focus={DocUtils.DefaultFocus}
                 whenChildContentsActiveChanged={emptyFunction}
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 3d9c048e5..d0f01c363 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -332,7 +332,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
             inkStrokeWidth, inkStrokeWidth + (highlightIndex && closed && fillColor && (new Color(fillColor)).alpha() < 1 ? 6 : 15),
             StrCast(this.layoutDoc.strokeLineJoin), StrCast(this.layoutDoc.strokeLineCap),
             StrCast(this.layoutDoc.strokeBezier), !closed ? "none" : fillColor === "transparent" || suppressFill ? "none" : fillColor, startMarker, endMarker,
-            markerScale, undefined, inkScaleX, inkScaleY, "", this.props.pointerEvents?.() ?? (this.props.layerProvider?.(this.props.Document) === false ? "none" : "visiblepainted"), 0.0,
+            markerScale, undefined, inkScaleX, inkScaleY, "", this.props.pointerEvents?.() ?? (this.rootDoc._lockedPosition ? "none" : "visiblepainted"), 0.0,
             false, downHdlr);
         const fsize = +(StrCast(this.props.Document.fontSize, "12px").replace("px", ""));
         // bootsrap 3 style sheet sets line height to be 20px for default 14 point font size. 
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index 59ed0dc92..9882693ee 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -246,7 +246,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                         docFilters={this.docFilters}
                         removeDocument={undefined}
                         styleProvider={DefaultStyleProvider}
-                        layerProvider={returnTrue}
                         ScreenToLocalTransform={this.lightboxScreenToLocal}
                         PanelWidth={this.lightboxWidth}
                         PanelHeight={this.lightboxHeight}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index db51c54f8..2e1d10955 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -281,7 +281,6 @@ export class MainView extends React.Component {
             addDocTab={this.addDocTabFunc}
             pinToPres={emptyFunction}
             docViewPath={returnEmptyDoclist}
-            layerProvider={undefined}
             styleProvider={undefined}
             rootSelected={returnTrue}
             isContentActive={returnTrue}
@@ -361,7 +360,6 @@ export class MainView extends React.Component {
                         addDocTab={this.addDocTabFunc}
                         pinToPres={emptyFunction}
                         docViewPath={returnEmptyDoclist}
-                        layerProvider={undefined}
                         styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards || this._sidebarContent.proto === Doc.UserDoc().myFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
                         rootSelected={returnTrue}
                         removeDocument={returnFalse}
@@ -402,7 +400,6 @@ export class MainView extends React.Component {
                 docViewPath={returnEmptyDoclist}
                 focus={DocUtils.DefaultFocus}
                 styleProvider={DefaultStyleProvider}
-                layerProvider={undefined}
                 isContentActive={returnTrue}
                 whenChildContentsActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
@@ -516,7 +513,6 @@ export class MainView extends React.Component {
                     dropAction={"alias"}
                     setHeight={returnFalse}
                     styleProvider={DefaultStyleProvider}
-                    layerProvider={undefined}
                     rootSelected={returnTrue}
                     bringToFront={emptyFunction}
                     select={emptyFunction}
@@ -591,7 +587,6 @@ export class MainView extends React.Component {
                     ContainingCollectionDoc={undefined}
                     Document={DocumentLinksButton.invisibleWebDoc}
                     dropAction={"move"}
-                    layerProvider={undefined}
                     styleProvider={undefined}
                     isSelected={returnFalse}
                     select={returnFalse}
@@ -673,7 +668,6 @@ export class MainView extends React.Component {
                             rootSelected={returnFalse}
                             renderDepth={0}
                             setHeight={returnFalse}
-                            layerProvider={undefined}
                             styleProvider={undefined}
                             addDocTab={returnFalse}
                             pinToPres={returnFalse}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 0f51cf9b2..ebad2981d 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -195,7 +195,6 @@ export class OverlayView extends React.Component {
                     whenChildContentsActiveChanged={emptyFunction}
                     focus={DocUtils.DefaultFocus}
                     styleProvider={DefaultStyleProvider}
-                    layerProvider={undefined}
                     docViewPath={returnEmptyDoclist}
                     addDocTab={returnFalse}
                     pinToPres={emptyFunction}
diff --git a/src/client/views/Palette.tsx b/src/client/views/Palette.tsx
index 529697f71..954529bc9 100644
--- a/src/client/views/Palette.tsx
+++ b/src/client/views/Palette.tsx
@@ -54,7 +54,6 @@ export default class Palette extends React.Component<PaletteProps> {
                             focus={emptyFunction}
                             docViewPath={returnEmptyDoclist}
                             styleProvider={returnEmptyString}
-                            layerProvider={undefined}
                             whenChildContentsActiveChanged={emptyFunction}
                             bringToFront={emptyFunction}
                             docFilters={returnEmptyFilter}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 21c688421..bba2ac211 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -303,7 +303,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     fitContentsToDoc={returnTrue}
                     rootSelected={returnFalse}
                     styleProvider={DefaultStyleProvider}
-                    layerProvider={undefined}
                     docViewPath={returnEmptyDoclist}
                     freezeDimensions={true}
                     dontCenter={"y"}
@@ -1010,7 +1009,6 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                             createNewFilterDoc={this.createNewFilterDoc}
                             updateFilterDoc={this.updateFilterDoc}
                             docViewPath={returnEmptyDoclist}
-                            layerProvider={undefined}
                             dontCenter="y"
                         />
                     </div>
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 5056dedaf..334415b38 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -26,10 +26,6 @@ import React = require("react");
 import { InkingStroke } from './InkingStroke';
 import { TreeSort } from './collections/TreeView';
 
-export enum StyleLayers {
-    Background = "background"
-}
-
 export enum StyleProp {
     TreeViewIcon = "treeViewIcon",
     TreeViewSortings = "treeViewSortings",// options for how to sort tree view items
@@ -56,14 +52,9 @@ export enum StyleProp {
 
 function darkScheme() { return CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark; }
 
-function toggleBackground(doc: Doc) {
+function toggleLockedPosition(doc: Doc) {
     UndoManager.RunInBatch(() => runInAction(() => {
-        const layers = StrListCast(doc._layerTags);
-        if (!layers.includes(StyleLayers.Background)) {
-            if (!layers.length) doc._layerTags = new List<string>([StyleLayers.Background]);
-            else layers.push(StyleLayers.Background);
-        }
-        else layers.splice(layers.indexOf(StyleLayers.Background), 1);
+        doc._lockedPosition = !doc._lockedPosition;
     }), "toggleBackground");
 }
 
@@ -86,7 +77,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
     const isOpen = property.includes(":open");
     const fieldKey = props?.fieldKey ? props.fieldKey + "-" : isCaption ? "caption-" : "";
     const comicStyle = () => doc && !Doc.IsSystem(doc) && Doc.UserDoc().renderStyle === "comic";
-    const isBackground = () => StrListCast(doc?._layerTags).includes(StyleLayers.Background);
+    const isBackground = () => doc && BoolCast(doc._lockedPosition);
     const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor);
     const opacity = () => props?.styleProvider?.(doc, props, StyleProp.Opacity);
     const showTitle = () => props?.styleProvider?.(doc, props, StyleProp.ShowTitle);
@@ -177,7 +168,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
                 default: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break;
             }
-            if (docColor && (!doc || props?.layerProvider?.(doc) === false)) docColor = DashColor(docColor).fade(0.5).toString();
+            if (docColor && !doc) docColor = DashColor(docColor).fade(0.5).toString();
             return docColor;
         }
         case StyleProp.BoxShadow: {
@@ -205,17 +196,15 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.PointerEvents:
             if (doc?.type === DocumentType.MARKER) return "none";
             if (props?.pointerEvents?.() === "none") return "none";
-            const layer = doc && props?.layerProvider?.(doc);
             const isInk = doc && StrCast(Doc.Layout(doc).layout).includes(InkingStroke.name);
-            if (opacity() === 0 || (isInk && !docProps?.treeViewDoc) || doc?.isInkMask) return "none";
-            if (layer === false && !selected && !SnappingManager.GetIsDragging()) return "none";
-            if (!isInk && layer === true) return "all";
+            if (doc?._lockedPosition || opacity() === 0 || (isInk && !docProps?.treeViewDoc) || doc?.isInkMask) return "none";
+            if (!isInk) return "all";
             return undefined;
         case StyleProp.Decorations:
             if (props?.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform || doc?.x !== undefined || doc?.y !== undefined) {
                 return doc && (isBackground() || selected) && (props?.renderDepth || 0) > 0 &&
                     ((doc.type === DocumentType.COL && doc._viewType !== CollectionViewType.Pile) || [DocumentType.RTF, DocumentType.IMG, DocumentType.INK].includes(doc.type as DocumentType)) ?
-                    <div className="styleProvider-lock" onClick={() => toggleBackground(doc)}>
+                    <div className="styleProvider-lock" onClick={() => toggleLockedPosition(doc)}>
                         <FontAwesomeIcon icon={isBackground() ? "lock" : "unlock"} style={{ color: isBackground() ? "red" : undefined }} size="lg" />
                     </div>
                     : (null);
@@ -251,30 +240,3 @@ export function DashboardStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps
     }
     return DefaultStyleProvider(doc, props, property);
 }
-
-//
-// a preliminary semantic-"layering/grouping" mechanism for determining interactive properties of documents
-//  currently, the provider tests whether the docuemnt's layer field matches the activeLayer field of the tab.
-//     if it matches, then the document gets pointer events, otherwise it does not.
-//
-export function DefaultLayerProvider(thisDoc: Doc) {
-    return (doc: Doc, assign?: boolean) => {
-        if (doc.z) return true;
-        if (assign) {
-            const activeLayer = StrCast(thisDoc?.activeLayer);
-            if (activeLayer) {
-                const layers = Cast(doc._layerTags, listSpec("string"), []);
-                if (layers.length && !layers.includes(activeLayer)) layers.push(activeLayer);
-                else if (!layers.length) doc._layerTags = new List<string>([activeLayer]);
-                if (activeLayer === "red" || activeLayer === "green" || activeLayer === "blue") doc._backgroundColor = activeLayer;
-            }
-            return true;
-        } else {
-            if (Doc.AreProtosEqual(doc, thisDoc)) return true;
-            const layers = StrListCast(doc._layerTags);
-            if (!layers.length && !thisDoc?.activeLayer) return true;
-            if (layers.includes(StrCast(thisDoc?.activeLayer))) return true;
-            return false;
-        }
-    };
-}
\ No newline at end of file
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index b3a24e031..636f7042f 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -131,7 +131,6 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
                 ContainingCollectionDoc={undefined}
                 ContainingCollectionView={undefined}
                 styleProvider={DefaultStyleProvider}
-                layerProvider={undefined}
                 setHeight={returnFalse}
                 docViewPath={returnEmptyDoclist}
                 docFilters={returnEmptyFilter}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 364a2440e..b3fbd4ca4 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -108,7 +108,6 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
                 dropAction={"alias"}
                 setHeight={returnFalse}
                 styleProvider={DefaultStyleProvider}
-                layerProvider={undefined}
                 rootSelected={returnTrue}
                 bringToFront={emptyFunction}
                 select={emptyFunction}
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index de307416f..683b6d51d 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -392,7 +392,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
     // handles dragging and dropping markers in timeline
     @action
     internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number) {
-        if (!de.embedKey && this.props.layerProvider?.(this.props.Document) !== false && this.props.Document._isGroup) return false;
+        if (!de.embedKey && this.props.Document._isGroup) return false;
         if (!super.onInternalDrop(e, de)) return false;
 
         // determine x coordinate of drop and assign it to the documents being dragged  --- see internalDocDrop of collectionFreeFormView.tsx for how it's done when dropping onto a 2D freeform view
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 509005b45..4338dedcc 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -222,7 +222,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             PanelWidth={width}
             PanelHeight={height}
             styleProvider={this.styleProvider}
-            layerProvider={this.props.layerProvider}
             docViewPath={this.props.docViewPath}
             fitWidth={this.props.childFitWidth}
             isContentActive={emptyFunction}
@@ -543,7 +542,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                     renderDepth={this.props.renderDepth}
                     focus={emptyFunction}
                     styleProvider={this.props.styleProvider}
-                    layerProvider={this.props.layerProvider}
                     docViewPath={returnEmptyDoclist}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 41970eb96..5e89016db 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -296,7 +296,6 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                     renderDepth={this.props.renderDepth + 1}
                     focus={emptyFunction}
                     styleProvider={this.props.styleProvider}
-                    layerProvider={this.props.layerProvider}
                     docViewPath={returnEmptyDoclist}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index ee2c28b5f..7c5f49b42 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -261,7 +261,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             CollectionView: this,
         };
         return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
-            style={{ pointerEvents: this.props.layerProvider?.(this.rootDoc) === false ? "none" : undefined }}>
+            style={{ pointerEvents: this.rootDoc._lockedPosition ? "none" : undefined }}>
             {this.showIsTagged()}
             {this.renderSubView(this.collectionViewType, props)}
         </div>);
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 8dfc7edc7..73e5a9178 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -26,7 +26,7 @@ import { Colors, Shadows } from '../global/globalEnums';
 import { LightboxView } from '../LightboxView';
 import { DocFocusOptions, DocumentView, DocumentViewProps } from "../nodes/DocumentView";
 import { PinProps, PresBox, PresMovement } from '../nodes/trails';
-import { DefaultLayerProvider, DefaultStyleProvider, StyleLayers, StyleProp } from '../StyleProvider';
+import { DefaultStyleProvider, StyleProp } from '../StyleProvider';
 import { CollectionDockingView } from './CollectionDockingView';
 import { CollectionDockingViewMenu } from './CollectionDockingViewMenu';
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
@@ -94,20 +94,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
 
 
             if (tab.element[0].children[1].children.length === 1) {
-                const toggle = document.createElement("div");
-                toggle.style.width = "10px";
-                toggle.style.height = "calc(100% - 2px)";
-                toggle.style.left = "-2px";
-                toggle.style.bottom = "1px";
-                toggle.style.borderTopRightRadius = "7px";
-                toggle.style.position = "relative";
-                toggle.style.display = "inline-block";
-                toggle.style.background = "transparent";
-                toggle.onclick = (e: MouseEvent) => {
-                    if (tab.contentItem === tab.header.parent.getActiveContentItem()) {
-                        tab.DashDoc.activeLayer = tab.DashDoc.activeLayer ? undefined : StyleLayers.Background;
-                    }
-                };
                 iconWrap.className = "lm_iconWrap";
                 iconWrap.id = "lm_iconWrap";
                 closeWrap.className = "lm_iconWrap";
@@ -357,7 +343,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     disableMinimap = () => !this._document || (this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._viewType !== CollectionViewType.Freeform);
     hideMinimap = () => this.disableMinimap() || BoolCast(this._document?.hideMinimap);
 
-    @computed get layerProvider() { return this._document && DefaultLayerProvider(this._document); }
     @computed get docView() {
         return !this._activated || !this._document || this._document._viewType === CollectionViewType.Docking ? (null) :
             <><DocumentView key={this._document[Id]} ref={action((r: DocumentView) => this._view = r)}
@@ -369,7 +354,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 isContentActive={returnTrue}
                 PanelWidth={this.PanelWidth}
                 PanelHeight={this.PanelHeight}
-                layerProvider={this.layerProvider}
                 styleProvider={DefaultStyleProvider}
                 docFilters={CollectionDockingView.Instance.childDocFilters}
                 docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
@@ -520,7 +504,6 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                     whenChildContentsActiveChanged={emptyFunction}
                     focus={DocUtils.DefaultFocus}
                     styleProvider={TabMinimapView.miniStyleProvider}
-                    layerProvider={undefined}
                     addDocTab={this.props.addDocTab}
                     pinToPres={TabDocView.PinDoc}
                     docFilters={CollectionDockingView.Instance.childDocFilters}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 164021358..d608f800c 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -663,7 +663,6 @@ export class TreeView extends React.Component<TreeViewProps> {
                 hideDecorationTitle={this.props.treeView.outlineMode}
                 hideResizeHandles={this.props.treeView.outlineMode}
                 styleProvider={this.titleStyleProvider}
-                layerProvider={returnTrue}
                 docViewPath={returnEmptyDoclist}
                 treeViewDoc={this.props.treeView.props.Document}
                 addDocument={undefined}
@@ -760,7 +759,6 @@ export class TreeView extends React.Component<TreeViewProps> {
             renderDepth={this.props.renderDepth + 1}
             treeViewDoc={this.props.treeView?.props.Document}
             rootSelected={returnTrue}
-            layerProvider={returnTrue}
             docViewPath={this.props.treeView.props.docViewPath}
             docFilters={returnEmptyFilter}
             docRangeFilters={returnEmptyFilter}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4f79d19ad..322944d92 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -44,7 +44,7 @@ import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
 import { PresBox } from "../../nodes/trails/PresBox";
 import { VideoBox } from "../../nodes/VideoBox";
 import { CreateImage } from "../../nodes/WebBoxRenderer";
-import { StyleLayers, StyleProp } from "../../StyleProvider";
+import { StyleProp } from "../../StyleProvider";
 import { CollectionDockingView } from "../CollectionDockingView";
 import { CollectionSubView } from "../CollectionSubView";
 import { CollectionViewType } from "../CollectionView";
@@ -122,8 +122,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @observable ChildDrag: DocumentView | undefined; // child document view being dragged.  needed to update drop areas of groups when a group item is dragged.
 
     @computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); }
-    @computed get backgroundEvents() { return this.props.layerProvider?.(this.layoutDoc) === false && SnappingManager.GetIsDragging(); }
-    @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && this.props.isContentActive(); }
     @computed get fitToContentVals() {
         return {
             bounds: { ...this.contentBounds, cx: (this.contentBounds.x + this.contentBounds.r) / 2, cy: (this.contentBounds.y + this.contentBounds.b) / 2 },
@@ -222,8 +220,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) {
-        if (!de.embedKey && !this.ChildDrag && this.props.layerProvider?.(this.props.Document) !== false && this.props.Document._isGroup) return false;
-        if (!super.onInternalDrop(e, de)) return false;
+        if (!de.embedKey && !this.ChildDrag && this.rootDoc._isGroup) return false;
+        if (this.rootDoc._lockedPosition || !super.onInternalDrop(e, de)) return false;
         const refDoc = docDragData.droppedDocuments[0];
         const [xpo, ypo] = this.getContainerTransform().transformPoint(de.x, de.y);
         const z = NumCast(refDoc.z);
@@ -251,7 +249,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)];
             layoutDoc._width = NumCast(layoutDoc._width, 300);
             layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300);
-            !StrListCast(d._layerTags).includes(StyleLayers.Background) && (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
+            (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
         }
 
         (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments);
@@ -418,8 +416,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 styleProp = colors[cluster % colors.length];
                 const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor);
                 // 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?.filter(s => !StrListCast(s._layerTags).includes(StyleLayers.Background)).map(s => styleProp = StrCast(s.backgroundColor));
-                set?.filter(s => StrListCast(s._layerTags).includes(StyleLayers.Background)).map(s => styleProp = StrCast(s.backgroundColor));
+                set?.map(s => styleProp = StrCast(s.backgroundColor));
             }
         } //else if (doc && NumCast(doc.group, -1) !== -1) styleProp = "gray";
         return styleProp;
@@ -1017,7 +1014,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     bringToFront = (doc: Doc, sendToBack?: boolean) => {
-        if (sendToBack || StrListCast(doc._layerTags).includes(StyleLayers.Background)) {
+        if (sendToBack) {
             doc.zIndex = 0;
         } else if (doc.isInkMask) {
             doc.zIndex = 5000;
@@ -1155,7 +1152,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     pointerEvents = () => {
         const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
-        const pointerEvents = this.props.isContentActive() === false ? "none" : this.backgroundActive || this.props.childPointerEvents ? "all" :
+        const pointerEvents = this.props.isContentActive() === false ? "none" : this.props.childPointerEvents ? "all" :
             (this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : this.props.pointerEvents?.();
         return pointerEvents;
     }
@@ -1195,7 +1192,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
             docViewPath={this.props.docViewPath}
             styleProvider={this.getClusterColor}
-            layerProvider={this.props.layerProvider}
             dataProvider={this.childDataProvider}
             sizeProvider={this.childSizeProvider}
             freezeDimensions={BoolCast(this.props.Document.childFreezeDimensions, this.props.childFreezeDimensions)}
@@ -1614,7 +1610,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const isDocInView = (doc: Doc, rect: { left: number, top: number, width: number, height: number }) => intersectRect(docDims(doc), rect);
 
         const otherBounds = { left: this.panX(), top: this.panY(), width: Math.abs(size[0]), height: Math.abs(size[1]) };
-        let snappableDocs = activeDocs.filter(doc => !StrListCast(doc._layerTags).includes(StyleLayers.Background) && doc.z === undefined && isDocInView(doc, selRect));  // first see if there are any foreground docs to snap to
+        let snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect));  // first see if there are any foreground docs to snap to
         !snappableDocs.length && (snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect))); // if not, see if there are background docs to snap to
         !snappableDocs.length && (snappableDocs = activeDocs.filter(doc => doc.z !== undefined && isDocInView(doc, otherBounds))); // if not, then why not snap to floating docs
 
@@ -1739,7 +1735,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             onContextMenu={this.onContextMenu}
             style={{
                 pointerEvents: this.props.Document.type === DocumentType.MARKER ? "none" :  // bcz: ugh.. this is here to prevent markers, which render as freeform views, from grabbing events -- need a better approach.
-                    this.backgroundEvents ? "all" : this.props.pointerEvents?.() as any,
+                    this.props.pointerEvents?.() as any,
                 transform: `scale(${this.contentScaling || 1})`,
                 width: `${100 / (this.contentScaling || 1)}%`,
                 height: this.isAnnotationOverlay && this.Document.scrollHeight ? NumCast(this.Document.scrollHeight) : `${100 / (this.contentScaling || 1)}%`// : this.isAnnotationOverlay ? (this.Document.scrollHeight ? this.Document.scrollHeight : "100%") : this.props.PanelHeight()
@@ -1767,7 +1763,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     </svg>
                 </div>}
 
-            {this.props.Document._isGroup && SnappingManager.GetIsDragging() && (this.ChildDrag || this.props.layerProvider?.(this.props.Document) === false) ?
+            {this.props.Document._isGroup && SnappingManager.GetIsDragging() && this.ChildDrag ?
                 <div className="collectionFreeForm-groupDropper" ref={this.createGroupEventsTarget} style={{
                     width: this.ChildDrag ? "10000" : "100%",
                     height: this.ChildDrag ? "10000" : "100%",
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 5f303b428..1c4d47d57 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -28,7 +28,6 @@ import { CollectionView } from "../CollectionView";
 import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
 import "./MarqueeView.scss";
 import React = require("react");
-import { StyleLayers } from "../../StyleProvider";
 import { TreeView } from "../TreeView";
 import { VideoBox } from "../../nodes/VideoBox";
 import { ImageField, WebField } from "../../../../fields/URLField";
@@ -348,7 +347,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         this.hideMarquee();
     }
 
-    getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, layers: string[], makeGroup: Opt<boolean>) => {
+    getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, makeGroup: Opt<boolean>) => {
         const newCollection = creator ? creator(selected, { title: "nested stack", }) : ((doc: Doc) => {
             Doc.GetProto(doc).data = new List<Doc>(selected);
             Doc.GetProto(doc).title = makeGroup ? "grouping" : "nested freeform";
@@ -357,7 +356,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             return doc;
         })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
         newCollection.system = undefined;
-        newCollection._layerTags = new List<string>(layers);
         newCollection._width = this.Bounds.width;
         newCollection._height = this.Bounds.height;
         newCollection._isGroup = makeGroup;
@@ -437,7 +435,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             }));
             this.props.removeDocument?.(selected);
         }
-        const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined, [], group);
+        const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined, group);
         this.props.addDocument?.(newCollection);
         this.props.selectDocuments([newCollection]);
         MarqueeOptionsMenu.Instance.fadeOut(true);
@@ -531,7 +529,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     @action
     background = (e: KeyboardEvent | React.PointerEvent | undefined) => {
-        const newCollection = this.getCollection([], undefined, [StyleLayers.Background], undefined);
+        const newCollection = this.getCollection([], undefined, undefined);
         this.props.addDocument?.(newCollection);
         MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
@@ -617,7 +615,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 (this.touchesLine(bounds) || this.boundingShape(bounds)) && selection.push(doc);
             }
         };
-        this.props.activeDocuments().filter(doc => this.props.layerProvider?.(doc) !== false && !doc.z).map(selectFunc);
+        this.props.activeDocuments().filter(doc => !doc.z && !doc._lockedPosition).map(selectFunc);
         if (!selection.length && selectBackgrounds) this.props.activeDocuments().filter(doc => doc.z === undefined).map(selectFunc);
         if (!selection.length) this.props.activeDocuments().filter(doc => doc.z !== undefined).map(selectFunc);
         return selection;
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index c615bfb8e..bec582dcd 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -155,7 +155,6 @@ export class CollectionLinearView extends CollectionSubView() {
                 renderDepth={this.props.renderDepth + 1}
                 focus={emptyFunction}
                 styleProvider={this.props.styleProvider}
-                layerProvider={this.props.layerProvider}
                 docViewPath={returnEmptyDoclist}
                 whenChildContentsActiveChanged={emptyFunction}
                 bringToFront={emptyFunction}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 6929a1cd8..92f9b2f49 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -219,7 +219,6 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             Document={layout}
             DataDoc={layout.resolvedDataDoc as Doc}
             styleProvider={this.props.styleProvider}
-            layerProvider={this.props.layerProvider}
             docViewPath={this.props.docViewPath}
             LayoutTemplate={this.props.childLayoutTemplate}
             LayoutTemplateString={this.props.childLayoutString}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 7e2b83230..4c4054b09 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -218,7 +218,6 @@ export class CollectionMultirowView extends CollectionSubView() {
             Document={layout}
             DataDoc={layout.resolvedDataDoc as Doc}
             styleProvider={this.props.styleProvider}
-            layerProvider={this.props.layerProvider}
             docViewPath={this.props.docViewPath}
             LayoutTemplate={this.props.childLayoutTemplate}
             LayoutTemplateString={this.props.childLayoutString}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index a93762ea4..b731479a5 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -417,7 +417,6 @@ export class CollectionSchemaView extends CollectionSubView() {
                     docRangeFilters={this.childDocRangeFilters}
                     searchFilterDocs={this.searchFilterDocs}
                     styleProvider={DefaultStyleProvider}
-                    layerProvider={undefined}
                     docViewPath={returnEmptyDoclist}
                     ContainingCollectionDoc={this.props.CollectionView?.props.Document}
                     ContainingCollectionView={this.props.CollectionView}
diff --git a/src/client/views/collections/collectionSchema/SchemaTable.tsx b/src/client/views/collections/collectionSchema/SchemaTable.tsx
index 605481ddf..bea5b3be6 100644
--- a/src/client/views/collections/collectionSchema/SchemaTable.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx
@@ -573,7 +573,6 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
                         Document={this._showDoc}
                         DataDoc={this._showDataDoc}
                         styleProvider={DefaultStyleProvider}
-                        layerProvider={undefined}
                         docViewPath={returnEmptyDoclist}
                         freezeDimensions={true}
                         focus={DocUtils.DefaultFocus}
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
index c8be9069c..4b33ef8ae 100644
--- a/src/client/views/linking/LinkPopup.tsx
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -67,7 +67,7 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
                     <input defaultValue={""} autoComplete="off" type="text" placeholder="Search for Document..." id="search-input"
                         className="linkPopup-searchBox searchBox-input" /> */}
 
-                    <SearchBox 
+                    <SearchBox
                         Document={CurrentUserUtils.MySearchPanelDoc}
                         DataDoc={CurrentUserUtils.MySearchPanelDoc}
                         linkFrom={linkDoc}
@@ -83,7 +83,6 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
                         pinToPres={emptyFunction}
                         rootSelected={returnTrue}
                         styleProvider={DefaultStyleProvider}
-                        layerProvider={undefined}
                         removeDocument={undefined}
                         ScreenToLocalTransform={Transform.Identity}
                         PanelWidth={this.getPWidth}
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index e28e5b453..d97cb6f84 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -654,7 +654,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             className="audiobox-container"
             onContextMenu={this.specificContextMenu}
             onClick={!this.path && !this._recorder ? this.recordAudioAnnotation : undefined}
-            style={{ pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined }}
+            style={{ pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined }}
         >
             {!this.path ? this.recordingControls : this.playbackControls}
         </div>;
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index edadd59c0..5a0ab9110 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -21,7 +21,6 @@ import React = require("react");
 export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps {
     dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined;
     sizeProvider?: (doc: Doc, replica: string) => { width: number, height: number } | undefined;
-    layerProvider: ((doc: Doc, assign?: boolean) => boolean) | undefined;
     renderCutoffProvider: (doc: Doc) => boolean;
     zIndex?: number;
     highlight?: boolean;
@@ -159,7 +158,6 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
             ...this.props,
             CollectionFreeFormDocumentView: this.returnThis,
             styleProvider: this.styleProvider,
-            layerProvider: this.props.layerProvider,
             ScreenToLocalTransform: this.screenToLocalTransform,
             PanelWidth: this.panelWidth,
             PanelHeight: this.panelHeight,
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f30d093a1..c999b3224 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -37,7 +37,7 @@ import { DocComponent } from "../DocComponent";
 import { EditableView } from '../EditableView';
 import { InkingStroke } from "../InkingStroke";
 import { LightboxView } from "../LightboxView";
-import { StyleLayers, StyleProp } from "../StyleProvider";
+import { StyleProp } from "../StyleProvider";
 import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
 import { DocumentContentsView } from "./DocumentContentsView";
 import { DocumentLinksButton } from './DocumentLinksButton';
@@ -123,7 +123,6 @@ export interface DocumentViewSharedProps {
     PanelHeight: () => number;
     docViewPath: () => DocumentView[];
     dataTransition?: string; // specifies animation transition - used by collectionPile and potentially other layout engines when changing the size of documents so that the change won't be abrupt
-    layerProvider: undefined | ((doc: Doc, assign?: boolean) => boolean);
     styleProvider: Opt<StyleProviderFunc>;
     focus: DocFocusFunc;
     fitWidth?: (doc: Doc) => boolean;
@@ -474,7 +473,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
             let stopPropagate = true;
             let preventDefault = true;
-            !StrListCast(this.props.Document._layerTags).includes(StyleLayers.Background) && (this.rootDoc._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
+            (this.rootDoc._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
             if (this._doubleTap && (this.props.Document.type !== DocumentType.FONTICON || this.onDoubleClickHandler)) {// && !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
                 if (this._timeout) {
                     clearTimeout(this._timeout);
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 78adf63cb..a47e17dfc 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -423,7 +423,6 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     treeViewHideHeaderFields={false}
                     dontRegisterView={true}
                     styleProvider={this.FilterStyleProvider}
-                    layerProvider={this.props.layerProvider}
                     docViewPath={this.props.docViewPath}
                     scriptContext={this.props.scriptContext}
                     moveDocument={returnFalse}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index e7c43e5cd..3b04aa807 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -379,7 +379,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             style={{
                 width: this.props.PanelWidth() ? undefined : `100%`,
                 height: this.props.PanelWidth() ? undefined : `100%`,
-                pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined,
+                pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined,
                 borderRadius
             }} >
             <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
diff --git a/src/client/views/nodes/KeyValuePair.tsx b/src/client/views/nodes/KeyValuePair.tsx
index 881cbf2bb..80def3025 100644
--- a/src/client/views/nodes/KeyValuePair.tsx
+++ b/src/client/views/nodes/KeyValuePair.tsx
@@ -60,7 +60,6 @@ export class KeyValuePair extends React.Component<KeyValuePairProps> {
             docRangeFilters: returnEmptyFilter,
             searchFilterDocs: returnEmptyDoclist,
             styleProvider: DefaultStyleProvider,
-            layerProvider: undefined,
             docViewPath: returnEmptyDoclist,
             ContainingCollectionView: undefined,
             ContainingCollectionDoc: undefined,
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 437d29f39..7fd289a97 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -89,7 +89,6 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
     openLinkTargetOnRight = (e: React.MouseEvent) => {
         const alias = Doc.MakeAlias(Cast(this.layoutDoc[this.fieldKey], Doc, null));
         alias._isLinkButton = undefined;
-        alias._layerTags = undefined;
         alias.layoutKey = "layout";
         this.props.addDocTab(alias, "add:right");
     }
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 375434933..ba515fb89 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -179,7 +179,6 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                             moveDocument={returnFalse}
                             rootSelected={returnFalse}
                             styleProvider={this.props.docProps?.styleProvider}
-                            layerProvider={this.props.docProps?.layerProvider}
                             docViewPath={returnEmptyDoclist}
                             ScreenToLocalTransform={Transform.Identity}
                             isDocumentActive={returnFalse}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 3b4c94562..c350e3139 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -809,7 +809,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / this.scaling()}px` : borderRad;
         return (<div className="videoBox" onContextMenu={this.specificContextMenu} ref={this._mainCont}
             style={{
-                pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined,
+                pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined,
                 borderRadius,
                 overflow: this.props.docViewPath?.().slice(-1)[0].fitWidth ? "auto" : undefined
             }} onWheel={e => { e.stopPropagation(); e.preventDefault(); }}>
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 20e01be05..445df8ddd 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -74,7 +74,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); }
     @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); }
     @computed get webField() { return Cast(this.dataDoc[this.props.fieldKey], WebField)?.url; }
-    @computed get webThumb() { return this.props.thumbShown?.() && ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumbScrollTop === this.layoutDoc._scrollTop ? this.layoutDoc.thumb : undefined))?.url }
+    @computed get webThumb() { return this.props.thumbShown?.() && ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumbScrollTop === this.layoutDoc._scrollTop ? this.layoutDoc.thumb : undefined))?.url; }
 
     constructor(props: any) {
         super(props);
@@ -134,7 +134,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         returnedfilename => setTimeout(action(() => {
                             this.lockout = false;
                             this.layoutDoc.thumb = new ImageField(returnedfilename);
-                            this.layoutDoc.thumbScrollTop = scrollTop
+                            this.layoutDoc.thumbScrollTop = scrollTop;
                         }), 500));
                 })
                 .catch(function (error: any) {
@@ -717,7 +717,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     pointerEvents = () => !this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none";
     annotationPointerEvents = () => this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none";
     render() {
-        const pointerEvents = this.props.layerProvider?.(this.layoutDoc) === false ? "none" : this.props.pointerEvents?.() as any;
+        const pointerEvents = this.layoutDoc._lockedPosition ? "none" : this.props.pointerEvents?.() as any;
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
         const scale = previewScale * (this.props.scaling?.() || 1);
         const renderAnnotations = (docFilters?: () => string[]) =>
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 364be461f..1d8e3a2cf 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -182,7 +182,6 @@ export class DashDocViewInternal extends React.Component<IDashDocViewInternal> {
                     removeDocument={this.removeDoc}
                     isDocumentActive={returnFalse}
                     isContentActive={this._textBox.props.isContentActive}
-                    layerProvider={this._textBox.props.layerProvider}
                     styleProvider={this._textBox.props.styleProvider}
                     docViewPath={this._textBox.props.docViewPath}
                     ScreenToLocalTransform={this.getDocTransform}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 29117794e..f2a222cee 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -1630,7 +1630,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const active = this.props.isContentActive();
         const scale = (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
         const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
-        const interactive = (CurrentUserUtils.SelectedTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || this.props.layerProvider?.(this.layoutDoc) !== false);
+        const interactive = (CurrentUserUtils.SelectedTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition);
         if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
         const minimal = this.props.ignoreAutoHeight;
         const paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0);
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 820d7ef96..64f5a296f 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -700,7 +700,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc);
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
-    isContentActive = (outsideReaction?: boolean) => ((CurrentUserUtils.SelectedTool === InkTool.None && this.props.layerProvider?.(this.layoutDoc) !== false) &&
+    isContentActive = (outsideReaction?: boolean) => ((CurrentUserUtils.SelectedTool === InkTool.None && !this.layoutDoc._lockedPosition) &&
         (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
 
     /**
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index f4dc9b615..ef918d991 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -80,7 +80,6 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     Document={this.targetDoc}
                     DataDoc={this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
                     styleProvider={this.styleProvider}
-                    layerProvider={this.props.layerProvider}
                     docViewPath={returnEmptyDoclist}
                     rootSelected={returnTrue}
                     addDocument={returnFalse}
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index fb2e33e2a..5bdce273d 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -26,7 +26,7 @@ export
             {DocListCast(this.props.anno.textInlineAnnotations).map(a =>
                 <RegionAnnotation pointerEvents={this.props.pointerEvents} {...this.props} document={a} key={a[Id]} />
             )}
-        </div>
+        </div>;
     }
 }
 
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 2905e96d9..152b7bbcb 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -127,7 +127,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
     static foreachRecursiveDoc(docs: Doc[], func: (depth: number, doc: Doc) => void) {
         let newarray: Doc[] = [];
         var depth = 0;
-        let visited: Doc[] = [];
+        const visited: Doc[] = [];
         while (docs.length > 0) {
             newarray = [];
             docs.filter(d => d && !visited.includes(d)).forEach(d => {
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 9de8d0831..194b3ba27 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1026,7 +1026,7 @@ export namespace Doc {
         if (status === DocBrushStatus.unbrushed) {
             const lastBrushed = Array.from(brushManager.BrushedDoc.keys()).lastElement();
             if (lastBrushed) {
-                for (var link of LinkManager.Instance.getAllDirectLinks(lastBrushed)) {
+                for (const link of LinkManager.Instance.getAllDirectLinks(lastBrushed)) {
                     const a1 = Cast(link.anchor1, Doc, null);
                     const a2 = Cast(link.anchor2, Doc, null);
                     if (Doc.AreProtosEqual(a1, doc) || Doc.AreProtosEqual(a2, doc) ||
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index 88221732e..418464f0e 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -96,7 +96,6 @@ export class AudioUpload extends React.Component {
                         isDocumentActive={returnTrue}
                         isContentActive={emptyFunction}
                         focus={emptyFunction}
-                        layerProvider={undefined}
                         styleProvider={() => "rgba(0,0,0,0)"}
                         docViewPath={returnEmptyDoclist}
                         whenChildContentsActiveChanged={emptyFunction}
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index cfcc48608..78ec706d7 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -214,7 +214,6 @@ export class MobileInterface extends React.Component {
                     isContentActive={emptyFunction}
                     focus={DocUtils.DefaultFocus}
                     styleProvider={this.whitebackground}
-                    layerProvider={undefined}
                     docViewPath={returnEmptyDoclist}
                     whenChildContentsActiveChanged={emptyFunction}
                     bringToFront={emptyFunction}
diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts
index bdea57cb2..fd000a83c 100644
--- a/src/server/server_Initialization.ts
+++ b/src/server/server_Initialization.ts
@@ -195,7 +195,7 @@ function proxyServe(req: any, requrl: string, response: any) {
                 console.log("EROR?: ", e);
             }
         } else req.pipe(request(requrl)).pipe(response);
-    }
+    };
     retrieveHTTPBody = () => {
         req.headers.cookie = "";
         req.pipe(request(requrl))
-- 
cgit v1.2.3-70-g09d2


From 51199aae80013073d4f1a0749a36051dcee591c1 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 20 May 2022 10:48:04 -0400
Subject: moved explore mode to topBar.  fixed highlighting of overlay toggle. 
 fixed pointerEvents on lockedPosition collectoins that are not children of a
 free form view.

---
 src/client/util/CurrentUserUtils.ts                | 10 ++++----
 src/client/views/MainView.tsx                      | 15 ++++++------
 src/client/views/StyleProvider.tsx                 | 20 +++++++---------
 src/client/views/collections/CollectionView.tsx    |  2 +-
 src/client/views/collections/TabDocView.tsx        |  4 +++-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  7 +-----
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/client/views/nodes/FieldView.tsx               |  2 +-
 src/client/views/nodes/button/ButtonScripts.ts     |  4 +++-
 src/client/views/topbar/TopBar.tsx                 | 28 +++++++++++++++-------
 10 files changed, 50 insertions(+), 44 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 78bf531c6..8fed6e940 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1009,7 +1009,7 @@ export class CurrentUserUtils {
                 title: "Header Color", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading",
                 script: "setHeaderColor(value, _readOnly_)", hidden: 'selectedDocumentType()',
             }, // Only when a document is selected
-            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay(_readOnly_)', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform
+            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", backgroundColor: 'toggleOverlay(true)', click: 'toggleOverlay()', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform
             // { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden: 'selectedDocumentType()' }, // Only when a document is selected 
             { title: "Text", type: "textTools", subMenu: true, expanded: 'selectedDocumentType("rtf")' }, // Always available
             { title: "Ink", type: "inkTools", subMenu: true, expanded: 'selectedDocumentType("ink")' }, // Always available
@@ -1023,7 +1023,7 @@ export class CurrentUserUtils {
         if (doc.contextMenuBtns === undefined) {
             const docList: Doc[] = [];
 
-            (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, type, btnType, click, script, subMenu, hidden, expanded }) => {
+            (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, backgroundColor, ignoreClick, icon, type, btnType, click, script, subMenu, hidden, expanded }) => {
                 const menuDocList: Doc[] = [];
                 if (subMenu) {
                     // default is textTools
@@ -1046,7 +1046,7 @@ export class CurrentUserUtils {
                             break;
                     }
                     tools.map(({ title, toolTip, icon, btnType, numBtnType, numBtnMax, numBtnMin, click, script, width, list, ignoreClick, switchToggle }) => {
-                        const computed = click ? ComputedField.MakeFunction(click) as any : "transparent";
+                        const backgroundColor = click ? ComputedField.MakeFunction(click) as any : "transparent";
                         menuDocList.push(Docs.Create.FontIconDocument({
                             _nativeWidth: width ? width : 25,
                             _nativeHeight: 25,
@@ -1069,7 +1069,7 @@ export class CurrentUserUtils {
                             title,
                             switchToggle,
                             color: Colors.WHITE,
-                            backgroundColor: computed,
+                            backgroundColor,
                             _dropAction: "alias",
                             _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
                             onClick: click ? ScriptField.MakeScript(click) : undefined
@@ -1105,7 +1105,7 @@ export class CurrentUserUtils {
                         dontUndo: true,
                         title,
                         color: Colors.WHITE,
-                        // backgroundColor: checkResult ? ComputedField.MakeFunction(checkResult, {}, {_readOnly_:true}) as any : "transparent",
+                        backgroundColor: backgroundColor ? ComputedField.MakeFunction(backgroundColor) as any : "transparent",
                         _dropAction: "alias",
                         hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined,
                         _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 2e1d10955..9be00fa9e 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -273,6 +273,12 @@ export class MainView extends React.Component {
         Doc.AddDocToList(this.userDoc.myFilesystem as Doc, "data", folder);
     }
 
+    @observable _exploreMode = false;
+    @computed get exploreMode() {
+        return () => this._exploreMode ? ScriptField.MakeScript("CollectionBrowseClick(documentView, clientX, clientY)",
+            { documentView: "any", clientX: "number", clientY: "number" })! : undefined;
+    }
+
     @computed get mainDocView() {
         return <DocumentView key="main"
             Document={this.mainContainer!}
@@ -571,13 +577,6 @@ export class MainView extends React.Component {
         </svg>;
     }
 
-    @computed get topbar() {
-        TraceMobx();
-        return <div className="mainView-topbar">
-            <TopBar />
-        </div>;
-    }
-
     @computed get invisibleWebBox() { // see note under the makeLink method in HypothesisUtils.ts
         return !DocumentLinksButton.invisibleWebDoc ? null :
             <div className="mainView-invisibleWebRef" ref={DocumentLinksButton.invisibleWebRef}>
@@ -625,7 +624,7 @@ export class MainView extends React.Component {
             <GoogleAuthenticationManager />
             <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} />
             <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} />
-            {this.topbar}
+            <TopBar />
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
             {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => DocumentLinksButton.LinkEditorDocView = undefined)} docView={DocumentLinksButton.LinkEditorDocView} /> : (null)}
             {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 93da2fa19..7ac40fc8e 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -156,15 +156,14 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                     if (StrCast(Doc.LayoutField(doc)).includes(SliderBox.name)) break;
                     docColor = docColor ||
                         (doc?._viewType === CollectionViewType.Pile || Doc.IsSystem(doc) ? (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY) : // system docs (seen in treeView) get a grayish background
-                            isBackground() ? "cyan" : // ?? is there a good default for a background collection
-                                doc.annotationOn ? "#00000015" : // faint interior for collections on PDFs, images, etc
-                                    (doc?._isGroup ? undefined :
-                                        StrCast((props?.renderDepth || 0) > 0 ?
-                                            Doc.UserDoc().activeCollectionNestedBackground :
-                                            Doc.UserDoc().activeCollectionBackground ?? (darkScheme() ?
-                                                Colors.BLACK :
-                                                "linear-gradient(#065fff, #85c1f9)"))
-                                    ));
+                            doc.annotationOn ? "#00000015" : // faint interior for collections on PDFs, images, etc
+                                (doc?._isGroup ? undefined :
+                                    StrCast((props?.renderDepth || 0) > 0 ?
+                                        Doc.UserDoc().activeCollectionNestedBackground :
+                                        Doc.UserDoc().activeCollectionBackground ?? (darkScheme() ?
+                                            Colors.BLACK :
+                                            "linear-gradient(#065fff, #85c1f9)"))
+                                ));
                     break;
                 //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
                 default: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break;
@@ -203,7 +202,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
             return undefined;
         case StyleProp.Decorations:
             if (props?.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform || doc?.x !== undefined || doc?.y !== undefined) {
-                return doc && (isBackground() || selected) && (props?.renderDepth || 0) > 0 &&
+                return doc && (isBackground() || selected) && !Doc.IsSystem(doc) && (props?.renderDepth || 0) > 0 &&
                     ((doc.type === DocumentType.COL && doc._viewType !== CollectionViewType.Pile) || [DocumentType.RTF, DocumentType.IMG, DocumentType.INK].includes(doc.type as DocumentType)) ?
                     <div className="styleProvider-lock" onClick={() => toggleLockedPosition(doc)}>
                         <FontAwesomeIcon icon={isBackground() ? "lock" : "unlock"} style={{ color: isBackground() ? "red" : undefined }} size="lg" />
@@ -236,7 +235,6 @@ export function DashboardStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps
                         DocFocusOrOpen(doc, props?.ContainingCollectionDoc);
                     }
                 })}
-                {DashboardToggleButton(doc, "lockedPosition", "lock", "unlock")}
             </>;
     }
     return DefaultStyleProvider(doc, props, property);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 7c5f49b42..19485a0eb 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -261,7 +261,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             CollectionView: this,
         };
         return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
-            style={{ pointerEvents: this.rootDoc._lockedPosition ? "none" : undefined }}>
+            style={{ pointerEvents: this.props.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform && this.rootDoc._lockedPosition ? "none" : undefined }}>
             {this.showIsTagged()}
             {this.renderSubView(this.collectionViewType, props)}
         </div>);
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index cebc3f779..43f31813c 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -9,6 +9,7 @@ import { observer } from "mobx-react";
 import * as ReactDOM from 'react-dom';
 import { DataSym, Doc, DocListCast, DocListCastAsync, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
 import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
 import { FieldId } from "../../../fields/RefField";
 import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
 import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
@@ -24,6 +25,7 @@ import { Transform } from '../../util/Transform';
 import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { Colors, Shadows } from '../global/globalEnums';
 import { LightboxView } from '../LightboxView';
+import { MainView } from '../MainView';
 import { DocFocusOptions, DocumentView, DocumentViewProps } from "../nodes/DocumentView";
 import { PinProps, PresBox, PresMovement } from '../nodes/trails';
 import { DefaultStyleProvider, StyleProp } from '../StyleProvider';
@@ -33,7 +35,6 @@ import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormV
 import { CollectionView, CollectionViewType } from './CollectionView';
 import "./TabDocView.scss";
 import React = require("react");
-import { List } from '../../../fields/List';
 const _global = (window /* browser */ || global /* node */) as any;
 
 interface TabDocViewProps {
@@ -354,6 +355,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={undefined}
+                onBrowseClick={MainView.Instance.exploreMode}
                 isContentActive={returnTrue}
                 PanelWidth={this.PanelWidth}
                 PanelHeight={this.PanelHeight}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 916cc9c05..df09569ef 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -624,10 +624,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     onClick = (e: React.MouseEvent) => {
         if (this.onBrowseClickHandler()) {
-            // if (this.Document[this.scaleFieldKey] !== 0.5) {
-            //     this.zoomSmoothlyAboutPt(this.getTransform().transformPoint(e.clientX, e.clientY), 0.5);
-            // }
-            // else 
             if (this.props.DocumentView?.()) {
                 this.onBrowseClickHandler().script.run({ documentView: this.props.DocumentView(), clientX: e.clientX, clientY: e.clientY });
             }
@@ -961,7 +957,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        if (this.layoutDoc._lockedTransform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || CurrentUserUtils.OverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === "outline") return;
+        if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || CurrentUserUtils.OverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === "outline") return;
         if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming
             e.stopPropagation();
         }
@@ -1564,7 +1560,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
         const viewctrls = ContextMenu.Instance.findByDescription("UI Controls...");
         const viewCtrlItems = viewctrls && "subitems" in viewctrls ? viewctrls.subitems : [];
-        viewCtrlItems.push({ description: (this.layoutDoc.onBrowseClick ? "Leave" : "Enter") + " Browse Mode", event: () => this.layoutDoc.onBrowseClick = this.layoutDoc.onBrowseClick ? undefined : ScriptField.MakeScript("CollectionBrowseClick(documentView, clientX, clientY)", { documentView: "any", clientX: "number", clientY: "number" }), icon: "compress-arrows-alt" });
         !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null;
         !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._useClusters), icon: "braille" }) : null;
         !viewctrls && ContextMenu.Instance.addItem({ description: "UI Controls...", subitems: viewCtrlItems, icon: "eye" });
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 1a74a4500..f5ad47ca7 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -177,7 +177,7 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
     onDoubleClick?: () => ScriptField;
     onPointerDown?: () => ScriptField;
     onPointerUp?: () => ScriptField;
-    onBrowseClick?: () => ScriptField;
+    onBrowseClick?: () => (ScriptField | undefined);
 }
 
 // these props are only available in DocumentViewIntenral
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 33ed8cd89..94937430e 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -24,7 +24,7 @@ export interface FieldViewProps extends DocumentViewSharedProps {
     isSelected: (outsideReaction?: boolean) => boolean;
     scaling?: () => number;
     setHeight?: (height: number) => void;
-    onBrowseClick?: () => ScriptField;
+    onBrowseClick?: () => (ScriptField | undefined);
 
     // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
     pointerEvents?: () => Opt<string>;
diff --git a/src/client/views/nodes/button/ButtonScripts.ts b/src/client/views/nodes/button/ButtonScripts.ts
index f3731b8f9..b4a382faf 100644
--- a/src/client/views/nodes/button/ButtonScripts.ts
+++ b/src/client/views/nodes/button/ButtonScripts.ts
@@ -1,5 +1,6 @@
 import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
 import { SelectionManager } from "../../../util/SelectionManager";
+import { Colors } from "../../global/globalEnums";
 
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function changeView(view: string) {
@@ -8,7 +9,8 @@ ScriptingGlobals.add(function changeView(view: string) {
 });
 
 // toggle: Set overlay status of selected document
-ScriptingGlobals.add(function toggleOverlay() {
+ScriptingGlobals.add(function toggleOverlay(readOnly?: boolean) {
     const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
+    if (readOnly) return selected?.Document.z ? Colors.MEDIUM_BLUE : "transparent";
     selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("failed");
 });
\ No newline at end of file
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 0af7de6af..be248ab92 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -1,4 +1,6 @@
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Tooltip } from "@material-ui/core";
+import { action } from "mobx";
 import { observer } from "mobx-react";
 import * as React from 'react';
 import { Doc, DocListCast } from '../../../fields/Doc';
@@ -9,6 +11,7 @@ import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 import { SettingsManager } from "../../util/SettingsManager";
 import { undoBatch, UndoManager } from "../../util/UndoManager";
 import { Borders, Colors } from "../global/globalEnums";
+import { MainView } from "../MainView";
 import "./TopBar.scss";
 
 /**
@@ -40,20 +43,28 @@ export class TopBar extends React.Component {
                             </select>
                         </div>
                         <div className="topbar-dashboards">
-                            <div className="topbar-icon" onClick={async () => {
+                            <Tooltip title={<div className="dash-tooltip">Create a new dashboard </div>} placement="bottom"><div className="topbar-icon" onClick={async () => {
                                 const batch = UndoManager.StartBatch("new dash");
                                 await CurrentUserUtils.createNewDashboard(Doc.UserDoc());
                                 batch.end();
                             }}>
                                 {"New"}<FontAwesomeIcon icon="plus" />
                             </div>
-                            {<div className="topbar-icon" onClick={async () => {
-                                const batch = UndoManager.StartBatch("snapshot");
-                                await CurrentUserUtils.snapshotDashboard(Doc.UserDoc());
-                                batch.end();
-                            }}>
-                                {"Snapshot"}<FontAwesomeIcon icon="camera" />
-                            </div>}
+                            </Tooltip>
+                            <Tooltip title={<div className="dash-tooltip">Work on a copy of the dashboard layout</div>} placement="bottom">
+                                <div className="topbar-icon" onClick={async () => {
+                                    const batch = UndoManager.StartBatch("snapshot");
+                                    await CurrentUserUtils.snapshotDashboard(Doc.UserDoc());
+                                    batch.end();
+                                }}>
+                                    {"Snapshot"}<FontAwesomeIcon icon="camera" />
+                                </div>
+                            </Tooltip>
+                            <Tooltip title={<div className="dash-tooltip">Browsing mode for directly navigating to documents</div>} placement="bottom">
+                                <div className="topbar-icon" style={{ background: MainView.Instance._exploreMode ? Colors.LIGHT_BLUE : undefined }} onClick={action(() => MainView.Instance._exploreMode = !MainView.Instance._exploreMode)}>
+                                    {"Explore"}<FontAwesomeIcon icon="map" />
+                                </div>
+                            </Tooltip>
                         </div>
                     </div>
                     <div className="topbar-right" >
@@ -64,7 +75,6 @@ export class TopBar extends React.Component {
                         <div className="topbar-icon" onClick={() => SettingsManager.Instance.open()}>
                             {"Settings"}<FontAwesomeIcon icon="cog" />
                         </div>
-
                     </div>
                 </div>
             </div >
-- 
cgit v1.2.3-70-g09d2


From 5dd7905530dbfbac3318e55fc625fe46427e23c4 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 20 May 2022 13:45:21 -0400
Subject: adjusted placement of fonticonbox labels

---
 src/client/util/CurrentUserUtils.ts            | 64 ++++++++++++--------------
 src/client/views/MainView.tsx                  |  2 +-
 src/client/views/nodes/button/FontIconBox.scss |  8 ++--
 src/client/views/nodes/button/FontIconBox.tsx  | 23 +++++----
 4 files changed, 51 insertions(+), 46 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index e0580d3c4..b9d2d63ba 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -181,21 +181,22 @@ export class CurrentUserUtils {
             "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, _width: 360 / 4, _height: 270 / 4,
             _showTitle: "title", system: true, onClick: deiconifyScript(), ...extra
         });
-        const newIconsList = [
-            makeIconTemplate(undefined, "title", () => labelBox({ _backgroundColor: "dimgray" })),
-            makeIconTemplate(DocumentType.AUDIO, "title", () => labelBox({ _backgroundColor: "lightgreen" })),
-            makeIconTemplate(DocumentType.PDF, "title", () => labelBox({ _backgroundColor: "pink" })),
-            makeIconTemplate(DocumentType.WEB, "title", () => labelBox({ _backgroundColor: "brown" })),
-            makeIconTemplate(DocumentType.RTF, "text", () => labelBox({ _showTitle: "creationDate" })),
-            makeIconTemplate(DocumentType.IMG, "data", () => imageBox("", { _height: undefined, })),
-            makeIconTemplate(DocumentType.COL, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})),
-            makeIconTemplate(DocumentType.VID, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})),
-            makeIconTemplate(DocumentType.BUTTON, "data", () => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, system: true, onClick: deiconifyScript() })),
-            // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {}))
-        ].filter(d => d).map(d => d!);
         if (!templateIconsDoc) {
+            const newIconsList = [
+                makeIconTemplate(undefined, "title", () => labelBox({ _backgroundColor: "dimgray" })),
+                makeIconTemplate(DocumentType.AUDIO, "title", () => labelBox({ _backgroundColor: "lightgreen" })),
+                makeIconTemplate(DocumentType.PDF, "title", () => labelBox({ _backgroundColor: "pink" })),
+                makeIconTemplate(DocumentType.WEB, "title", () => labelBox({ _backgroundColor: "brown" })),
+                makeIconTemplate(DocumentType.RTF, "text", () => labelBox({ _showTitle: "creationDate" })),
+                makeIconTemplate(DocumentType.IMG, "data", () => imageBox("", { _height: undefined, })),
+                makeIconTemplate(DocumentType.COL, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})),
+                makeIconTemplate(DocumentType.VID, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})),
+                makeIconTemplate(DocumentType.BUTTON, "data", () => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, system: true, onClick: deiconifyScript() })),
+                // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {}))
+            ].filter(d => d).map(d => d!);
+
             doc["template-icons"] = Docs.Create.TreeDocument(newIconsList, { title: "icon templates", _height: 75, system: true });
-            newIconsList.map(d => (doc["template-icons"] as Doc)[StrCast(d.title)] = new PrefetchProxy(d));
+            newIconsList.forEach(d => (doc["template-icons"] as Doc)[StrCast(d.title)] = new PrefetchProxy(d));
         }
     }
 
@@ -224,7 +225,6 @@ export class CurrentUserUtils {
                 backgroundColor: "white", system: true, cloneFieldFilter: new List<string>(["system"])
             });
             Doc.GetProto(textDoc).title = ComputedField.MakeFunction('self.text?.Text');
-            FormattedTextBox.SelectOnLoad = textDoc[Id];
             doc.emptySlide = textDoc;
         }
         if ((doc.emptyHeader as Doc)?.version !== headerViewVersion) {
@@ -287,11 +287,7 @@ export class CurrentUserUtils {
             ((doc.emptyAudio as Doc).proto as Doc)["dragFactory-count"] = 0;
         }
         if (doc.emptyNote === undefined) {
-            doc.emptyNote = Docs.Create.TextDocument("", {
-                _width: 200, title: "text note", _autoHeight: true, system: true,
-                _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize),
-                cloneFieldFilter: new List<string>(["system"])
-            });
+            doc.emptyNote = Docs.Create.TextDocument("", { _width: 200, title: "text note", _autoHeight: true, system: true, cloneFieldFilter: new List<string>(["system"]) });
             ((doc.emptyNote as Doc).proto as Doc)["dragFactory-count"] = 0;
         }
         if (doc.emptyImage === undefined) {
@@ -790,20 +786,20 @@ export class CurrentUserUtils {
                         "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"],
                     script: 'setFont(value, _readOnly_)'
                 },
-                { title: "Font size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'setFontSize(value, _readOnly_)' },
-                { title: "Font color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script: 'setFontColor(value, _readOnly_)' },
+                { title: "Size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'setFontSize(value, _readOnly_)' },
+                { title: "Color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script: 'setFontColor(value, _readOnly_)' },
                 { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold(_readOnly_)' },
                 { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic(_readOnly_)' },
-                { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline(_readOnly_)' },
-                { title: "Bullet List", toolTip: "Bullet", btnType: ButtonType.ToggleButton, icon: "list", click: 'setBulletList("bullet", _readOnly_)' },
-                { title: "Number List", toolTip: "Number", btnType: ButtonType.ToggleButton, icon: "list-ol", click: 'setBulletList("decimal", _readOnly_)' },
+                { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline(_readOnly_)' },
+                { title: "Bullets", toolTip: "Bullet List", btnType: ButtonType.ToggleButton, icon: "list", click: 'setBulletList("bullet", _readOnly_)' },
+                { title: "#", toolTip: "Number List", btnType: ButtonType.ToggleButton, icon: "list-ol", click: 'setBulletList("decimal", _readOnly_)' },
 
                 // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", click: 'toggleStrikethrough()'},
                 // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", click: 'toggleSuperscript()'},
                 // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", click: 'toggleSubscript()'},
-                { title: "Left align", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left", _readOnly_)' },
-                { title: "Center align", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center", _readOnly_)' },
-                { title: "Right align", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right", _readOnly_)' },
+                { title: "Left", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left", _readOnly_)' },
+                { title: "Center", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center", _readOnly_)' },
+                { title: "Right", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right", _readOnly_)' },
             ];
         return tools;
     }
@@ -816,9 +812,9 @@ export class CurrentUserUtils {
             { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle", _readOnly_)' },
             // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")' },
             { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", click: 'setActiveInkTool("line", _readOnly_)' },
-            { title: "Fill color", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setFillColor(value, _readOnly_)" },
-            { title: "Stroke width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, script: 'setStrokeWidth(value, _readOnly_)' },
-            { title: "Stroke color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, script: 'setStrokeColor(value, _readOnly_)' },
+            { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setFillColor(value, _readOnly_)" },
+            { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, script: 'setStrokeWidth(value, _readOnly_)' },
+            { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, script: 'setStrokeColor(value, _readOnly_)' },
         ];
         return tools;
     }
@@ -862,19 +858,19 @@ export class CurrentUserUtils {
                 script: 'setView(value, _readOnly_)',
             }, // Always show
             {
-                title: "back", toolTip: "prev", width: 20, btnType: ButtonType.ClickButton, click: 'prevKeyFrame()', icon: "chevron-left",
+                title: "Back", toolTip: "Prev AnimationFrame", width: 20, btnType: ButtonType.ClickButton, click: 'prevKeyFrame()', icon: "chevron-left",
                 hidden: 'IsNoviceMode()'
             },
             {
-                title: "Forward", toolTip: "next", width: 20, btnType: ButtonType.ClickButton, click: 'nextKeyFrame()', icon: "chevron-right",
+                title: "Fwd", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, click: 'nextKeyFrame()', icon: "chevron-right",
                 hidden: 'IsNoviceMode()'
             },
             {
-                title: "Background Color", toolTip: "Background Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip",
+                title: "Fill", toolTip: "Background Fill Color", width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip",
                 script: "setBackgroundColor(value, _readOnly_)", hidden: 'selectedDocumentType()'
             }, // Only when a document is selected
             {
-                title: "Header Color", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading",
+                title: "Header", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading",
                 script: "setHeaderColor(value, _readOnly_)", hidden: 'selectedDocumentType()',
             }, // Only when a document is selected
             { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", backgroundColor: 'toggleOverlay(true)', click: 'toggleOverlay()', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 9be00fa9e..45dd84e31 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -173,7 +173,7 @@ export class MainView extends React.Component {
         fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt,
         fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer,
         fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV,
-        fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes,
+        fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes, fa.faFlag,
         fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
         fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
         fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any,
diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss
index e3e2be515..df9046f12 100644
--- a/src/client/views/nodes/button/FontIconBox.scss
+++ b/src/client/views/nodes/button/FontIconBox.scss
@@ -64,7 +64,7 @@
         justify-items: center;
 
         &:hover {
-            filter:brightness(0.85) !important;
+            filter: brightness(0.85) !important;
         }
     }
 
@@ -166,8 +166,8 @@
             width: 45% !important;
             height: 45%;
         }
-        
-        &:hover{
+
+        &:hover {
             filter: brightness(0.85);
         }
     }
@@ -278,10 +278,12 @@
                 background-color: #e3e3e3;
                 box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
                 border-radius: $standard-border-radius;
+
                 input[type=range]::-webkit-slider-runnable-track {
                     background: gray;
                     height: 3px;
                 }
+
                 input[type=range]::-webkit-slider-thumb {
                     box-shadow: 1px 1px 1px #000000;
                     border: 1px solid #000000;
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index d3ff2586b..20b2be216 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -107,6 +107,11 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         // Script for checking the outcome of the toggle
         const checkResult: number = numScript?.script.run({ value: 0, _readOnly_: true }).result || 0;
 
+        const label = !Doc.UserDoc()._showLabel ? (null) :
+            <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 37 ? -4 : 0, position: "absolute" }}>
+                {this.label}
+            </div>;
+
         if (numBtnType === NumButtonType.Slider) {
             const dropdown =
                 <div
@@ -126,6 +131,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                     onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}
                 >
                     {checkResult}
+                    {label}
                     {this.rootDoc.dropDownOpen ? dropdown : null}
                 </div>
             );
@@ -206,7 +212,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
                 onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}>
                 <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
-                {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
+                {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 20 ? -4 : 0, color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
                 <div
                     className="menuButton-dropdown"
                     style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
@@ -277,7 +283,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         });
 
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}>
+            <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 37 ? -4 : 0, color: color, backgroundColor: backgroundColor, position: "absolute" }}>
                 {this.label}
             </div>;
 
@@ -331,7 +337,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const curColor = this.colorScript?.script.run({ value: undefined, _readOnly_: true }).result ?? "transparent";
 
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
+            <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 37 ? -4 : 0, color, backgroundColor, position: "absolute" }}>
                 {this.label}
             </div>;
 
@@ -375,7 +381,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
 
         // Button label
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
+            <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 37 ? -4 : 0, color, backgroundColor, position: "absolute" }}>
                 {this.label}
             </div>;
 
@@ -387,7 +393,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                         <input type="checkbox"
                             checked={backgroundColor === Colors.MEDIUM_BLUE}
                         />
-                        <span className="slider round"></span>
+                        <span className="slider round" />
                     </label>
                 </div>
             );
@@ -416,7 +422,8 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 style={{ backgroundColor: "transparent", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}>
                 <div className="menuButton-wrap">
                     <FontAwesomeIcon className={`menuButton-icon-${this.type}`} icon={this.icon} color={"black"} size={"sm"} />
-                    {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
+                    {!this.label || !Doc.UserDoc()._showLabel ? (null) :
+                        <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 37 ? -4 : 0, color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
                 </div>
             </div>
         );
@@ -443,12 +450,12 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
         const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}>
+            <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 20 ? -4 : 0, color, backgroundColor, position: "absolute" }}>
                 {this.label}
             </div>;
 
         const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) :
-            <div className="fontIconBox-label" style={{ color, backgroundColor: "transparent" }}>
+            <div className="fontIconBox-label" style={{ bottom: this.props.PanelHeight() < 20 ? -4 : 0, color, backgroundColor: "transparent" }}>
                 {this.label}
             </div>;
 
-- 
cgit v1.2.3-70-g09d2


From 0cddc2350124f7ae870e362a4774de6f4d9a0545 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 23 May 2022 10:12:19 -0400
Subject: more code cleanup in currentuserutils.  some fixes to
 dragFactory-count titles.

---
 src/Utils.ts                                       |   1 +
 src/client/documents/Documents.ts                  |   9 +-
 src/client/util/CurrentUserUtils.ts                | 156 ++++++---------------
 src/client/util/DragManager.ts                     |   6 +-
 src/client/views/MainView.tsx                      |   3 +-
 src/client/views/collections/CollectionMenu.tsx    |   2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  13 +-
 7 files changed, 66 insertions(+), 124 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index c3c13a555..dbae0aa8c 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -8,6 +8,7 @@ import Color = require('color');
 
 export namespace Utils {
     export let DRAG_THRESHOLD = 4;
+    export let SNAP_THRESHOLD = 10;
 
     export function readUploadedFileAsText(inputFile: File) {
         const temporaryFileReader = new FileReader();
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 4be9674f7..6fa467368 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -156,6 +156,7 @@ export class DocumentOptions {
     "_carousel-caption-yMargin"?: number;
     "icon-nativeWidth"?: number;
     "icon-nativeHeight"?: number;
+    "dragFactory-count"?: number; // number of items created from a drag button (used for setting title with incrementing index)
     x?: number;
     y?: number;
     z?: number; // whether document is in overlay (1) or not (0 or undefined)
@@ -672,8 +673,8 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(url), ...options });
         }
 
-        export function PresDocument(initial: List<Doc> = new List(), options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.PRES), initial, options);
+        export function PresDocument(options: DocumentOptions = {}) {
+            return InstanceFromProto(Prototypes.get(DocumentType.PRES), new List<Doc>(), options);
         }
 
         export function ScriptingDocument(script: Opt<ScriptField>, options: DocumentOptions = {}, fieldKey?: string) {
@@ -693,8 +694,8 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.WEBCAM), "", options);
         }
 
-        export function ScreenshotDocument(title: string, options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.SCREENSHOT), "", { ...options, title });
+        export function ScreenshotDocument(options: DocumentOptions = {}) {
+            return InstanceFromProto(Prototypes.get(DocumentType.SCREENSHOT), "", options);
         }
 
         export function ComparisonDocument(options: DocumentOptions = { title: "Comparison Box" }) {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b9789ab7f..85769d915 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -24,7 +24,7 @@ import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox";
 import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView";
 import { OverlayView } from "../views/OverlayView";
 import { DocumentManager } from "./DocumentManager";
-import { DragManager } from "./DragManager";
+import { DragManager, dropActionType } from "./DragManager";
 import { makeTemplate, MakeTemplate } from "./DropConverter";
 import { HistoryUtil } from "./History";
 import { LinkManager } from "./LinkManager";
@@ -55,7 +55,6 @@ interface Button {
 }
 
 export let resolvedPorts: { server: number, socket: number };
-const headerViewVersion = "0.1";
 export class CurrentUserUtils {
     private static curr_id: string;
     //TODO tfs: these should be temporary...
@@ -87,7 +86,7 @@ export class CurrentUserUtils {
                 },
                 {
                     type: "mobile", icon: "mobile", template: this.mobileButton({ title: "NEW MOBILE BUTTON", onClick: undefined, },
-                        [this.createToolButton({ ignoreClick: true, icon: "mobile", btnType: ButtonType.ToolButton, backgroundColor: "transparent" }),
+                        [this.createToolButton({ ignoreClick: true, icon: "mobile", backgroundColor: "transparent" }),
                         this.mobileTextContainer({},
                             [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])
                         ]
@@ -102,7 +101,6 @@ export class CurrentUserUtils {
                         dragFactory: new PrefetchProxy(template) as any as Doc,
                         title: type,
                         icon,
-                        btnType: ButtonType.ToolButton,
                     });
                 }
                 return doc[`template-button-${type}`] as Doc;
@@ -179,6 +177,9 @@ export class CurrentUserUtils {
             "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, _width: 360 / 4, _height: 270 / 4,
             _showTitle: "title", system: true, onClick: deiconifyScript(), ...extra
         });
+        const fontBox = () => Docs.Create.FontIconDocument({
+            _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, system: true, onClick: deiconifyScript()
+        });
         if (!templateIconsDoc) {
             const newIconsList = [
                 makeIconTemplate(undefined, "title", () => labelBox({ _backgroundColor: "dimgray" })),
@@ -189,7 +190,7 @@ export class CurrentUserUtils {
                 makeIconTemplate(DocumentType.IMG, "data", () => imageBox("", { _height: undefined, })),
                 makeIconTemplate(DocumentType.COL, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})),
                 makeIconTemplate(DocumentType.VID, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})),
-                makeIconTemplate(DocumentType.BUTTON, "data", () => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, system: true, onClick: deiconifyScript() })),
+                makeIconTemplate(DocumentType.BUTTON, "data", () => fontBox()),
                 // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {}))
             ].filter(d => d).map(d => d!);
 
@@ -201,30 +202,23 @@ export class CurrentUserUtils {
         title: string, toolTip: string, icon: string, drag?: string, ignoreClick?: boolean,
         click?: string, backgroundColor?: string, dragFactory?: Doc, noviceMode?: boolean, clickFactory?: Doc
     }[] {
+        const standardOps = () => ({ _fitWidth: true, system: true, "dragFactory-count": 0, cloneFieldFilter: new List<string>(["system"]) });
         if (doc.emptyPresentation === undefined) {
-            doc.emptyPresentation = Docs.Create.PresDocument(new List<Doc>(),
-                { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0", system: true, cloneFieldFilter: new List<string>(["system"]) });
-            ((doc.emptyPresentation as Doc).proto as Doc)["dragFactory-count"] = 0;
+            doc.emptyPresentation = Docs.Create.PresDocument({ ...standardOps(), title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
         }
         if (doc.emptyCollection === undefined) {
-            doc.emptyCollection = Docs.Create.FreeformDocument([],
-                { _nativeWidth: undefined, _nativeHeight: undefined, _fitWidth: true, _width: 150, _height: 100, title: "freeform", system: true, cloneFieldFilter: new List<string>(["system"]) });
-            ((doc.emptyCollection as Doc).proto as Doc)["dragFactory-count"] = 0;
+            doc.emptyCollection = Docs.Create.FreeformDocument([], { ...standardOps(), title: "freeform", _width: 150, _height: 100 });
         }
         if (doc.emptyPane === undefined) {
-            doc.emptyPane = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _backgroundGridShow: true, _nativeHeight: undefined, _width: 500, _height: 800, title: "Untitled Tab", system: true, cloneFieldFilter: new List<string>(["system"]) });
-            ((doc.emptyPane as Doc).proto as Doc)["dragFactory-count"] = 0;
+            doc.emptyPane = Docs.Create.FreeformDocument([], { ...standardOps(), title: "Untitled Tab", _backgroundGridShow: true, _width: 500, _height: 800 });
         }
         if (doc.emptySlide === undefined) {
-            const textDoc = Docs.Create.TreeDocument([], {
-                title: "Slide", _viewType: CollectionViewType.Tree, treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true,
-                allowOverlayDrop: true, treeViewType: "outline", _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true,
-                backgroundColor: "white", system: true, cloneFieldFilter: new List<string>(["system"])
+            doc.emptySlide = Docs.Create.TreeDocument([], {
+                ...standardOps(), title: ComputedField.MakeFunction('self.text?.Text') as any, _viewType: CollectionViewType.Tree, treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, "dragFactory-count": undefined,
+                allowOverlayDrop: true, treeViewType: "outline", _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, backgroundColor: "white"
             });
-            Doc.GetProto(textDoc).title = ComputedField.MakeFunction('self.text?.Text');
-            doc.emptySlide = textDoc;
         }
-        if ((doc.emptyHeader as Doc)?.version !== headerViewVersion) {
+        if (doc.emptyHeader === undefined) {
             const json = {
                 doc: {
                     type: "doc",
@@ -245,9 +239,8 @@ export class CurrentUserUtils {
                 storedMarks: []
             };
             const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), {
-                title: "text", version: headerViewVersion, _height: 70, _headerPointerEvents: "all",
-                _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, _fitWidth: true,
-                cloneFieldFilter: new List<string>(["system"])
+                ...standardOps(), title: "text", _height: 70,
+                _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true,
             }, "header");
             const headerBtnHgt = 10;
             headerTemplate[DataSym].layout =
@@ -263,43 +256,34 @@ export class CurrentUserUtils {
             // "</div>";
             (headerTemplate.proto as Doc).isTemplateDoc = makeTemplate(headerTemplate.proto as Doc, true, "headerView");
             doc.emptyHeader = headerTemplate;
-            ((doc.emptyHeader as Doc).proto as Doc)["dragFactory-count"] = 0;
         }
         if (doc.emptyComparison === undefined) {
-            doc.emptyComparison = Docs.Create.ComparisonDocument({ title: "comparison box", _width: 300, _height: 300, system: true, cloneFieldFilter: new List<string>(["system"]) });
+            doc.emptyComparison = Docs.Create.ComparisonDocument({ ...standardOps(), title: "Comparer", _width: 300, _height: 300 });
         }
         if (doc.emptyScript === undefined) {
-            doc.emptyScript = Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250, title: "script", system: true, cloneFieldFilter: new List<string>(["system"]) });
-            ((doc.emptyScript as Doc).proto as Doc)["dragFactory-count"] = 0;
+            doc.emptyScript = Docs.Create.ScriptingDocument(undefined, { ...standardOps(), title: "script", _width: 200, _height: 250, });
         }
         if (doc.emptyScreenshot === undefined) {
-            doc.emptyScreenshot = Docs.Create.ScreenshotDocument("empty screenshot", { _fitWidth: true, title: "empty screenshot", _width: 400, _height: 200, system: true, cloneFieldFilter: new List<string>(["system"]) });
+            doc.emptyScreenshot = Docs.Create.ScreenshotDocument({ ...standardOps(), title: "empty screenshot", _width: 400, _height: 200 });
         }
         if (doc.emptyWall === undefined) {
-            doc.emptyWall = Docs.Create.ScreenshotDocument("", { _fitWidth: true, _width: 400, _height: 200, title: "screen snapshot", system: true, cloneFieldFilter: new List<string>(["system"]) });
+            doc.emptyWall = Docs.Create.ScreenshotDocument({ ...standardOps(), title: "screen snapshot", _width: 400, _height: 200, });
             (doc.emptyWall as Doc).videoWall = true;
         }
         if (doc.emptyAudio === undefined) {
-            doc.emptyAudio = Docs.Create.AudioDocument(nullAudio, { _width: 200, _height: 100, title: "audio recording", system: true, cloneFieldFilter: new List<string>(["system"]) });
-            ((doc.emptyAudio as Doc).proto as Doc)["dragFactory-count"] = 0;
+            doc.emptyAudio = Docs.Create.AudioDocument(nullAudio, { ...standardOps(), title: "audio recording", _width: 200, _height: 100, });
         }
         if (doc.emptyNote === undefined) {
-            doc.emptyNote = Docs.Create.TextDocument("", { _width: 200, title: "text note", _autoHeight: true, system: true, cloneFieldFilter: new List<string>(["system"]) });
-            ((doc.emptyNote as Doc).proto as Doc)["dragFactory-count"] = 0;
-        }
-        if (doc.emptyImage === undefined) {
-            doc.emptyImage = Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { _width: 250, _nativeWidth: 250, title: "an image of a cat", system: true });
+            doc.emptyNote = Docs.Create.TextDocument("", { ...standardOps(), title: "text note", _width: 200, _autoHeight: true });
         }
         if (doc.emptyButton === undefined) {
-            doc.emptyButton = Docs.Create.ButtonDocument({ _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, title: "Button", system: true, cloneFieldFilter: new List<string>(["system"]) });
-            ((doc.emptyButton as Doc).proto as Doc)["dragFactory-count"] = 0;
+            doc.emptyButton = Docs.Create.ButtonDocument({ ...standardOps(), title: "Button", _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, });
         }
         if (doc.emptyWebpage === undefined) {
-            doc.emptyWebpage = Docs.Create.WebDocument("http://www.bing.com/", { title: "webpage", _nativeWidth: 850, _height: 512, _width: 400, useCors: true, system: true, cloneFieldFilter: new List<string>(["system"]) });
+            doc.emptyWebpage = Docs.Create.WebDocument("http://www.bing.com/", { ...standardOps(), title: "webpage", _nativeWidth: 850, _height: 512, _width: 400, useCors: true, });
         }
         if (doc.emptyMap === undefined) {
-            doc.emptyMap = Docs.Create.MapDocument([], { title: "map", _showSidebar: true, _width: 800, _height: 600, system: true, cloneFieldFilter: new List<string>(["system"]) });
-            ((doc.emptyMap as Doc).proto as Doc)["dragFactory-count"] = 0;
+            doc.emptyMap = Docs.Create.MapDocument([], { ...standardOps(), title: "map", _showSidebar: true, _width: 800, _height: 600, });
         }
         if (doc.activeMobileMenu === undefined) {
             this.setupActiveMobileMenu(doc);
@@ -309,7 +293,6 @@ export class CurrentUserUtils {
             { toolTip: "Tap to create a collection in a new pane, drag for a collection", title: "Col", icon: "folder", click: 'openOnRight(copyDragFactory(this.clickFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyCollection as Doc, noviceMode: true, clickFactory: doc.emptyPane as Doc, },
             { toolTip: "Tap to create a webpage in a new pane, drag for a webpage", title: "Web", icon: "globe-asia", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWebpage as Doc, noviceMode: true },
             { toolTip: "Tap to create a progressive slide", title: "Slide", icon: "file", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptySlide as Doc },
-            { toolTip: "Tap to create a cat image in a new pane, drag for a cat image", title: "Image", icon: "cat", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyImage as Doc },
             { toolTip: "Tap to create a comparison box in a new pane, drag for a comparison box", title: "Compare", icon: "columns", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyComparison as Doc, noviceMode: true },
             { toolTip: "Tap to create a screen grabber in a new pane, drag for a screen grabber", title: "Grab", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScreenshot as Doc },
             { toolTip: "Tap to create a videoWall", title: "Wall", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWall as Doc },
@@ -719,30 +702,21 @@ export class CurrentUserUtils {
     })) as any as Doc
 
     static createToolButton = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({
-        ...opts, btnType: ButtonType.ToolButton, _forceActive: true, _dropAction: "alias", _removeDropProperties: new List<string>(["_dropAction", "stayInCollection"]), _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true
+        btnType: ButtonType.ToolButton, _forceActive: true, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List<string>(["_dropAction", "_hideContextMenu", "stayInCollection"]), _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true, ...opts,
     })) as any as Doc
 
     /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window
     static setupDockedButtons(doc: Doc) {
         if (doc.dockedBtns === undefined) {
-            if (doc["dockedBtn-undo"] === undefined) {
-                doc["dockedBtn-undo"] = CurrentUserUtils.createToolButton({
-                    title: "undo", icon: "undo-alt", _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, _hideContextMenu: true, system: true,
-                    onClick: ScriptField.MakeScript("undo()"), btnType: ButtonType.ToolButton, _dropAction: "alias",
-                    _removeDropProperties: new List<string>(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to undo"
-                });
-            }
-            if (doc["dockedBtn-redo"] === undefined) {
-                doc["dockedBtn-redo"] = CurrentUserUtils.createToolButton({
-                    title: "redo", icon: "redo-alt", _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, _hideContextMenu: true, system: true,
-                    onClick: ScriptField.MakeScript("redo()"), btnType: ButtonType.ToolButton, _dropAction: "alias",
-                    _removeDropProperties: new List<string>(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to redo",
-                });
-            }
+            const dockBtn = (opts: DocumentOptions) => CurrentUserUtils.createToolButton({ _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, ...opts });
+            const btnDescs = [
+                { title: "undo", opts: () => ({ icon: "undo-alt", onClick: ScriptField.MakeScript("undo()"), toolTip: "Click to undo" }) },
+                { title: "redo", opts: () => ({ icon: "redo-alt", onClick: ScriptField.MakeScript("redo()"), toolTip: "Click to redo" }) }
+            ];
             doc.dockedBtns = CurrentUserUtils.linearButtonList({
                 title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true,
                 linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true
-            }, [doc["dockedBtn-undo"] as Doc, doc["dockedBtn-redo"] as Doc]);
+            }, btnDescs.map(desc => doc[`dockedBtn-${desc.title}`] as Doc ?? (doc[`dockedBtn-${desc.title}`] = dockBtn({ title: desc.title, ...desc.opts() }))));
         }
     }
 
@@ -996,7 +970,7 @@ export class CurrentUserUtils {
 
     static async updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
         if (!doc.globalGroupDatabase) doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument();
-        const groups = await DocListCastAsync((doc.globalGroupDatabase as Doc).data);
+        await DocListCastAsync((doc.globalGroupDatabase as Doc).data);
         reaction(() => DateCast((doc.globalGroupDatabase as Doc)["data-lastModified"]),
             async () => {
                 const groups = await DocListCastAsync((doc.globalGroupDatabase as Doc).data);
@@ -1025,9 +999,6 @@ export class CurrentUserUtils {
         doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, false);
         doc.activeCollectionNestedBackground = Cast(doc.activeCollectionNestedBackground, "string", null);
         doc.noviceMode = BoolCast(doc.noviceMode, true);
-        doc["constants-snapThreshold"] = NumCast(doc["constants-snapThreshold"], 10); //
-        doc["constants-dragThreshold"] = NumCast(doc["constants-dragThreshold"], 4); //
-        Utils.DRAG_THRESHOLD = NumCast(doc["constants-dragThreshold"]);
         doc.savedFilters = new List<Doc>();
         doc.filterDocCount = 0;
         doc.freezeChildren = "remove|add";
@@ -1049,15 +1020,6 @@ export class CurrentUserUtils {
         doc["dockedBtn-undo"] && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc["dockedBtn-undo"] as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true });
         doc["dockedBtn-redo"] && reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(doc["dockedBtn-redo"] as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
 
-        // uncomment this to setup a default note style that uses the custom header layout
-        // PromiseValue(doc.emptyHeader).then(factory => {
-        //     if (Cast(doc.defaultTextLayout, Doc, null)?.version !== headerViewVersion) {
-        //         const deleg = Doc.delegateDragFactory(factory as Doc);
-        //         deleg.title = "header";
-        //         doc.defaultTextLayout = new PrefetchProxy(deleg);
-        //         Doc.AddDocToList(Cast(doc["template-notes"], Doc, null), "data", deleg);
-        //     }
-        // });
         setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500);
         doc.fieldInfos = await Docs.setupFieldInfos();
         if (doc.activeDashboard instanceof Doc) {
@@ -1264,26 +1226,16 @@ ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) {
         view && SelectionManager.SelectView(view, false);
     }
 });
-ScriptingGlobals.add(function MySharedDocs() { return Doc.SharingDoc(); },
-    "document containing all shared Docs");
-ScriptingGlobals.add(function IsNoviceMode() { return Doc.UserDoc().noviceMode; },
-    "is Dash in novice mode");
-ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); },
-    "creates a snapshot copy of a dashboard");
-ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); },
-    "creates a new dashboard when called");
-ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance.createNewPresentation(); },
-    "creates a new presentation when called");
-ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); },
-    "creates a new folder in myFiles when called");
-ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); },
-    "returns all the links to the document or its annotations", "(doc: any)");
-ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); },
-    "imports files from device directly into the import sidebar");
-ScriptingGlobals.add(function shareDashboard(dashboard: Doc) {
-    SharingManager.Instance.open(undefined, dashboard);
-},
-    "opens sharing dialog for Dashboard");
+ScriptingGlobals.add(function MySharedDocs() { return Doc.SharingDoc(); }, "document containing all shared Docs");
+ScriptingGlobals.add(function IsNoviceMode() { return Doc.UserDoc().noviceMode; }, "is Dash in novice mode");
+ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering");
+ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); }, "creates a snapshot copy of a dashboard");
+ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); }, "creates a new dashboard when called");
+ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance.createNewPresentation(); }, "creates a new presentation when called");
+ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called");
+ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, "returns all the links to the document or its annotations", "(doc: any)");
+ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar");
+ScriptingGlobals.add(function shareDashboard(dashboard: Doc) { SharingManager.Instance.open(undefined, dashboard); }, "opens sharing dialog for Dashboard");
 ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) {
     const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data);
     if (dashboards && dashboards.length > 1) {
@@ -1292,21 +1244,10 @@ ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) {
     }
 },
     "Remove Dashboard from Dashboards");
-ScriptingGlobals.add(async function addToDashboards(dashboard: Doc) {
+ScriptingGlobals.add(function addToDashboards(dashboard: Doc) {
     const dashboardAlias = Doc.MakeAlias(dashboard);
-
-    const allDocs = await DocListCastAsync(dashboard[DataSym]["data-all"]);
-
-    // moves the data-all field from the datadoc to the layoutdoc, necessary for off screen docs tab to function properly
-    // dashboard["data-all"] = new List<Doc>(allDocs);
-    // dashboardAlias["data-all"] = new List<Doc>((allDocs || []).map(doc => Doc.MakeAlias(doc)));
-
-    // const dockingConfig = JSON.parse(StrCast(dashboardAlias.dockingConfig));
-    // dashboardAlias.dockingConfig = JSON.stringify(dockingConfig);
-
     dashboardAlias.data = new List<Doc>(DocListCast(dashboard.data).map(tabFolder => Doc.MakeAlias(tabFolder)));
     DocListCast(dashboardAlias.data).forEach(doc => doc.dashboard = dashboardAlias);
-    //new List<Doc>();
     DocListCast(dashboardAlias.data)[1].data = ComputedField.MakeFunction(`dynamicOffScreenDocs(self.dashboard)`) as any;
     Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", dashboardAlias);
     CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboardAlias);
@@ -1344,9 +1285,6 @@ ScriptingGlobals.add(function makeTopLevelFolder() {
     TreeView._editTitleOnLoad = { id: folder[Id], parent: undefined };
     return Doc.AddDocToList(Doc.UserDoc().myFilesystem as Doc, "data", folder);
 });
-ScriptingGlobals.add(function toggleComicMode() {
-    Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic";
-});
 ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) {
     if (readOnly) return;
     const sel = SelectionManager.Views()[0];
@@ -1359,8 +1297,7 @@ ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) {
     CollectionFreeFormDocumentView.updateKeyframe(col.childDocs, currentFrame || 0);
     sel.rootDoc._currentFrame = Math.max(0, (currentFrame || 0) + 1);
     sel.rootDoc.lastFrame = Math.max(NumCast(sel.rootDoc._currentFrame), NumCast(sel.rootDoc.lastFrame));
-}
-);
+});
 ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) {
     if (readOnly) return;
     const sel = SelectionManager.Views()[0];
@@ -1372,5 +1309,4 @@ ScriptingGlobals.add(function prevKeyFrame(readOnly: boolean) {
     }
     CollectionFreeFormDocumentView.gotoKeyframe(col.childDocs.slice());
     sel.rootDoc._currentFrame = Math.max(0, (currentFrame || 0) - 1);
-}
-);
\ No newline at end of file
+});
\ No newline at end of file
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 5a00e0c04..9f8c49081 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -7,7 +7,7 @@ import { listSpec } from "../../fields/Schema";
 import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
 import { ScriptField } from "../../fields/ScriptField";
 import { Cast, NumCast, ScriptCast, StrCast } from "../../fields/Types";
-import { emptyFunction } from "../../Utils";
+import { emptyFunction, Utils } from "../../Utils";
 import { Docs, DocUtils } from "../documents/Documents";
 import * as globalCssVariables from "../views/global/globalCssVariables.scss";
 import { DocumentView } from "../views/nodes/DocumentView";
@@ -277,7 +277,7 @@ export namespace DragManager {
         SnappingManager.setSnapLines(horizLines, vertLines);
     }
     export function snapDragAspect(dragPt: number[], snapAspect: number) {
-        let closest = NumCast(Doc.UserDoc()["constants-snapThreshold"], 10);
+        let closest = Utils.SNAP_THRESHOLD;
         let near = dragPt;
         const intersect = (x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number, dragx: number, dragy: number) => {
             if ((x1 === x2 && y1 === y2) || (x3 === x4 && y3 === y4)) return undefined; // Check if none of the lines are of length 0
@@ -312,7 +312,7 @@ export namespace DragManager {
     }
     // snap to the active snap lines - if oneAxis is set (eg, for maintaining aspect ratios), then it only snaps to the nearest horizontal/vertical line 
     export function snapDrag(e: PointerEvent, xFromLeft: number, yFromTop: number, xFromRight: number, yFromBottom: number) {
-        const snapThreshold = NumCast(Doc.UserDoc()["constants-snapThreshold"], 10);
+        const snapThreshold = Utils.SNAP_THRESHOLD;
         const snapVal = (pts: number[], drag: number, snapLines: number[]) => {
             if (snapLines.length) {
                 const offs = [pts[0], (pts[0] - pts[1]) / 2, -pts[1]];   // offsets from drag pt
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 45dd84e31..6a2df3b51 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -244,8 +244,7 @@ export class MainView extends React.Component {
                 title: "TRAILS", childDontRegisterViews: true, _height: 100, _forceActive: true, boxShadow: "0 0", _lockedPosition: true, treeViewOpen: true, system: true
             }));
         }
-        const pres = Docs.Create.PresDocument(new List<Doc>(),
-            { title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
+        const pres = Docs.Create.PresDocument({ title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
         CollectionDockingView.AddSplit(pres, "right");
         this.userDoc.activePresentation = pres;
         Doc.AddDocToList(this.userDoc.myTrails as Doc, "data", pres);
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index b3fbd4ca4..c322d9e16 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -467,7 +467,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     @undoBatch
     @action
     startRecording = () => {
-        const doc = Docs.Create.ScreenshotDocument("screen recording", { _fitWidth: true, _width: 400, _height: 200, mediaState: "pendingRecording" });
+        const doc = Docs.Create.ScreenshotDocument({ title: "screen recording", _fitWidth: true, _width: 400, _height: 200, mediaState: "pendingRecording" });
         //Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
         CollectionDockingView.AddSplit(doc, "right");
     }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index d0c8c5826..164bcb9b0 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -17,7 +17,7 @@ import { InkTool } from '../../../../fields/InkField';
 import { PrefetchProxy } from '../../../../fields/Proxy';
 import { RichTextField } from "../../../../fields/RichTextField";
 import { RichTextUtils } from '../../../../fields/RichTextUtils';
-import { Cast, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
+import { Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
 import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util';
 import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils';
 import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils';
@@ -62,6 +62,7 @@ import { schema } from "./schema_rts";
 import { SummaryView } from "./SummaryView";
 import applyDevTools = require("prosemirror-dev-tools");
 import React = require("react");
+import { ComputedField } from '../../../../fields/ScriptField';
 const translateGoogleApi = require("translate-google-api");
 
 export interface FormattedTextBoxProps {
@@ -371,9 +372,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             while (node.firstChild && node.firstChild.type.name !== "text") node = node.firstChild;
             const str = node.textContent;
             const prefix = str.startsWith("@") ? "" : "-";
-            this.dataDoc.title = prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? "..." : "");
-            if (str.startsWith("@") && str.length > 1) {
-                Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", this.rootDoc);
+
+            const cfield = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc.title));
+            if (!(cfield instanceof ComputedField)) {
+                this.dataDoc.title = prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? "..." : "");
+                if (str.startsWith("@") && str.length > 1) {
+                    Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", this.rootDoc);
+                }
             }
         }
     }
-- 
cgit v1.2.3-70-g09d2


From 3fe0e0e02a6c9bd717b3df9b000c13d1131f6eb4 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 23 May 2022 12:28:31 -0400
Subject: cleaning up singleLine text box api and TreeView types

---
 src/client/util/CurrentUserUtils.ts                |  7 +--
 src/client/views/MainView.tsx                      |  5 +-
 .../views/collections/CollectionTreeView.tsx       | 29 ++++++----
 src/client/views/collections/TreeView.tsx          | 35 ++++++------
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 34 ++++++++++--
 .../collections/collectionFreeForm/MarqueeView.tsx | 13 +++--
 src/client/views/nodes/DocumentView.tsx            |  2 +
 src/client/views/nodes/FieldView.tsx               |  3 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  7 +--
 .../formattedText/ProsemirrorExampleTransfer.ts    | 62 ++++++----------------
 10 files changed, 101 insertions(+), 96 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 85769d915..1ebb5365a 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -16,6 +16,7 @@ import { Docs, DocumentOptions, DocUtils } from "../documents/Documents";
 import { DocumentType } from "../documents/DocumentTypes";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
 import { CollectionFreeFormView } from "../views/collections/collectionFreeForm";
+import { TreeViewType } from "../views/collections/CollectionTreeView";
 import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
 import { TreeView } from "../views/collections/TreeView";
 import { Colors } from "../views/global/globalEnums";
@@ -215,7 +216,7 @@ export class CurrentUserUtils {
         if (doc.emptySlide === undefined) {
             doc.emptySlide = Docs.Create.TreeDocument([], {
                 ...standardOps(), title: ComputedField.MakeFunction('self.text?.Text') as any, _viewType: CollectionViewType.Tree, treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, "dragFactory-count": undefined,
-                allowOverlayDrop: true, treeViewType: "outline", _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, backgroundColor: "white"
+                allowOverlayDrop: true, treeViewType: TreeViewType.outline, _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, backgroundColor: "white"
             });
         }
         if (doc.emptyHeader === undefined) {
@@ -563,7 +564,7 @@ export class CurrentUserUtils {
                 title: "My Dashboards", _showTitle: "title", _height: 400, childHideLinkButton: true, freezeChildren: "remove|add",
                 treeViewHideTitle: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, ignoreClick: true, buttonMenu: true, buttonMenuDoc: newDashboardButton,
-                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: "fileSystem", isFolder: true, system: true,
+                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, system: true,
                 explainer: "This is your collection of dashboards. A dashboard represents the tab configuration of your workspace. To manage documents as folders, go to the Files."
             }));
             const toggleDarkTheme = ScriptField.MakeScript(`this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`);
@@ -616,7 +617,7 @@ export class CurrentUserUtils {
                 title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100,
                 treeViewHideTitle: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, ignoreClick: true,
-                isFolder: true, treeViewType: "fileSystem", childHideLinkButton: true,
+                isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true,
                 _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true,
                 explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
             }));
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 6a2df3b51..09a57843c 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -8,11 +8,9 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import * as ReactDOM from 'react-dom';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
-import { List } from '../../fields/List';
 import { PrefetchProxy } from '../../fields/Proxy';
 import { ScriptField } from '../../fields/ScriptField';
 import { PromiseValue, StrCast } from '../../fields/Types';
-import { TraceMobx } from '../../fields/util';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
@@ -34,6 +32,7 @@ import { CollectionDockingView } from './collections/CollectionDockingView';
 import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu';
 import { CollectionLinearView } from './collections/collectionLinear';
 import { CollectionMenu } from './collections/CollectionMenu';
+import { TreeViewType } from './collections/CollectionTreeView';
 import { CollectionViewType } from './collections/CollectionView';
 import "./collections/TreeView.scss";
 import { ComponentDecorations } from './ComponentDecorations';
@@ -263,7 +262,7 @@ export class MainView extends React.Component {
                 title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100,
                 treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
                 treeViewTruncateTitleWidth: 150, ignoreClick: true,
-                isFolder: true, treeViewType: "fileSystem", childHideLinkButton: true,
+                isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true,
                 _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true,
                 explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
             }));
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index 5e89016db..99ffb9bb0 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -27,6 +27,7 @@ import { CollectionSubView } from "./CollectionSubView";
 import "./CollectionTreeView.scss";
 import { TreeView } from "./TreeView";
 import React = require("react");
+import { FieldViewProps } from "../nodes/FieldView";
 const _global = (window /* browser */ || global /* node */) as any;
 
 export type collectionTreeViewProps = {
@@ -39,6 +40,12 @@ export type collectionTreeViewProps = {
     onChildClick?: () => ScriptField;
 };
 
+export enum TreeViewType {
+    outline = "outline",
+    fileSystem = "fileSystem",
+    default = "default"
+}
+
 @observer
 export class CollectionTreeView extends CollectionSubView<Partial<collectionTreeViewProps>>() {
     private _treedropDisposer?: DragManager.DragDropDisposer;
@@ -54,8 +61,8 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     @computed get dataDoc() { return this.props.DataDoc || this.doc; }
     @computed get treeViewtruncateTitleWidth() { return NumCast(this.doc.treeViewTruncateTitleWidth, this.panelWidth()); }
     @computed get treeChildren() { TraceMobx(); return this.props.childDocuments || this.childDocs; }
-    @computed get outlineMode() { return this.doc.treeViewType === "outline"; }
-    @computed get fileSysMode() { return this.doc.treeViewType === "fileSystem"; }
+    @computed get outlineMode() { return this.doc.treeViewType === TreeViewType.outline }
+    @computed get fileSysMode() { return this.doc.treeViewType === TreeViewType.fileSystem; }
     @computed get dashboardMode() { return this.doc === Doc.UserDoc().myDashboards; }
 
     @observable _explainerHeight = 0; // height of the description of the tree view
@@ -180,13 +187,20 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             height={"auto"}
             GetValue={() => StrCast(this.dataDoc.title)}
             SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => {
-                if (enter && this.props.Document.treeViewType === "outline") this.makeTextCollection(this.treeChildren);
+                if (enter && this.props.Document.treeViewType === TreeViewType.outline) this.makeTextCollection(this.treeChildren);
                 this.dataDoc.title = value;
                 return true;
             })} />;
     }
 
 
+    onKey = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
+        if (this.outlineMode && e.key === "Enter") {
+            e.stopPropagation();
+            this.makeTextCollection(this.treeChildren);
+            return true;
+        }
+    }
     get documentTitle() {
         return <FormattedTextBox
             {...this.props}
@@ -199,6 +213,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             PanelWidth={this.documentTitleWidth}
             PanelHeight={this.documentTitleHeight}
             scaling={returnOne}
+            onKey={this.onKey}
             docFilters={returnEmptyFilter}
             docRangeFilters={returnEmptyFilter}
             searchFilterDocs={returnEmptyDoclist}
@@ -257,13 +272,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         return this.dataDoc === null ? (null) :
             <div className="collectionTreeView-titleBar" key={this.doc[Id]}
                 style={!this.outlineMode ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}}
-                ref={r => this._titleRef = r}
-                onKeyDown={e => {
-                    if (this.outlineMode) {
-                        e.stopPropagation();
-                        e.key === "Enter" && this.makeTextCollection(this.treeChildren);
-                    }
-                }}>
+                ref={r => this._titleRef = r}>
                 {this.outlineMode ? this.documentTitle : this.editableTitle}
             </div>;
     }
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 65f8fe248..70ad23f41 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -26,11 +26,12 @@ import { DocumentView, DocumentViewInternal, DocumentViewProps, StyleProviderFun
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
 import { StyleProp } from '../StyleProvider';
-import { CollectionTreeView } from './CollectionTreeView';
+import { CollectionTreeView, TreeViewType } from './CollectionTreeView';
 import { CollectionView, CollectionViewType } from './CollectionView';
 import "./TreeView.scss";
 import React = require("react");
 import { KeyValueBox } from '../nodes/KeyValueBox';
+import { FieldViewProps } from '../nodes/FieldView';
 
 export interface TreeViewProps {
     treeView: CollectionTreeView;
@@ -241,7 +242,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             layout: CollectionView.LayoutString("data"),
             title: "-title-",
             treeViewExpandedViewLock: true, treeViewExpandedView: "data",
-            _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewType: "outline",
+            _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewType: TreeViewType.outline,
             x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _width: 1000, _height: 10
         });
         Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
@@ -604,24 +605,29 @@ export class TreeView extends React.Component<TreeViewProps> {
         if (property.startsWith(StyleProp.Decorations)) return (null);
         return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
     }
-    onKeyDown = (e: React.KeyboardEvent) => {
+    onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
         if (this.doc.treeViewHideHeader || (this.doc.treeViewHideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode) {
             switch (e.key) {
                 case "Tab":
-                    e.stopPropagation();
-                    e.preventDefault();
+                    e.stopPropagation?.();
+                    e.preventDefault?.();
                     setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
-                    return UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.(true) : this.props.indentDocument?.(true), "tab");
+                    UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.(true) : this.props.indentDocument?.(true), "tab");
+                    return true;
                 case "Backspace":
-                    e.stopPropagation();
-                    e.preventDefault();
-                    return !(this.doc.text as RichTextField)?.Text && this.props.removeDoc?.(this.doc);
+                    if (!(this.doc.text as RichTextField)?.Text && this.props.removeDoc?.(this.doc)) {
+                        e.stopPropagation?.();
+                        e.preventDefault?.();
+                        return true;
+                    }
+                    break;
                 case "Enter":
-                    e.stopPropagation();
-                    e.preventDefault();
+                    e.stopPropagation?.();
+                    e.preventDefault?.();
                     return UndoManager.RunInBatch(this.makeTextCollection, "bullet");
             }
         }
+        return false;
     }
     titleWidth = () => Math.max(20, Math.min(this.props.treeView.truncateTitleWidth(), this.props.panelWidth() - 2 * treeBulletWidth()));
 
@@ -758,6 +764,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             hideResizeHandles={this.props.treeView.outlineMode}
             focus={this.refocus}
             ContentScaling={returnOne}
+            onKey={this.onKeyDown}
             hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)}
             dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)}
             ScreenToLocalTransform={this.docTransform}
@@ -800,7 +807,7 @@ export class TreeView extends React.Component<TreeViewProps> {
     @computed get renderBorder() {
         const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None);
         const sortings = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string, label: string } };
-        return <div className={`treeView-border${this.props.treeView.outlineMode ? "outline" : ""}`} style={{ borderColor: sortings[sorting]?.color }}>
+        return <div className={`treeView-border${this.props.treeView.outlineMode ? TreeViewType.outline : ""}`} style={{ borderColor: sortings[sorting]?.color }}>
             {!this.treeViewOpen ? (null) : this.renderContent}
         </div>;
     }
@@ -821,9 +828,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         return this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? "<" + this.doc.title + ">" : // just print the title of documents we've previously rendered in this hierarchical path to avoid cycles
             <div className={`treeView-container${this.props.isContentActive() ? "-active" : ""}`}
                 ref={this.createTreeDropTarget}
-                onDrop={this.onTreeDrop}
-                //onPointerDown={e => this.props.isContentActive(true) && SelectionManager.DeselectAll()} // bcz: this breaks entering a text filter in a filterBox since it deselects the filter's target document
-                onKeyDown={this.onKeyDown}>
+                onDrop={this.onTreeDrop}>
                 <li className="collection-child">
                     {hideTitle && this.doc.type !== DocumentType.RTF && !this.doc.treeViewRenderAsBulletHeader ? // should test for prop 'treeViewRenderDocWithBulletAsHeader"
                         this.renderEmbeddedDocument(false, returnFalse) :
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index df09569ef..3b32cf57d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,18 +1,18 @@
 import { Bezier } from "bezier-js";
-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 { computedFn } from "mobx-utils";
 import { DateField } from "../../../../fields/DateField";
-import { Doc, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc";
+import { DataSym, Doc, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc";
 import { Id } from "../../../../fields/FieldSymbols";
 import { InkData, InkField, InkTool, PointData, Segment } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
 import { ObjectField } from "../../../../fields/ObjectField";
 import { RichTextField } from "../../../../fields/RichTextField";
-import { ImageField } from "../../../../fields/URLField";
 import { createSchema, listSpec } from "../../../../fields/Schema";
 import { ScriptField } from "../../../../fields/ScriptField";
 import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
+import { ImageField } from "../../../../fields/URLField";
 import { TraceMobx } from "../../../../fields/util";
 import { GestureUtils } from "../../../../pen-gestures/GestureUtils";
 import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUpEvents, Utils } from "../../../../Utils";
@@ -26,6 +26,7 @@ import { DragManager, dropActionType } from "../../../util/DragManager";
 import { HistoryUtil } from "../../../util/History";
 import { InteractionUtils } from "../../../util/InteractionUtils";
 import { LinkManager } from "../../../util/LinkManager";
+import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
 import { SearchUtil } from "../../../util/SearchUtil";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { ColorScheme } from "../../../util/SettingsManager";
@@ -47,13 +48,14 @@ import { CreateImage } from "../../nodes/WebBoxRenderer";
 import { StyleProp } from "../../StyleProvider";
 import { CollectionDockingView } from "../CollectionDockingView";
 import { CollectionSubView } from "../CollectionSubView";
+import { TreeViewType } from "../CollectionTreeView";
 import { CollectionViewType } from "../CollectionView";
 import { computePivotLayout, computerPassLayout, computerStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from "./CollectionFreeFormLayoutEngines";
 import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
 import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
-import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
+import { FieldView, FieldViewProps } from "../../nodes/FieldView";
 
 export const panZoomSchema = createSchema({
     _panX: "number",
@@ -957,7 +959,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || CurrentUserUtils.OverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === "outline") return;
+        if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || CurrentUserUtils.OverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
         if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming
             e.stopPropagation();
         }
@@ -1160,6 +1162,27 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
 
+    @undoBatch
+    @action
+    onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
+        const docView = fieldProps.DocumentView?.();
+        if (docView && docView.rootDoc._singleLine && ["Tab", "Enter"].includes(e.key)) {
+            e.stopPropagation?.();
+            const below = !e.altKey && e.key !== "Tab";
+            const layoutKey = StrCast(docView.LayoutFieldKey);
+            const newDoc = Doc.MakeCopy(docView.rootDoc, true);
+            const dataField = docView.rootDoc[Doc.LayoutFieldKey(newDoc)];
+            newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
+            if (below) newDoc.y = NumCast(docView.rootDoc.y) + NumCast(docView.rootDoc._height) + 10;
+            else newDoc.x = NumCast(docView.rootDoc.x) + NumCast(docView.rootDoc._width) + 10;
+            if (layoutKey !== "layout" && docView.rootDoc[layoutKey] instanceof Doc) {
+                newDoc[layoutKey] = docView.rootDoc[layoutKey];
+            }
+            Doc.GetProto(newDoc).text = undefined;
+            FormattedTextBox.SelectOnLoad = newDoc[Id];
+            return this.addDocument?.(newDoc);
+        }
+    }
     pointerEvents = () => {
         const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine);
         const pointerEvents = this.props.isContentActive() === false ? "none" :
@@ -1185,6 +1208,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             LayoutTemplateString={childLayout.z ? undefined : this.props.childLayoutString}
             rootSelected={childData ? this.rootSelected : returnFalse}
             onClick={this.onChildClickHandler}
+            onKey={this.onKeyDown}
             onDoubleClick={this.onChildDoubleClickHandler}
             onBrowseClick={this.onBrowseClickHandler}
             ScreenToLocalTransform={childLayout.z ? this.getContainerTransform : this.getTransform}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 1c4d47d57..29bdae170 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,14 +1,15 @@
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
-import { AclAugment, AclAdmin, AclEdit, DataSym, Doc, Opt } from "../../../../fields/Doc";
+import { AclAdmin, AclAugment, AclEdit, DataSym, Doc, Opt } from "../../../../fields/Doc";
 import { Id } from "../../../../fields/FieldSymbols";
 import { InkData, InkField, InkTool } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
 import { RichTextField } from "../../../../fields/RichTextField";
 import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
 import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
+import { ImageField } from "../../../../fields/URLField";
 import { GetEffectiveAcl } from "../../../../fields/util";
-import { Utils, intersectRect, returnFalse } from "../../../../Utils";
+import { intersectRect, returnFalse, Utils } from "../../../../Utils";
 import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
 import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents";
 import { DocumentType } from "../../../documents/DocumentTypes";
@@ -21,17 +22,15 @@ import { ContextMenu } from "../../ContextMenu";
 import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
 import { PresBox } from "../../nodes/trails/PresBox";
 import { PresMovement } from "../../nodes/trails/PresEnums";
+import { VideoBox } from "../../nodes/VideoBox";
+import { pasteImageBitmap } from "../../nodes/WebBoxRenderer";
 import { PreviewCursor } from "../../PreviewCursor";
 import { CollectionDockingView } from "../CollectionDockingView";
 import { SubCollectionViewProps } from "../CollectionSubView";
-import { CollectionView } from "../CollectionView";
+import { TreeView } from "../TreeView";
 import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
 import "./MarqueeView.scss";
 import React = require("react");
-import { TreeView } from "../TreeView";
-import { VideoBox } from "../../nodes/VideoBox";
-import { ImageField, WebField } from "../../../../fields/URLField";
-import { pasteImageBitmap } from "../../nodes/WebBoxRenderer";
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f5ad47ca7..f7312e59c 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -50,6 +50,7 @@ import { ScriptingBox } from "./ScriptingBox";
 import { PresBox } from './trails/PresBox';
 import React = require("react");
 import { DocServer } from "../../DocServer";
+import { FieldViewProps } from "./FieldView";
 const { Howl } = require('howler');
 
 interface Window {
@@ -178,6 +179,7 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
     onPointerDown?: () => ScriptField;
     onPointerUp?: () => ScriptField;
     onBrowseClick?: () => (ScriptField | undefined);
+    onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => (boolean | undefined);
 }
 
 // these props are only available in DocumentViewIntenral
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 94937430e..79c1f1c40 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -5,7 +5,7 @@ import { DateField } from "../../../fields/DateField";
 import { Doc, Field, FieldResult, Opt } from "../../../fields/Doc";
 import { List } from "../../../fields/List";
 import { WebField } from "../../../fields/URLField";
-import { DocumentViewSharedProps } from "./DocumentView";
+import { DocumentView, DocumentViewSharedProps } from "./DocumentView";
 import { ScriptField } from "../../../fields/ScriptField";
 
 //
@@ -25,6 +25,7 @@ export interface FieldViewProps extends DocumentViewSharedProps {
     scaling?: () => number;
     setHeight?: (height: number) => void;
     onBrowseClick?: () => (ScriptField | undefined);
+    onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => (boolean | undefined);
 
     // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React)
     pointerEvents?: () => Opt<string>;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 164bcb9b0..0e29461a6 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -17,6 +17,7 @@ import { InkTool } from '../../../../fields/InkField';
 import { PrefetchProxy } from '../../../../fields/Proxy';
 import { RichTextField } from "../../../../fields/RichTextField";
 import { RichTextUtils } from '../../../../fields/RichTextUtils';
+import { ComputedField } from '../../../../fields/ScriptField';
 import { Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
 import { GetEffectiveAcl, TraceMobx } from '../../../../fields/util';
 import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, numberRange, OmitKeys, returnZero, setupMoveUpEvents, smoothScroll, unimplementedFunction, Utils } from '../../../../Utils';
@@ -62,7 +63,6 @@ import { schema } from "./schema_rts";
 import { SummaryView } from "./SummaryView";
 import applyDevTools = require("prosemirror-dev-tools");
 import React = require("react");
-import { ComputedField } from '../../../../fields/ScriptField';
 const translateGoogleApi = require("translate-google-api");
 
 export interface FormattedTextBoxProps {
@@ -1471,11 +1471,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }, "titler");
         }
     }
+
     onKeyDown = (e: React.KeyboardEvent) => {
-        // single line text boxes need to pass through tab/enter/backspace so that their containers can respond (eg, an outline container)
-        if (this.rootDoc._singleLine && ((e.key === "Backspace" && !this.dataDoc[this.fieldKey]?.Text) || ["Tab", "Enter"].includes(e.key))) {
-            return;
-        }
         if (e.altKey) {
             e.preventDefault();
             return;
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index c76eda859..0dd0a8411 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -1,20 +1,15 @@
-import { chainCommands, exitCode, joinDown, joinUp, lift, deleteSelection, joinBackward, selectNodeBackward, setBlockType, splitBlockKeepMarks, toggleMark, wrapIn, newlineInCode } from "prosemirror-commands";
-import { liftTarget } from "prosemirror-transform";
+import { chainCommands, deleteSelection, exitCode, joinBackward, joinDown, joinUp, lift, newlineInCode, selectNodeBackward, setBlockType, splitBlockKeepMarks, toggleMark, wrapIn } from "prosemirror-commands";
 import { redo, undo } from "prosemirror-history";
 import { Schema } from "prosemirror-model";
-import { liftListItem, sinkListItem } from "./prosemirrorPatches.js";
-import { splitListItem, wrapInList, } from "prosemirror-schema-list";
-import { EditorState, Transaction, TextSelection } from "prosemirror-state";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { NumCast, BoolCast, Cast, StrCast } from "../../../../fields/Types";
-import { Doc, DataSym, DocListCast, AclAugment, AclSelfEdit } from "../../../../fields/Doc";
-import { FormattedTextBox } from "./FormattedTextBox";
-import { Id } from "../../../../fields/FieldSymbols";
-import { Docs } from "../../../documents/Documents";
-import { Utils } from "../../../../Utils";
-import { listSpec } from "../../../../fields/Schema";
-import { List } from "../../../../fields/List";
+import { splitListItem, wrapInList } from "prosemirror-schema-list";
+import { EditorState, TextSelection, Transaction } from "prosemirror-state";
+import { liftTarget } from "prosemirror-transform";
+import { AclAugment, AclSelfEdit, Doc } from "../../../../fields/Doc";
 import { GetEffectiveAcl } from "../../../../fields/util";
+import { Utils } from "../../../../Utils";
+import { Docs } from "../../../documents/Documents";
+import { SelectionManager } from "../../../util/SelectionManager";
+import { liftListItem, sinkListItem } from "./prosemirrorPatches.js";
 
 const mac = typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
 
@@ -48,29 +43,6 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
         keys[key] = cmd;
     }
 
-    /// bcz; Argh!!  replace with an onEnter func that conditionally handles Enter 
-    const addTextBox = (below: boolean, force?: boolean) => {
-        if (props.Document.treeViewType === "outline") return true;  // bcz: Arghh .. need to determine if this is an treeViewOutlineBox in which case Enter's are ignored..
-        const layoutDoc = props.Document;
-        const originalDoc = layoutDoc.rootDocument || layoutDoc;
-        if (force || props.Document._singleLine) {
-            const layoutKey = StrCast(originalDoc.layoutKey);
-            const newDoc = Doc.MakeCopy(originalDoc, true);
-            const dataField = originalDoc[Doc.LayoutFieldKey(newDoc)];
-            newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
-            if (below) newDoc.y = NumCast(originalDoc.y) + NumCast(originalDoc._height) + 10;
-            else newDoc.x = NumCast(originalDoc.x) + NumCast(originalDoc._width) + 10;
-            if (layoutKey !== "layout" && originalDoc[layoutKey] instanceof Doc) {
-                newDoc[layoutKey] = originalDoc[layoutKey];
-            }
-            Doc.GetProto(newDoc).text = undefined;
-            FormattedTextBox.SelectOnLoad = newDoc[Id];
-            props.addDocument(newDoc);
-            return true;
-        }
-        return false;
-    };
-
     const canEdit = (state: any) => {
         switch (GetEffectiveAcl(props.Document)) {
             case AclAugment: return false;
@@ -109,11 +81,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     bind("Ctrl-i", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state as any, dispatch as any));
 
     bind("Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
-        /// bcz; Argh!!  replace layotuTEmpalteString with a onTab prop conditionally handles Tab);
-        if (props.Document._singleLine) {
-            if (!props.LayoutTemplateString) return addTextBox(false, true);
-            return true;
-        }
+        if (props.onKey?.({ key: "Tab" }, props)) return true;
         if (!canEdit(state)) return true;
         const ref = state.selection;
         const range = ref.$from.blockRange(ref.$to);
@@ -138,8 +106,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     });
 
     bind("Shift-Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
-        /// bcz; Argh!!  replace with a onShiftTab prop conditionally handles Tab);
-        if (props.Document._singleLine) return true;
+        if (props.onKey?.({ key: "Tab", shiftKey: true })) return true; // single line docs don't process tabs so that their containers can decide what to do.  This should be a prop
         if (!canEdit(state)) return true;
         const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
 
@@ -188,13 +155,14 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     };
 
     //Command to create a text document to the right of the selected textbox
-    bind("Alt-Enter", () => addTextBox(false, true));
+    bind("Alt-Enter", () => true);
 
     //Command to create a text document to the bottom of the selected textbox
-    bind("Ctrl-Enter", () => addTextBox(true, true));
+    bind("Ctrl-Enter", () => true);
 
     // backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward);
     bind("Backspace", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
+        if (props.onKey?.(event, props)) return true;
         if (!canEdit(state)) return true;
 
         if (!deleteSelection(state, (tx: Transaction<S>) => {
@@ -216,8 +184,8 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     //newlineInCode, createParagraphNear, liftEmptyBlock, splitBlock
     //command to break line
     bind("Enter", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
-        if (addTextBox(true, false)) return true;
 
+        if (props.onKey?.(event, props)) return true;
         if (!canEdit(state)) return true;
 
         const trange = state.selection.$from.blockRange(state.selection.$to);
-- 
cgit v1.2.3-70-g09d2


From 414b56260c55f5ea7cac43dad83b41688fb924b5 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 23 May 2022 20:34:07 -0400
Subject: added headerBar for closed tabs and other docs. extended multirow/col
 to support hiding chile doc decorations and for making child content/document
 active work better.  changed background style to work better for sub
 collections.

---
 src/client/util/CurrentUserUtils.ts                |   3 +
 src/client/views/DocumentDecorations.tsx           |   7 +-
 src/client/views/MainView.tsx                      | 115 +++++++++++++++------
 src/client/views/StyleProvider.tsx                 |   6 +-
 src/client/views/collections/CollectionMenu.tsx    |  17 ++-
 src/client/views/collections/CollectionView.tsx    |   6 +-
 src/client/views/collections/TabDocView.tsx        |   2 +
 .../CollectionMulticolumnView.tsx                  |  11 +-
 .../CollectionMultirowView.tsx                     |  11 +-
 src/client/views/nodes/DocumentView.tsx            |   6 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   1 -
 11 files changed, 125 insertions(+), 60 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 1ebb5365a..1e42b9073 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -70,6 +70,7 @@ export class CurrentUserUtils {
     @observable public static GuestDashboard: Doc | undefined;
     @observable public static GuestMobile: Doc | undefined;
     @observable public static propertiesWidth: number = 0;
+    @observable public static headerBarHeight: number = 0;
     @observable public static searchPanelWidth: number = 0;
 
     // sets up the default User Templates - slideView,  headerView
@@ -1003,6 +1004,7 @@ export class CurrentUserUtils {
         doc.savedFilters = new List<Doc>();
         doc.filterDocCount = 0;
         doc.freezeChildren = "remove|add";
+        doc.myHeaderBarDoc = doc.myHeaderBarDoc ?? Docs.Create.MulticolumnDocument([], { title: "header bar", system: true });
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
         this.setupDocTemplates(doc); // sets up the template menu of templates
         this.setupImportSidebar(doc); // sets up the import sidebar
@@ -1210,6 +1212,7 @@ export class CurrentUserUtils {
     public static get DockedBtns() { return Cast(Doc.UserDoc().dockedBtns, Doc, null); }
     public static get MySearchPanelDoc() { return Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null); }
     public static get ActiveDashboard() { return Cast(Doc.UserDoc().activeDashboard, Doc, null); }
+    public static get MyHeaderBarDoc() { return Cast(Doc.UserDoc().myHeaderBarDoc, Doc, null); }
     public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); }
     public static get MyRecentlyClosed() { return Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); }
     public static get MyDashboards() { return Cast(Doc.UserDoc().myDashboards, Doc, null); }
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index ae3a91c4d..29e088143 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -511,11 +511,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                     <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${this.selectionTitle}`}</span>
                 </div>;
 
-        let inMainMenuPanel = false;
-        for (let node = seldoc.ContentDiv; node && !inMainMenuPanel; node = node?.parentNode as any) {
-            if (node.className === "mainView-mainContent") inMainMenuPanel = true;
-        }
-        const leftBounds = inMainMenuPanel ? 0 : this.props.boundsLeft;
+
+        const leftBounds = this.props.boundsLeft;
         const topBounds = LightboxView.LightboxDoc ? 0 : this.props.boundsTop;
         bounds.x = Math.max(leftBounds, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2;
         bounds.y = Math.max(topBounds, bounds.y - this._resizeBorderWidth / 2 - this._titleHeight) + this._resizeBorderWidth / 2 + this._titleHeight;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 09a57843c..69e394790 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -33,7 +33,7 @@ import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOpti
 import { CollectionLinearView } from './collections/collectionLinear';
 import { CollectionMenu } from './collections/CollectionMenu';
 import { TreeViewType } from './collections/CollectionTreeView';
-import { CollectionViewType } from './collections/CollectionView';
+import { CollectionView, CollectionViewType } from './collections/CollectionView';
 import "./collections/TreeView.scss";
 import { ComponentDecorations } from './ComponentDecorations';
 import { ContextMenu } from './ContextMenu';
@@ -81,14 +81,19 @@ export class MainView extends React.Component {
 
     @computed private get dashboardTabHeight() { return 27; } //  27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js
     @computed private get topOfDashUI() { return Number(DASHBOARD_SELECTOR_HEIGHT.replace("px", "")); }
-    @computed private get topOfMainDoc() { return this.topOfDashUI + this.topMenuHeight(); }
+    @computed private get topOfHeaderBarDoc() { return this.topOfDashUI + this.topMenuHeight(); }
+    @computed private get topOfSidebarDoc() { return this.topOfDashUI + this.topMenuHeight(); }
+    @computed private get topOfMainDoc() { return this.topOfDashUI + this.topMenuHeight() + this.headerBarDocHeight(); }
     @computed private get topOfMainDocContent() { return this.topOfMainDoc + this.dashboardTabHeight; }
     @computed private get leftScreenOffsetOfMainDocView() { return this.leftMenuWidth() - 2; }
     @computed private get userDoc() { return Doc.UserDoc(); }
     @computed private get colorScheme() { return StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme); }
     @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; }
+    @computed private get headerBarDoc() { return this.userDoc ? CurrentUserUtils.MyHeaderBarDoc : CurrentUserUtils.MyHeaderBarDoc; }
     @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs?.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
 
+    headerBarDocWidth = () => this._dashUIWidth;
+    headerBarDocHeight = () => CurrentUserUtils.headerBarHeight ?? 0;
     topMenuHeight = () => 35;
     topMenuWidth = returnZero; // value is ignored ...
     leftMenuWidth = () => Number(LEFT_MENU_WIDTH.replace("px", ""));
@@ -98,7 +103,7 @@ export class MainView extends React.Component {
     propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, CurrentUserUtils.propertiesWidth || 0));
     propertiesHeight = () => this._dashUIHeight;
     mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth();
-    mainDocViewHeight = () => this._dashUIHeight;
+    mainDocViewHeight = () => this._dashUIHeight - this.headerBarDocHeight();
 
     componentDidMount() {
         document.getElementById("root")?.addEventListener("scroll", e => ((ele) => ele.scrollLeft = ele.scrollTop = 0)(document.getElementById("root")!));
@@ -177,8 +182,8 @@ export class MainView extends React.Component {
         fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
         fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any,
         fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle as any,
-        fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer as any, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp,
-        fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
+        fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, fa.faAngleDoubleDown, fa.faAngleDoubleLeft, fa.faAngleDoubleUp, faBuffer as any, fa.faExpand, fa.faUndo,
+        fa.faSlidersH, fa.faAngleUp, fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
         fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo,
         fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
         fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
@@ -276,33 +281,67 @@ export class MainView extends React.Component {
         return () => this._exploreMode ? ScriptField.MakeScript("CollectionBrowseClick(documentView, clientX, clientY)",
             { documentView: "any", clientX: "number", clientY: "number" })! : undefined;
     }
-
+    headerBarScreenXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.headerBarDocHeight(), 1);
+
+    headerContentActive = () => SnappingManager.GetIsDragging() ? false : true;
+    @computed get headerBarDocView() {
+        console.log(this.headerBarDoc)
+        return <div style={{ height: this.headerBarDocHeight() }}>
+            <DocumentView key="headerBarDoc"
+                Document={this.headerBarDoc}
+                DataDoc={undefined}
+                addDocument={undefined}
+                addDocTab={this.addDocTabFunc}
+                pinToPres={emptyFunction}
+                docViewPath={returnEmptyDoclist}
+                styleProvider={DefaultStyleProvider}
+                rootSelected={returnTrue}
+                removeDocument={returnFalse}
+                fitContentsToDoc={returnTrue}
+                isContentActive={this.headerContentActive}
+                ScreenToLocalTransform={this.headerBarScreenXf}
+                hideResizeHandles={true}
+                PanelWidth={this.headerBarDocWidth}
+                PanelHeight={this.headerBarDocHeight}
+                renderDepth={0}
+                focus={DocUtils.DefaultFocus}
+                whenChildContentsActiveChanged={emptyFunction}
+                bringToFront={emptyFunction}
+                docFilters={returnEmptyFilter}
+                docRangeFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
+                ContainingCollectionView={undefined}
+                ContainingCollectionDoc={undefined}
+            /></div>;
+    }
     @computed get mainDocView() {
-        return <DocumentView key="main"
-            Document={this.mainContainer!}
-            DataDoc={undefined}
-            addDocument={undefined}
-            addDocTab={this.addDocTabFunc}
-            pinToPres={emptyFunction}
-            docViewPath={returnEmptyDoclist}
-            styleProvider={undefined}
-            rootSelected={returnTrue}
-            isContentActive={returnTrue}
-            removeDocument={undefined}
-            ScreenToLocalTransform={Transform.Identity}
-            PanelWidth={this.mainDocViewWidth}
-            PanelHeight={this.mainDocViewHeight}
-            focus={DocUtils.DefaultFocus}
-            whenChildContentsActiveChanged={emptyFunction}
-            bringToFront={emptyFunction}
-            docFilters={returnEmptyFilter}
-            docRangeFilters={returnEmptyFilter}
-            searchFilterDocs={returnEmptyDoclist}
-            ContainingCollectionView={undefined}
-            ContainingCollectionDoc={undefined}
-            suppressSetHeight={true}
-            renderDepth={-1}
-        />;
+        return <>
+            {this.headerBarDocView}
+            <DocumentView key="main"
+                Document={this.mainContainer!}
+                DataDoc={undefined}
+                addDocument={undefined}
+                addDocTab={this.addDocTabFunc}
+                pinToPres={emptyFunction}
+                docViewPath={returnEmptyDoclist}
+                styleProvider={undefined}
+                rootSelected={returnTrue}
+                isContentActive={returnTrue}
+                removeDocument={undefined}
+                ScreenToLocalTransform={Transform.Identity}
+                PanelWidth={this.mainDocViewWidth}
+                PanelHeight={this.mainDocViewHeight}
+                focus={DocUtils.DefaultFocus}
+                whenChildContentsActiveChanged={emptyFunction}
+                bringToFront={emptyFunction}
+                docFilters={returnEmptyFilter}
+                docRangeFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
+                ContainingCollectionView={undefined}
+                ContainingCollectionDoc={undefined}
+                suppressSetHeight={true}
+                renderDepth={-1}
+            /></>;
     }
 
     @computed get dockingContent() {
@@ -331,7 +370,7 @@ export class MainView extends React.Component {
             this.closeFlyout);
     }
 
-    sidebarScreenToLocal = () => new Transform(0, -this.topOfMainDoc, 1);
+    sidebarScreenToLocal = () => new Transform(0, -this.topOfSidebarDoc, 1);
     mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftScreenOffsetOfMainDocView, 0);
     addDocTabFunc = (doc: Doc, location: string): boolean => {
         const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":");
@@ -461,6 +500,16 @@ export class MainView extends React.Component {
         </>;
     }
 
+    @computed get headerBar() {
+        return !this.userDoc ? (null) :
+            <div className="mainView-dashboardArea" style={{
+                height: this.headerBarDocHeight(),
+                width: "100%",
+            }} >
+                {this.headerBarDocView}
+            </div>;
+    }
+
     @computed get mainDashboardArea() {
         return !this.userDoc ? (null) :
             <div className="mainView-dashboardArea" ref={r => {
@@ -620,7 +669,7 @@ export class MainView extends React.Component {
             <CaptureManager />
             <GroupManager />
             <GoogleAuthenticationManager />
-            <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} />
+            <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfHeaderBarDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} />
             <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} />
             <TopBar />
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 7ac40fc8e..c44443264 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -158,12 +158,12 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                         (doc?._viewType === CollectionViewType.Pile || Doc.IsSystem(doc) ? (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY) : // system docs (seen in treeView) get a grayish background
                             doc.annotationOn ? "#00000015" : // faint interior for collections on PDFs, images, etc
                                 (doc?._isGroup ? undefined :
-                                    StrCast((props?.renderDepth || 0) > 0 ?
+                                    Cast((props?.renderDepth || 0) > 0 ?
                                         Doc.UserDoc().activeCollectionNestedBackground :
-                                        Doc.UserDoc().activeCollectionBackground ?? (darkScheme() ?
+                                        Doc.UserDoc().activeCollectionBackground, "string") ?? (darkScheme() ?
                                             Colors.BLACK :
                                             "linear-gradient(#065fff, #85c1f9)"))
-                                ));
+                        );
                     break;
                 //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)";
                 default: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break;
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index c322d9e16..96029a8e4 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -84,11 +84,11 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
     }
 
     @action
-    toggleProperties = () => {
-        if (CurrentUserUtils.propertiesWidth > 0) {
-            CurrentUserUtils.propertiesWidth = 0;
+    toggleTopBar = () => {
+        if (CurrentUserUtils.headerBarHeight > 0) {
+            CurrentUserUtils.headerBarHeight = 0;
         } else {
-            CurrentUserUtils.propertiesWidth = 250;
+            CurrentUserUtils.headerBarHeight = 60;
         }
     }
 
@@ -137,14 +137,13 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
 
     render() {
 
-        const propIcon = CurrentUserUtils.propertiesWidth > 0 ? "angle-double-right" : "angle-double-left";
-        const propTitle = CurrentUserUtils.propertiesWidth > 0 ? "Close Properties Panel" : "Open Properties Panel";
+        const propIcon = CurrentUserUtils.headerBarHeight > 0 ? "angle-double-up" : "angle-double-down";
+        const propTitle = CurrentUserUtils.headerBarHeight > 0 ? "Close Header Bar" : "Open Header Bar";
 
-        const prop = <Tooltip title={<div className="dash-tooltip">{propTitle}</div>} key="properties" placement="bottom">
+        const prop = <Tooltip title={<div className="dash-tooltip">{propTitle}</div>} key="topar" placement="bottom">
             <div className="collectionMenu-hardCodedButton"
                 style={{ backgroundColor: CurrentUserUtils.propertiesWidth > 0 ? Colors.MEDIUM_BLUE : undefined }}
-                key="properties"
-                onPointerDown={this.toggleProperties}>
+                onPointerDown={this.toggleTopBar}>
                 <FontAwesomeIcon icon={propIcon} size="lg" />
             </div>
         </Tooltip>;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 19485a0eb..4f92e305e 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -6,7 +6,7 @@ import { Doc, DocListCast } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { ObjectField } from '../../../fields/ObjectField';
 import { ScriptField } from '../../../fields/ScriptField';
-import { Cast, ScriptCast, StrCast } from '../../../fields/Types';
+import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
 import { returnEmptyString } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
@@ -237,6 +237,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
 
     bodyPanelWidth = () => this.props.PanelWidth();
 
+    childHideResizeHandles = () => this.props.childHideResizeHandles?.() ?? BoolCast(this.Document.childHideResizeHandles);
+    childHideDecorationTitle = () => this.props.childHideDecorationTitle?.() ?? BoolCast(this.Document.childHideDecorationTitle);
     childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null);
     @computed get childLayoutString() { return StrCast(this.rootDoc.childLayoutString); }
 
@@ -258,6 +260,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             ScreenToLocalTransform: this.screenToLocalTransform,
             childLayoutTemplate: this.childLayoutTemplate,
             childLayoutString: this.childLayoutString,
+            childHideResizeHandles: this.childHideResizeHandles,
+            childHideDecorationTitle: this.childHideDecorationTitle,
             CollectionView: this,
         };
         return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 43f31813c..0070d0b9d 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -104,6 +104,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 closeWrap.id = "lm_closeWrap";
                 closeWrap.onclick = (e: MouseEvent) => {
                     tab.header.parent.contentItem.remove();
+                    Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", tab.DashDoc);
                     Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true);
                 };
                 const docIcon = <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} />;
@@ -179,6 +180,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             tab.closeElement.off('click') //unbind the current click handler
                 .click(function () {
                     Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
+                    Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", doc);
                     Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", doc, undefined, true, true);
                     SelectionManager.DeselectAll();
                     UndoManager.RunInBatch(() => tab.contentItem.remove(), "delete tab");
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index b6b59a2d6..66cc3613d 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -241,7 +241,9 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         }
         return this.props.addDocTab(doc, where);
     }
-    isContentActive = () => this.props.isSelected() || this.props.isContentActive();
+    isContentActive = () => this.props.isSelected() ? true : undefined;
+    isChildContentActive = () => this.props.isContentActive?.() === false ? false : undefined;
+    isChildDocumentActive = () => (this.props.childDocumentsActive?.() && this.props.isDocumentActive?.()) || this.isContentActive() || this.props.isContentActive();
     getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
         return <DocumentView
             Document={layout}
@@ -252,7 +254,11 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             LayoutTemplateString={this.props.childLayoutString}
             freezeDimensions={this.props.childFreezeDimensions}
             renderDepth={this.props.renderDepth + 1}
-            isContentActive={emptyFunction}
+            isContentActive={this.isChildContentActive}
+            isDocumentActive={this.isChildDocumentActive}
+            hideResizeHandles={this.props.childHideResizeHandles?.()}
+            hideDecorationTitle={this.props.childHideDecorationTitle?.()}
+            fitContentsToDoc={this.props.fitContentsToDoc}
             PanelWidth={width}
             PanelHeight={height}
             rootSelected={this.rootSelected}
@@ -261,7 +267,6 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             onDoubleClick={this.onChildDoubleClickHandler}
             suppressSetHeight={true}
             ScreenToLocalTransform={dxf}
-            isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
             focus={this.props.focus}
             docFilters={this.childDocFilters}
             docRangeFilters={this.childDocRangeFilters}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 4074f8d69..8218c49b5 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -241,7 +241,9 @@ export class CollectionMultirowView extends CollectionSubView() {
         }
         return this.props.addDocTab(doc, where);
     }
-    isContentActive = () => this.props.isSelected() || this.props.isContentActive();
+    isContentActive = () => this.props.isSelected() ? true : undefined;
+    isChildContentActive = () => this.props.isContentActive?.() === false ? false : undefined;
+    isChildDocumentActive = () => (this.props.childDocumentsActive?.() && this.props.isDocumentActive?.()) || this.isContentActive() || this.props.isContentActive();
     getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
         return <DocumentView
             Document={layout}
@@ -259,10 +261,13 @@ export class CollectionMultirowView extends CollectionSubView() {
             onClick={this.onChildClickHandler}
             onDoubleClick={this.onChildDoubleClickHandler}
             ScreenToLocalTransform={dxf}
-            isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
+            isContentActive={this.isChildContentActive}
+            isDocumentActive={this.isChildDocumentActive}
+            hideResizeHandles={this.props.childHideResizeHandles?.()}
+            hideDecorationTitle={this.props.childHideDecorationTitle?.()}
+            fitContentsToDoc={this.props.fitContentsToDoc}
             focus={this.props.focus}
             docFilters={this.childDocFilters}
-            isContentActive={emptyFunction}
             docRangeFilters={this.childDocRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f7312e59c..54bb66f7f 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -747,7 +747,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     const alias = Doc.MakeAlias(this.rootDoc);
                     alias.layout = FormattedTextBox.LayoutString("newfield");
                     alias.title = "newfield";
-                    alias._yMargin = 10;
                     alias._height = 35;
                     alias._width = 100;
                     alias.syncLayoutFieldWithTitle = true;
@@ -881,7 +880,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
         return <div className="documentView-contentsView"
             style={{
-                pointerEvents: this.props.pointerEvents?.() as any ?? this.rootDoc.layoutKey === "layout_icon" ? "none" : (this.rootDoc.type !== DocumentType.INK && ((this.props.contentPointerEvents as any) || (this.isContentActive())) ? "all" : "none"),
+                pointerEvents: this.props.pointerEvents?.() as any ?? this.rootDoc.layoutKey === "layout_icon" ? "none" :
+                    this.props.contentPointerEvents as any ? this.props.contentPointerEvents as any :
+                        this.rootDoc.type !== DocumentType.INK && this.isContentActive() ? "all" :
+                            "none",
                 height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
             }}>
             {!this._retryThumb || !this.thumbShown() ? (null) :
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 0e29461a6..822bc996b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -635,7 +635,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 }), icon: icon
             });
         });
-        !Doc.UserDoc().noviceMode && changeItems.push({ description: "FreeForm", event: () => DocUtils.makeCustomViewClicked(this.rootDoc, Docs.Create.FreeformDocument, "freeform"), icon: "eye" });
         const highlighting: ContextMenuProps[] = [];
         const noviceHighlighting = ["Audio Tags", "My Text", "Text from Others"];
         const expertHighlighting = [...noviceHighlighting, "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"];
-- 
cgit v1.2.3-70-g09d2


From d50b851d8e92797d214fc35cf243bfb29e970b36 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 23 May 2022 21:58:26 -0400
Subject: simplified dashboards by getting rid of confusing
 on-screen/off-screen tabs.  Now off screen tabs appear in the header bar
 which is not currently specific to a dashboard.

---
 src/client/documents/Documents.ts                  |  4 +-
 src/client/util/CurrentUserUtils.ts                | 33 +--------------
 src/client/views/MainView.tsx                      |  4 +-
 .../views/collections/CollectionDockingView.tsx    | 48 +++++++++++++++++-----
 src/client/views/collections/TabDocView.tsx        |  3 +-
 5 files changed, 43 insertions(+), 49 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 6fa467368..c3b6953b5 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -905,9 +905,7 @@ export namespace Docs {
         }
 
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
-            const tabs = TreeDocument(documents, { title: "On-Screen Tabs", childDontRegisterViews: true, freezeChildren: "remove|add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", _fitWidth: true, system: true, isFolder: true });
-            const all = TreeDocument([], { title: "Off-Screen Tabs", childDontRegisterViews: true, freezeChildren: "add", treeViewExpandedViewLock: true, treeViewExpandedView: "data", system: true, isFolder: true });
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List([tabs, all]), { freezeChildren: "remove|add", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { freezeChildren: "remove|add", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
         }
 
         export function DirectoryImportDocument(options: DocumentOptions = {}) {
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 1e42b9073..9212d8ed9 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1172,22 +1172,8 @@ export class CurrentUserUtils {
         freeformDoc.context = dashboardDoc;
 
         // switching the tabs from the datadoc to the regular doc
-        const dashboardTabs = dashboardDoc[DataSym].data;
-        dashboardDoc[DataSym].data = new List<Doc>();
-        dashboardDoc.data = dashboardTabs;
-
-        // collating all docs on the dashboard to make a data-all field
-        const allDocs = new List<Doc>();
-        const allDocs2 = new List<Doc>(); // Array.from, spread, splice all cause so stack or acl issues for some reason
-        DocListCast(dashboardTabs).forEach(doc => {
-            const tabDocs = DocListCast(doc.data);
-            allDocs.push(...tabDocs);
-            allDocs2.push(...tabDocs);
-        });
-        dashboardDoc[DataSym]["data-all"] = allDocs;
-        dashboardDoc["data-all"] = allDocs2;
-        DocListCast(dashboardDoc.data).forEach(doc => doc.dashboard = dashboardDoc);
-        DocListCast(dashboardDoc.data)[1].data = ComputedField.MakeFunction(`dynamicOffScreenDocs(self.dashboard)`) as any;
+        const dashboardTabs = DocListCast(dashboardDoc[DataSym].data);
+        dashboardDoc.data = new List<Doc>(dashboardTabs);
 
         userDoc.activePresentation = presentation;
 
@@ -1258,21 +1244,6 @@ ScriptingGlobals.add(function addToDashboards(dashboard: Doc) {
 },
     "adds Dashboard to set of Dashboards");
 
-/**
- * Dynamically computes which docs should be rendered in the off-screen tabs tree of a dashboard.
- */
-ScriptingGlobals.add(function dynamicOffScreenDocs(dashboard: Doc) {
-    if (dashboard[DataSym] instanceof Doc) {
-        const allDocs = DocListCast(dashboard["data-all"]);
-        const onScreenTab = DocListCast(dashboard.data)[0];
-        const onScreenDocs = DocListCast(onScreenTab.data);
-        return new List<Doc>(allDocs.reduce((result: Doc[], doc) => {
-            !onScreenDocs.includes(doc) && !onScreenDocs.includes(doc.aliasOf as Doc) && (result.push(doc));
-            return result;
-        }, []));
-    }
-    return [];
-});
 ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkParent?: boolean) {
     let selected = SelectionManager.Docs().length ? SelectionManager.Docs()[0] : undefined;
     if (selected && checkParent) {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 69e394790..f3dc2cf85 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -92,7 +92,7 @@ export class MainView extends React.Component {
     @computed private get headerBarDoc() { return this.userDoc ? CurrentUserUtils.MyHeaderBarDoc : CurrentUserUtils.MyHeaderBarDoc; }
     @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs?.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
 
-    headerBarDocWidth = () => this._dashUIWidth;
+    headerBarDocWidth = () => this.mainDocViewWidth();
     headerBarDocHeight = () => CurrentUserUtils.headerBarHeight ?? 0;
     topMenuHeight = () => 35;
     topMenuWidth = returnZero; // value is ignored ...
@@ -102,7 +102,7 @@ export class MainView extends React.Component {
     leftMenuFlyoutHeight = () => this._dashUIHeight;
     propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, CurrentUserUtils.propertiesWidth || 0));
     propertiesHeight = () => this._dashUIHeight;
-    mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth();
+    mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth() - this.leftMenuFlyoutWidth();
     mainDocViewHeight = () => this._dashUIHeight - this.headerBarDocHeight();
 
     componentDidMount() {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 30f60bba6..1d65256a6 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -81,6 +81,8 @@ export class CollectionDockingView extends CollectionSubView() {
 
     tabItemDropped = () => DragManager.CompleteWindowDrag?.(false);
     tabDragStart = (proxy: any, finishDrag?: (aborted: boolean) => void) => {
+        const dashDoc = proxy?._contentItem?.tab?.DashDoc as Doc;
+        dashDoc && (DragManager.DocDragData = new DragManager.DocumentDragData([proxy._contentItem.tab.DashDoc]));
         DragManager.CompleteWindowDrag = (aborted: boolean) => {
             if (aborted) {
                 proxy._dragListener.AbortDrag();
@@ -90,6 +92,8 @@ export class CollectionDockingView extends CollectionSubView() {
                     this.setupGoldenLayout(); // restore golden layout to where it was before the drag (this is a no-op when using StartOtherDrag because the proxy dragged item was never in the golden layout)
                 }
                 DragManager.CompleteWindowDrag = undefined;
+            } else {
+                DragManager.DocDragData?.droppedDocuments.forEach(doc => this.addToTabDocList(doc));
             }
             finishDrag?.(aborted);
         };
@@ -100,12 +104,43 @@ export class CollectionDockingView extends CollectionSubView() {
         this.stateChanged();
     }
 
+    addToTabDocList = (document: Doc) => {
+        const instance = CollectionDockingView.Instance;
+        if (instance) {
+            const docList = DocListCast(instance.props.Document[DataSym].data);
+            // adds the doc of the newly created tab to the data-all field if it doesn't already include that doc or one of its aliases
+            !docList.includes(document) && !docList.includes(document.aliasOf as Doc) && Doc.AddDocToList(instance.props.Document[DataSym], "data", document);
+            // adds an alias of the doc to the data-all field of the layoutdocs of the aliases
+            DocListCast(instance.props.Document.aliases).forEach(alias => {
+                const aliasDocList = DocListCast(alias.data);
+                // if aliasDocList contains the alias, don't do anything
+                // otherwise add the original or an alias depending on whether the doc you're looking at is the current doc or a different alias
+                !DocListCast(document.aliases).some(a => aliasDocList.includes(a)) && Doc.AddDocToList(alias, "data", document);//alias !== instance.props.Document ? Doc.MakeAlias(document) : document);
+            });
+        }
+    }
+    removeFromTabDocList = (document: Doc) => {
+        const instance = CollectionDockingView.Instance;
+        if (instance) {
+            // adds the doc of the newly created tab to the data-all field if it doesn't already include that doc or one of its aliases
+            Doc.RemoveDocFromList(instance.props.Document[DataSym], "data", document);
+            // adds an alias of the doc to the data-all field of the layoutdocs of the aliases
+            DocListCast(instance.props.Document.aliases).forEach(alias => {
+                // if aliasDocList contains the alias, don't do anything
+                // otherwise add the original or an alias depending on whether the doc you're looking at is the current doc or a different alias
+                Doc.RemoveDocFromList(alias, "data", document);//alias !== instance.props.Document ? Doc.MakeAlias(document) : document);
+            });
+            document.type !== DocumentType.KVP && Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", document);
+        }
+    }
+
     @undoBatch
     public static CloseSplit(document: Opt<Doc>, panelName?: string): boolean {
         const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find((tab) => panelName ? tab.contentItem.config.props.panelName === panelName : tab.DashDoc === document);
         if (tab) {
             const j = tab.header.parent.contentItems.indexOf(tab.contentItem);
             if (j !== -1) {
+                CollectionDockingView.Instance.removeFromTabDocList(tab.dashDoc as Doc);
                 tab.header.parent.contentItems[j].remove();
                 return CollectionDockingView.Instance.layoutChanged();
             }
@@ -185,16 +220,7 @@ export class CollectionDockingView extends CollectionSubView() {
         const instance = CollectionDockingView.Instance;
         if (!instance) return false;
         else {
-            const docList = DocListCast(instance.props.Document[DataSym]["data-all"]);
-            // adds the doc of the newly created tab to the data-all field if it doesn't already include that doc or one of its aliases
-            !docList.includes(document) && !docList.includes(document.aliasOf as Doc) && Doc.AddDocToList(instance.props.Document[DataSym], "data-all", document);
-            // adds an alias of the doc to the data-all field of the layoutdocs of the aliases
-            DocListCast(instance.props.Document[DataSym].aliases).forEach(alias => {
-                const aliasDocList = DocListCast(alias["data-all"]);
-                // if aliasDocList contains the alias, don't do anything
-                // otherwise add the original or an alias depending on whether the doc you're looking at is the current doc or a different alias
-                !DocListCast(document.aliases).some(a => aliasDocList.includes(a)) && Doc.AddDocToList(alias, "data-all", document);//alias !== instance.props.Document ? Doc.MakeAlias(document) : document);
-            });
+            instance.addToTabDocList(document);
         }
         const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName);
 
@@ -477,7 +503,7 @@ export class CollectionDockingView extends CollectionSubView() {
                 //if (confirm('really close this?')) {
                 if (!stack.parent.parent.isRoot || stack.parent.contentItems.length > 1) {
                     stack.remove();
-                    stack.contentItems.forEach((contentItem: any) => Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", contentItem.tab.DashDoc, undefined, true, true));
+                    stack.contentItems.forEach((contentItem: any) => CollectionDockingView.Instance.removeFromTabDocList(contentItem.tab.DashDoc));
                 } else {
                     alert('cant delete the last stack');
                 }
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 0070d0b9d..88d14fdfe 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -180,8 +180,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             tab.closeElement.off('click') //unbind the current click handler
                 .click(function () {
                     Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
-                    Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", doc);
-                    Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", doc, undefined, true, true);
+                    CollectionDockingView.Instance?.removeFromTabDocList(doc);
                     SelectionManager.DeselectAll();
                     UndoManager.RunInBatch(() => tab.contentItem.remove(), "delete tab");
                 });
-- 
cgit v1.2.3-70-g09d2


From 52aa355e4af3f1c5f785dcf1536418119176e5a2 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 23 May 2022 22:13:00 -0400
Subject: tweaked pointerEvents for headerBar to stay on if there are no
 documents in the header so that you can still drag and drop onto it.

---
 src/client/views/MainView.tsx                                         | 3 +--
 .../collections/collectionMulticolumn/CollectionMulticolumnView.tsx   | 4 ++--
 2 files changed, 3 insertions(+), 4 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index f3dc2cf85..dccbdb313 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -283,9 +283,8 @@ export class MainView extends React.Component {
     }
     headerBarScreenXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.headerBarDocHeight(), 1);
 
-    headerContentActive = () => SnappingManager.GetIsDragging() ? false : true;
+    headerContentActive = () => SnappingManager.GetIsDragging() && DocListCast(CurrentUserUtils.MyHeaderBarDoc.data).length ? false : true;
     @computed get headerBarDocView() {
-        console.log(this.headerBarDoc)
         return <div style={{ height: this.headerBarDocHeight() }}>
             <DocumentView key="headerBarDoc"
                 Document={this.headerBarDoc}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 66cc3613d..345ac7854 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -324,13 +324,13 @@ export class CollectionMulticolumnView extends CollectionSubView() {
 
     render(): JSX.Element {
         return (
-            <div className={"collectionMulticolumnView_contents"}
+            <div className={"collectionMulticolumnView_contents"} ref={this.createDashEventsTarget}
                 style={{
                     width: `calc(100% - ${2 * NumCast(this.props.Document._xMargin)}px)`,
                     height: `calc(100% - ${2 * NumCast(this.props.Document._yMargin)}px)`,
                     marginLeft: NumCast(this.props.Document._xMargin), marginRight: NumCast(this.props.Document._xMargin),
                     marginTop: NumCast(this.props.Document._yMargin), marginBottom: NumCast(this.props.Document._yMargin)
-                }} ref={this.createDashEventsTarget}>
+                }} >
                 {this.contents}
             </div>
         );
-- 
cgit v1.2.3-70-g09d2


From dd583ba1728d2b941cd253f68636a43b5b6c1930 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 24 May 2022 09:08:04 -0400
Subject: hide resize handles on headerBar items.

---
 src/client/views/MainView.tsx           | 1 +
 src/client/views/nodes/DocumentView.tsx | 2 ++
 2 files changed, 3 insertions(+)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index dccbdb313..4b7b9fc23 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -299,6 +299,7 @@ export class MainView extends React.Component {
                 fitContentsToDoc={returnTrue}
                 isContentActive={this.headerContentActive}
                 ScreenToLocalTransform={this.headerBarScreenXf}
+                childHideResizeHandles={returnTrue}
                 hideResizeHandles={true}
                 PanelWidth={this.headerBarDocWidth}
                 PanelHeight={this.headerBarDocHeight}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 54bb66f7f..6b0b92889 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -123,6 +123,8 @@ export interface DocumentViewSharedProps {
     PanelWidth: () => number;
     PanelHeight: () => number;
     docViewPath: () => DocumentView[];
+    childHideDecorationTitle?: () => boolean;
+    childHideResizeHandles?: () => boolean;
     dataTransition?: string; // specifies animation transition - used by collectionPile and potentially other layout engines when changing the size of documents so that the change won't be abrupt
     styleProvider: Opt<StyleProviderFunc>;
     focus: DocFocusFunc;
-- 
cgit v1.2.3-70-g09d2


From 097998abda6857a498f6cfc8a7214a18b09b8451 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 24 May 2022 13:10:33 -0400
Subject: fixed TabDocView's screenToLocal xf when headerBar opens/closes. 
 changed webBox to update thumbnails when native dims change.  cleaned up
 content dragging/activation for multiRow/col for headerBar

---
 src/client/views/MainView.tsx                      |  4 ++--
 src/client/views/collections/TabDocView.tsx        |  3 +++
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../CollectionMulticolumnView.tsx                  |  9 ++++----
 .../CollectionMultirowView.tsx                     | 11 +++++-----
 src/client/views/nodes/WebBox.tsx                  | 24 +++++++++++++++++-----
 6 files changed, 34 insertions(+), 19 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 4b7b9fc23..183efc944 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -283,7 +283,6 @@ export class MainView extends React.Component {
     }
     headerBarScreenXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.headerBarDocHeight(), 1);
 
-    headerContentActive = () => SnappingManager.GetIsDragging() && DocListCast(CurrentUserUtils.MyHeaderBarDoc.data).length ? false : true;
     @computed get headerBarDocView() {
         return <div style={{ height: this.headerBarDocHeight() }}>
             <DocumentView key="headerBarDoc"
@@ -297,7 +296,8 @@ export class MainView extends React.Component {
                 rootSelected={returnTrue}
                 removeDocument={returnFalse}
                 fitContentsToDoc={returnTrue}
-                isContentActive={this.headerContentActive}
+                isDocumentActive={returnTrue} // headerBar is always documentActive (ie, the docView gets pointer events)
+                isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive
                 ScreenToLocalTransform={this.headerBarScreenXf}
                 childHideResizeHandles={returnTrue}
                 hideResizeHandles={true}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 88d14fdfe..a2ba504dc 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -337,7 +337,9 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         }
     }
     active = () => this._isActive;
+    @observable _forceInvalidateScreenToLocal = 0;
     ScreenToLocalTransform = () => {
+        this._forceInvalidateScreenToLocal;
         const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont?.children?.[0] as HTMLElement);
         return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY);
     }
@@ -408,6 +410,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 if (this._mainCont = ref) {
                     (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document);
                     DocServer.GetRefField(this.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.tab && this.init(this.tab, this._document)));
+                    new _global.ResizeObserver(action((entries: any) => this._forceInvalidateScreenToLocal++)).observe(ref);
                 }
             }} >
                 {this.docView}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 3b32cf57d..dcff32d04 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1217,8 +1217,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             docFilters={this.childDocFilters}
             docRangeFilters={this.childDocRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
+            isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
             isContentActive={emptyFunction}
-            isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
             focus={this.focusDocument}
             addDocTab={this.addDocTab}
             addDocument={this.props.addDocument}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 345ac7854..e2dfb25e2 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -4,7 +4,7 @@ import * as React from "react";
 import { Doc, DocListCast } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnFalse, returnTrue } from '../../../../Utils';
+import { returnFalse } from '../../../../Utils';
 import { DragManager, dropActionType } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
@@ -241,9 +241,8 @@ export class CollectionMulticolumnView extends CollectionSubView() {
         }
         return this.props.addDocTab(doc, where);
     }
-    isContentActive = () => this.props.isSelected() ? true : undefined;
-    isChildContentActive = () => this.props.isContentActive?.() === false ? false : undefined;
-    isChildDocumentActive = () => (this.props.childDocumentsActive?.() && this.props.isDocumentActive?.()) || this.isContentActive() || this.props.isContentActive();
+    isContentActive = () => this.props.isSelected() || this.props.isContentActive();
+    isChildContentActive = () => this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false;
     getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
         return <DocumentView
             Document={layout}
@@ -255,7 +254,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             freezeDimensions={this.props.childFreezeDimensions}
             renderDepth={this.props.renderDepth + 1}
             isContentActive={this.isChildContentActive}
-            isDocumentActive={this.isChildDocumentActive}
+            isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
             hideResizeHandles={this.props.childHideResizeHandles?.()}
             hideDecorationTitle={this.props.childHideDecorationTitle?.()}
             fitContentsToDoc={this.props.fitContentsToDoc}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 8218c49b5..3010e36aa 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -4,7 +4,7 @@ import * as React from "react";
 import { Doc, DocListCast } from '../../../../fields/Doc';
 import { List } from '../../../../fields/List';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnFalse } from '../../../../Utils';
+import { returnFalse } from '../../../../Utils';
 import { DragManager, dropActionType } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
 import { undoBatch } from '../../../util/UndoManager';
@@ -241,10 +241,9 @@ export class CollectionMultirowView extends CollectionSubView() {
         }
         return this.props.addDocTab(doc, where);
     }
-    isContentActive = () => this.props.isSelected() ? true : undefined;
-    isChildContentActive = () => this.props.isContentActive?.() === false ? false : undefined;
-    isChildDocumentActive = () => (this.props.childDocumentsActive?.() && this.props.isDocumentActive?.()) || this.isContentActive() || this.props.isContentActive();
-    getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
+    isContentActive = () => this.props.isSelected() || this.props.isContentActive();
+    isChildContentActive = () => this.props.isSelected() || this.props.isAnyChildContentActive() ? true : false;
+    getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => {
         return <DocumentView
             Document={layout}
             DataDoc={layout.resolvedDataDoc as Doc}
@@ -262,7 +261,7 @@ export class CollectionMultirowView extends CollectionSubView() {
             onDoubleClick={this.onChildDoubleClickHandler}
             ScreenToLocalTransform={dxf}
             isContentActive={this.isChildContentActive}
-            isDocumentActive={this.isChildDocumentActive}
+            isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
             hideResizeHandles={this.props.childHideResizeHandles?.()}
             hideDecorationTitle={this.props.childHideDecorationTitle?.()}
             fitContentsToDoc={this.props.fitContentsToDoc}
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 2112c1d44..dbbcdc9de 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -75,7 +75,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); }
     @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); }
     @computed get webField() { return Cast(this.dataDoc[this.props.fieldKey], WebField)?.url; }
-    @computed get webThumb() { return this.props.thumbShown?.() && ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumbScrollTop === this.layoutDoc._scrollTop ? this.layoutDoc.thumb : undefined))?.url; }
+    @computed get webThumb() {
+        return this.props.thumbShown?.() &&
+            ImageCast(this.layoutDoc["thumb-frozen"],
+                ImageCast(this.layoutDoc.thumbScrollTop === this.layoutDoc._scrollTop && this.layoutDoc.thumbNativeWidth === NumCast(this.layoutDoc.nativeWidth) &&
+                    this.layoutDoc.thumbNativeHeight === NumCast(this.layoutDoc.nativeHeight) ? this.layoutDoc.thumb : undefined))?.url;
+    }
 
     constructor(props: any) {
         super(props);
@@ -114,20 +119,21 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     updateThumb = async () => {
         const imageBitmap = ImageCast(this.layoutDoc["thumb-frozen"])?.url.href;
         const scrollTop = NumCast(this.layoutDoc._scrollTop);
-        if (!this.lockout && this._iframe && !imageBitmap && scrollTop !== this.layoutDoc.thumbScrollTop) {
+        const nativeWidth = NumCast(this.layoutDoc.nativeWidth);
+        const nativeHeight = nativeWidth * this.props.PanelHeight() / this.props.PanelWidth();
+        if (!this.lockout && this._iframe && !imageBitmap && (scrollTop !== this.layoutDoc.thumbScrollTop || nativeWidth !== this.layoutDoc.thumbNativeWidth || nativeHeight !== this.layoutDoc.thumbNativeHeight)) {
             var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument);
             if (!htmlString) {
                 htmlString = await (await fetch(Utils.CorsProxy(this.webField!.href))).text();
             }
             this.layoutDoc.thumb = undefined;
             this.lockout = true; // lock to prevent multiple thumb updates.
-            const nativeWidth = NumCast(this.layoutDoc.nativeWidth);
             CreateImage(
                 this._webUrl.endsWith("/") ? this._webUrl.substring(0, this._webUrl.length - 1) : this._webUrl,
                 this._iframe.contentDocument?.styleSheets ?? [],
                 htmlString,
                 nativeWidth,
-                nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(),
+                nativeHeight,
                 scrollTop
             ).then
                 ((data_url: any) => {
@@ -136,6 +142,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                             this.lockout = false;
                             this.layoutDoc.thumb = new ImageField(returnedfilename);
                             this.layoutDoc.thumbScrollTop = scrollTop;
+                            this.layoutDoc.thumbNativeWidth = nativeWidth;
+                            this.layoutDoc.thumbNativeHeight = nativeHeight;
                         }), 500));
                 })
                 .catch(function (error: any) {
@@ -387,6 +395,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
                 this.layoutDoc.thumb = undefined;
                 this.layoutDoc.thumbScrollTop = undefined;
+                this.layoutDoc.thumbNativeWidth = undefined;
+                this.layoutDoc.thumbNativeHeight = undefined;
                 this.layoutDoc.scrollTop = this._outerRef.current.scrollTop = newScrollTop;
             } else if (this._outerRef.current) this._outerRef.current.scrollTop = newScrollTop;
         }), timeout);
@@ -464,6 +474,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 if (this._webPageHasBeenRendered) {
                     this.layoutDoc.thumb = undefined;
                     this.layoutDoc.thumbScrollTop = undefined;
+                    this.layoutDoc.thumbNativeWidth = undefined;
+                    this.layoutDoc.thumbNativeHeight = undefined;
                 }
                 future && (future.length = 0);
             }
@@ -596,7 +608,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
         setTimeout(action(() => {
             this._scrollHeight = Math.max(this._scrollHeight, this._iframe && this._iframe.contentDocument && this._iframe.contentDocument.body ? this._iframe.contentDocument.body.scrollHeight : 0);
-            if (this._initialScroll === undefined && !this._webPageHasBeenRendered) this.setScrollPos(NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop)));
+            if (this._initialScroll === undefined && !this._webPageHasBeenRendered) {
+                this.setScrollPos(NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop)));
+            }
             this._webPageHasBeenRendered = true;
         }));
         return view;
-- 
cgit v1.2.3-70-g09d2


From a01cd55030f549b1c4c207d23731a00e689989c3 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 25 May 2022 15:18:46 -0400
Subject: variety of small fixes for text / sharing.   made typed text default
 to 35 height so that  when shared it doesn't start big and jump to small. 
 changed permissions to be private until an Acl is set - this prevents private
 docs from flicking on momentarily when shared since fields are not
 distributed atomically.  added Shift/Alt/Ctrl Enter for freeform and stacking
 views to create neighboring docs.  fixed first typed char of text to have a
 user_mark. made shared text doc header overlap to prevent scrolling.

---
 src/client/DocServer.ts                            | 60 ++++++++++------------
 src/client/documents/Documents.ts                  |  2 +-
 src/client/util/CurrentUserUtils.ts                |  2 +-
 src/client/views/MainView.tsx                      |  4 +-
 src/client/views/StyleProvider.tsx                 |  5 +-
 .../views/collections/CollectionStackingView.tsx   | 22 ++++++++
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx | 16 +++---
 .../formattedText/ProsemirrorExampleTransfer.ts    | 14 +++--
 src/fields/util.ts                                 |  7 ++-
 10 files changed, 77 insertions(+), 57 deletions(-)

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

diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index e498a7cca..dbc4783d8 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -364,40 +364,36 @@ export namespace DocServer {
                 const proms: Promise<void>[] = [];
                 runInAction(() => {
                     for (const field of fields) {
-                        if (field !== undefined && field !== null && !_cache[field.id]) {
+                        const cached = _cache[field.id];
+                        if (!cached) {
                             // deserialize
-                            const cached = _cache[field.id];
-                            if (!cached) {
-                                const prom = SerializationHelper.Deserialize(field).then(deserialized => {
-                                    fieldMap[field.id] = deserialized;
-
-                                    //overwrite or delete any promises (that we inserted as flags
-                                    // to indicate that the field was in the process of being fetched). Now everything
-                                    // should be an actual value within or entirely absent from the cache.
-                                    if (deserialized !== undefined) {
-                                        _cache[field.id] = deserialized;
-                                    } else {
-                                        delete _cache[field.id];
-                                    }
-                                    return deserialized;
-                                });
-                                // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache)
-                                // we set the value at the field's id to a promise that will resolve to the field. 
-                                // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method).
-                                // The mapping in the .then call ensures that when other callers await these promises, they'll
-                                // get the resolved field
-                                _cache[field.id] = prom;
-
-                                // adds to a list of promises that will be awaited asynchronously
-                                proms.push(prom);
-                            } else if (cached instanceof Promise) {
-                                proms.push(cached as any);
-                            }
-                        } else if (_cache[field.id] instanceof Promise) {
-                            proms.push(_cache[field.id] as any);
-                            (_cache[field.id] as any).then((f: any) => fieldMap[field.id] = f);
+                            const prom = SerializationHelper.Deserialize(field).then(deserialized => {
+                                fieldMap[field.id] = deserialized;
+
+                                //overwrite or delete any promises (that we inserted as flags
+                                // to indicate that the field was in the process of being fetched). Now everything
+                                // should be an actual value within or entirely absent from the cache.
+                                if (deserialized !== undefined) {
+                                    _cache[field.id] = deserialized;
+                                } else {
+                                    delete _cache[field.id];
+                                }
+                                return deserialized;
+                            });
+                            // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache)
+                            // we set the value at the field's id to a promise that will resolve to the field. 
+                            // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method).
+                            // The mapping in the .then call ensures that when other callers await these promises, they'll
+                            // get the resolved field
+                            _cache[field.id] = prom;
+
+                            // adds to a list of promises that will be awaited asynchronously
+                            proms.push(prom);
+                        } else if (cached instanceof Promise) {
+                            proms.push(cached as any);
+                            cached.then((f: any) => fieldMap[field.id] = f);
                         } else if (field) {
-                            proms.push(_cache[field.id] as any);
+                            proms.push(cached as any);
                             fieldMap[field.id] = DocServer.GetCachedRefField(field.id) || field;
                         }
                     }
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index c3b6953b5..ed37c48dc 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -369,7 +369,7 @@ export namespace Docs {
             [DocumentType.RTF, {
                 layout: { view: FormattedTextBox, dataField: "text" },
                 options: {
-                    _height: 150, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, treeViewGrowsHorizontally: true,
+                    _height: 35, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, nativeHeightUnfrozen: true, treeViewGrowsHorizontally: true,
                     links: "@links(self)"
                 }
             }],
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index e4dc175bd..436237d6c 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1184,7 +1184,7 @@ export class CurrentUserUtils {
     public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) {
         const tbox = Docs.Create.TextDocument("", {
             _xMargin: noMargins ? 0 : undefined, _yMargin: noMargins ? 0 : undefined, annotationOn, docMaxAutoHeight: maxHeight, backgroundColor: backgroundColor,
-            _width: width || 200, _height: height || 100, x: x, y: y, _fitWidth: true, _autoHeight: true, title
+            _width: width || 200, _height: 35, x: x, y: y, _fitWidth: true, _autoHeight: true, title
         });
         const template = Doc.UserDoc().defaultTextLayout;
         if (template instanceof Doc) {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 183efc944..ff2857739 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -120,8 +120,8 @@ export class MainView extends React.Component {
         }
         this._sidebarContent.proto = undefined;
         if (!MainView.Live) {
-            DocServer.setPlaygroundFields(["dataTransition", "treeViewOpen", "autoHeight", "showSidebar", "sidebarWidthPercent", "viewTransition",
-                "panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "text-scrollHeight", "text-height", "hideMinimap",
+            DocServer.setPlaygroundFields(["dataTransition", "treeViewOpen", "showSidebar", "sidebarWidthPercent", "viewTransition",
+                "panX", "panY", "nativeWidth", "nativeHeight", "text-scrollHeight", "text-height", "hideMinimap",
                 "viewScale", "scrollTop", "hidden", "curPage", "viewType", "chromeHidden", "nativeWidth"]); // can play with these fields on someone else's
         }
         DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg)));
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index c44443264..553f84a67 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -70,6 +70,7 @@ export function wavyBorderPath(pw: number, ph: number, inset: number = 0.05) {
 // a preliminary implementation of a dash style sheet for setting rendering properties of documents nested within a Tab
 // 
 export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any {
+    const remoteDocHeader = "author;creationDate;noMargin";
     const docProps = testDocProps(props) ? props : undefined;
     const selected = property.includes(":selected");
     const isCaption = property.includes(":caption");
@@ -111,7 +112,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
                 props?.showTitle?.() ||
                 (!Doc.IsSystem(doc) && [DocumentType.COL, DocumentType.LABEL, DocumentType.RTF, DocumentType.IMG, DocumentType.VID].includes(doc.type as any) ?
                     (doc.author === Doc.CurrentUserEmail ? StrCast(Doc.UserDoc().showTitle) :
-                        "author;creationDate") : "")) || "");
+                        remoteDocHeader) : "")) || "");
         case StyleProp.Color:
             if (MainView.Instance.LastButton === doc) return Colors.DARK_GRAY;
             const docColor: Opt<string> = StrCast(doc?.[fieldKey + "color"], StrCast(doc?._color));
@@ -126,7 +127,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
         case StyleProp.BorderPath: return comicStyle() && props?.renderDepth && doc?.type !== DocumentType.INK ? { path: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0), fill: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0, .08), width: 3 } : { path: undefined, width: 0 };
         case StyleProp.JitterRotation: return comicStyle() ? random(-1, 1, NumCast(doc?.x), NumCast(doc?.y)) * ((props?.PanelWidth() || 0) > (props?.PanelHeight() || 0) ? 5 : 10) : 0;
         case StyleProp.HeaderMargin: return ([CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Tree].includes(doc?._viewType as any) ||
-            doc?.type === DocumentType.RTF || doc?.type === DocumentType.LABEL) && showTitle() && !StrCast(doc?.showTitle).includes(":hover") ? 15 : 0;
+            (doc?.type === DocumentType.RTF && !showTitle()?.includes("noMargin")) || doc?.type === DocumentType.LABEL) && showTitle() && !StrCast(doc?.showTitle).includes(":hover") ? 15 : 0;
         case StyleProp.BackgroundColor: {
             if (MainView.Instance.LastButton === doc) return Colors.LIGHT_GRAY;
             let docColor: Opt<string> =
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 4630b3bf2..dddae4a34 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -28,6 +28,8 @@ import "./CollectionStackingView.scss";
 import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
 import { CollectionSubView } from "./CollectionSubView";
 import { CollectionViewType } from "./CollectionView";
+import { FieldViewProps } from "../nodes/FieldView";
+import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
 const _global = (window /* browser */ || global /* node */) as any;
 
 
@@ -207,6 +209,25 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         }
         return this.props.styleProvider?.(doc, props, property);
     }
+    @undoBatch
+    @action
+    onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
+        const docView = fieldProps.DocumentView?.();
+        if (docView && ["Enter"].includes(e.key) && e.ctrlKey) {
+            e.stopPropagation?.();
+            const below = !e.altKey && e.key !== "Tab";
+            const layoutKey = StrCast(docView.LayoutFieldKey);
+            const newDoc = Doc.MakeCopy(docView.rootDoc, true);
+            const dataField = docView.rootDoc[Doc.LayoutFieldKey(newDoc)];
+            newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
+            if (layoutKey !== "layout" && docView.rootDoc[layoutKey] instanceof Doc) {
+                newDoc[layoutKey] = docView.rootDoc[layoutKey];
+            }
+            Doc.GetProto(newDoc).text = undefined;
+            FormattedTextBox.SelectOnLoad = newDoc[Id];
+            return this.addDocument?.(newDoc);
+        }
+    }
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
     getDisplayDoc(doc: Doc, width: () => number) {
         const dataDoc = (!doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS) ? undefined : this.props.DataDoc;
@@ -225,6 +246,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             docViewPath={this.props.docViewPath}
             fitWidth={this.props.childFitWidth}
             isContentActive={emptyFunction}
+            onKey={this.onKeyDown}
             isDocumentActive={this.isContentActive}
             LayoutTemplate={this.props.childLayoutTemplate}
             LayoutTemplateString={this.props.childLayoutString}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index dcff32d04..f4cb08f8f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1166,7 +1166,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @action
     onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
         const docView = fieldProps.DocumentView?.();
-        if (docView && docView.rootDoc._singleLine && ["Tab", "Enter"].includes(e.key)) {
+        if (docView && (e.ctrlKey || e.shiftKey || e.altKey || docView.rootDoc._singleLine) && ["Tab", "Enter"].includes(e.key)) {
             e.stopPropagation?.();
             const below = !e.altKey && e.key !== "Tab";
             const layoutKey = StrCast(docView.LayoutFieldKey);
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 822bc996b..d468822c0 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -245,8 +245,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             const state = this._editorView.state.apply(tx);
             this._editorView.updateState(state);
 
-            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 * 1000)));
             const curText = state.doc.textBetween(0, state.doc.content.size, " \n");
             const curTemp = this.layoutDoc.resolvedDataDoc ? Cast(this.layoutDoc[this.props.fieldKey], RichTextField) : undefined;               // the actual text in the text box
             const curProto = Cast(Cast(this.dataDoc.proto, Doc, null)?.[this.fieldKey], RichTextField, null);              // the default text inherited from a prototype
@@ -348,7 +346,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
     }
 
     autoLink = () => {
-        if (this._editorView) {
+        if (this._editorView?.state.doc.textContent) {
             const newAutoLinks = new Set<Doc>();
             const oldAutoLinks = DocListCast(this.props.Document.links).filter(link => link.linkRelationship === LinkManager.AutoKeywords);
             const f = this._editorView.state.selection.from;
@@ -962,7 +960,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }, { fireImmediately: true }
         );
         quickScroll = undefined;
-        setTimeout(this.tryUpdateScrollHeight, 10);
+        this.tryUpdateScrollHeight();
     }
 
     pushToGoogleDoc = async () => {
@@ -1193,19 +1191,21 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
 
         const selectOnLoad = this.rootDoc[Id] === FormattedTextBox.SelectOnLoad && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()));
         if (selectOnLoad && !this.props.dontRegisterView && !this.props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
+            const selLoadChar = FormattedTextBox.SelectOnLoadChar;
             FormattedTextBox.SelectOnLoad = "";
             this.props.select(false);
-            if (FormattedTextBox.SelectOnLoadChar && this._editorView) {
+            if (selLoadChar && this._editorView) {
                 const $from = this._editorView.state.selection.anchor ? this._editorView.state.doc.resolve(this._editorView.state.selection.anchor - 1) : undefined;
                 const mark = schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) });
                 const curMarks = this._editorView.state.storedMarks ?? $from?.marksAcross(this._editorView.state.selection.$head) ?? [];
                 const storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark];
                 const tr = this._editorView.state.tr.setStoredMarks(storedMarks).insertText(FormattedTextBox.SelectOnLoadChar, this._editorView.state.doc.content.size - 1, this._editorView.state.doc.content.size).setStoredMarks(storedMarks);
                 this._editorView.dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(tr.doc.content.size))));
-                FormattedTextBox.SelectOnLoadChar = "";
-            } else if (curText && !FormattedTextBox.DontSelectInitialText) {
-                selectAll(this._editorView!.state, this._editorView?.dispatch);
+            } else if (this._editorView && curText && !FormattedTextBox.DontSelectInitialText) {
+                selectAll(this._editorView.state, this._editorView?.dispatch)
                 this.startUndoTypingBatch();
+            } else if (this._editorView) {
+                this._editorView.dispatch(this._editorView.state.tr.addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
             }
             FormattedTextBox.DontSelectInitialText = false;
         }
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index 0dd0a8411..e979ae59e 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -80,8 +80,10 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     //Commands for lists
     bind("Ctrl-i", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => canEdit(state) && wrapInList(schema.nodes.ordered_list)(state as any, dispatch as any));
 
+    bind("Ctrl-Tab", () => props.onKey?.(event, props) ? true : true);
+    bind("Alt-Tab", () => props.onKey?.(event, props) ? true : true);
     bind("Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
-        if (props.onKey?.({ key: "Tab" }, props)) return true;
+        if (props.onKey?.(event, props)) return true;
         if (!canEdit(state)) return true;
         const ref = state.selection;
         const range = ref.$from.blockRange(ref.$to);
@@ -106,7 +108,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     });
 
     bind("Shift-Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
-        if (props.onKey?.({ key: "Tab", shiftKey: true })) return true; // single line docs don't process tabs so that their containers can decide what to do.  This should be a prop
+        if (props.onKey?.(event, props)) return true;
         if (!canEdit(state)) return true;
         const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks());
 
@@ -154,11 +156,9 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
         return tx;
     };
 
-    //Command to create a text document to the right of the selected textbox
-    bind("Alt-Enter", () => true);
 
-    //Command to create a text document to the bottom of the selected textbox
-    bind("Ctrl-Enter", () => true);
+    bind("Alt-Enter", () => props.onKey?.(event, props) ? true : true);
+    bind("Ctrl-Enter", () => props.onKey?.(event, props) ? true : true);
 
     // backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward);
     bind("Backspace", (state: EditorState<S>, dispatch: (tx: Transaction<Schema<any, any>>) => void) => {
@@ -244,8 +244,6 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
         return false;
     });
 
-    // mac && bind("Ctrl-Enter", cmd);
-    // bind("Mod-Enter", cmd);
     bind("Shift-Enter", cmd);
 
     return keys;
diff --git a/src/fields/util.ts b/src/fields/util.ts
index c708affe3..ef5ac79b8 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -175,8 +175,11 @@ const getEffectiveAclCache = computedFn(function (target: any, user?: string) {
  * Calculates the effective access right to a document for the current user.
  */
 export function GetEffectiveAcl(target: any, user?: string): symbol {
-    return !target ? AclPrivate :
-        target[UpdatingFromServer] ? AclAdmin : getEffectiveAclCache(target, user);// all changes received from the server must be processed as Admin.  return this directly so that the acls aren't cached (UpdatingFromServer is not observable)
+    if (!target) return AclPrivate;
+    if (target[UpdatingFromServer]) return AclAdmin;
+    // authored documents are private until an ACL is set.  this also fixes notes that flicker on and off when a remote types to create a private note into a shared collection.
+    if (!target[AclSym] && target.author && target.author !== Doc.CurrentUserEmail) return AclPrivate;
+    return getEffectiveAclCache(target, user);// all changes received from the server must be processed as Admin.  return this directly so that the acls aren't cached (UpdatingFromServer is not observable)
 }
 
 function getPropAcl(target: any, prop: string | symbol | number) {
-- 
cgit v1.2.3-70-g09d2


From 53cae5e2ab9267295a824ff721c119ada5e5dc20 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 31 May 2022 13:03:19 -0400
Subject: added a pop up menu for viewing dashField view collection.  added a
 menu button for turning off autoLinking to text selection.  added ability to
 add button to top contextMenu buttons without blowing away db.

---
 src/client/util/CurrentUserUtils.ts                | 34 +++++++++
 src/client/views/MainView.tsx                      |  2 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  2 +-
 src/client/views/nodes/button/FontIconBox.tsx      |  8 +++
 .../views/nodes/formattedText/DashFieldView.tsx    | 81 +++++++++++++++++-----
 .../views/nodes/formattedText/FormattedTextBox.tsx | 12 ++--
 .../formattedText/ProsemirrorExampleTransfer.ts    |  2 +
 .../views/nodes/formattedText/RichTextMenu.tsx     | 14 +++-
 .../views/nodes/formattedText/RichTextRules.ts     |  2 +-
 src/client/views/nodes/formattedText/marks_rts.ts  | 14 ++++
 10 files changed, 144 insertions(+), 27 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 083530ce7..c4e2afd08 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -745,6 +745,7 @@ export class CurrentUserUtils {
                 { title: "Left", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left", _readOnly_)' },
                 { title: "Center", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center", _readOnly_)' },
                 { title: "Right", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right", _readOnly_)' },
+                { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", click: 'toggleNoAutoLinkAnchor(_readOnly_)' },
             ];
         return tools;
     }
@@ -834,11 +835,44 @@ export class CurrentUserUtils {
                     !params.subMenu ?
                         btnFunc(params) :
                         CurrentUserUtils.linearButtonList({
+                            title: params.title,
                             linearViewSubMenu: true, flexGap: 0, ignoreClick: true,
                             linearViewExpandable: true, icon: params.title, _height: 30,
                             linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined,
                             hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined,
                         }, params.subMenu.map(btnFunc))));
+        } else {
+            const menuBtnList = DocListCast((doc.contextMenuBtns as Doc).data);
+            let prev = "";
+            CurrentUserUtils.contextMenuTools(doc).forEach(params => {
+                const menuBtnDoc = menuBtnList.find(doc => doc.title === params.title);
+                if (!menuBtnDoc) {
+                    const newMenuBtnDoc = !params.subMenu ?
+                        btnFunc(params) :
+                        CurrentUserUtils.linearButtonList({
+                            title: params.title,
+                            linearViewSubMenu: true, flexGap: 0, ignoreClick: true,
+                            linearViewExpandable: true, icon: params.title, _height: 30,
+                            linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined,
+                            hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined,
+                        }, params.subMenu.map(btnFunc));
+                    const after = menuBtnList.find(doc => doc.title === prev);
+                    Doc.AddDocToList(doc.contextMenuBtns as Doc, "data", newMenuBtnDoc, after, false, !after);
+                }
+                const subMenuBtnList = menuBtnDoc?.data ? DocListCast(menuBtnDoc.data) : undefined;
+                if (menuBtnDoc && subMenuBtnList && params.subMenu && DocListCast(doc.data).length !== subMenuBtnList.length) {
+                    let prevSub = "";
+                    params.subMenu.forEach(sub => {
+                        if (!subMenuBtnList.find(doc => doc.title === sub.title)) {
+                            const newSubMenuBtnDoc = btnFunc(sub);
+                            const after = subMenuBtnList.find(doc => doc.title === prevSub);
+                            Doc.AddDocToList(menuBtnDoc, "data", newSubMenuBtnDoc, after, false, !prevSub);
+                        }
+                        prevSub = params.title;
+                    })
+                }
+                prev = params.title;
+            });
         }
     }
 
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index ff2857739..55190001b 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -50,6 +50,7 @@ import { AudioBox } from './nodes/AudioBox';
 import { ButtonType } from './nodes/button/FontIconBox';
 import { DocumentLinksButton } from './nodes/DocumentLinksButton';
 import { DocumentView } from './nodes/DocumentView';
+import { DashFieldViewMenu } from './nodes/formattedText/DashFieldView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { RichTextMenu } from './nodes/formattedText/RichTextMenu';
 import { LinkDescriptionPopup } from './nodes/LinkDescriptionPopup';
@@ -686,6 +687,7 @@ export class MainView extends React.Component {
             <ContextMenu />
             <RadialMenu />
             <AnchorMenu />
+            <DashFieldViewMenu />
             <MarqueeOptionsMenu />
             <OverlayView />
             <TimelineMenu />
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f4cb08f8f..259cc1ee5 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1166,7 +1166,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     @action
     onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
         const docView = fieldProps.DocumentView?.();
-        if (docView && (e.ctrlKey || e.shiftKey || e.altKey || docView.rootDoc._singleLine) && ["Tab", "Enter"].includes(e.key)) {
+        if (docView && (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || docView.rootDoc._singleLine) && ["Tab", "Enter"].includes(e.key)) {
             e.stopPropagation?.();
             const below = !e.altKey && e.key !== "Tab";
             const layoutKey = StrCast(docView.LayoutFieldKey);
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index e6d8fe88d..625f5d14a 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -666,6 +666,14 @@ ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: b
     if (editorView) RichTextMenu.Instance.setFontSize(size);
     else Doc.UserDoc()._fontSize = size;
 });
+ScriptingGlobals.add(function toggleNoAutoLinkAnchor(checkResult?: boolean) {
+    const editorView = RichTextMenu.Instance?.TextView?.EditorView;
+    if (checkResult) {
+        return (editorView ? RichTextMenu.Instance.noAutoLink : Doc.UserDoc().noAutoLink) ? Colors.MEDIUM_BLUE : "transparent";
+    }
+    if (editorView) RichTextMenu.Instance?.toggleNoAutoLinkAnchor();
+    else Doc.UserDoc().noAutoLink = Doc.UserDoc().noAutoLink ? true : false;
+});
 
 ScriptingGlobals.add(function toggleBold(checkResult?: boolean) {
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 1a8352b72..6a3f9ed00 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -8,35 +8,41 @@ import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
 import { ComputedField } from "../../../../fields/ScriptField";
 import { Cast, StrCast } from "../../../../fields/Types";
 import { DocServer } from "../../../DocServer";
-import { DocUtils } from "../../../documents/Documents";
 import { CollectionViewType } from "../../collections/CollectionView";
 import "./DashFieldView.scss";
 import { FormattedTextBox } from "./FormattedTextBox";
 import React = require("react");
+import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../../Utils";
+import { AntimodeMenu, AntimodeMenuProps } from "../../AntimodeMenu";
+import { Tooltip } from "@material-ui/core";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 
 export class DashFieldView {
     _fieldWrapper: HTMLDivElement; // container for label and value
 
     constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
+        const { boolVal, strVal } = DashFieldViewInternal.fieldContent(tbox.props.Document, tbox.rootDoc, node.attrs.fieldKey);
+
         this._fieldWrapper = document.createElement("div");
         this._fieldWrapper.style.width = node.attrs.width;
         this._fieldWrapper.style.height = node.attrs.height;
         this._fieldWrapper.style.fontWeight = "bold";
         this._fieldWrapper.style.position = "relative";
         this._fieldWrapper.style.display = "inline-block";
+        this._fieldWrapper.textContent = node.attrs.fieldKey.startsWith("#") ? node.attrs.fieldKey : node.attrs.fieldKey + " " + strVal;
         this._fieldWrapper.onkeypress = function (e: any) { e.stopPropagation(); };
         this._fieldWrapper.onkeydown = function (e: any) { e.stopPropagation(); };
         this._fieldWrapper.onkeyup = function (e: any) { e.stopPropagation(); };
         this._fieldWrapper.onmousedown = function (e: any) { e.stopPropagation(); };
 
-        ReactDOM.render(<DashFieldViewInternal
+        setTimeout(() => ReactDOM.render(<DashFieldViewInternal
             fieldKey={node.attrs.fieldKey}
             docid={node.attrs.docid}
             width={node.attrs.width}
             height={node.attrs.height}
             hideKey={node.attrs.hideKey}
             tbox={tbox}
-        />, this._fieldWrapper);
+        />, this._fieldWrapper));
         (this as any).dom = this._fieldWrapper;
     }
     destroy() { ReactDOM.unmountComponentAtNode(this._fieldWrapper); }
@@ -76,16 +82,17 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
         this._reactionDisposer?.();
     }
 
-    multiValueDelimeter = ";";
+    public static multiValueDelimeter = ";";
+    public static fieldContent(textBoxDoc: Doc, dashDoc: Doc, fieldKey: string) {
+        const dashVal = dashDoc[fieldKey] ?? dashDoc[DataSym][fieldKey] ?? (fieldKey === "PARAMS" ? textBoxDoc[fieldKey] : "");
+        const fval = dashVal instanceof List ? dashVal.join(DashFieldViewInternal.multiValueDelimeter) : StrCast(dashVal).startsWith(":=") || dashVal === "" ? Doc.Layout(textBoxDoc)[fieldKey] : dashVal;
+        return { boolVal: Cast(fval, "boolean", null), strVal: Field.toString(fval as Field) || "" }
+    }
 
     // set the display of the field's value (checkbox for booleans, span of text for strings)
     @computed get fieldValueContent() {
         if (this._dashDoc) {
-            const dashVal = this._dashDoc[this._fieldKey] ?? this._dashDoc[DataSym][this._fieldKey] ?? (this._fieldKey === "PARAMS" ? this._textBoxDoc[this._fieldKey] : "");
-            const fval = dashVal instanceof List ? dashVal.join(this.multiValueDelimeter) : StrCast(dashVal).startsWith(":=") || dashVal === "" ? Doc.Layout(this._textBoxDoc)[this._fieldKey] : dashVal;
-            const boolVal = Cast(fval, "boolean", null);
-            const strVal = Field.toString(fval as Field) || "";
-
+            const { boolVal, strVal } = DashFieldViewInternal.fieldContent(this._textBoxDoc, this._dashDoc, this._fieldKey);
             // field value is a boolean, so use a checkbox or similar widget to display it
             if (boolVal === true || boolVal === false) {
                 return <input
@@ -108,9 +115,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
                     ref={r => {
                         r?.addEventListener("keydown", e => this.fieldSpanKeyDown(e, r));
                         r?.addEventListener("blur", e => r && this.updateText(r.textContent!, false));
-                        r?.addEventListener("pointerdown", action((e) => {
-                            e.stopPropagation();
-                        }));
+                        r?.addEventListener("pointerdown", action(e => e.stopPropagation()));
                     }} >
                     {strVal}
                 </span>;
@@ -161,7 +166,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
                         if (this._fieldKey.startsWith("_")) Doc.Layout(this._textBoxDoc)[this._fieldKey] = Number(newText);
                         Doc.SetInPlace(this._dashDoc!, this._fieldKey, newText, true);
                     } else {
-                        const splits = newText.split(this.multiValueDelimeter);
+                        const splits = newText.split(DashFieldViewInternal.multiValueDelimeter);
                         if (this._fieldKey !== "PARAMS" || !this._textBoxDoc[this._fieldKey] || this._dashDoc?.PARAMS) {
                             const strVal = splits.length > 1 ? new List<string>(splits) : newText;
                             if (this._fieldKey.startsWith("_")) Doc.Layout(this._textBoxDoc)[this._fieldKey] = strVal;
@@ -173,11 +178,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
         }
     }
 
-
-    // clicking on the label creates a pivot view collection of all documents
-    // in the same collection.  The pivot field is the fieldKey of this label
-    onPointerDownLabelSpan = (e: any) => {
-        e.stopPropagation();
+    createPivotForField = (e: React.MouseEvent) => {
         let container = this.props.tbox.props.ContainingCollectionView;
         while (container?.props.Document.isTemplateForField || container?.props.Document.isTemplateDoc) {
             container = container.props.ContainingCollectionView;
@@ -196,6 +197,16 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
         }
     }
 
+
+    // clicking on the label creates a pivot view collection of all documents
+    // in the same collection.  The pivot field is the fieldKey of this label
+    onPointerDownLabelSpan = (e: any) => {
+        setupMoveUpEvents(this, e, returnFalse, returnFalse, (e) => {
+            DashFieldViewMenu.createFieldView = this.createPivotForField;
+            DashFieldViewMenu.Instance.show(e.clientX, e.clientY + 16);
+        });
+    }
+
     render() {
         return <div className="dashFieldView" style={{
             width: this.props.width,
@@ -210,4 +221,38 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna
 
         </div >;
     }
+}
+@observer
+export class DashFieldViewMenu extends AntimodeMenu<AntimodeMenuProps> {
+    static Instance: DashFieldViewMenu;
+    static createFieldView: (e: React.MouseEvent) => void = emptyFunction;
+    constructor(props: any) {
+        super(props);
+        DashFieldViewMenu.Instance = this;
+    }
+    @action
+    showFields = (e: React.MouseEvent) => {
+        DashFieldViewMenu.createFieldView(e);
+        DashFieldViewMenu.Instance.fadeOut(true);
+    }
+
+    public show = (x: number, y: number) => {
+        this.jumpTo(x, y, true);
+        const hideMenu = () => {
+            this.fadeOut(true);
+            document.removeEventListener("pointerdown", hideMenu);
+        }
+        document.addEventListener("pointerdown", hideMenu)
+    }
+    render() {
+        const buttons = [
+            <Tooltip key="trash" title={<div className="dash-tooltip">{"Remove Link Anchor"}</div>}>
+                <button className="antimodeMenu-button" onPointerDown={this.showFields}>
+                    <FontAwesomeIcon icon="eye" size="lg" />
+                </button>
+            </Tooltip>,
+        ];
+
+        return this.getElement(buttons);
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index d468822c0..ce82821b6 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -391,16 +391,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             flattened1.forEach((flat, i) => {
                 const flattened = this.findInNode(this._editorView!, this._editorView!.state.doc, autoLinkTerm);
                 this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex;
-                alink = alink ?? (DocListCast(this.Document.links).find(link =>
-                    Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) &&
-                    Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || DocUtils.MakeLink({ doc: this.props.Document }, { doc: target },
-                        LinkManager.AutoKeywords)!);
-                newAutoLinks.add(alink);
                 const splitter = editorView.state.schema.marks.splitter.create({ id: Utils.GenerateGuid() });
                 const sel = flattened[i];
                 tr = tr.addMark(sel.from, sel.to, splitter);
                 tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => {
-                    if (node.firstChild === null && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
+                    if (node.firstChild === null && !node.marks.find((m: Mark) => m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) {
+                        alink = alink ?? (DocListCast(this.Document.links).find(link =>
+                            Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) &&
+                            Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || DocUtils.MakeLink({ doc: this.props.Document }, { doc: target },
+                                LinkManager.AutoKeywords)!);
+                        newAutoLinks.add(alink);
                         const allAnchors = [{ href: Doc.localServerPath(target), title: "a link", anchorId: this.props.Document[Id] }];
                         allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? []));
                         const link = editorView.state.schema.marks.autoLinkAnchor.create({ allAnchors, title: "auto term", location: "add:right" });
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index e979ae59e..fb49b0698 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -82,6 +82,8 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
 
     bind("Ctrl-Tab", () => props.onKey?.(event, props) ? true : true);
     bind("Alt-Tab", () => props.onKey?.(event, props) ? true : true);
+    bind("Meta-Tab", () => props.onKey?.(event, props) ? true : true);
+    bind("Meta-Enter", () => props.onKey?.(event, props) ? true : true);
     bind("Tab", (state: EditorState<S>, dispatch: (tx: Transaction<S>) => void) => {
         if (props.onKey?.(event, props)) return true;
         if (!canEdit(state)) return true;
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index c5f76cc8d..3df1e45a5 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -35,6 +35,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     public _brushMap: Map<string, Set<Mark>> = new Map();
 
     @observable private collapsed: boolean = false;
+    @observable private _noLinkActive: boolean = false;
     @observable private _boldActive: boolean = false;
     @observable private _italicsActive: boolean = false;
     @observable private _underlineActive: boolean = false;
@@ -79,6 +80,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         this._reaction?.();
     }
 
+    @computed get noAutoLink() { return this._noLinkActive; }
     @computed get bold() { return this._boldActive; }
     @computed get underline() { return this._underlineActive; }
     @computed get italics() { return this._italicsActive; }
@@ -220,7 +222,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         let activeMarks: MarkType[] = [];
         if (!this.view || !this.TextView.props.isSelected(true)) return activeMarks;
 
-        const markGroup = [schema.marks.strong, schema.marks.em, schema.marks.underline, schema.marks.strikethrough, schema.marks.superscript, schema.marks.subscript];
+        const markGroup = [schema.marks.noAutoLinkAnchor, schema.marks.strong, schema.marks.em, schema.marks.underline, schema.marks.strikethrough, schema.marks.superscript, schema.marks.subscript];
         if (this.view.state.storedMarks) return this.view.state.storedMarks.map(mark => mark.type);
         //current selection
         const { empty, ranges, $to } = this.view.state.selection as TextSelection;
@@ -264,6 +266,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
     setActiveMarkButtons(activeMarks: MarkType[] | undefined) {
         if (!activeMarks) return;
 
+        this._noLinkActive = false;
         this._boldActive = false;
         this._italicsActive = false;
         this._underlineActive = false;
@@ -273,6 +276,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
 
         activeMarks.forEach(mark => {
             switch (mark.name) {
+                case "noAutoLinkAnchor": this._noLinkActive = true; break;
                 case "strong": this._boldActive = true; break;
                 case "em": this._italicsActive = true; break;
                 case "underline": this._underlineActive = true; break;
@@ -283,6 +287,14 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         });
     }
 
+    toggleNoAutoLinkAnchor = () => {
+        if (this.view) {
+            const mark = this.view.state.schema.mark(this.view.state.schema.marks.noAutoLinkAnchor);
+            this.setMark(mark, this.view.state, this.view.dispatch, false);
+            this.TextView.autoLink();
+            this.view.focus();
+        }
+    }
     toggleBold = () => {
         if (this.view) {
             const mark = this.view.state.schema.mark(this.view.state.schema.marks.strong);
diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts
index 427e05edb..8851d52e4 100644
--- a/src/client/views/nodes/formattedText/RichTextRules.ts
+++ b/src/client/views/nodes/formattedText/RichTextRules.ts
@@ -348,7 +348,7 @@ export class RichTextRules {
                         this.Document[DataSym].tags = `${tags + "#" + tag + ':'}`;
                     }
                     const fieldView = state.schema.nodes.dashField.create({ fieldKey: "#" + tag });
-                    return state.tr.deleteRange(start, end).insert(start, fieldView);
+                    return state.tr.deleteRange(start, end).insert(start, fieldView).insertText(" ");
                 }),
 
 
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index 1f6ce014f..2fde5c7ba 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -48,6 +48,20 @@ export const marks: { [index: string]: MarkSpec } = {
             return ["a", { class: anchorids, "data-targethrefs": targethrefs, "data-linkdoc": node.attrs.linkDoc, title: node.attrs.title, location: node.attrs.location, style: `background: lightBlue` }, 0];
         }
     },
+    noAutoLinkAnchor: {
+        attrs: {},
+        inclusive: false,
+        parseDOM: [{
+            tag: "div", getAttrs(dom: any) {
+                return {
+                    noAutoLink: dom.getAttribute("data-noAutoLink"),
+                };
+            }
+        }],
+        toDOM(node: any) {
+            return ["span", { "data-noAutoLink": "true" }, 0];
+        }
+    },
     // :: MarkSpec A linkAnchor. The anchor can have multiple links, where each linkAnchor specifies an href to the URL of the source selection Marker text,
     // and a title for use in menus and hover. `title`
     // defaults to the empty string. Rendered and parsed as an `<a>`
-- 
cgit v1.2.3-70-g09d2


From c2da7a2f1074ea7c7859394d394ad0a99882a286 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 6 Jun 2022 18:30:14 -0400
Subject: fixed docking view layout when headerBar is expanded to not leave a
 gap.

---
 src/client/views/MainView.scss | 10 +++++++---
 src/client/views/MainView.tsx  | 11 +----------
 2 files changed, 8 insertions(+), 13 deletions(-)

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

diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index 15cd2c144..001c91b40 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -20,6 +20,8 @@
     position: relative;
     width: 100%;
     height: 100%;
+    display: flex;
+    flex-direction: column;
 }
 
 // add nodes menu. Note that the + button is actually an input label, not an actual button.
@@ -148,7 +150,8 @@
     }
 }
 
-.mainView-innerContent, .mainView-innerContent-Dark {
+.mainView-innerContent,
+.mainView-innerContent-Dark {
     display: contents;
     flex-direction: row;
     position: relative;
@@ -181,8 +184,8 @@
 .mainView-libraryHandle {
     background-color: $light-gray;
 }
-.mainView-innerContent-Dark 
-{
+
+.mainView-innerContent-Dark {
     .propertiesView {
         background-color: #252525;
 
@@ -208,6 +211,7 @@
         background: #353535;
     }
 }
+
 .mainView-container-Dark {
     .contextMenu-cont {
         background: $medium-gray;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 9c9b19122..cdf85c905 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -286,7 +286,7 @@ export class MainView extends React.Component {
     headerBarScreenXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.headerBarDocHeight(), 1);
 
     @computed get headerBarDocView() {
-        return <div style={{ height: this.headerBarDocHeight() }}>
+        return <div className="mainView-headerBar" style={{ height: this.headerBarDocHeight() }}>
             <DocumentView key="headerBarDoc"
                 Document={this.headerBarDoc}
                 DataDoc={undefined}
@@ -502,15 +502,6 @@ export class MainView extends React.Component {
         </>;
     }
 
-    @computed get headerBar() {
-        return !this.userDoc ? (null) :
-            <div className="mainView-dashboardArea" style={{
-                height: this.headerBarDocHeight(),
-                width: "100%",
-            }} >
-                {this.headerBarDocView}
-            </div>;
-    }
 
     @computed get mainDashboardArea() {
         return !this.userDoc ? (null) :
-- 
cgit v1.2.3-70-g09d2


From f8840672a7bcb3f337d8e50098f374a1b2d441ce Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 8 Jun 2022 01:17:24 -0400
Subject: added ability to make thumbnails of dashboards. started to cleanup
 dockingView/goldenlayout undo event handling.  cleaned up tab doc list in
 docking view.  made titles editable again in treeview.  fixed overlay view to
 work with image docs etc by setting top/left in css

---
 src/client/documents/Documents.ts                  |  6 +--
 src/client/goldenLayout.js                         |  2 +-
 src/client/util/CurrentUserUtils.ts                | 26 ++++++++---
 src/client/views/DocumentButtonBar.tsx             | 53 ++++++++++------------
 src/client/views/MainView.tsx                      | 12 +----
 src/client/views/OverlayView.scss                  |  2 +
 src/client/views/OverlayView.tsx                   |  2 +-
 .../views/collections/CollectionDockingView.tsx    | 21 ++++++---
 src/client/views/collections/TabDocView.tsx        | 28 ++++--------
 src/client/views/collections/TreeView.tsx          |  4 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 45 ++++++++++--------
 src/client/views/nodes/WebBoxRenderer.js           |  4 +-
 src/client/views/topbar/TopBar.tsx                 |  6 +--
 13 files changed, 108 insertions(+), 103 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 817478b2f..acc1e341a 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -671,9 +671,9 @@ export namespace Docs {
             return viewDoc;
         }
 
-        export function ImageDocument(url: string, options: DocumentOptions = {}) {
-            const imgField = new ImageField(url);
-            return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(url), ...options });
+        export function ImageDocument(url: string|ImageField, options: DocumentOptions = {}) {
+            const imgField =  url instanceof ImageField ? url : new ImageField(url);
+            return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField.url.href), ...options });
         }
 
         export function PresDocument(options: DocumentOptions = {}) {
diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js
index 82b10608d..4735f216f 100644
--- a/src/client/goldenLayout.js
+++ b/src/client/goldenLayout.js
@@ -1431,7 +1431,7 @@
             this.root.callDownwards('_$init');
 
             if (config.maximisedItemId === '__glMaximised') {
-                this.root.getItemsById(config.maximisedItemId)[0].toggleMaximise();
+                this.root.getItemsById(config.maximisedItemId)[0]?.toggleMaximise();
             }
         },
 
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 51bfdbbd2..885679b64 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -8,7 +8,7 @@ import { PrefetchProxy } from "../../fields/Proxy";
 import { RichTextField } from "../../fields/RichTextField";
 import { ComputedField, ScriptField } from "../../fields/ScriptField";
 import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types";
-import { nullAudio } from "../../fields/URLField";
+import { ImageField, nullAudio } from "../../fields/URLField";
 import { SharingPermissions } from "../../fields/util";
 import { Utils } from "../../Utils";
 import { DocServer } from "../DocServer";
@@ -22,11 +22,10 @@ import { TreeView } from "../views/collections/TreeView";
 import { Colors } from "../views/global/globalEnums";
 import { MainView } from "../views/MainView";
 import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox";
-import { LabelBox } from "../views/nodes/LabelBox";
 import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView";
 import { OverlayView } from "../views/OverlayView";
 import { DocumentManager } from "./DocumentManager";
-import { DragManager, dropActionType } from "./DragManager";
+import { DragManager } from "./DragManager";
 import { makeTemplate, MakeTemplate } from "./DropConverter";
 import { HistoryUtil } from "./History";
 import { LinkManager } from "./LinkManager";
@@ -1185,9 +1184,24 @@ export class CurrentUserUtils {
     }
 
     public static async snapshotDashboard(userDoc: Doc) {
-        const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard);
-        Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy);
-        CurrentUserUtils.openDashboard(userDoc, copy);
+        const docView = CollectionDockingView.Instance.props.DocumentView?.();
+        const content = docView?.ContentDiv;
+        if (docView && content) {
+            const _width = Number(getComputedStyle(content).width.replace("px",""));
+            const _height = Number(getComputedStyle(content).height.replace("px",""));
+            CollectionFreeFormView.UpdateIcon(
+                docView.layoutDoc[Id] + "-icon" + (new Date()).getTime(),
+                content,
+                _width, _height,
+                _width, _height, 0,
+                (iconFile, _nativeWidth, _nativeHeight) => {
+                    const img = Docs.Create.ImageDocument(new ImageField(iconFile), { title: docView.rootDoc.title+"-icon", _width, _height, _nativeWidth, _nativeHeight});
+                    const proto = Cast(img.proto, Doc, null)!;
+                    proto["data-nativeWidth"] = _width;
+                    proto["data-nativeHeight"] = _height;
+                    docView.dataDoc.thumb = img;
+                });
+        }
     }
 
     public static createNewDashboard = async (userDoc: Doc, id?: string) => {
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index c0645f0ab..103734b9b 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -213,34 +213,31 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
     @undoBatch
     @action
     pinWithView = (targetDoc: Doc) => {
-        if (targetDoc) {
-            TabDocView.PinDoc(targetDoc);
-            setTimeout(() => {
-                const activeDoc = PresBox.Instance.childDocs[PresBox.Instance.childDocs.length - 1];
-                const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(targetDoc.type as any) || targetDoc._viewType === CollectionViewType.Stacking;
-                const pannable: boolean = ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG);
-                if (scrollable) {
-                    const scroll = targetDoc._scrollTop;
-                    activeDoc.presPinView = true;
-                    activeDoc.presPinViewScroll = scroll;
-                } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
-                    activeDoc.presPinView = true;
-                    activeDoc.presStartTime = targetDoc._currentTimecode;
-                    activeDoc.presEndTime = NumCast(targetDoc._currentTimecode) + 0.1;
-                } else if (pannable) {
-                    const x = targetDoc._panX;
-                    const y = targetDoc._panY;
-                    const scale = targetDoc._viewScale;
-                    activeDoc.presPinView = true;
-                    activeDoc.presPinViewX = x;
-                    activeDoc.presPinViewY = y;
-                    activeDoc.presPinViewScale = scale;
-                } else if (targetDoc.type === DocumentType.COMPARISON) {
-                    const width = targetDoc._clipWidth;
-                    activeDoc.presPinClipWidth = width;
-                    activeDoc.presPinView = true;
-                }
-            });
+        const activeDoc = targetDoc && TabDocView.PinDoc(targetDoc);
+        if (activeDoc) {
+            const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(targetDoc.type as any) || targetDoc._viewType === CollectionViewType.Stacking;
+            const pannable: boolean = ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG);
+            if (scrollable) {
+                const scroll = targetDoc._scrollTop;
+                activeDoc.presPinView = true;
+                activeDoc.presPinViewScroll = scroll;
+            } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
+                activeDoc.presPinView = true;
+                activeDoc.presStartTime = targetDoc._currentTimecode;
+                activeDoc.presEndTime = NumCast(targetDoc._currentTimecode) + 0.1;
+            } else if (pannable) {
+                const x = targetDoc._panX;
+                const y = targetDoc._panY;
+                const scale = targetDoc._viewScale;
+                activeDoc.presPinView = true;
+                activeDoc.presPinViewX = x;
+                activeDoc.presPinViewY = y;
+                activeDoc.presPinViewScale = scale;
+            } else if (targetDoc.type === DocumentType.COMPARISON) {
+                const width = targetDoc._clipWidth;
+                activeDoc.presPinClipWidth = width;
+                activeDoc.presPinView = true;
+            }
         }
     }
 
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index cdf85c905..a30db66e4 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -226,17 +226,7 @@ export class MainView extends React.Component {
         if (received && !this.userDoc) {
             reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()), { fireImmediately: true });
         } else {
-            if (received && CurrentUserUtils._urlState.sharing) {
-                reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized,
-                    initialized => initialized && received && DocServer.GetRefField(received).then(docField => {
-                        if (docField instanceof Doc && docField._viewType !== CollectionViewType.Docking) {
-                            CollectionDockingView.AddSplit(docField, "right");
-                        }
-                    }),
-                );
-            }
-            const activeDash = PromiseValue(this.userDoc.activeDashboard);
-            activeDash.then(dash => {
+            PromiseValue(this.userDoc.activeDashboard).then(dash => {
                 if (dash instanceof Doc) CurrentUserUtils.openDashboard(this.userDoc, dash);
                 else CurrentUserUtils.createNewDashboard(this.userDoc);
             });
diff --git a/src/client/views/OverlayView.scss b/src/client/views/OverlayView.scss
index 555f4298d..f22e68ff5 100644
--- a/src/client/views/OverlayView.scss
+++ b/src/client/views/OverlayView.scss
@@ -46,4 +46,6 @@
 .overlayView-doc {
     z-index: 9002; //so that it appears above chroma 
     position: absolute;
+    top: 0;
+    left: 0;
 }
\ No newline at end of file
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index c3c103b50..f5e686473 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -200,7 +200,7 @@ export class OverlayView extends React.Component {
                     ScreenToLocalTransform={this.docScreenToLocalXf(d)}
                     renderDepth={1}
                     isDocumentActive={returnTrue}
-                    isContentActive={retu}
+                    isContentActive={returnTrue}
                     whenChildContentsActiveChanged={emptyFunction}
                     focus={DocUtils.DefaultFocus}
                     styleProvider={DefaultStyleProvider}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index de2106e4a..5af72b7d1 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -26,6 +26,7 @@ import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView";
 import { CollectionViewType } from './CollectionView';
 import { TabDocView } from './TabDocView';
 import React = require("react");
+import { SelectionManager } from '../../util/SelectionManager';
 const _global = (window /* browser */ || global /* node */) as any;
 
 @observer
@@ -50,9 +51,8 @@ export class CollectionDockingView extends CollectionSubView() {
     public _flush: UndoManager.Batch | undefined;
     private _ignoreStateChange = "";
     public tabMap: Set<any> = new Set();
-    public get initialized() { return this._goldenLayout !== null; }
     public get HasFullScreen() { return this._goldenLayout._maximisedItem !== null; }
-    @observable private _goldenLayout: any = null;
+    private _goldenLayout: any = null;
 
     constructor(props: SubCollectionViewProps) {
         super(props);
@@ -118,6 +118,7 @@ export class CollectionDockingView extends CollectionSubView() {
 
     @undoBatch
     public static OpenFullScreen(doc: Doc) {
+        SelectionManager.DeselectAll();
         const instance = CollectionDockingView.Instance;
         if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") {
             return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
@@ -271,7 +272,7 @@ export class CollectionDockingView extends CollectionSubView() {
         return true;
     }
 
-    async setupGoldenLayout() {
+    setupGoldenLayout = async () => {
         const config = StrCast(this.props.Document.dockingConfig);
         if (config) {
             const matches = config.match(/\"documentId\":\"[a-z0-9-]+\"/g);
@@ -291,7 +292,7 @@ export class CollectionDockingView extends CollectionSubView() {
             }
             this.tabMap.clear();
             this._goldenLayout?.destroy();
-            runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config)));
+            this._goldenLayout = new GoldenLayout(JSON.parse(config));
             this._goldenLayout.on('tabCreated', this.tabCreated);
             this._goldenLayout.on('tabDestroyed', this.tabDestroyed);
             this._goldenLayout.on('stackCreated', this.stackCreated);
@@ -322,7 +323,7 @@ export class CollectionDockingView extends CollectionSubView() {
                     }
                     this._ignoreStateChange = "";
                 });
-            setTimeout(() => this.setupGoldenLayout(), 0);
+            setTimeout(this.setupGoldenLayout);
             //window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window
         }
     }
@@ -413,19 +414,25 @@ export class CollectionDockingView extends CollectionSubView() {
         const docs = !docids ? [] : docids.map(id => DocServer.GetCachedRefField(id)).filter(f => f).map(f => f as Doc);
         const changesMade = this.props.Document.dockcingConfig !== json;
         if (changesMade && !this._flush) {
-            this.props.Document.dockingConfig = json;
-            this.props.Document.data = new List<Doc>(docs);
+            UndoManager.RunInBatch(() => {
+                this.props.Document.dockingConfig = json;
+                this.props.Document.data = new List<Doc>(docs);
+            }, "state changed");
         }
         return changesMade;
     }
 
     tabDestroyed = (tab: any) => {
+        const dview = CollectionDockingView.Instance.props.Document;
+        const fieldKey = CollectionDockingView.Instance.props.fieldKey;
+        Doc.RemoveDocFromList(dview, fieldKey, tab.DashDoc);
         this.tabMap.delete(tab);
         tab._disposers && Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
         tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele));
         this.stateChanged();
     }
     tabCreated = (tab: any) => {
+        this.tabMap.add(tab);
         tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab?.(tab);  // have to explicitly initialize tabs that reuse contents from previous tabs (ie, when dragging a tab around a new tab is created for the old content)
     }
 
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 0141724ea..75a8207dd 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -82,7 +82,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             const iconWrap = document.createElement("div");
             const closeWrap = document.createElement("div");
 
-
             titleEle.size = StrCast(doc.title).length + 3;
             titleEle.value = doc.title;
             titleEle.onkeydown = (e: KeyboardEvent) => {
@@ -185,14 +184,13 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     UndoManager.RunInBatch(() => tab.contentItem.remove(), "delete tab");
                 });
         }
-        CollectionDockingView.Instance.tabMap.add(tab);
     }
 
     /**
      * Adds a document to the presentation view
      **/
     @action
-    public static async PinDoc(doc: Doc, pinProps?: PinProps) {
+    public static PinDoc(doc: Doc, pinProps?: PinProps) {
         //add this new doc to props.Document
 
         // all docs will be added to the ActivePresentation as stored on CurrentUserUtils
@@ -246,19 +244,14 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 pinDoc.presMovement = PresMovement.None;
             }
             if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
-            const dview = CollectionDockingView.Instance.props.Document;
-            const fieldKey = CollectionDockingView.Instance.props.fieldKey;
-            const tabdocs = await DocListCastAsync(dview[fieldKey]);
-            runInAction(() => {
-                if (!pinProps?.hidePresBox && !tabdocs?.includes(curPres)) {
-                    tabdocs?.push(curPres);  // bcz: Argh! this is annoying.  if multiple documents are pinned, this will get called multiple times before the presentation view is drawn.  Thus it won't be in the tabdocs list and it will get created multple times.  so need to explicilty add the presbox to the list of open tabs
-                    CollectionDockingView.AddSplit(curPres, "right");
-                }
-                PresBox.Instance?._selectedArray.clear();
-                pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
-                DocumentManager.Instance.jumpToDocument(doc, false, undefined, []);
-                batch.end();
-            });
+            if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) {
+                CollectionDockingView.AddSplit(curPres, "right");
+                setTimeout(() => DocumentManager.Instance.jumpToDocument(doc, false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things
+            }
+            PresBox.Instance?._selectedArray.clear();
+            pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
+            setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs
+            return pinDoc;
         }
     }
 
@@ -282,7 +275,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     componentWillUnmount() {
         this._tabReaction?.();
         this._view && DocumentManager.Instance.RemoveView(this._view);
-        this.tab && CollectionDockingView.Instance.tabMap.delete(this.tab);
 
         this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged);
     }
@@ -430,9 +422,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             }} ref={ref => {
                 if (this._mainCont = ref) {
                     if (this._lastTab) {
-                        console.log("DUP tab")
                         this._view && DocumentManager.Instance.RemoveView(this._view);
-                        CollectionDockingView.Instance.tabMap.delete(this._lastTab);
                     }
                     this._lastTab = this.tab;
                     (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document);
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index befd2020c..09f05f69a 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -609,10 +609,8 @@ export class TreeView extends React.Component<TreeViewProps> {
         return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
     }
 
-    // TODO: currently doc focus works, but can't seem to edit title
-    // onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
     onChildClick = () => {
-        return this.props.onChildClick?.() ?? (ScriptField.MakeFunction(`DocFocusOrOpen(self)`)! || this._editTitleScript?.());
+        return this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!);
     }
 
     onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 77cf0fc84..5f927a2a9 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1489,7 +1489,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         }));
     }
 
-    replaceCanvases = (oldDiv: HTMLElement, newDiv: HTMLElement) => {
+    static replaceCanvases(oldDiv: HTMLElement, newDiv: HTMLElement) {
         if (oldDiv.childNodes && newDiv) {
             for (let i = 0; i < oldDiv.childNodes.length; i++) {
                 this.replaceCanvases(oldDiv.childNodes[i] as HTMLElement, newDiv.childNodes[i] as HTMLElement);
@@ -1508,32 +1508,39 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         }
     }
 
-    updateIcon = () => {
-        const docViewContent = this.props.docViewPath().lastElement().ContentDiv!;
+    updateIcon = () => CollectionFreeFormView.UpdateIcon(
+        this.layoutDoc[Id] + "-icon" + (new Date()).getTime(),
+        this.props.docViewPath().lastElement().ContentDiv!,
+        this.layoutDoc[WidthSym](), this.layoutDoc[HeightSym](), 
+        this.props.PanelWidth(), this.props.PanelHeight(), 0, 
+        (iconFile, nativeWidth, nativeHeight) => {
+                    this.dataDoc.icon = new ImageField(iconFile);
+                    this.dataDoc["icon-nativeWidth"] = nativeWidth;
+                    this.dataDoc["icon-nativeHeight"] = nativeHeight;
+        });
+
+    public static UpdateIcon(filename:string, docViewContent:HTMLElement, width: number, height: number, 
+        panelWidth:number, panelHeight: number, scrollTop:number, cb:(iconFile:string, nativeWidth:number, nativeHeight:number) => any) {
         const newDiv = docViewContent.cloneNode(true) as HTMLDivElement;
-        newDiv.style.width = (this.layoutDoc[WidthSym]()).toString();
-        newDiv.style.height = (this.layoutDoc[HeightSym]()).toString();
+        newDiv.style.width = width.toString();
+        newDiv.style.height = height.toString();
         this.replaceCanvases(docViewContent, newDiv);
-        const htmlString = this._mainCont && new XMLSerializer().serializeToString(newDiv);
-        const nativeWidth = this.layoutDoc[WidthSym]();
-        const nativeHeight = this.layoutDoc[HeightSym]();
-
+        const htmlString = new XMLSerializer().serializeToString(newDiv);
+        const nativeWidth = width;
+        const nativeHeight = height;
         CreateImage(
-            "",
+            Utils.prepend(""),
             document.styleSheets,
             htmlString,
             nativeWidth,
-            nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(),
-            NumCast(this.layoutDoc._scrollTop)
+            nativeWidth * panelHeight / panelWidth,
+            scrollTop
         ).then
             ((data_url: any) => {
-                VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true, this.layoutDoc[Id] + "-icon").then(
-                    returnedfilename => setTimeout(action(() => {
-
-                        this.dataDoc.icon = new ImageField(returnedfilename);
-                        this.dataDoc["icon-nativeWidth"] = nativeWidth;
-                        this.dataDoc["icon-nativeHeight"] = nativeHeight;
-                    }), 500));
+                VideoBox.convertDataUri(data_url, filename).then(
+                    returnedfilename => setTimeout(() => {
+                        cb(returnedfilename as string, nativeWidth, nativeHeight);
+                    }, 500));
             })
             .catch(function (error: any) {
                 console.error('oops, something went wrong!', error);
diff --git a/src/client/views/nodes/WebBoxRenderer.js b/src/client/views/nodes/WebBoxRenderer.js
index d9524dd6e..f3f1bcf5c 100644
--- a/src/client/views/nodes/WebBoxRenderer.js
+++ b/src/client/views/nodes/WebBoxRenderer.js
@@ -214,8 +214,8 @@ var ForeignHtmlRenderer = function (styleSheets) {
                 .replace(/noscript/g, "div").replace(/<div class="mediaset"><\/div>/g, "")  // when scripting isn't available (ie, rendering web pages here), <noscript> tags should become <div>'s.  But for Brown CS, there's a layout problem if you leave the empty <mediaset> tag
                 .replace(/<link[^>]*>/g, "")  // don't need to keep any linked style sheets because we've already processed all style sheets above
                 .replace(/srcset="([^ "]*)[^"]*"/g, "src=\"$1\""); // instead of converting each item in the srcset to a data url, just convert the first one and use that
-            let urlsFoundInHtml = getImageUrlsFromFromHtml(contentHtml);
-            const fetchedResources = await getMultipleResourcesAsBase64(webUrl, urlsFoundInHtml);
+            let urlsFoundInHtml = getImageUrlsFromFromHtml(contentHtml).filter(url => !url.startsWith("data:"));
+            const fetchedResources = webUrl ? await getMultipleResourcesAsBase64(webUrl, urlsFoundInHtml) : [];
             for (let i = 0; i < fetchedResources.length; i++) {
                 const r = fetchedResources[i];
                 if (r.resourceUrl) {
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index be248ab92..e486bcb52 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -48,7 +48,7 @@ export class TopBar extends React.Component {
                                 await CurrentUserUtils.createNewDashboard(Doc.UserDoc());
                                 batch.end();
                             }}>
-                                {"New"}<FontAwesomeIcon icon="plus" />
+                                New<FontAwesomeIcon icon="plus" />
                             </div>
                             </Tooltip>
                             <Tooltip title={<div className="dash-tooltip">Work on a copy of the dashboard layout</div>} placement="bottom">
@@ -57,12 +57,12 @@ export class TopBar extends React.Component {
                                     await CurrentUserUtils.snapshotDashboard(Doc.UserDoc());
                                     batch.end();
                                 }}>
-                                    {"Snapshot"}<FontAwesomeIcon icon="camera" />
+                                    Snapshot<FontAwesomeIcon icon="camera" />
                                 </div>
                             </Tooltip>
                             <Tooltip title={<div className="dash-tooltip">Browsing mode for directly navigating to documents</div>} placement="bottom">
                                 <div className="topbar-icon" style={{ background: MainView.Instance._exploreMode ? Colors.LIGHT_BLUE : undefined }} onClick={action(() => MainView.Instance._exploreMode = !MainView.Instance._exploreMode)}>
-                                    {"Explore"}<FontAwesomeIcon icon="map" />
+                                    Explore<FontAwesomeIcon icon="map" />
                                 </div>
                             </Tooltip>
                         </div>
-- 
cgit v1.2.3-70-g09d2


From b316f7676c5b2acd9e5bd1202f1cdf33a81a6f6a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 8 Jun 2022 13:46:23 -0400
Subject: tweaked doc decorations to be visible when clicking on items in
 headerBar. restored dashboard snapshotting.

---
 src/client/util/CurrentUserUtils.ts | 4 ++++
 src/client/views/MainView.tsx       | 4 ++--
 2 files changed, 6 insertions(+), 2 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 885679b64..a325122bc 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1202,6 +1202,10 @@ export class CurrentUserUtils {
                     docView.dataDoc.thumb = img;
                 });
         }
+
+        const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard);
+        Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy);
+        CurrentUserUtils.openDashboard(userDoc, copy);
     }
 
     public static createNewDashboard = async (userDoc: Doc, id?: string) => {
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index a30db66e4..641178cd9 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -83,7 +83,7 @@ export class MainView extends React.Component {
 
     @computed private get dashboardTabHeight() { return 27; } //  27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js
     @computed private get topOfDashUI() { return Number(DASHBOARD_SELECTOR_HEIGHT.replace("px", "")); }
-    @computed private get topOfHeaderBarDoc() { return this.topOfDashUI + this.topMenuHeight(); }
+    @computed private get topOfHeaderBarDoc() { return this.topOfDashUI; }
     @computed private get topOfSidebarDoc() { return this.topOfDashUI + this.topMenuHeight(); }
     @computed private get topOfMainDoc() { return this.topOfDashUI + this.topMenuHeight() + this.headerBarDocHeight(); }
     @computed private get topOfMainDocContent() { return this.topOfMainDoc + this.dashboardTabHeight; }
@@ -658,7 +658,7 @@ export class MainView extends React.Component {
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
             {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => DocumentLinksButton.LinkEditorDocView = undefined)} docView={DocumentLinksButton.LinkEditorDocView} /> : (null)}
             {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)}
-            <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 2001 }} >
+            <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 1999 }} >
                 <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
             </div>
             <GestureOverlay >
-- 
cgit v1.2.3-70-g09d2


From 0e6f5e580b7e64ecc2002534e1a14ccee36cd28e Mon Sep 17 00:00:00 2001
From: Jenny Yu <jennyyu212@outlook.com>
Date: Wed, 8 Jun 2022 22:55:16 -0700
Subject: feat: basic dashboard view

---
 src/client/views/DashboardView.scss             |  39 +++
 src/client/views/DashboardView.tsx              |  50 ++++
 src/client/views/Main.tsx                       |   1 +
 src/client/views/MainView.tsx                   |  20 +-
 src/client/views/global/globalCssVariables.scss |   2 +-
 src/client/views/topbar/TopBar.scss             | 366 ++++++++++++------------
 src/client/views/topbar/TopBar.tsx              |  85 ++++--
 7 files changed, 341 insertions(+), 222 deletions(-)
 create mode 100644 src/client/views/DashboardView.scss
 create mode 100644 src/client/views/DashboardView.tsx

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

diff --git a/src/client/views/DashboardView.scss b/src/client/views/DashboardView.scss
new file mode 100644
index 000000000..1896d7bfb
--- /dev/null
+++ b/src/client/views/DashboardView.scss
@@ -0,0 +1,39 @@
+.dashboard-view {
+      padding: 50px;
+      display: flex;
+      flex-direction: row;
+
+      .left-menu {
+            display: flex;
+            flex-direction: column;
+            width: 300px;
+      }
+
+      .all-dashboards {
+            display: flex;
+            flex-direction: row;
+            flex-wrap: wrap;
+            overflow-y: scroll;
+      }
+}
+
+
+
+.dashboard-container {
+      border-radius: 10px;
+      width: 250px;
+      border: solid .5px grey;
+      display: flex;
+      flex-direction: column;
+      margin: 0 30px 30px 30px;
+      overflow: hidden;
+
+      &:hover {
+            border: solid 1.5px grey;  
+      }
+
+      .title {
+            margin: 10px;
+            font-weight: 500;
+      }
+}
\ No newline at end of file
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
new file mode 100644
index 000000000..5fd9b550d
--- /dev/null
+++ b/src/client/views/DashboardView.tsx
@@ -0,0 +1,50 @@
+import { observable } from "mobx";
+import { observer } from "mobx-react";
+import * as React from 'react';
+import { Doc, DocListCast } from "../../fields/Doc";
+import { Id } from "../../fields/FieldSymbols";
+import { Cast, ImageCast, StrCast } from "../../fields/Types";
+import { CurrentUserUtils } from "../util/CurrentUserUtils";
+import { UndoManager } from "../util/UndoManager";
+import "./DashboardView.scss"
+
+@observer
+export class DashboardView extends React.Component {
+
+      //TODO: delete dashboard, share dashboard, etc.
+
+      newDashboard = async () => {
+            const batch = UndoManager.StartBatch("new dash");
+            await CurrentUserUtils.createNewDashboard(Doc.UserDoc());
+            batch.end();
+      }
+
+      clickDashboard = async (e: React.MouseEvent, dashboard: Doc) => {
+            if (e.detail === 2) {
+                  Doc.UserDoc().activeDashboard = dashboard
+            }
+      }
+
+      render() {
+            const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
+            return <div className="dashboard-view">
+                  <div className="left-menu">
+                        <div onClick={this.newDashboard}>New</div>
+                        <div>All Dashboards</div>
+                  </div>
+                  <div className="all-dashboards">
+                        {myDashboards.map((dashboard) =>
+                              <div className="dashboard-container" key={dashboard[Id]} onClick={(e) => { this.clickDashboard(e, dashboard) }}>
+                                    <img src="https://media.istockphoto.com/photos/hot-air-balloons-flying-over-the-botan-canyon-in-turkey-picture-id1297349747?b=1&k=20&m=1297349747&s=170667a&w=0&h=oH31fJty_4xWl_JQ4OIQWZKP8C6ji9Mz7L4XmEnbqRU="></img>
+                                    <div className="title"> {StrCast(dashboard.title)} </div>
+                              </div>
+
+                        )}
+                        {myDashboards.map((dashboard) => {
+                              console.log(dashboard.thumb)
+                        })}
+
+                  </div>
+            </div>
+      }
+}
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 517fe097c..49c2dcf34 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -10,6 +10,7 @@ import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { LinkManager } from "../util/LinkManager";
 import { RecordingApi } from "../util/RecordingApi";
 import { CollectionView } from "./collections/CollectionView";
+import { DashboardView } from './DashboardView';
 import { MainView } from "./MainView";
 
 AssignAllExtensions();
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 15e1dbe18..cdf341c3b 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -37,6 +37,7 @@ import { CollectionView, CollectionViewType } from './collections/CollectionView
 import "./collections/TreeView.scss";
 import { ComponentDecorations } from './ComponentDecorations';
 import { ContextMenu } from './ContextMenu';
+import { DashboardView } from './DashboardView';
 import { DictationOverlay } from './DictationOverlay';
 import { DocumentDecorations } from './DocumentDecorations';
 import { GestureOverlay } from './GestureOverlay';
@@ -659,12 +660,19 @@ export class MainView extends React.Component {
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
             {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => DocumentLinksButton.LinkEditorDocView = undefined)} docView={DocumentLinksButton.LinkEditorDocView} /> : (null)}
             {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)}
-            <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 1999 }} >
-                <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
-            </div>
-            <GestureOverlay >
-                {this.mainDashboardArea}
-            </GestureOverlay>
+            
+            {Doc.UserDoc().activeDashboard ? 
+            <>
+                  <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 1999 }} >
+                        <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
+                  </div> 
+                   <GestureOverlay >
+                         {this.mainDashboardArea} 
+                  </GestureOverlay>
+            </> :
+                  <DashboardView/>
+            }
+            
             <PreviewCursor />
             <TaskCompletionBox />
             <ContextMenu />
diff --git a/src/client/views/global/globalCssVariables.scss b/src/client/views/global/globalCssVariables.scss
index a14634bdc..d41f9e06a 100644
--- a/src/client/views/global/globalCssVariables.scss
+++ b/src/client/views/global/globalCssVariables.scss
@@ -38,7 +38,7 @@ $small-text: 9px;
 // misc values
 $border-radius: 0.3em;
 $search-thumnail-size: 130;
-$topbar-height: 32px;
+$topbar-height: 50px;
 $antimodemenu-height: 36px;
 
 // dragged items
diff --git a/src/client/views/topbar/TopBar.scss b/src/client/views/topbar/TopBar.scss
index 923f1892e..9a8e7c2e5 100644
--- a/src/client/views/topbar/TopBar.scss
+++ b/src/client/views/topbar/TopBar.scss
@@ -1,29 +1,30 @@
 @import "../global/globalCssVariables";
 
 .topbar-container {
-    display: flex;
-    flex-direction: column;
-    width: 100%;
-    position: relative;
-    font-size: 10px;
-    line-height: 1;
-    overflow-y: auto;
-    overflow-x: visible;
-    background: $dark-gray;
-    overflow: visible;
-    z-index: 1000;
-
-    .topbar-bar {
-        height: $topbar-height;
-        display: grid;
-        grid-auto-columns: 33.3% 33.3% 33.3%;
-        align-items: center;
-        background-color: $dark-gray;
-
-        .topBar-icon {
+      display: flex;
+      flex-direction: column;
+      width: 100%;
+      position: relative;
+      font-size: 10px;
+      line-height: 1;
+      overflow-y: auto;
+      overflow-x: visible;
+      background: $dark-gray;
+      overflow: visible;
+      z-index: 1000;
+
+      .topbar-button-text {
+            color: $white;
+            padding: 10px;
+            size: 15;
+
+            &:hover {
+                  font-weight: 500;
+            }
+      }
+
+      .topbar-button-icon {
             cursor: pointer;
-            font-size: 12.5px;
-            font-family: 'Roboto';
             width: fit-content;
             display: flex;
             justify-content: center;
@@ -31,190 +32,179 @@
             align-items: center;
             justify-self: center;
             align-self: center;
-            border-radius: $standard-border-radius;
             padding: 5px;
             transition: linear 0.2s;
-            color: $black;
-            background-color: $light-gray;
+            color: $white;
 
             &:hover {
-                background-color: darken($color: $light-gray, $amount: 20);
+                  background-color: darken($color: $light-gray, $amount: 20);
             }
-        }
-
-
-
-
-        .topbar-center {
-            grid-column: 2;
-            display: inline-flex;
-            justify-content: center;
+      }
+
+      .topbar-title {
+            color: $white;
+            font-size: 17;
+            font-weight: 500;
+      }
+
+      .topbar-bar {
+            height: $topbar-height;
+            display: grid;
+            grid-auto-columns: 33.3% 33.3% 33.3%;
             align-items: center;
-            gap: 5px;
-
-            .topbar-dashboards {
-                display: flex;
-                flex-direction: row;
-                gap: 5px;
+            background-color: $dark-gray;
+
+            .topbar-center {
+                  grid-column: 2;
+                  display: inline-flex;
+                  justify-content: center;
+                  align-items: center;
+                  gap: 5px;
+
+                  .topbar-dashboards {
+                        display: flex;
+                        flex-direction: row;
+                        gap: 5px;
+                  }
             }
 
-            .topbar-lozenge-dashboard {
-                display: flex;
-
-
-
-                .topbar-dashSelect {
-                    border: none;
-                    background-color: $dark-gray;
-                    color: $white;
-                    font-family: 'Roboto';
-                    font-size: 17;
-                    font-weight: 500;
-
-                    &:hover {
-                        cursor: pointer;
-                    }
-                }
-            }
-        }
-
-
-        .topbar-right {
-            grid-column: 3;
-            position: relative;
-            display: flex;
-            justify-content: flex-end;
-            gap: 5px;
-            margin-right: 5px;
-        }
-
-        .topbar-left {
-            grid-column: 1;
-            color: black;
-            font-family: 'Roboto';
-            position: relative;
-            display: flex;
-            width: fit-content;
-            gap: 5px;
 
-            .topBar-icon:hover {
-                background-color: $close-red;
+            .topbar-right {
+                  grid-column: 3;
+                  position: relative;
+                  display: flex;
+                  justify-content: flex-end;
+                  gap: 5px;
+                  margin-right: 5px;
             }
 
-            .topbar-lozenge-user,
-            .topbar-lozenge {
-                height: 23;
-                font-size: 12;
-                color: white;
-                font-family: 'Roboto';
-                font-weight: 400;
-                padding: 4px;
-                align-self: center;
-                margin-left: 7px;
-                display: flex;
-                align-items: center;
-
-                .topbar-dashSelect {
-                    border: none;
-                    background-color: transparent;
-                    color: black;
-                    font-family: 'Roboto';
-                    font-size: 17;
-                    font-weight: 500;
-
-                    &:hover {
+            .topbar-left {
+                  grid-column: 1;
+                  color: black;
+                  font-family: 'Roboto';
+                  position: relative;
+                  display: flex;
+                  width: fit-content;
+                  gap: 5px;
+
+                  .topBar-icon:hover {
+                        background-color: $close-red;
+                  }
+
+                  .topbar-lozenge-user,
+                  .topbar-lozenge {
+                        height: 23;
+                        font-size: 12;
+                        color: white;
+                        font-family: 'Roboto';
+                        font-weight: 400;
+                        padding: 4px;
+                        align-self: center;
+                        margin-left: 7px;
+                        display: flex;
+                        align-items: center;
+
+                        .topbar-dashSelect {
+                              border: none;
+                              background-color: transparent;
+                              color: black;
+                              font-family: 'Roboto';
+                              font-size: 17;
+                              font-weight: 500;
+
+                              &:hover {
+                                    cursor: pointer;
+                              }
+                        }
+                  }
+
+                  .topbar-logoff {
+                        border-radius: 3px;
+                        background: olivedrab;
+                        color: white;
+                        display: none;
+                        margin-left: 5px;
+                        padding: 1px 2px 1px 2px;
                         cursor: pointer;
-                    }
-                }
-            }
-
-            .topbar-logoff {
-                border-radius: 3px;
-                background: olivedrab;
-                color: white;
-                display: none;
-                margin-left: 5px;
-                padding: 1px 2px 1px 2px;
-                cursor: pointer;
-            }
-
-            .topbar-logoff {
-                background: red;
-            }
-
-            .topbar-lozenge-user:hover {
-                .topbar-logoff {
-                    display: inline-block;
-                }
-            }
-        }
-
-        .topbar-barChild {
+                  }
 
-            &.topbar-collection {
-                flex: 0 1 auto;
-                margin-left: 2px;
-                margin-right: 2px
-            }
-
-            &.topbar-input {
-                margin: 5px;
-                border-radius: 20px;
-                border: $dark-gray;
-                display: block;
-                width: 130px;
-                -webkit-transition: width 0.4s;
-                transition: width 0.4s;
-                /* align-self: stretch; */
-                outline: none;
-
-                &:focus {
-                    width: 500px;
-                    outline: none;
-                }
-            }
-
-            &.topbar-filter {
-                align-self: stretch;
-
-                button {
-                    transform: none;
-
-                    &:hover {
-                        transform: none;
-                    }
-                }
-            }
+                  .topbar-logoff {
+                        background: red;
+                  }
 
-            &.topbar-submit {
-                margin-left: 2px;
-                margin-right: 2px
+                  .topbar-lozenge-user:hover {
+                        .topbar-logoff {
+                              display: inline-block;
+                        }
+                  }
             }
 
-            &.topbar-close {
-                color: $white;
-                max-height: $topbar-height;
+            .topbar-barChild {
+
+                  &.topbar-collection {
+                        flex: 0 1 auto;
+                        margin-left: 2px;
+                        margin-right: 2px
+                  }
+
+                  &.topbar-input {
+                        margin: 5px;
+                        border-radius: 20px;
+                        border: $dark-gray;
+                        display: block;
+                        width: 130px;
+                        -webkit-transition: width 0.4s;
+                        transition: width 0.4s;
+                        /* align-self: stretch; */
+                        outline: none;
+
+                        &:focus {
+                              width: 500px;
+                              outline: none;
+                        }
+                  }
+
+                  &.topbar-filter {
+                        align-self: stretch;
+
+                        button {
+                              transform: none;
+
+                              &:hover {
+                                    transform: none;
+                              }
+                        }
+                  }
+
+                  &.topbar-submit {
+                        margin-left: 2px;
+                        margin-right: 2px
+                  }
+
+                  &.topbar-close {
+                        color: $white;
+                        max-height: $topbar-height;
+                  }
             }
-        }
-    }
+      }
 }
 
 .topbar-results {
-    display: flex;
-    flex-direction: column;
-    top: 300px;
-    display: flex;
-    flex-direction: column;
-    height: 100%;
-    overflow: visible;
-
-    .no-result {
-        width: 500px;
-        background: $light-gray;
-        padding: 10px;
-        height: 50px;
-        text-transform: uppercase;
-        text-align: left;
-        font-weight: bold;
-    }
+      display: flex;
+      flex-direction: column;
+      top: 300px;
+      display: flex;
+      flex-direction: column;
+      height: 100%;
+      overflow: visible;
+
+      .no-result {
+            width: 500px;
+            background: $light-gray;
+            padding: 10px;
+            height: 50px;
+            text-transform: uppercase;
+            text-align: left;
+            font-weight: bold;
+      }
 }
\ No newline at end of file
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index e486bcb52..1a11925dc 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -5,7 +5,7 @@ import { observer } from "mobx-react";
 import * as React from 'react';
 import { Doc, DocListCast } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
-import { StrCast } from '../../../fields/Types';
+import { Cast, StrCast } from '../../../fields/Types';
 import { Utils } from '../../../Utils';
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
 import { SettingsManager } from "../../util/SettingsManager";
@@ -20,29 +20,54 @@ import "./TopBar.scss";
  */
 @observer
 export class TopBar extends React.Component {
-    render() {
-        const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
-        return (
-            //TODO:glr Add support for light / dark mode
-            <div style={{ pointerEvents: "all" }} className="topbar-container">
-                <div className="topbar-bar" style={{ background: Colors.DARK_GRAY, borderBottom: Borders.STANDARD }}>
-                    <div className="topbar-left">
+      navigateToHome = () => {
+            Doc.UserDoc().activeDashboard = undefined
+      }
+      render() {
+            const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
+            const activeDashboard = Cast(Doc.UserDoc().activeDashboard, Doc, null)
+            return (
+                  //TODO:glr Add support for light / dark mode
+                  <div style={{ pointerEvents: "all" }} className="topbar-container">
+                        <div className="topbar-bar" style={{ background: Colors.DARK_GRAY, borderBottom: Borders.STANDARD }}>
+                              <div className="topbar-left">
+                                    <div className="topbar-button-text" onClick={this.navigateToHome}>Home</div>
+                              </div>
+                              <div className="topbar-center" >
+                                    <div className="topbar-title">
+                                          {activeDashboard ? StrCast(activeDashboard.title) : "Dash"}
+                                    </div>
+                              </div>
+                              <div className="topbar-right" >
+                                    <div className="topbar-button-icon" >
+                                          <FontAwesomeIcon icon="question-circle" />
+                                    </div>
+                                    <div className="topbar-button-icon" onClick={() => SettingsManager.Instance.open()}>
+                                          <FontAwesomeIcon icon="cog" />
+                                    </div>
+                              </div>
+
+                              {/* <div className="topbar-left">
                         <div className="topbar-lozenge-user">
                             {`${Doc.CurrentUserEmail}`}
                         </div>
                         <div className="topbar-icon" onClick={() => window.location.assign(Utils.prepend("/logout"))}>
                             {"Log out"}
                         </div>
-                    </div>
-                    <div className="topbar-center" >
+                    </div> */}
+                              {/* <div className="topbar-center" >
                         <div className="topbar-lozenge-dashboard">
-                            <select className="topbar-dashSelect" onChange={undoBatch(e => CurrentUserUtils.openDashboard(Doc.UserDoc(), myDashboards[Number(e.target.value)]))}
+                              <div style={{ color: Colors.WHITE }}>
+                                    {activeDashboard ? StrCast(activeDashboard.title) : "Dash"}
+                              </div> */}
+
+                              {/* <select className="topbar-dashSelect" onChange={undoBatch(e => CurrentUserUtils.openDashboard(Doc.UserDoc(), myDashboards[Number(e.target.value)]))}
                                 value={myDashboards.indexOf(CurrentUserUtils.ActiveDashboard)}
                                 style={{ color: Colors.WHITE }}>
                                 {myDashboards.map((dash, i) => <option key={dash[Id]} value={i}> {StrCast(dash.title)} </option>)}
-                            </select>
-                        </div>
-                        <div className="topbar-dashboards">
+                            </select> */}
+                              {/* </div> */}
+                              {/* <div className="topbar-dashboards">
                             <Tooltip title={<div className="dash-tooltip">Create a new dashboard </div>} placement="bottom"><div className="topbar-icon" onClick={async () => {
                                 const batch = UndoManager.StartBatch("new dash");
                                 await CurrentUserUtils.createNewDashboard(Doc.UserDoc());
@@ -65,19 +90,25 @@ export class TopBar extends React.Component {
                                     Explore<FontAwesomeIcon icon="map" />
                                 </div>
                             </Tooltip>
-                        </div>
-                    </div>
-                    <div className="topbar-right" >
-                        <div className="topbar-icon" onClick={() => window.open(
+                        </div> */}
+                              {/* </div> */}
+                              {/* <div className="topbar-right" > */}
+                              {/* <div onClick={() => window.open(
                             "https://brown-dash.github.io/Dash-Documentation/", "_blank")}>
-                            {"Help"}<FontAwesomeIcon icon="question-circle" />
-                        </div>
-                        <div className="topbar-icon" onClick={() => SettingsManager.Instance.open()}>
-                            {"Settings"}<FontAwesomeIcon icon="cog" />
+                              <FontAwesomeIcon icon="question-circle" />
+                          </div>
+                          <div onClick={() => SettingsManager.Instance.open()}>
+                          <FontAwesomeIcon icon="cog" />
+                          </div> */}
+                              {/* <div className="topbar-icon" >
+                                          <FontAwesomeIcon icon="question-circle" />
+                                    </div>
+                                    <div className="topbar-icon" onClick={() => SettingsManager.Instance.open()}>
+                                          <FontAwesomeIcon icon="cog" />
+                                    </div>
+                              </div> */}
                         </div>
-                    </div>
-                </div>
-            </div >
-        );
-    }
+                  </div >
+            );
+      }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 3213c24210d67af0a4b027863773ad383404a59c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 9 Jun 2022 11:07:34 -0400
Subject: renamed fitToBox to fitContentsToBox.  fixed pinning multiple
 documents at same time to not bring up presBox multiple times.

---
 src/client/documents/Documents.ts                  |  3 +-
 src/client/views/DocumentButtonBar.tsx             | 21 ++----------
 src/client/views/LightboxView.tsx                  |  2 --
 src/client/views/MainView.tsx                      |  2 +-
 src/client/views/PropertiesButtons.tsx             |  2 +-
 src/client/views/PropertiesView.tsx                |  2 +-
 src/client/views/collections/CollectionMenu.tsx    |  4 +--
 .../views/collections/CollectionTimeView.tsx       |  2 +-
 src/client/views/collections/TabDocView.tsx        | 25 +++++++--------
 src/client/views/collections/TreeView.tsx          |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 37 +++++++---------------
 .../collections/collectionFreeForm/MarqueeView.tsx |  2 +-
 .../CollectionMulticolumnView.tsx                  |  2 +-
 .../CollectionMultirowView.tsx                     |  2 +-
 .../collectionSchema/CollectionSchemaView.tsx      |  2 +-
 src/client/views/nodes/DocumentView.tsx            |  4 +--
 src/client/views/nodes/MapBox/MapBox.tsx           |  6 ++--
 .../views/nodes/formattedText/FormattedTextBox.tsx |  6 ++--
 src/client/views/nodes/trails/PresBox.tsx          | 14 ++++----
 src/client/views/nodes/trails/PresElementBox.tsx   |  2 +-
 src/fields/documentSchemas.ts                      |  6 ++--
 src/mobile/AudioUpload.tsx                         |  4 +--
 22 files changed, 60 insertions(+), 92 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index acc1e341a..144a3d9cf 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -113,7 +113,8 @@ export class DocumentOptions {
     _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height", true);
     _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units", true);
     _fitWidth?: BOOLt = new BoolInfo("whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)", true);
-    _fitToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents
+    _fitContentsToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents
+    _contentBounds?: List<number>; // the (forced) bounds of the document to display. format is: [left, top, right, bottom]
     _lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged
     _lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed
     _isPushpin?: boolean; // whether document, when clicked, toggles display of its link target
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 41224b1c5..0bbe473d7 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -204,27 +204,13 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <div className="dash-tooltip">{SelectionManager.Views().length > 1 ? "Pin multiple documents to presentation" : "Pin to presentation"}</div>}>
             <div className="documentButtonBar-icon"
                 style={{ color: "white" }}
-                onClick={undoBatch(e => this.props.views().map(view => view && this.pinWithView(view.props.Document)))}>
+                onClick={(e => TabDocView.PinDoc(this.props.views().filter(v => v).map(dv => dv!.rootDoc), {pinDocView: true}))}
+             >
                 <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
             </div>
         </Tooltip>;
     }
 
-    @undoBatch
-    @action
-    pinWithView = (targetDoc: Doc) =>  targetDoc && TabDocView.PinDoc(targetDoc, {pinDocView: true});
-
-    @computed
-    get pinWithViewButton() {
-        const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: "auto", width: 17, transform: 'translate(0, 1px)' }} />;
-        const targetDoc = this.view0?.props.Document;
-        return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Pin with current view"}</div></>}>
-            <div className="documentButtonBar-icon" onClick={() => this.pinWithView(targetDoc)}>
-                {presPinWithViewIcon}
-            </div>
-        </Tooltip>;
-    }
-
     @computed
     get shareButton() {
         const targetDoc = this.view0?.props.Document;
@@ -349,9 +335,6 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <div className="documentButtonBar-button">
                 {this.pinButton}
             </div>
-            {/* <div className="documentButtonBar-button">
-                {this.pinWithViewButton}
-            </div> */}
             {!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button">
                 {this.shareButton}
             </div>}
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index f67f37bfb..fcc4aea13 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -207,7 +207,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
 
     future = () => LightboxView._future;
     tourMap = () => LightboxView._tourMap;
-    fitToBox = () => LightboxView._docTarget === LightboxView.LightboxDoc;
     render() {
         let downx = 0, downy = 0;
         return !LightboxView.LightboxDoc ? (null) :
@@ -241,7 +240,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                         DataDoc={undefined}
                         LayoutTemplate={LightboxView.LightboxDocTemplate}
                         addDocument={undefined}
-                        // fitContentsToDoc={this.fitToBox} // bcz: why do we want this?  when we initially open a colletion, we shrinkwrap it which allows for user navigation.  if we later encounter a collection, it's not clear to me that we want to make it either shrinkwrap or fitContents...
                         isDocumentActive={returnFalse}
                         isContentActive={returnTrue}
                         addDocTab={this.addDocTab}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 641178cd9..84a131a57 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -287,7 +287,7 @@ export class MainView extends React.Component {
                 styleProvider={DefaultStyleProvider}
                 rootSelected={returnTrue}
                 removeDocument={returnFalse}
-                fitContentsToDoc={returnTrue}
+                fitContentsToBox={returnTrue}
                 isDocumentActive={returnTrue} // headerBar is always documentActive (ie, the docView gets pointer events)
                 isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive
                 ScreenToLocalTransform={this.headerBarScreenXf}
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index f24ff09db..74055f057 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -71,7 +71,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         return this.propertyToggleBtn("Lock\xA0View", "_lockedTransform", on => `${on ? "Unlock" : "Lock"} panning of view`, on => "lock");
     }
     @computed get fitContentButton() {
-        return this.propertyToggleBtn("View All", "_fitToBox", on => `${on ? "Don't" : "Do"} fit content to container visible area`, on => "eye");
+        return this.propertyToggleBtn("View All", "_fitContentsToBox", on => `${on ? "Don't" : "Do"} fit content to container visible area`, on => "eye");
     }
     @computed get fitWidthButton() {
         return this.propertyToggleBtn("Fit\xA0Width", "_fitWidth", on => `${on ? "Don't" : "Do"} fit content to width of container`, on => "arrows-alt-h");
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index bcfd2dd56..16eb95cf4 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -300,7 +300,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     Document={layoutDoc}
                     DataDoc={this.dataDoc}
                     renderDepth={1}
-                    fitContentsToDoc={returnTrue}
+                    fitContentsToBox={returnTrue}
                     rootSelected={returnFalse}
                     styleProvider={DefaultStyleProvider}
                     docViewPath={returnEmptyDoclist}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 01e77f342..39f6466d6 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -252,8 +252,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     };
     _clusterCommand = {
         params: ["target"], title: "fit content",
-        script: "self.target._fitToBox = !self.target._fitToBox;",
-        immediate: undoBatch((source: Doc[]) => this.target._fitToBox = !this.target._fitToBox),
+        script: "self.target._fitContentsToBox = !self.target._fitContentsToBox;",
+        immediate: undoBatch((source: Doc[]) => this.target._fitContentsToBox = !this.target._fitContentsToBox),
         initialize: emptyFunction
     };
     _fitContentCommand = {
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 4f6f45d2f..7573b938a 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -135,7 +135,7 @@ export class CollectionTimeView extends CollectionSubView() {
             onClick={this.contentsDown}>
             <CollectionFreeFormView {...this.props}
                 engineProps={{ pivotField: this.pivotField, docFilters: this.childDocFilters, docRangeFilters: this.childDocRangeFilters }}
-                fitContentsToDoc={returnTrue}
+                fitContentsToBox={returnTrue}
                 childClickScript={this._childClickedScript}
                 viewDefDivClick={this._viewDefDivClick}
                 childFreezeDimensions={true}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index f6a75e2db..5cc92a305 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -192,15 +192,15 @@ export class TabDocView extends React.Component<TabDocViewProps> {
      * Adds a document to the presentation view
      **/
     @action
-    public static PinDoc(doc: Doc, pinProps?: PinProps) {
-        //add this new doc to props.Document
+    public static PinDoc(docs: Doc|Doc[], pinProps?: PinProps) {
+        const docList = ((docs instanceof Doc) ? [docs]: docs);
+        const batch = UndoManager.StartBatch("pinning doc");
 
         // all docs will be added to the ActivePresentation as stored on CurrentUserUtils
         const curPres = CurrentUserUtils.ActivePresentation;
-        if (curPres) {
+        curPres && docList.forEach(doc => {
             // Edge Case 1: Cannot pin document to itself
             if (doc === curPres) { alert("Cannot pin presentation document to itself"); return; }
-            const batch = UndoManager.StartBatch("pinning doc");
             const pinDoc = Doc.MakeAlias(doc);
             pinDoc.presentationTargetDoc = doc;
             pinDoc.title = doc.title + " - Slide";
@@ -279,17 +279,16 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 pinDoc.presMovement = PresMovement.None;
             }
             if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
-            if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) {
-                const docs = Cast(Cast(Doc.UserDoc().myOverlayDocs, Doc, null).data, listSpec(Doc), []);
-                if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1);
-                CollectionDockingView.AddSplit(curPres, "right");
-                setTimeout(() => DocumentManager.Instance.jumpToDocument(doc, false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things
-            }
             PresBox.Instance?._selectedArray.clear();
             pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
-            setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs
-            return pinDoc;
+        });
+        if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) {
+            const docs = Cast(Cast(Doc.UserDoc().myOverlayDocs, Doc, null).data, listSpec(Doc), []);
+            if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1);
+            CollectionDockingView.AddSplit(curPres, "right");
+            setTimeout(() => DocumentManager.Instance.jumpToDocument(docList.lastElement(), false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things
         }
+        setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs
     }
 
     componentDidMount() {
@@ -572,7 +571,7 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                     docFilters={CollectionDockingView.Instance.childDocFilters}
                     docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
                     searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
-                    fitContentsToDoc={returnTrue}
+                    fitContentsToBox={returnTrue}
                 />
                 <div className="miniOverlay" onPointerDown={this.miniDown} >
                     <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% `, }} />
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 09f05f69a..8824750a3 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -796,7 +796,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             isDocumentActive={isActive}
             styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
             hideTitle={asText}
-            fitContentsToDoc={returnTrue}
+            fitContentsToBox={returnTrue}
             hideDecorationTitle={this.props.treeView.outlineMode}
             hideResizeHandles={this.props.treeView.outlineMode}
             onClick={this.onChildClick}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index b70e6ff98..ec3cf1fe8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -9,7 +9,7 @@ import { InkData, InkField, InkTool, PointData, Segment } from "../../../../fiel
 import { List } from "../../../../fields/List";
 import { ObjectField } from "../../../../fields/ObjectField";
 import { RichTextField } from "../../../../fields/RichTextField";
-import { createSchema, listSpec } from "../../../../fields/Schema";
+import { listSpec } from "../../../../fields/Schema";
 import { ScriptField } from "../../../../fields/ScriptField";
 import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
 import { ImageField } from "../../../../fields/URLField";
@@ -41,6 +41,7 @@ import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveIn
 import { LightboxView } from "../../LightboxView";
 import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
 import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from "../../nodes/DocumentView";
+import { FieldViewProps } from "../../nodes/FieldView";
 import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
 import { PresBox } from "../../nodes/trails/PresBox";
 import { VideoBox } from "../../nodes/VideoBox";
@@ -50,27 +51,13 @@ import { CollectionDockingView } from "../CollectionDockingView";
 import { CollectionSubView } from "../CollectionSubView";
 import { TreeViewType } from "../CollectionTreeView";
 import { CollectionViewType } from "../CollectionView";
+import { TabDocView } from "../TabDocView";
 import { computePivotLayout, computerPassLayout, computerStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from "./CollectionFreeFormLayoutEngines";
 import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
 import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
-import { FieldView, FieldViewProps } from "../../nodes/FieldView";
-import { TabDocView } from "../TabDocView";
 
-export const panZoomSchema = createSchema({
-    _panX: "number",
-    _panY: "number",
-    _currentTimecode: "number",
-    _timecodeToShow: "number",
-    _currentFrame: "number",
-    _useClusters: "boolean",
-    _viewTransition: "string",
-    _xPadding: "number",         // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
-    _yPadding: "number",         // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
-    _fitToBox: "boolean",
-    scrollHeight: "number"    // this will be set when the collection is an annotation overlay for a PDF/Webpage
-});
 
 export type collectionFreeformViewProps = {
     annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox)
@@ -138,20 +125,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x))
         };
     }
-    @computed get fitToContent() { return (this.props.fitContentsToDoc?.() || this.Document._fitToBox) && !this.isAnnotationOverlay; }
+    @computed get fitContentsToBox() { return (this.props.fitContentsToBox?.() || this.Document._fitContentsToBox) && !this.isAnnotationOverlay; }
     @computed get contentBounds() { 
         const cb = Cast(this.rootDoc.contentBounds, listSpec("number"));
         return cb ? {x:cb[0], y:cb[1], r:cb[2], b: cb[3]} : 
             this.props.contentBounds?.() ?? 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.fitToContent ? 0 : Doc.NativeWidth(this.Document); }
-    @computed get nativeHeight() { return this.fitToContent ? 0 : Doc.NativeHeight(this.Document); }
+    @computed get nativeWidth() { return this.fitContentsToBox ? 0 : Doc.NativeWidth(this.Document); }
+    @computed get nativeHeight() { return this.fitContentsToBox ? 0 : Doc.NativeHeight(this.Document); }
     @computed get cachedCenteringShiftX(): number {
-        const scaling = this.fitToContent || !this.contentScaling ? 1 : this.contentScaling;
+        const scaling = this.fitContentsToBox || !this.contentScaling ? 1 : this.contentScaling;
         return this.props.isAnnotationOverlay ? 0 : this.props.PanelWidth() / 2 / scaling;  // shift so pan position is at center of window for non-overlay collections
     }
     @computed get cachedCenteringShiftY(): number {
-        const scaling = this.fitToContent || !this.contentScaling ? 1 : this.contentScaling;
+        const scaling = this.fitContentsToBox || !this.contentScaling ? 1 : this.contentScaling;
         return this.props.isAnnotationOverlay ? 0 : this.props.PanelHeight() / 2 / scaling;// shift so pan position is at center of window for non-overlay collections
     }
     @computed get cachedGetLocalTransform(): Transform {
@@ -176,8 +163,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         this.layoutDoc._panY = vals.bounds.cy;
         this.layoutDoc._viewScale = vals.scale;
     }
-    freeformData = (force?: boolean) => !this._firstRender && (this.fitToContent || force) ? this.fitToContentVals : undefined;
-    reverseNativeScaling = () => this.fitToContent ? true : false;
+    freeformData = (force?: boolean) => !this._firstRender && (this.fitContentsToBox || force) ? this.fitToContentVals : undefined;
+    reverseNativeScaling = () => this.fitContentsToBox ? true : false;
     panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document._panX);
     panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY);
     zoomScaling = () => (this.freeformData()?.scale ?? NumCast(this.Document[this.scaleFieldKey], 1));
@@ -1272,7 +1259,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
             pointerEvents={this.pointerEvents}
             jitterRotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
-        //fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
+        //fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
         />;
     }
     addDocTab = action((doc: Doc, where: string) => {
@@ -1616,7 +1603,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
         appearanceItems.push({ description: "Reset View", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" });
         !Doc.UserDoc().noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
-        appearanceItems.push({ description: `${this.fitToContent ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
+        appearanceItems.push({ description: `${this.fitContentsToBox ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitContentsToBox = !this.fitContentsToBox, icon: !this.fitContentsToBox ? "expand-arrows-alt" : "compress-arrows-alt" });
         appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, {pinDocView:true, panelWidth: this.props.PanelWidth(), panelHeight:this.props.PanelHeight()}), icon: "map-pin" });
         //appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" });
         this.props.ContainingCollectionView &&
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 3fac3350a..6df2bb102 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -516,7 +516,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             d.y = NumCast(d.y) - this.Bounds.top;
             return d;
         });
-        const summary = Docs.Create.TextDocument("", { _backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" });
+        const summary = Docs.Create.TextDocument("", { _backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitContentsToBox: true, _showSidebar: true, title: "overview" });
         const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: "transparent" });
         DocUtils.MakeLink({ doc: summary }, { doc: portal }, "summary of:summarized by", "");
 
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index e2dfb25e2..7147ae485 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -257,7 +257,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
             hideResizeHandles={this.props.childHideResizeHandles?.()}
             hideDecorationTitle={this.props.childHideDecorationTitle?.()}
-            fitContentsToDoc={this.props.fitContentsToDoc}
+            fitContentsToBox={this.props.fitContentsToBox}
             PanelWidth={width}
             PanelHeight={height}
             rootSelected={this.rootSelected}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 3010e36aa..3bda56c33 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -264,7 +264,7 @@ export class CollectionMultirowView extends CollectionSubView() {
             isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
             hideResizeHandles={this.props.childHideResizeHandles?.()}
             hideDecorationTitle={this.props.childHideDecorationTitle?.()}
-            fitContentsToDoc={this.props.fitContentsToDoc}
+            fitContentsToBox={this.props.fitContentsToBox}
             focus={this.props.focus}
             docFilters={this.childDocFilters}
             docRangeFilters={this.childDocRangeFilters}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index b731479a5..f45068b6a 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -402,7 +402,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                 <DocumentView
                     Document={this.previewDocument}
                     DataDoc={undefined}
-                    fitContentsToDoc={returnTrue}
+                    fitContentsToBox={returnTrue}
                     freezeDimensions={true}
                     dontCenter={"y"}
                     focus={DocUtils.DefaultFocus}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 26dec3bd5..2a518bb57 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -85,7 +85,7 @@ export interface DocComponentView {
     getAnchor?: () => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
     scrollFocus?: (doc: Doc, smooth: boolean) => Opt<number>; // returns the duration of the focus
     setViewSpec?: (anchor: Doc, preview: boolean) => void;  // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
-    reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height.  However, some content views (e.g., FreeFormView w/ fitToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
+    reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height.  However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
     shrinkWrap?: () => void;  // requests a document to display all of its contents with no white space.  currently only implemented (needed?) for freeform views
     menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected.
     isAnyChildContentActive?: () => boolean; // is any child content of the document active
@@ -113,7 +113,7 @@ export interface DocumentViewSharedProps {
     Document: Doc;
     DataDoc?: Doc;
     contentBounds?: () => (undefined|{x:number, y:number, r:number, b:number});
-    fitContentsToDoc?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitToBox property on a Document
+    fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitContentsToBox property on a Document
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
     suppressSetHeight?: boolean;
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 9f1c019fe..5f4c17ee6 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -259,7 +259,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
             if (this._loadPending && this._map.getBounds()) {
                 this._loadPending = false;
-                this.layoutDoc.fitToBox && this.fitBounds(this._map);
+                this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
             }
         }, 250);
         // listener to addmarker event
@@ -274,7 +274,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     centered = () => {
         if (this._loadPending && this._map.getBounds()) {
             this._loadPending = false;
-            this.layoutDoc.fitToBox && this.fitBounds(this._map);
+            this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
         }
         this.dataDoc.mapLat = this._map.getCenter()?.lat();
         this.dataDoc.mapLng = this._map.getCenter()?.lng();
@@ -284,7 +284,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     zoomChanged = () => {
         if (this._loadPending && this._map.getBounds()) {
             this._loadPending = false;
-            this.layoutDoc.fitToBox && this.fitBounds(this._map);
+            this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
         }
         this.dataDoc.mapZoom = this._map.getZoom();
     }
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 7d0302b26..4e431e7bd 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -495,7 +495,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 // embed document when dragg marked as embed
             } else if (de.embedKey) {
                 const target = dragData.droppedDocuments[0];
-                target._fitToBox = true;
+                target._fitContentsToBox = true;
                 const node = schema.nodes.dashDoc.create({
                     width: target[WidthSym](),
                     height: target[HeightSym](),
@@ -1557,7 +1557,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }
         }
     }
-    fitToBox = () => this.props.Document._fitToBox;
+    fitContentsToBox = () => this.props.Document._fitContentsToBox;
     sidebarContentScaling = () => (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
         if (!this.layoutDoc._showSidebar) this.toggleSidebar();
@@ -1632,7 +1632,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     ScreenToLocalTransform={this.sidebarScreenToLocal}
                     renderDepth={this.props.renderDepth + 1}
                     setHeight={this.setSidebarHeight}
-                    fitContentsToDoc={this.fitToBox}
+                    fitContentsToBox={this.fitContentsToBox}
                     noSidebar={true}
                     fieldKey={this.layoutDoc.sidebarViewType === "translation" ? `${this.fieldKey}-translation` : `${this.fieldKey}-annotations`} />;
         };
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 6de04bd31..cebf9487a 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -35,8 +35,8 @@ export interface PinProps {
     setPosition?: boolean;
     hidePresBox?: boolean;
     pinWithView?: PinViewProps;
-    pinDocView?: boolean;
-    panelWidth?: number;
+    pinDocView?: boolean;  // whether the current view specs of the document should be saved the pinned document
+    panelWidth?: number;  // panel width and height of the document (used to compute the bounds of the pinned view area)
     panelHeight?: number
 }
 
@@ -997,7 +997,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     /**
      * Method called for viewing paths which adds a single line with
      * points at the center of each document added.
-     * Design choice: When this is called it sets _fitToBox as true so the
+     * Design choice: When this is called it sets _fitContentsToBox as true so the
      * user can have an overview of all of the documents in the collection.
      * (Design needed for when documents in presentation trail are in another
      * collection)
@@ -1800,16 +1800,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 doc = Docs.Create.FreeformDocument([], { title: input ? input : "Blank slide", _width: 400, _height: 225, x: x, y: y });
                 break;
             case 'title':
-                doc = Docs.Create.FreeformDocument([title, subtitle], { title: input ? input : "Title slide", _width: 400, _height: 225, _fitToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([title, subtitle], { title: input ? input : "Title slide", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             case 'header':
-                doc = Docs.Create.FreeformDocument([header], { title: input ? input : "Section header", _width: 400, _height: 225, _fitToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([header], { title: input ? input : "Section header", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             case 'content':
-                doc = Docs.Create.FreeformDocument([contentTitle, content], { title: input ? input : "Title and content", _width: 400, _height: 225, _fitToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([contentTitle, content], { title: input ? input : "Title and content", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             case 'twoColumns':
-                doc = Docs.Create.FreeformDocument([contentTitle, content1, content2], { title: input ? input : "Title and two columns", _width: 400, _height: 225, _fitToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([contentTitle, content1, content2], { title: input ? input : "Title and two columns", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             default:
                 break;
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 5eff47a86..ba0193e4b 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -85,7 +85,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     isContentActive={this.props.isContentActive}
                     addDocTab={returnFalse}
                     pinToPres={returnFalse}
-                    fitContentsToDoc={returnTrue}
+                    fitContentsToBox={returnTrue}
                     PanelWidth={this.embedWidth}
                     PanelHeight={this.embedHeight}
                     ScreenToLocalTransform={Transform.Identity}
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index e532becb5..be39e0709 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -36,8 +36,8 @@ export const documentSchema = createSchema({
     _nativeHeight: "number",    // "
     _width: "number",           // width of document in its container's coordinate system
     _height: "number",          // "
-    _xPadding: "number",        // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
-    _yPadding: "number",        // pixels of padding on top/bottom of collectionfreeformview contents when fitToBox is set
+    _xPadding: "number",        // pixels of padding on left/right of collectionfreeformview contents when fitContentsToBox is set
+    _yPadding: "number",        // pixels of padding on top/bottom of collectionfreeformview contents when fitContentsToBox is set
     _xMargin: "number",         // margin added on left/right of most documents to add separation from their container
     _yMargin: "number",         // margin added on top/bottom of most documents to add separation from their container
     _overflow: "string",        // sets overflow behvavior for CollectionFreeForm views
@@ -59,7 +59,7 @@ export const documentSchema = createSchema({
     borderRounding: "string",   // border radius rounding of document
     boxShadow: "string",        // the amount of shadow around the perimeter of a document
     color: "string",            // foreground color of document
-    fitToBox: "boolean",        // whether freeform view contents should be zoomed/panned to fill the area of the document view
+    fitContentsToBox: "boolean",// whether freeform view contents should be zoomed/panned to fill the area of the document view box
     fontSize: "string",
     hidden: "boolean",          // whether a document should not be displayed
     isInkMask: "boolean",       // is the document a mask (ie, sits on top of other documents, has an unbounded width/height that is dark, and content uses 'hard-light' mix-blend-mode to let other documents pop through)
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index 418464f0e..64baa351c 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -19,7 +19,7 @@ import React = require('react');
 
 @observer
 export class AudioUpload extends React.Component {
-    @observable public _audioCol: Doc = FieldValue(Cast(Docs.Create.FreeformDocument([Cast(Docs.Create.AudioDocument(nullAudio, { title: "mobile audio", _width: 500, _height: 100 }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
+    @observable public _audioCol: Doc = FieldValue(Cast(Docs.Create.FreeformDocument([Cast(Docs.Create.AudioDocument(nullAudio, { title: "mobile audio", _width: 500, _height: 100 }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitContentsToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
 
     /**
      * Handles the onclick functionality for the 'Restart' button
@@ -36,7 +36,7 @@ export class AudioUpload extends React.Component {
                     title: "mobile audio",
                     _width: 500,
                     _height: 100
-                }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
+                }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitContentsToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
     }
 
     /** 
-- 
cgit v1.2.3-70-g09d2


From 331cb0bec8089c4435126542ed3181fe227eff51 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 9 Jun 2022 11:53:33 -0400
Subject: fixes to navigating to slide document targets in header bar

---
 src/client/views/MainView.tsx                                 |  1 +
 .../collectionMulticolumn/CollectionMulticolumnView.tsx       |  1 +
 .../collectionMulticolumn/CollectionMultirowView.tsx          |  1 +
 src/client/views/nodes/trails/PresBox.tsx                     | 11 ++++++++---
 4 files changed, 11 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index adcfd82dc..9516ea017 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -292,6 +292,7 @@ export class MainView extends React.Component {
                 isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive
                 ScreenToLocalTransform={this.headerBarScreenXf}
                 childHideResizeHandles={returnTrue}
+                dontRegisterView={true}
                 hideResizeHandles={true}
                 PanelWidth={this.headerBarDocWidth}
                 PanelHeight={this.headerBarDocHeight}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 7147ae485..b7ba94940 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -272,6 +272,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
+            dontRegisterView={this.props.dontRegisterView}
             addDocument={this.props.addDocument}
             moveDocument={this.props.moveDocument}
             removeDocument={this.props.removeDocument}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 3bda56c33..338639a83 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -271,6 +271,7 @@ export class CollectionMultirowView extends CollectionSubView() {
             searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
+            dontRegisterView={this.props.dontRegisterView}
             addDocument={this.props.addDocument}
             moveDocument={this.props.moveDocument}
             removeDocument={this.props.removeDocument}
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index db2f58be7..da65e7919 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -333,11 +333,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document;
         if (bestTarget) console.log(bestTarget.title, bestTarget.type);
         else console.log("no best target");
-        if (bestTarget)  this._navTimer = PresBox.navigateToDoc(bestTarget, activeItem);
+        if (bestTarget)  this._navTimer = PresBox.navigateToDoc(bestTarget, activeItem, false);
     }
 
     @action
-    static navigateToDoc(bestTarget:Doc, activeItem:Doc)  {
+    static navigateToDoc(bestTarget:Doc, activeItem:Doc, jumpToDoc:boolean)  {
+        if (jumpToDoc) {
+            const srcContext = Cast(bestTarget.context, Doc, null) ?? Cast(Cast(bestTarget.annotationOn, Doc, null)?.context, Doc, null);
+            const openInTab = (doc: Doc, finished?: () => void) => CollectionDockingView.AddSplit(doc, "right");
+            DocumentManager.Instance.jumpToDocument(bestTarget, true, openInTab, srcContext ? [srcContext]:[], undefined, undefined, undefined,undefined, undefined, true, NumCast(activeItem.presZoom)); // documents open in new tab instead of on right    
+        }
         if (bestTarget.type === DocumentType.PDF || bestTarget.type === DocumentType.WEB || bestTarget.type === DocumentType.RTF || bestTarget._viewType === CollectionViewType.Stacking) {
             bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
             bestTarget._scrollTop = activeItem.presPinViewScroll;
@@ -2576,5 +2581,5 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 }
 
 ScriptingGlobals.add(function navigateToDoc(bestTarget:Doc, activeItem:Doc) {
-    PresBox.navigateToDoc(bestTarget, activeItem);
+    PresBox.navigateToDoc(bestTarget, activeItem, true);
 });
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 43968431d3c94cea07f06421d29ac0f190c55cde Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 9 Jun 2022 17:25:55 -0400
Subject: added activePage field for choosing dash page layout.

---
 src/client/util/CurrentUserUtils.ts |  1 +
 src/client/views/DashboardView.tsx  |  3 ++-
 src/client/views/MainView.tsx       | 21 +++++++++++----------
 src/client/views/topbar/TopBar.tsx  |  2 +-
 4 files changed, 15 insertions(+), 12 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 3107650d9..3b1009532 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1093,6 +1093,7 @@ export class CurrentUserUtils {
                     Docs.newAccount = !(field instanceof Doc);
                     await Docs.Prototypes.initialize();
                     const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc;
+                    Docs.newAccount &&(userDoc.activePage = "home");
                     const updated = this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId);
                     (await DocListCastAsync(Cast(Doc.UserDoc().myLinkDatabase, Doc, null)?.data))?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager
                         const a1 = await Cast(link?.anchor1, Doc, null);
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 3cfece970..b08151c0f 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -22,7 +22,8 @@ export class DashboardView extends React.Component {
 
       clickDashboard = async (e: React.MouseEvent, dashboard: Doc) => {
             if (e.detail === 2) {
-                  Doc.UserDoc().activeDashboard = dashboard
+                  Doc.UserDoc().activeDashboard = dashboard;
+                  Doc.UserDoc().activePage = "dashboard";
             }
       }
 
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index ad041384c..5fd76c388 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -662,16 +662,17 @@ export class MainView extends React.Component {
             {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => DocumentLinksButton.LinkEditorDocView = undefined)} docView={DocumentLinksButton.LinkEditorDocView} /> : (null)}
             {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)}
             
-            {Doc.UserDoc().activeDashboard ? 
-            <>
-                  <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 1999 }} >
-                        <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
-                  </div> 
-                   <GestureOverlay >
-                         {this.mainDashboardArea} 
-                  </GestureOverlay>
-            </> :
-                  <DashboardView/>
+            {((page:string) => {
+                switch (page) {
+                    case "dashboard":
+                    default:return <>
+                            <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 1999 }} >
+                                    <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
+                            </div> 
+                            <GestureOverlay> {this.mainDashboardArea}   </GestureOverlay>
+                        </>;
+                    case "home":  return <DashboardView/>;
+                } })(StrCast(Doc.UserDoc().activePage))
             }
             
             <PreviewCursor />
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 21ef807ff..258891099 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -21,7 +21,7 @@ import "./TopBar.scss";
 @observer
 export class TopBar extends React.Component {
       navigateToHome = () => {
-            CurrentUserUtils.CaptureDashboardThumbnail()?.then(() => Doc.UserDoc().activeDashboard = undefined);
+            CurrentUserUtils.CaptureDashboardThumbnail()?.then(() => Doc.UserDoc().activePage = "home");
       }
       render() {
             const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
-- 
cgit v1.2.3-70-g09d2


From a42db3ee7baf57772df13726d10c43996039c014 Mon Sep 17 00:00:00 2001
From: Jenny Yu <jennyyu212@outlook.com>
Date: Wed, 15 Jun 2022 13:50:14 -0700
Subject: fix: do not auto create dashboard

---
 src/client/views/DashboardView.scss | 16 ++++++++++++++++
 src/client/views/DashboardView.tsx  | 29 +++++++++++++++++++++++++++--
 src/client/views/MainView.tsx       | 10 ++--------
 src/client/views/topbar/TopBar.scss |  2 +-
 4 files changed, 46 insertions(+), 11 deletions(-)

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

diff --git a/src/client/views/DashboardView.scss b/src/client/views/DashboardView.scss
index 06f331c13..3db23b86f 100644
--- a/src/client/views/DashboardView.scss
+++ b/src/client/views/DashboardView.scss
@@ -47,4 +47,20 @@
             margin: 10px;
             font-weight: 500;
       }
+
+      img {
+            width: auto;
+            height: 80%;
+      }
+
+      .info {
+            display: flex;
+            flex-direction: row;
+            justify-content: space-between;
+            align-items: center;
+      }
+
+      .more {
+            z-index: 100;
+      }
 }
\ No newline at end of file
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index fa8a98402..027cfd376 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -9,11 +9,16 @@ import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { UndoManager } from "../util/UndoManager";
 import "./DashboardView.scss"
 import { MainViewModal } from "./MainViewModal";
+import { ContextMenu } from "./ContextMenu";
+import { DocumentManager } from "../util/DocumentManager";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 
 enum DashboardGroup {
       MyDashboards, SharedDashboards
 }
 
+// DashboardView is the view with the dashboard previews, rendered when the app first loads
+
 @observer
 export class DashboardView extends React.Component {
 
@@ -23,7 +28,7 @@ export class DashboardView extends React.Component {
 
       @observable private newDashboardName: string | undefined = undefined;
       @action abortCreateNewDashboard = () => { this.newDashboardName = undefined }
-      @action setNewDashboardName(name: string) { this.newDashboardName = name}
+      @action setNewDashboardName(name: string) { this.newDashboardName = name }
 
       @action
       selectDashboardGroup = (group: DashboardGroup) => {
@@ -75,7 +80,27 @@ export class DashboardView extends React.Component {
                                     const href = ImageCast((dashboard.thumb as Doc)?.data)?.url.href;
                                     return <div className="dashboard-container" key={dashboard[Id]} onClick={e => this.clickDashboard(e, dashboard)}>
                                           <img src={href ?? "https://media.istockphoto.com/photos/hot-air-balloons-flying-over-the-botan-canyon-in-turkey-picture-id1297349747?b=1&k=20&m=1297349747&s=170667a&w=0&h=oH31fJty_4xWl_JQ4OIQWZKP8C6ji9Mz7L4XmEnbqRU="}></img>
-                                          <div className="title"> {StrCast(dashboard.title)} </div>
+                                          <div className="info">
+                                                <div className="title"> {StrCast(dashboard.title)} </div>
+                                                <div className="more" onClick={e => {
+                                                      const dashView = DocumentManager.Instance.getDocumentView(dashboard);
+                                                      console.log(dashView)
+                                                      ContextMenu.Instance.addItem({
+                                                            description: "Share Dashboard", event: async () => {
+                                                               // ...
+                                                            }, icon: "edit"
+                                                      });
+                                                      ContextMenu.Instance.addItem({
+                                                            description: "Delete Dashboard", event: async () => {
+                                                               // ...
+                                                            }, icon: "trash"
+                                                      });
+                                                      dashView?.showContextMenu(e.clientX + 20, e.clientY + 30);
+                                                }}>
+                                                      <FontAwesomeIcon color="black" size="lg" icon="bars" />
+                                                </div>
+                                          </div>
+
                                     </div>
 
                               })}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 5fd76c388..c32198f68 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -222,16 +222,10 @@ export class MainView extends React.Component {
     }
 
     initAuthenticationRouters = async () => {
-        // Load the user's active dashboard, or create a new one if initial session after signup
         const received = CurrentUserUtils.MainDocId;
         if (received && !this.userDoc) {
-            reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()), { fireImmediately: true });
-        } else {
-            PromiseValue(this.userDoc.activeDashboard).then(dash => {
-                if (dash instanceof Doc) CurrentUserUtils.openDashboard(this.userDoc, dash);
-                else CurrentUserUtils.createNewDashboard(this.userDoc);
-            });
-        }
+            reaction(() => CurrentUserUtils.GuestTarget, target => target);
+        } 
     }
 
     @action
diff --git a/src/client/views/topbar/TopBar.scss b/src/client/views/topbar/TopBar.scss
index c5b340514..214b20193 100644
--- a/src/client/views/topbar/TopBar.scss
+++ b/src/client/views/topbar/TopBar.scss
@@ -2,7 +2,6 @@
 
 
 .topbar-container {
-      display: flex;
       flex-direction: column;
       font-size: 10px;
       line-height: 1;
@@ -11,6 +10,7 @@
       background: $dark-gray;
       overflow: visible;
       z-index: 1000;
+      align-items: center;
       height: $topbar-height;
       background-color: $dark-gray;
 
-- 
cgit v1.2.3-70-g09d2


From c0826fb904f5d2448635b5dae1fc4337fead939e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 20 Jun 2022 05:31:25 -0400
Subject: lots of changes to try to cleanup CurrentUserUtils so changes can be
 made without rebuilding the DB.

---
 src/client/documents/Documents.ts                  |   10 +-
 src/client/util/CurrentUserUtils.ts                | 1216 +++++++++-----------
 src/client/util/DocumentManager.ts                 |    3 +-
 src/client/util/Scripting.ts                       |    2 +-
 src/client/views/DocComponent.tsx                  |    2 +-
 src/client/views/GlobalKeyHandler.ts               |    2 +-
 src/client/views/MainView.tsx                      |   36 +-
 src/client/views/OverlayView.tsx                   |    2 +-
 src/client/views/PropertiesView.tsx                |    5 +-
 .../views/collections/CollectionDockingView.tsx    |    2 +-
 src/client/views/collections/CollectionMenu.tsx    |    6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |    4 +-
 .../collectionLinear/CollectionLinearView.tsx      |    4 +-
 src/client/views/linking/LinkPopup.tsx             |    4 +-
 src/client/views/nodes/DocumentView.tsx            |    8 +-
 src/client/views/nodes/FilterBox.tsx               |    2 +-
 src/client/views/nodes/WebBox.tsx                  |   12 +-
 src/client/views/nodes/button/FontIconBox.tsx      |    2 +-
 src/client/views/nodes/trails/PresBox.tsx          |    8 +-
 src/fields/Doc.ts                                  |    5 +-
 src/fields/util.ts                                 |    3 +
 src/mobile/MobileInterface.tsx                     |    2 +-
 22 files changed, 629 insertions(+), 711 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 5f009573e..927996af2 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -178,10 +178,15 @@ export class DocumentOptions {
     hidden?: boolean;
     _hidden?: boolean;
     pointerEvents?: string; // pointer events that the documentview should have
-    mediaState?: string; // status of media document: "pendingRecording", "recording", "paused", "playing"
+    mediaState?: string; // status of audio/video media document: "pendingRecording", "recording", "paused", "playing"
+    recording?: boolean; // whether WebCam is recording or not
     autoPlayAnchors?: boolean; // whether to play audio/video when an anchor is clicked in a stackedTimeline.
     dontPlayLinkOnSelect?: boolean;  // whether an audio/video should start playing when a link is followed to it.
     toolTip?: string; // tooltip to display on hover
+    contextMenuFilters?: List<ScriptField>;
+    contextMenuScripts?: List<ScriptField>;
+    contextMenuLabels?: List<string>;
+    contextMenuIcons?: List<string>;
     dontUndo?: boolean; // whether button clicks should be undoable (this is set to true for Undo/Redo/and sidebar buttons that open the siebar panel)
     description?: string; // added for links
     layout?: string | Doc; // default layout string for a document
@@ -238,7 +243,7 @@ export class DocumentOptions {
     isPushpin?: boolean;
     isGroup?: boolean; // whether a collection should use a grouping UI behavior
     _removeDropProperties?: List<string>; // list of properties that should be removed from a document when it is dropped.  e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document
-
+    noteType?: string;
     // BACKGROUND GRID
     _backgroundGridShow?: boolean;
 
@@ -286,6 +291,7 @@ export class DocumentOptions {
     clickFactory?: Doc; // document to create when clicking on a button with a suitable onClick script
     onDragStart?: ScriptField; //script to execute at start of drag operation --  e.g., when a "creator" button is dragged this script generates a different document to drop
     cloneFieldFilter?: List<string>; // fields not to copy when the document is clonedclipboard?: Doc;
+    filterBoolean ?: string;
     useCors?: boolean;
     icon?: string;
     target?: Doc; // available for use in scripts as the primary target document
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b4e18a8bb..afb1442bf 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1,16 +1,17 @@
 import { computed, observable, reaction } from "mobx";
 import * as rp from 'request-promise';
-import { DataSym, Doc, DocListCast, DocListCastAsync } from "../../fields/Doc";
+import { DataSym, Doc, DocListCast, DocListCastAsync, StrListCast } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { InkTool } from "../../fields/InkField";
 import { List } from "../../fields/List";
 import { PrefetchProxy } from "../../fields/Proxy";
 import { RichTextField } from "../../fields/RichTextField";
+import { listSpec } from "../../fields/Schema";
 import { ComputedField, ScriptField } from "../../fields/ScriptField";
-import { BoolCast, Cast, DateCast, DocCast, NumCast, PromiseValue, StrCast } from "../../fields/Types";
+import { BoolCast, Cast, DateCast, DocCast, FieldValue, NumCast, PromiseValue, ScriptCast, StrCast } from "../../fields/Types";
 import { ImageField, nullAudio } from "../../fields/URLField";
 import { SharingPermissions } from "../../fields/util";
-import { Utils } from "../../Utils";
+import { OmitKeys, Utils } from "../../Utils";
 import { DocServer } from "../DocServer";
 import { Docs, DocumentOptions, DocUtils } from "../documents/Documents";
 import { DocumentType } from "../documents/DocumentTypes";
@@ -42,17 +43,17 @@ interface Button {
     toolTip?: string;
     icon?: string;
     btnType?: ButtonType;
-    click?: string;
     numBtnType?: NumButtonType;
     numBtnMin?: number;
     numBtnMax?: number;
     switchToggle?: boolean;
-    script?: string;
     width?: number;
-    list?: string[];
+    btnList?: List<string>;
     ignoreClick?: boolean;
     buttonText?: string;
-    hidden?: string;
+    scripts?: { script?: string; onClick?: string; }
+    funcs?: { [key:string]: string };
+    subMenu?: Button[];
 }
 
 export let resolvedPorts: { server: number, socket: number };
@@ -73,101 +74,134 @@ export class CurrentUserUtils {
     @observable public static headerBarHeight: number = 0;
     @observable public static searchPanelWidth: number = 0;
 
-    // sets up the default User Templates - slideView,  headerView
-    static setupExperimentalTemplateButtons(doc: Doc) {
-        if (doc["template-experimental-buttons"] === undefined) {
-            const requiredTypeNameFields = [
-                {
-                    type: "slide", icon: "address-card", template: Docs.Create.MultirowDocument(
-                        [
-                            Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, system: true }),
-                            Docs.Create.TextDocument("", { title: "text", _height: 100, system: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) })
-                        ],
-                        { _width: 400, _height: 300, title: "slideView", _xMargin: 3, _yMargin: 3, system: true }
-                    )
-                },
-                {
-                    type: "mobile", icon: "mobile", template: this.mobileButton({ title: "NEW MOBILE BUTTON", onClick: undefined, },
-                        [this.createToolButton({ ignoreClick: true, icon: "mobile", backgroundColor: "transparent" }),
-                        this.mobileTextContainer({},
-                            [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])
-                        ]
-                    )
-                },
-            ];
-            const requiredTypes = requiredTypeNameFields.map(({ type, icon, template }) => {
-                if (doc[`template-button-${type}`] === undefined) {
-                    template.isTemplateDoc = makeTemplate(template);
-                    doc[`template-button-${type}`] = CurrentUserUtils.createToolButton({
-                        onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'),
-                        dragFactory: new PrefetchProxy(template) as any as Doc,
-                        title: type,
-                        icon,
-                    });
+    static AssignScripts(doc:Doc, scripts?:{ [key: string]: string;}, functions?:{[key:string]: string}) {
+        scripts && Object.keys(scripts).map(key => {
+            if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && scripts[key]) {
+                doc[key] = ScriptField.MakeScript(scripts[key], { dragData: DragManager.DocumentDragData.name,  value:"any", scriptContext: "any" }, {"_readOnly_": true});
+            }
+        }); 
+        functions && Object.keys(functions).map(key => {
+            const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+            if (ScriptCast(cfield)?.script.originalScript !== functions[key] && functions[key]) {
+                doc[key] = ComputedField.MakeFunction(functions[key], { dragData: DragManager.DocumentDragData.name }, {"_readOnly_": true});
+            }
+        });
+        return doc;
+    }
+    static AssignOpts(doc:Doc|undefined, reqdOpts:DocumentOptions, items?:Doc[]) {
+        if (doc) {
+            const compareValues = (val1:any, val2:any) => {
+                if (val1 instanceof List && val2 instanceof List && val1.length === val2.length) {
+                    return !val1.some(v => !val2.includes(v)) || !val2.some(v => val1.includes(v));
+                }
+                return val1 === val2;
+            }
+            Object.entries(reqdOpts).forEach(pair => {
+                const targetDoc = pair[0].startsWith("_") ? doc : Doc.GetProto(doc as Doc);
+                if (!Object.getOwnPropertyNames(targetDoc).includes(pair[0].replace(/^_/,"")) ||
+                    !compareValues(pair[1], targetDoc[pair[0]])) {
+                    targetDoc[pair[0]] = pair[1];
                 }
-                return doc[`template-button-${type}`] as Doc;
             });
-
-            doc["template-experimental-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument(requiredTypes, {
-                title: "Experimental Tools", _xMargin: 0, _showTitle: "title", _chromeHidden: true,
-                hidden: ComputedField.MakeFunction("IsNoviceMode()") as any,
-                _stayInCollection: true, _hideContextMenu: true, _forceActive: true,
-                _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true,
-                dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), system: true
-            }));
+            items?.forEach(item => !DocListCast(doc.data).includes(item) && Doc.AddDocToList(Doc.GetProto(doc), "data", item));
         }
-        return doc["template-experimental-buttons"] as Doc;
+        return doc;
     }
 
-    // setup the different note type skins
-    static setupNoteTemplates(doc: Doc) {
-        if (doc["template-notes"] === undefined) {
-            const requiredTypeNameFields = [
-                { type: "Note", backgroundColor: "yellow", icon: "sticky-note" },
-                { type: "Idea", backgroundColor: "pink", icon: "lightbulb" },
-                { type: "Topic", backgroundColor: "lightblue", icon: "book-open" }];
-            const requiredTypes = requiredTypeNameFields.map(({ type, backgroundColor, icon }) =>
-                doc[`template-note-${type}`] as Doc ??
-                (() => {
-                    const noteView = Docs.Create.TextDocument("", { title: "text", backgroundColor, system: true, icon });
-                    noteView.isTemplateDoc = makeTemplate(noteView, true, type);
-                    return doc[`template-note-${type}`] = new PrefetchProxy(noteView);
-                })());
-
-            doc["template-notes"] = new PrefetchProxy(Docs.Create.TreeDocument(requiredTypes, { title: "Note Layouts", _height: 75, system: true }));
-        }
+    // initializes experimental advanced template views - slideView,  headerView
+    static setupExperimentalTemplateButtons(doc: Doc, field="template-experimental-buttons") {
+        const tempDocs = DocCast(doc[field]);
+        const requiredTypeNameFields:{opts:DocumentOptions, template:() => Doc}[] = [
+            {
+                opts:{type: "slide", icon: "address-card"}, template: () => Docs.Create.MultirowDocument(
+                    [
+                        Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, system: true }),
+                        Docs.Create.TextDocument("", { title: "text", _height: 100, system: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) })
+                    ],
+                    { _width: 400, _height: 300, title: "slideView", _xMargin: 3, _yMargin: 3, system: true }
+                )
+            },
+            {
+                opts:{type: "mobile", icon: "mobile"}, template: () => this.mobileButton({ title: "NEW MOBILE BUTTON", onClick: undefined, },
+                    [this.createToolButton({ ignoreClick: true, icon: "mobile", backgroundColor: "transparent" }),
+                    this.mobileTextContainer({},
+                        [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])
+                    ]
+                )
+            },
+        ];
+        const requiredTypes = requiredTypeNameFields.map(({ opts, template }) => {
+            const docType =  DocListCast(tempDocs?.data)?.find(doc => doc.title === opts.type);
+            const reqdOpts = {
+                dragFactory: template(),
+                title: opts.type,
+                icon: opts.icon
+            };
+            const reqdScripts = {onDragStart: 'copyDragFactory(this.dragFactory)'};
+            const makeTemp = (doc:Doc) => {
+                doc.isTemplateDoc = makeTemplate(doc);
+                return doc;
+            }
+            return this.AssignScripts(!docType ? makeTemp(CurrentUserUtils.createToolButton(reqdOpts)) : this.AssignOpts(docType, reqdOpts)!, reqdScripts)!;
+        });
+
+        const reqdOpts = {
+            title: "Experimental Tools", _xMargin: 0, _showTitle: "title", _chromeHidden: true,
+            _stayInCollection: true, _hideContextMenu: true, _forceActive: true, system: true,
+            _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true,
+        };
+        const reqdScripts = {dropConverter : "convertToButtons(dragData)"};
+        const reqdFuncs = {hidden: "IsNoviceMode()"}
+        return this.AssignScripts(!tempDocs ? 
+                (doc[field] = Docs.Create.MasonryDocument(requiredTypes, reqdOpts)) :
+                this.AssignOpts(tempDocs, reqdOpts, requiredTypes)!, 
+            reqdScripts, reqdFuncs);
+    }
+
+    /// Initializes templates that can be applied to notes
+    static setupNoteTemplates(doc: Doc, field="template-notes") {
+        const tempNotes = DocCast(doc[field]);
+        const reqdTempOpts:DocumentOptions[] = [
+            { noteType: "Note", backgroundColor: "yellow", icon: "sticky-note"},
+            { noteType: "Idea", backgroundColor: "pink", icon: "lightbulb" },
+            { noteType: "Topic", backgroundColor: "lightblue", icon: "book-open" }];
+        const reqdNoteList = reqdTempOpts.map(opts => {
+            const reqdOpts = {...opts, title: "text", system: true};
+            const noteType = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.noteType === opts.noteType): undefined;
+            const makeTemp = (doc:Doc, noteType?:string) => {
+                doc.isTemplateDoc = makeTemplate(doc, true, noteType??"Note");
+                return doc;
+            }
+            return this.AssignOpts(noteType, reqdOpts) ?? makeTemp(Docs.Create.TextDocument("",reqdOpts), opts.noteType);
+        });
 
-        return doc["template-notes"] as Doc;
+        const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, system: true };
+        return this.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts));
     }
 
-    // creates Note templates, and initial "user" templates
+    /// Initializes collection of templates for notes and click functions
     static setupDocTemplates(doc: Doc) {
-        if (doc.templateDocs === undefined) {
-            doc.templateDocs = new PrefetchProxy(Docs.Create.TreeDocument([
-                CurrentUserUtils.setupNoteTemplates(doc),
-                CurrentUserUtils.setupExperimentalTemplateButtons(doc),
-                CurrentUserUtils.setupClickEditorTemplates(doc)
-            ], {
-                title: "template layouts", _xMargin: 0, system: true,
-                dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name })
-            }));
-        }
+        const templates = [
+            CurrentUserUtils.setupNoteTemplates(doc),
+            CurrentUserUtils.setupClickEditorTemplates(doc)
+        ];
+        const reqdOpts = { title: "template layouts", _xMargin: 0, system: true,  };
+        const reqdScripts = {  dropConverter: "convertToButtons(dragData)"  };
+        return this.AssignScripts(this.AssignOpts(DocCast(doc.templateDocs), reqdOpts, templates) ?? (doc.templateDocs = Docs.Create.TreeDocument(templates, reqdOpts)), reqdScripts);
     }
 
     // setup templates for different document types when they are iconified from Document Decorations
-    static setupDefaultIconTemplates(doc: Doc) {
-        const templateIconsDoc = Cast(doc["template-icons"], Doc, null);
+    static setupDefaultIconTemplates(doc: Doc, field="template-icons") {
+        const templateIconsDoc = DocCast(doc[field]);
 
         const makeIconTemplate = (type: DocumentType | undefined, templateField: string, iconTemplate: () => Doc) => {
             const iconFieldName = "icon" + (type ? "_" + type : "");
             if (!templateIconsDoc?.[iconFieldName]) {
                 const template = MakeTemplate(iconTemplate(), true, iconFieldName, templateField);
                 if (templateIconsDoc) {
-                    templateIconsDoc[iconFieldName] = new PrefetchProxy(template);
-                    Doc.AddDocToList(templateIconsDoc, "data", template);
-                } else {
-                    return template;
+                    templateIconsDoc[iconFieldName] = template;
                 }
+                return template;
             }
         };
         const deiconifyScript = () => ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" });
@@ -182,46 +216,57 @@ export class CurrentUserUtils {
         const fontBox = () => Docs.Create.FontIconDocument({
             _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, system: true, onClick: deiconifyScript()
         });
-        if (!templateIconsDoc) {
-            const newIconsList = [
-                makeIconTemplate(undefined, "title", () => labelBox({ _backgroundColor: "dimgray" })),
-                makeIconTemplate(DocumentType.AUDIO, "title", () => labelBox({ _backgroundColor: "lightgreen" })),
-                makeIconTemplate(DocumentType.PDF, "title", () => labelBox({ _backgroundColor: "pink" })),
-                makeIconTemplate(DocumentType.WEB, "title", () => labelBox({ _backgroundColor: "brown" })),
-                makeIconTemplate(DocumentType.RTF, "text", () => labelBox({ _showTitle: "creationDate" })),
-                makeIconTemplate(DocumentType.IMG, "data", () => imageBox("", { _height: undefined, })),
-                makeIconTemplate(DocumentType.COL, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})),
-                makeIconTemplate(DocumentType.VID, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})),
-                makeIconTemplate(DocumentType.BUTTON, "data", fontBox),
-                //nasty hack .. templates are looked up exclusively by type -- but we want a template for a document with a certain field (transcription) .. so this hack and the companion hack in createCustomView does this for now
-                makeIconTemplate("transcription" as any, "transcription", () => labelBox({ _backgroundColor: "orange" })),
-                // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {}))
-            ].filter(d => d).map(d => d!);
-
-            doc["template-icons"] = Docs.Create.TreeDocument(newIconsList, { title: "icon templates", _height: 75, system: true });
-        }
+        const iconTemplates = [
+            makeIconTemplate(undefined, "title", () => labelBox({ _backgroundColor: "dimgray" })),
+            makeIconTemplate(DocumentType.AUDIO, "title", () => labelBox({ _backgroundColor: "lightgreen" })),
+            makeIconTemplate(DocumentType.PDF, "title", () => labelBox({ _backgroundColor: "pink" })),
+            makeIconTemplate(DocumentType.WEB, "title", () => labelBox({ _backgroundColor: "brown" })),
+            makeIconTemplate(DocumentType.RTF, "text", () => labelBox({ _showTitle: "creationDate" })),
+            makeIconTemplate(DocumentType.IMG, "data", () => imageBox("", { _height: undefined, })),
+            makeIconTemplate(DocumentType.COL, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})),
+            makeIconTemplate(DocumentType.VID, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})),
+            makeIconTemplate(DocumentType.BUTTON, "data", fontBox),
+            //nasty hack .. templates are looked up exclusively by type -- but we want a template for a document with a certain field (transcription) .. so this hack and the companion hack in createCustomView does this for now
+            makeIconTemplate("transcription" as any, "transcription", () => labelBox({ _backgroundColor: "orange" })),
+            // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {}))
+        ].filter(d => d).map(d => d!);
+
+        const reqdOpts = { title: "icon templates", _height: 75, system: true };
+        this.AssignOpts(templateIconsDoc, reqdOpts, iconTemplates) ?? (doc[field] = Docs.Create.TreeDocument(iconTemplates, reqdOpts));
     }
 
+    /// initalizes the set of default versions of most document types
     static creatorBtnDescriptors(doc: Doc): {
-        title: string, toolTip: string, icon: string, drag?: string, ignoreClick?: boolean,
-        click?: string, backgroundColor?: string, dragFactory?: Doc, noviceMode?: boolean, clickFactory?: Doc
+        title: string, toolTip: string, icon: string,  ignoreClick?: boolean, dragFactory?: Doc, 
+        backgroundColor?: string,  clickFactory?: Doc, scripts?: { onClick?: string, onDragStart?: string}, funcs?: {onDragStart?:string, hidden?: string},
     }[] {
         const standardOps = () => ({ _fitWidth: true, system: true, "dragFactory-count": 0, cloneFieldFilter: new List<string>(["system"]) });
-        if (doc.emptyPresentation === undefined) {
-            doc.emptyPresentation = Docs.Create.PresDocument({ ...standardOps(), title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
-        }
-        if (doc.emptyCollection === undefined) {
-            doc.emptyCollection = Docs.Create.FreeformDocument([], { ...standardOps(), title: "freeform", _width: 150, _height: 100 });
-        }
-        if (doc.emptyPane === undefined) {
-            doc.emptyPane = Docs.Create.FreeformDocument([], { ...standardOps(), title: "Untitled Tab", _backgroundGridShow: true, _width: 500, _height: 800 });
-        }
-        if (doc.emptySlide === undefined) {
-            doc.emptySlide = Docs.Create.TreeDocument([], {
-                ...standardOps(), title: ComputedField.MakeFunction('self.text?.Text') as any, _viewType: CollectionViewType.Tree, treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, "dragFactory-count": undefined,
-                allowOverlayDrop: true, treeViewType: TreeViewType.outline, _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, backgroundColor: "white"
-            });
-        }
+        const emptyThings:{key:string,  // the field name where the empty thing will be stored
+                          opts:DocumentOptions,   // the document options that are required for the empty thing
+                          funcs?:{[key:string]: any},  // computed fields that are rquired for the empth thing
+                          creator:(opts:DocumentOptions)=> any // how to create the empty thing if it doesn't exist
+              }[] = [
+            {key: "emptyPresentation", creator: Docs.Create.PresDocument,  opts: { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _width: 400, _height: 500, targetDropAction: "alias" as any, _chromeHidden: true, boxShadow: "0 0" }},
+            {key: "emptyCollection", creator: (opts) => Docs.Create.FreeformDocument([], opts), opts: { title: "freeform", _width: 150, _height: 100 }},
+            {key: "emptyPane", creator: (opts) => Docs.Create.FreeformDocument([], opts), opts :  { title: "Untitled Tab", _backgroundGridShow: true, _width: 500, _height: 800 }},
+            {key: "emptySlide", creator: (opts) => Docs.Create.TreeDocument([], opts), funcs: {title: 'self.text?.Text'}, opts: {
+                _viewType: CollectionViewType.Tree, treeViewHasOverlay: true, _fontSize: "20px", _autoHeight: true, 
+                "dragFactory-count": undefined,  allowOverlayDrop: true, treeViewType: TreeViewType.outline, _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, backgroundColor: "white"
+            }},
+            {key: "emptyComparison", creator: Docs.Create.ComparisonDocument, opts: { title: "Comparer", _width: 300, _height: 300 }},
+            {key: "emptyScript", creator: (opts) => Docs.Create.ScriptingDocument(undefined, opts), opts:  { title: "script", _width: 200, _height: 250, }},
+            {key: "emptyScreenshot", creator: Docs.Create.ScreenshotDocument, opts: { title: "empty screenshot", _width: 400, _height: 200 }},
+            {key: "emptyWebCam", creator: (opts) => Docs.Create.WebCamDocument("", opts), opts: { _width: 400, _height: 200, title: "recording", recording:true, system: true, cloneFieldFilter: new List<string>(["system"]) }},
+            {key: "emptyAudio", creator: (opts) => Docs.Create.AudioDocument(nullAudio, opts), opts: { title: "audio recording", x: 200, y: 200, _width: 200, _height: 100, }},
+            {key: "emptyNote", creator: (opts) => Docs.Create.TextDocument("", opts), opts:  { title: "text note", _width: 200, _autoHeight: true }},
+            {key: "emptyButton", creator: Docs.Create.ButtonDocument, opts: { title: "Button", _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, }},
+            {key: "emptyWebpage", creator: (opts) => Docs.Create.WebDocument("http://www.bing.com/", opts), opts: { title: "webpage", _nativeWidth: 850, _height: 512, _width: 400, useCors: true, }},
+            {key: "emptyMap", creator: (opts) =>  Docs.Create.MapDocument([], opts), opts: { title: "map", _showSidebar: true, _width: 800, _height: 600, }}
+        ];
+
+        emptyThings.forEach(thing => 
+            this.AssignScripts(this.AssignOpts(DocCast(doc[thing.key]), {...standardOps(), ...thing.opts}) ?? (doc[thing.key] = thing.creator({...standardOps(), ...thing.opts})), thing.funcs))
+        
         if (doc.emptyHeader === undefined) {
             const json = {
                 doc: {
@@ -242,196 +287,124 @@ export class CurrentUserUtils {
                 selection: { type: "text", anchor: 1, head: 1 },
                 storedMarks: []
             };
-            const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), {
-                ...standardOps(), title: "text", _height: 70,
-                _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true,
-            }, "header");
             const headerBtnHgt = 10;
-            headerTemplate[DataSym].layout =
+            const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), {
+                ...standardOps(), title: "text", _height: 70, _headerPointerEvents: "all", _headerHeight: 12, _headerFontSize: 9, _autoHeight: true,
+                layout:
                 "<HTMLdiv transformOrigin='top left' width='{100/scale}%' height='{100/scale}%' transform='scale({scale})'>" +
                 `    <FormattedTextBox {...props} dontScale='true' fieldKey={'text'}  height='calc(100% - ${headerBtnHgt}px - {this._headerHeight||0}px)'/>` +
                 "    <FormattedTextBox {...props} dontScale='true' fieldKey={'header'}  dontSelectOnLoad='true' ignoreAutoHeight='true' fontSize='{this._headerFontSize||9}px' height='{(this._headerHeight||0)}px' background='{this._headerColor || MySharedDocs().userColor||`lightGray`}' />" +
                 `    <HTMLdiv fontSize='${headerBtnHgt - 1}px' height='${headerBtnHgt}px' background='yellow' onClick={‘(this._headerHeight=scale*Math.min(Math.max(0,this._height-30),this._headerHeight===0?50:0)) + (this._autoHeightMargins=this._headerHeight ? this._headerHeight+${headerBtnHgt}:0)’}  >Metadata</HTMLdiv>` +
-                "</HTMLdiv>";
+                "</HTMLdiv>"
+            }, "header");
 
             // "<div style={'height:100%'}>" +
             // "    <FormattedTextBox {...props} fieldKey={'header'} dontSelectOnLoad={'true'} ignoreAutoHeight={'true'} pointerEvents='{this._headerPointerEvents||`none`}' fontSize='{this._headerFontSize}px' height='{this._headerHeight}px' background='{this._headerColor||this.target.mySharedDocs.userColor}' />" +
             // "    <FormattedTextBox {...props} fieldKey={'text'} position='absolute' top='{(this._headerHeight)*scale}px' height='calc({100/scale}% - {this._headerHeight}px)'/>" +
             // "</div>";
-            (headerTemplate.proto as Doc).isTemplateDoc = makeTemplate(headerTemplate.proto as Doc, true, "headerView");
+            Doc.GetProto(headerTemplate).isTemplateDoc = makeTemplate(Doc.GetProto(headerTemplate), true, "headerView");
             doc.emptyHeader = headerTemplate;
         }
-        if (doc.emptyComparison === undefined) {
-            doc.emptyComparison = Docs.Create.ComparisonDocument({ ...standardOps(), title: "Comparer", _width: 300, _height: 300 });
-        }
-        if (doc.emptyScript === undefined) {
-            doc.emptyScript = Docs.Create.ScriptingDocument(undefined, { ...standardOps(), title: "script", _width: 200, _height: 250, });
-        }
-        if (doc.emptyScreenshot === undefined) {
-            doc.emptyScreenshot = Docs.Create.ScreenshotDocument({ ...standardOps(), title: "empty screenshot", _width: 400, _height: 200 });
-        }
-        if (doc.emptyWall === undefined) {
-            doc.emptyWall = Docs.Create.WebCamDocument("", { _width: 400, _height: 200, title: "recording", system: true, cloneFieldFilter: new List<string>(["system"]) });
-            (doc.emptyWall as Doc).recording = true;
-        }
-        if (doc.emptyAudio === undefined) {
-            doc.emptyAudio = Docs.Create.AudioDocument(nullAudio, { ...standardOps(), title: "audio recording", x: 200, y: 200, _width: 200, _height: 100, });
-        }
-        if (doc.emptyNote === undefined) {
-            doc.emptyNote = Docs.Create.TextDocument("", { ...standardOps(), title: "text note", _width: 200, _autoHeight: true });
-        }
-        if (doc.emptyButton === undefined) {
-            doc.emptyButton = Docs.Create.ButtonDocument({ ...standardOps(), title: "Button", _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, });
-        }
-        if (doc.emptyWebpage === undefined) {
-            doc.emptyWebpage = Docs.Create.WebDocument("http://www.bing.com/", { ...standardOps(), title: "webpage", _nativeWidth: 850, _height: 512, _width: 400, useCors: true, });
-        }
-        if (doc.emptyMap === undefined) {
-            doc.emptyMap = Docs.Create.MapDocument([], { ...standardOps(), title: "map", _showSidebar: true, _width: 800, _height: 600, });
-        }
-        if (doc.activeMobileMenu === undefined) {
-            this.setupActiveMobileMenu(doc);
-        }
+        
         return [
-            { toolTip: "Tap to create a note in a new pane, drag for a note", title: "Note", icon: "sticky-note", click: 'openOnRight(copyDragFactory(this.clickFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyNote as Doc, noviceMode: true, clickFactory: doc.emptyNote as Doc, },
-            { toolTip: "Tap to create a collection in a new pane, drag for a collection", title: "Col", icon: "folder", click: 'openOnRight(copyDragFactory(this.clickFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyCollection as Doc, noviceMode: true, clickFactory: doc.emptyPane as Doc, },
-            { toolTip: "Tap to create a webpage in a new pane, drag for a webpage", title: "Web", icon: "globe-asia", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWebpage as Doc, noviceMode: true },
-            { toolTip: "Tap to create a progressive slide", title: "Slide", icon: "file", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptySlide as Doc },
-            { toolTip: "Tap to create a comparison box in a new pane, drag for a comparison box", title: "Compare", icon: "columns", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyComparison as Doc, noviceMode: true },
-            { toolTip: "Tap to create a screen grabber in a new pane, drag for a screen grabber", title: "Grab", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScreenshot as Doc },
-            { toolTip: "Tap to create a videoWall", title: "Wall", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWall as Doc },
-            { toolTip: "Tap to create an audio recorder in a overlay pane, drag for an audio recorder", title: "Audio", icon: "microphone", click: 'openInOverlay(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyAudio as Doc, noviceMode: true },
-            { toolTip: "Tap to create a button in a new pane, drag for a button", title: "Button", icon: "bolt", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyButton as Doc },
-            // { toolTip: "Tap to create a presentation in a new pane, drag for a presentation", title: "Trails", icon: "pres-trail", click: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', drag: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`, dragFactory: doc.emptyPresentation as Doc, noviceMode: true },
-            { toolTip: "Tap to create a scripting box in a new pane, drag for a scripting box", title: "Script", icon: "terminal", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScript as Doc },
-            { toolTip: "Tap to create a mobile view in a new pane, drag for a mobile view", title: "Phone", icon: "mobile", click: 'openOnRight(Doc.UserDoc().activeMobileMenu)', drag: 'this.dragFactory', dragFactory: doc.activeMobileMenu as Doc },
-            { toolTip: "Tap to create a custom header note document, drag for a custom header note", title: "Custom", icon: "window-maximize", click: 'openOnRight(delegateDragFactory(this.dragFactory))', drag: 'delegateDragFactory(this.dragFactory)', dragFactory: doc.emptyHeader as Doc },
-            { toolTip: "Toggle a Calculator REPL", title: "repl", icon: "calculator", click: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' },
-            { toolTip: "Tap to create a map in the new pane, drag for a map", title: "Map", icon: "map-marker-alt", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyMap as Doc, noviceMode: true }
-        ];
-
+            { toolTip: "Tap or drag to create a note",            title: "Note", icon: "sticky-note",   dragFactory: doc.emptyNote as Doc,        scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, },
+            { toolTip: "Tap or drag to create a collection",      title: "Col", icon: "folder",         dragFactory: doc.emptyCollection as Doc,  scripts: {onClick: 'openOnRight(copyDragFactory(this.clickFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, clickFactory: doc.emptyPane as Doc, },
+            { toolTip: "Tap or drag to create a webpage",         title: "Web", icon: "globe-asia",     dragFactory: doc.emptyWebpage as Doc,     scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, },
+            { toolTip: "Tap or drag to create a comparison box",  title: "Compare", icon: "columns",    dragFactory: doc.emptyComparison as Doc,  scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, },
+            { toolTip: "Tap or drag to create an audio recorder", title: "Audio", icon: "microphone",   dragFactory: doc.emptyAudio as Doc,       scripts: {onClick: 'openInOverlay(copyDragFactory(this.dragFactory))',onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, },
+            { toolTip: "Tap or drag to create a map",             title: "Map", icon: "map-marker-alt", dragFactory: doc.emptyMap as Doc,         scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, },
+            { toolTip: "Tap or drag to create a custom note",     title: "Custom", icon: "window-maximize", dragFactory: doc.emptyHeader as Doc,  scripts: {onClick: 'openOnRight(delegateDragFactory(this.dragFactory))', onDragStart: '{ return delegateDragFactory(this.dragFactory);}'},  },
+            { toolTip: "Tap or drag to create a progressive slide",title: "Slide", icon: "file",        dragFactory: doc.emptySlide as Doc,       scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'} },
+            { toolTip: "Tap or drag to create a screen grabber",  title: "Grab", icon: "photo-video",   dragFactory: doc.emptyScreenshot as Doc,  scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'} },
+            { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc,      scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'}},
+            { toolTip: "Tap or drag to create a button",          title: "Button", icon: "bolt",        dragFactory: doc.emptyButton as Doc,      scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs:{ hidden: 'IsNoviceMode()'} },
+            { toolTip: "Tap or drag to create a scripting box",   title: "Script", icon: "terminal",    dragFactory: doc.emptyScript as Doc,      scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'}},
+            { toolTip: "Tap or drag to create a mobile view",     title: "Phone", icon: "mobile",       dragFactory: doc.activeMobileMenu as Doc, scripts: {onClick: 'openOnRight(Doc.UserDoc().activeMobileMenu)',     onDragStart: 'this.dragFactory'},                             funcs: {hidden: 'IsNoviceMode()'} },
+            { toolTip: "Toggle a Calculator REPL",                title: "repl", icon: "calculator",                                              scripts: {onClick: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' } },
+         // { toolTip: "Tap or drag to create a presentation",    title: "Trails", icon: "pres-trail",  dragFactory: doc.emptyPresentation as Doc,scripts: {onClick: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', onDragStart: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`}, funcs: {hidden: 'IsNoviceMode()'} },
+             ];
     }
 
-    // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
-    static setupCreatorButtons(doc: Doc) {
-        let alreadyCreatedButtons: string[] = [];
-        const dragCreatorSet = Cast(doc.myItemCreators, Doc, null);
-        if (dragCreatorSet) {
-            alreadyCreatedButtons = DocListCast(dragCreatorSet.data).map(d => StrCast(d.title));
-        }
-        const buttons = CurrentUserUtils.creatorBtnDescriptors(doc).filter(d => !alreadyCreatedButtons?.includes(d.title));
-        const creatorBtns = buttons.map(({ title, toolTip, icon, ignoreClick, drag, click, backgroundColor, dragFactory, noviceMode, clickFactory }) => Docs.Create.FontIconDocument({
-            _nativeWidth: 50, _nativeHeight: 50, _width: 35, _height: 35,
-            icon,
-            title,
-            toolTip,
-            btnType: ButtonType.ToolButton,
-            ignoreClick,
-            _dropAction: "alias",
-            onDragStart: drag ? ScriptField.MakeFunction(drag) : undefined,
-            onClick: click ? ScriptField.MakeScript(click) : undefined,
-            backgroundColor: backgroundColor ? backgroundColor : Colors.DARK_GRAY,
-            color: Colors.WHITE,
-            _hideContextMenu: true,
-            _removeDropProperties: new List<string>(["_stayInCollection"]),
-            _stayInCollection: true,
-            dragFactory,
-            clickFactory,
-            hidden: !noviceMode ? ComputedField.MakeFunction("IsNoviceMode()") as any : undefined,
-            system: true,
-        }));
+    /// Initalizes the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
+    static setupCreatorButtons(doc: Doc, dragCreatorDoc?:Doc):Doc {
+        const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).map((reqdOpts) => {
+            const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined;
+            const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["backgroundColor"]).omit,
+                _nativeWidth: 50, _nativeHeight: 50, _width: 35, _height: 35, _hideContextMenu: true, _stayInCollection: true, _dropAction: "alias",
+                btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, system: true,
+                _removeDropProperties: new List<string>(["_stayInCollection"]),
+                };
+            return this.AssignScripts(this.AssignOpts(btn, opts) ?? Docs.Create.FontIconDocument(opts), reqdOpts.scripts, reqdOpts.funcs);
+        });
 
-        if (dragCreatorSet === undefined) {
-            doc.myItemCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, {
-                title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true,
-                _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true,
-                dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), system: true
-            }));
-        } else {
-            creatorBtns.forEach(nb => Doc.AddDocToList(doc.myItemCreators as Doc, "data", nb));
-        }
-        return doc.myItemCreators as Doc;
+        const reqdOpts = {
+            title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, system: true,
+            _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 40, ignoreClick: true, _lockedPosition: true, _forceActive: true,
+        };
+        const reqdScripts = { dropConverter: "convertToButtons(dragData)" };
+        return this.AssignScripts(this.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts);
     }
 
-    static menuBtnDescriptions(doc: Doc) {
-        const badgeValue = ScriptField.MakeFunction("((len) => len ? len: undefined)(docList(self.target.data).filter(doc => !docList(self.target.viewed).includes(doc)).length)")
+    /// returns descriptions needed to buttons for the left sidebar to open up panes displaying different collections of documents
+    static leftSidebarMenuBtnDescriptions(doc: Doc):{title:string, target:Doc, icon:string, scripts:{[key:string]:any}, funcs?:{[key:string]:any}}[] {
+        const badgeValue = "((len) => len && len !== '0' ? len: undefined)(docList(self.target.data).filter(doc => !docList(self.target.viewed).includes(doc)).length.toString())";
         return [
-            { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
-            { title: "Search", target: Cast(doc.mySearchPanel, Doc, null), icon: "search", click: 'selectMainMenu(self)' },
-            { title: "Files", target: Cast(doc.myFilesystem, Doc, null), icon: "folder-open", click: 'selectMainMenu(self)' },
-            { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" },
-            { title: "Imports", target: Cast(doc.myImportDocs, Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
-            { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
-            { title: "Shared  with me", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', badgeValue},
-            { title: "Trails", target: Cast(doc.myTrails, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
-            { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" },
+            { title: "Dashboards",      target: CurrentUserUtils.MyDashboards,     icon: "desktop",     scripts:{onClick: 'selectMainMenu(self)'} },
+            { title: "Search",          target: CurrentUserUtils.MySearcher,       icon: "search",      scripts:{onClick: 'selectMainMenu(self)'} },
+            { title: "Files",           target: CurrentUserUtils.MyFilesystem,     icon: "folder-open", scripts:{onClick: 'selectMainMenu(self)'} },
+            { title: "Tools",           target: CurrentUserUtils.MyTools,          icon: "wrench",      scripts:{onClick: 'selectMainMenu(self)'}, funcs: {hidden: "IsNoviceMode()"} },
+            { title: "Imports",         target: CurrentUserUtils.MyImports,        icon: "upload",      scripts:{onClick: 'selectMainMenu(self)'} },
+            { title: "Recently Closed", target: CurrentUserUtils.MyRecentlyClosed, icon: "archive",     scripts:{onClick: 'selectMainMenu(self)'} },
+            { title: "Shared with me",  target: CurrentUserUtils.MySharedDocs,     icon: "users",       scripts:{onClick: 'selectMainMenu(self)'}, funcs:{badgeValue:badgeValue}},
+            { title: "Trails",          target: CurrentUserUtils.MyTrails,         icon: "pres-trail",  scripts:{onClick: 'selectMainMenu(self)'} },
+            { title: "User Doc",        target: CurrentUserUtils.MyUserDocView,    icon: "address-card",scripts:{onClick: 'selectMainMenu(self)'}, funcs: {hidden: "IsNoviceMode()"} },
         ];
     }
 
-    static async setupMenuPanel(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
-        if (doc.menuStack === undefined) {
-            await this.setupLinkDocs(doc, linkDatabaseId);
-            await this.setupSharedDocs(doc, sharingDocumentId);  // sets up the right sidebar collection for mobile upload documents and sharing
-            const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, badgeValue, hidden }) =>
-                Docs.Create.FontIconDocument({
-                    icon,
-                    btnType: ButtonType.MenuButton,
-                    _stayInCollection: true,
-                    _hideContextMenu: true,
-                    _chromeHidden: true,
-                    system: true,
-                    dontUndo: true,
-                    title,
-                    target,
-                    dontRegisterView: true,
-                    hidden: hidden ? ComputedField.MakeFunction("IsNoviceMode()") as any : undefined,
-                    _dropAction: "alias",
-                    _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
-                    _width: 60,
-                    _height: 60,
-                    badgeValue,
-                    onClick: ScriptField.MakeScript(click, { scriptContext: "any" })
-                })
-            );
-
-            doc.searchBtn = menuBtns.find(btn => btn.title === "Search");
-
-            doc.menuStack = new PrefetchProxy(Docs.Create.StackingDocument(menuBtns, {
-                title: "menuItemPanel",
-                childDropAction: "alias",
-                _chromeHidden: true,
-                backgroundColor: Colors.DARK_GRAY,
-                boxShadow: "rgba(0,0,0,0)",
-                dontRegisterView: true,
-                dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
-                ignoreClick: true,
-                _gridGap: 0,
-                _yMargin: 0,
-                _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true
-            }));
-        }
-        return doc.menuStack as Doc;
+    /// setup the left sidebar container and panels that can be displayed within it
+    static setupLeftSidebarPanel(doc: Doc, field="myLeftSidebarPanel") {
+        this.AssignOpts(DocCast(doc[field]), {}) ?? (doc[field] = ((doc:Doc) => {doc.system = true; return doc;})(new Doc()));
+        CurrentUserUtils.setupSearcher(doc, "mySearcher");
+        CurrentUserUtils.setupToolsBtnPanel(doc, "myTools");
+        CurrentUserUtils.setupImportSidebar(doc, "myImports");
+        CurrentUserUtils.setupDashboards(doc, "myDashboards");
+        CurrentUserUtils.setupTrails(doc, "myTrails");
+        CurrentUserUtils.setupFilesystem(doc, "myFilesystem");
+        CurrentUserUtils.setupRecentlyClosedDocs(doc, "myRecentlyClosed");
+        CurrentUserUtils.setupUserDocView(doc, "myUserDocView");
     }
 
+    /// Initializes the left sidebar menu buttons and the panels they open up
+    static setupLeftSidebarMenu(doc: Doc, field="myLeftSidebarMenu") {
+        this.setupLeftSidebarPanel(doc); // the tools/panels opened up by the menu buttons
+        const myLeftSidebarMenu = DocCast(doc[field]);
+        const menuBtns = CurrentUserUtils.leftSidebarMenuBtnDescriptions(doc).map(({ title, target, icon, scripts, funcs }) => {
+            const btnDoc = myLeftSidebarMenu ? DocListCast(myLeftSidebarMenu.data).find(doc => doc.title === title) : undefined;
+            const reqdBtnOpts:DocumentOptions = {
+                title, icon, target, btnType: ButtonType.MenuButton, system: true, dontUndo: true,  dontRegisterView: true,
+                _width: 60, _height: 60,  _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, _dropAction: "alias",
+                _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
+            };
+            return this.AssignScripts(this.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs);
+        });
 
-    // Sets up mobile menu if it is undefined creates a new one, otherwise returns existing menu
-    static setupActiveMobileMenu(doc: Doc) {
-        if (doc.activeMobileMenu === undefined) {
-            doc.activeMobileMenu = this.setupMobileMenu();
-        }
-        return doc.activeMobileMenu as Doc;
+        const reqdStackOpts:DocumentOptions ={
+            title: "menuItemPanel", childDropAction: "alias", backgroundColor: Colors.DARK_GRAY, boxShadow: "rgba(0,0,0,0)",  dontRegisterView: true, ignoreClick: true,
+            _chromeHidden: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true
+        };
+        const reqdScripts = { dropConverter: "convertToButtons(dragData)" }
+        return this.AssignScripts(this.AssignOpts(myLeftSidebarMenu, reqdStackOpts, menuBtns) ?? (doc[field] = Docs.Create.StackingDocument(menuBtns, reqdStackOpts)), reqdScripts);
     }
 
-    // Sets up mobileMenu stacking document
-    static setupMobileMenu() {
-        const menu = new PrefetchProxy(Docs.Create.StackingDocument(this.setupMobileButtons(), {
-            _width: 980, ignoreClick: true, _lockedPosition: false, title: "home", _yMargin: 100, system: true, _chromeHidden: true,
-        }));
-        return menu;
+    // Sets up mobile menu if it is undefined creates a new one, otherwise returns existing menu
+    static setupActiveMobileMenu(doc: Doc, field="activeMobileMenu") {
+        const reqdOpts = { _width: 980, ignoreClick: true, _lockedPosition: false, title: "home", _yMargin: 100, system: true, _chromeHidden: true,};
+        this.AssignOpts(DocCast(doc[field]), reqdOpts, this.setupMobileButtons()) ?? (doc[field] = Docs.Create.StackingDocument(this.setupMobileButtons(), reqdOpts));
     }
 
-    // SEts up mobile buttons for inside mobile menu
+    // Sets up mobile buttons for inside mobile menu
     static setupMobileButtons(doc?: Doc, buttons?: string[]) {
+        return [];
         const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, backgroundColor?: string, info: string, dragFactory?: Doc }[] = [
             { title: "DASHBOARDS", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Dashboards from your mobile, and navigate through all of your documents. " },
             { title: "UPLOAD", icon: "upload", click: 'openMobileUploads()', backgroundColor: "lightgrey", info: "Upload files from your mobile device so they can be accessed on Dash Web." },
@@ -533,375 +506,329 @@ export class CurrentUserUtils {
         });
     }
 
-    static setupLibrary(userDoc: Doc) {
-        return CurrentUserUtils.setupDashboards(userDoc);
-    }
-
-    // setup the Creator button which will display the creator panel.  This panel will include the drag creators and the color picker.
-    // when clicked, this panel will be displayed in the target container (ie, sidebarContainer)
-    static setupToolsBtnPanel(doc: Doc) {
-        // setup a masonry view of all he creators
-        const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc);
-        const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc);
-
-        doc.myCreators = doc.myCreators ?? new PrefetchProxy(Docs.Create.StackingDocument([creatorBtns, templateBtns], {
-            title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0, _fitWidth: true,
-            _width: 500, _height: 300, ignoreClick: true, _lockedPosition: true, system: true, _chromeHidden: true,
-        }));
-        if (!DocListCast(doc.myCreators).includes(creatorBtns) || !DocListCast(doc.myCreators).includes(templateBtns)) Doc.GetProto(doc.myCreators as Doc).data = new List<Doc>([creatorBtns, templateBtns]);
-
-        doc.myColorPicker = doc.myColorPicker ?? new PrefetchProxy(Docs.Create.ColorDocument({
-            title: "color picker", _width: 220, _dropAction: "alias", _hideContextMenu: true, _stayInCollection: true, _forceActive: true, _removeDropProperties: new List<string>(["dropAction", "_stayInCollection", "_hideContextMenu", "forceActive"]), system: true
-        }));
-
-        doc.myTools = doc.myTools ?? new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc], {
-            title: "My Tools", _showTitle: "title", _width: 500, _yMargin: 20, ignoreClick: true, _lockedPosition: true, _forceActive: true,
-            system: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, boxShadow: "0 0",
-        })) as any as Doc;
-        if (!DocListCast(doc.myTools).includes(doc.myCreators as Doc)) Doc.GetProto(doc.myTools as Doc).data = new List<Doc>([doc.myCreators as Doc]);
-    }
-
-    static async setupDashboards(doc: Doc) {
-        // setup dashboards library item
-        await doc.myDashboards;
-        if (doc.myDashboards === undefined) {
-            const newDashboard = ScriptField.MakeScript(`createNewDashboard(Doc.UserDoc())`);
-            const newDashboardButton: Doc = Docs.Create.FontIconDocument({ onClick: newDashboard, _forceActive: true, toolTip: "Create new dashboard", _stayInCollection: true, _hideContextMenu: true, title: "new dashboard", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New trail", icon: "plus", system: true });
-            doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], {
-                title: "My Dashboards", _showTitle: "title", _height: 400, childHideLinkButton: true, freezeChildren: "remove|add",
-                treeViewHideTitle: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, ignoreClick: true, buttonMenu: true, buttonMenuDoc: newDashboardButton,
-                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, system: true,
-                explainer: "This is your collection of dashboards. A dashboard represents the tab configuration of your workspace. To manage documents as folders, go to the Files."
-            }));
-            const toggleDarkTheme = ScriptField.MakeScript(`this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`);
-            const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
-            const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`);
-            const shareDashboard = ScriptField.MakeScript(`shareDashboard(self)`);
-            const removeDashboard = ScriptField.MakeScript('removeDashboard(self)');
-            const developerFilter = ScriptField.MakeFunction('!IsNoviceMode()');
-            // (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, shareDashboard!, removeDashboard!]);
-            // (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Share Dashboard", "Remove Dashboard"]);
-            // (doc.myDashboards as any as Doc).childContextMenuIcons = new List<string>(["plus", "user-friends", "times"]);
-            (doc.myDashboards as any as Doc).childContextMenuScripts = new List<ScriptField>([newDashboard!, toggleDarkTheme!, toggleComic!, snapshotDashboard!, shareDashboard!, removeDashboard!]);
-            (doc.myDashboards as any as Doc).childContextMenuLabels = new List<string>(["Create New Dashboard", "Toggle Dark Theme", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]);
-            (doc.myDashboards as any as Doc).childContextMenuIcons = new List<string>(["plus", "chalkboard", "tv", "camera", "users", "times"]);
-            (doc.myDashboards as any as Doc).childContextMenuFilters = new List<ScriptField>([undefined as any, developerFilter, developerFilter, developerFilter, undefined as any, undefined as any]);
-        }
-        return doc.myDashboards as any as Doc;
+    /// Search option on the left side button panel
+    static setupSearcher(doc: Doc, field:string) {
+        const reqdOpts:DocumentOptions = {
+            dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", system: true, childDropAction: "alias", 
+            _lockedPosition: true, _viewType: CollectionViewType.Schema, _searchDoc: true,
+        };
+        this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.SearchDocument(reqdOpts));
     }
 
-    static setupPresentations(doc: Doc) {
-        if (doc.myTrails === undefined) {
-            const newTrail = ScriptField.MakeScript(`createNewPresentation()`);
-            const newTrailButton: Doc = Docs.Create.FontIconDocument({ onClick: newTrail, _forceActive: true, toolTip: "Create new trail", _stayInCollection: true, _hideContextMenu: true, title: "New trail", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New trail", icon: "plus", system: true });
-            doc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], {
-                title: "My Trails", _showTitle: "title", _height: 100,
-                treeViewHideTitle: true, _fitWidth: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, ignoreClick: true, buttonMenu: true, buttonMenuDoc: newTrailButton,
-                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true,
-                explainer: "All of the trails that you have created will appear here."
-            }));
-            (doc.myTrails as any as Doc).contextMenuScripts = new List<ScriptField>([newTrail!]);
-            (doc.myTrails as any as Doc).contextMenuLabels = new List<string>(["Create New Trail"]);
-            (doc.myTrails as any as Doc).childContextMenuIcons = new List<string>(["plus"]);
-        }
-        return doc.myTrails as any as Doc;
+    /// Initializes the panel of draggable tools that is opened from the left sidebar.
+    static setupToolsBtnPanel(doc: Doc, field:string) {
+        const myTools = DocCast(doc[field]);
+        const creatorBtns = CurrentUserUtils.setupCreatorButtons(doc, DocListCast(myTools?.data)?.length ? DocListCast(myTools.data)[0]:undefined);
+        //const templateBtns = CurrentUserUtils.setupExperimentalTemplateButtons(doc);
+        const reqdToolOps:DocumentOptions = {
+            title: "My Tools", system: true, ignoreClick: true, boxShadow: "0 0",
+            _showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, 
+        };
+        this.AssignOpts(myTools, reqdToolOps, [creatorBtns, /*templateBtns*/]) ?? (doc[field] = Docs.Create.StackingDocument([creatorBtns, /*templateBtns*/], reqdToolOps));
     }
 
-    static async setupFilesystem(doc: Doc) {
-        await doc.myFilesystem;
-        if (doc.myFilesystem === undefined) {
-            doc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
-            // doc.myFileRoot = Docs.Create.TreeDocument([], { title: "file root", _stayInCollection: true, system: true, isFolder: true });
-            const newFolder = ScriptField.MakeFunction(`makeTopLevelFolder()`, { scriptContext: "any" })!;
-            const newFolderButton: Doc = Docs.Create.FontIconDocument({
-                onClick: newFolder, _forceActive: true, toolTip: "Create new folder",
-                _stayInCollection: true, _hideContextMenu: true, title: "New folder", btnType: ButtonType.ClickButton, _width: 30, _height: 30,
-                buttonText: "New folder", icon: "folder-plus", system: true
-            });
-            doc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([doc.myFileOrphans as Doc], {
-                title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100,
-                treeViewHideTitle: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, ignoreClick: true,
-                isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true,
-                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true,
-                explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
-            }));
-            (doc.myFilesystem as any as Doc).contextMenuScripts = new List<ScriptField>([newFolder]);
-            (doc.myFilesystem as any as Doc).contextMenuLabels = new List<string>(["Create new folder"]);
-            (doc.myFilesystem as any as Doc).childContextMenuIcons = new List<string>(["plus"]);
+    /// initializes the left sidebar dashboard pane
+    static setupDashboards(doc: Doc, field:string) {
+        var myDashboards = DocCast(doc[field]);
+
+        const newDashboard = `createNewDashboard(Doc.UserDoc())`;
+        const reqdBtnOpts:DocumentOptions = { _forceActive: true,  _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true,
+             title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", system: true };
+        const reqdBtnScript = {onClick: newDashboard,}
+        const newDashboardButton = this.AssignScripts(this.AssignOpts(DocCast(myDashboards?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
+       
+        const reqdOpts:DocumentOptions = {
+            title: "My Dashboards", childHideLinkButton: true, freezeChildren: "remove|add", treeViewHideTitle: true, boxShadow: "0 0", childDontRegisterViews: true,
+            targetDropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, system: true,  treeViewTruncateTitleWidth: 150, ignoreClick: true, 
+            buttonMenu: true, buttonMenuDoc: newDashboardButton, childDropAction: "alias",
+            _showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true,
+            contextMenuLabels: new List<string>(["Create New Dashboard"]),
+            contextMenuIcons: new List<string>(["plus"]),
+            childContextMenuLabels: new List<string>(["Toggle Dark Theme", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]),// entries must be kept in synch with childContextMenuScripts, childContextMenuIcons, and childContextMenuFilters
+            childContextMenuIcons: new List<string>(["chalkboard", "tv", "camera", "users", "times"]), // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters
+            explainer: "This is your collection of dashboards. A dashboard represents the tab configuration of your workspace. To manage documents as folders, go to the Files."
+        };
+        myDashboards = this.AssignOpts(myDashboards, reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
+        const toggleDarkTheme = `this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`;
+        const contextMenuScripts = [newDashboard];
+        const childContextMenuScripts = [toggleDarkTheme, `toggleComicMode()`, `snapshotDashboard()`, `shareDashboard(self)`, 'removeDashboard(self)']; // entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuFilters
+        const childContextMenuFilters =  ['!IsNoviceMode()', '!IsNoviceMode()', '!IsNoviceMode()', undefined as any, undefined as any];// entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuScripts
+        if (Cast(myDashboards.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) {
+            myDashboards.contextMenuScripts = new List<ScriptField>(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!));
+        } 
+        if (Cast(myDashboards.childContextMenuScripts, listSpec(ScriptField), null)?.length !== childContextMenuScripts.length) {
+            myDashboards.childContextMenuScripts = new List<ScriptField>(childContextMenuScripts.map(script => ScriptField.MakeFunction(script)!));
+        } 
+        if (Cast(myDashboards.childContextMenuFilters, listSpec(ScriptField), null)?.length !== childContextMenuFilters.length) {
+            myDashboards.childContextMenuFilters = new List<ScriptField>(childContextMenuFilters.map(script => !script ? script: ScriptField.MakeFunction(script)!));
         }
-        return doc.myFilesystem as any as Doc;
     }
 
-    static setupRecentlyClosedDocs(doc: Doc) {
-        if (doc.myRecentlyClosedDocs === undefined) {
-            const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`);
-            const clearDocsButton: Doc = Docs.Create.FontIconDocument({ onClick: clearAll, _forceActive: true, toolTip: "Empty recently closed", _stayInCollection: true, _hideContextMenu: true, title: "Empty", btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "Empty", icon: "trash", system: true });
-            doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([], {
-                title: "My Recently Closed", _showTitle: "title", buttonMenu: true, buttonMenuDoc: clearDocsButton, childHideLinkButton: true,
-                treeViewHideTitle: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, ignoreClick: true,
-                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true,
-                explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list."
-
-            }));
-            (doc.myRecentlyClosedDocs as any as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
-            (doc.myRecentlyClosedDocs as any as Doc).contextMenuLabels = new List<string>(["Empty recently closed"]);
-            (doc.myRecentlyClosedDocs as any as Doc).contextMenuIcons = new List<string>(["trash"]);
-
+    /// initializes the left sidebar Trails pane
+    static setupTrails(doc: Doc, field:string) {
+        var myTrails = DocCast(doc[field]);
+        const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30,  _stayInCollection: true, _hideContextMenu: true, 
+            title: "New trail",  toolTip: "Create new trail", btnType: ButtonType.ClickButton, buttonText: "New trail", icon: "plus", system: true };
+        const reqdBtnScript = {onClick: `createNewPresentation()`};
+        const newTrailButton = this.AssignScripts(this.AssignOpts(DocCast(myTrails?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
+        
+        const reqdOpts:DocumentOptions = {
+            title: "My Trails", _showTitle: "title", _height: 100,
+            treeViewHideTitle: true, _fitWidth: true, _gridGap: 5, _forceActive: true, childDropAction: "alias",
+            treeViewTruncateTitleWidth: 150, ignoreClick: true, buttonMenu: true, buttonMenuDoc: newTrailButton,
+            contextMenuIcons: new List<string>(["plus"]),
+            contextMenuLabels: new List<string>(["Create New Trail"]),
+            _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true,
+            explainer: "All of the trails that you have created will appear here."
+        };
+        myTrails = this.AssignOpts(myTrails, reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([],reqdOpts ));
+        const contextMenuScripts = [reqdBtnScript.onClick];
+        if (Cast(myTrails.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) {
+            myTrails.contextMenuScripts = new List<ScriptField>(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!));
         }
     }
 
-    static setupFilterDocs(doc: Doc) {
-        // setup Filter item
-        if (doc.currentFilter === undefined) {
-            doc.currentFilter = Docs.Create.FilterDocument({
-                title: "Unnamed Filter", _height: 150,
-                treeViewHideTitle: true, _xPadding: 5, _yPadding: 5, _gridGap: 5, _forceActive: true, childDropAction: "none",
-                treeViewTruncateTitleWidth: 150, ignoreClick: true,
-                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true, _autoHeight: true, _fitWidth: true
-            });
-            const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`);
-            (doc.currentFilter as Doc).contextMenuScripts = new List<ScriptField>([clearAll!]);
-            (doc.currentFilter as Doc).contextMenuLabels = new List<string>(["Clear All"]);
-            (doc.currentFilter as Doc).filterBoolean = "AND";
+    /// initializes the left sidebar File system pane
+    static setupFilesystem(doc: Doc, field:string) {
+        var myFilesystem = DocCast(doc[field]);
+        const reqdOrphansOpts:DocumentOptions = { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true };
+        this.AssignOpts(DocCast(doc.myFileOrphans), reqdOrphansOpts) ?? (doc.myFileOrphans = Docs.Create.TreeDocument([], reqdOrphansOpts));
+        
+        const newFolder = `makeTopLevelFolder()`;
+        const newFolderOpts: DocumentOptions = {
+            _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _width: 30, _height: 30,
+            title: "New folder", btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", system: true
+        };
+        const newFolderScript = { onClick: newFolder};
+        const newFolderButton = this.AssignScripts(this.AssignOpts(DocCast(myFilesystem?.buttonMenuDoc), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript);
+        
+        const reqdOpts:DocumentOptions = { _showTitle: "title", _height: 100, _gridGap: 5, _forceActive: true, _lockedPosition: true, 
+            title: "My Documents", buttonMenu: true, buttonMenuDoc: newFolderButton, treeViewHideTitle: true, targetDropAction: "proto", system: true,
+            isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true, boxShadow: "0 0", childDontRegisterViews: true, 
+            treeViewTruncateTitleWidth: 150, ignoreClick: true, childDropAction: "alias",
+            childContextMenuLabels: new List<string>(["Create new folder"]),
+            childContextMenuIcons: new List<string>(["plus"]),
+            explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
+        };
+        myFilesystem = this.AssignOpts(myFilesystem, reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([DocCast(doc.myFileOrphans)], reqdOpts));
+        const childContextMenuScripts = [newFolder];
+        if (Cast(myFilesystem.childContextMenuScripts, listSpec(ScriptField), null)?.length !== childContextMenuScripts.length) {
+            myFilesystem.childContextMenuScripts = new List<ScriptField>(childContextMenuScripts.map(script => ScriptField.MakeFunction(script)!));
         }
     }
 
-    static setupUserDoc(doc: Doc) {
-        if (doc.myUserDoc === undefined) {
-            doc.treeViewOpen = true;
-            doc.treeViewExpandedView = "fields";
-            doc.myUserDoc = new PrefetchProxy(Docs.Create.TreeDocument([doc], {
-                treeViewHideTitle: true, _gridGap: 5, _forceActive: true, title: "My UserDoc", _showTitle: "title",
-                treeViewTruncateTitleWidth: 150, ignoreClick: true,
-                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true
-            })) as any as Doc;
+    /// initializes the panel displaying docs that have been recently closed
+    static setupRecentlyClosedDocs(doc: Doc, field:string) {
+        const reqdOpts:DocumentOptions = { _showTitle: "title", _lockedPosition: true, _gridGap: 5, _forceActive: true, 
+            title: "My Recently Closed", buttonMenu: true, childHideLinkButton: true, treeViewHideTitle: true, childDropAction: "alias", system: true,
+            treeViewTruncateTitleWidth: 150, ignoreClick: true,  boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same",
+            contextMenuLabels: new List<string>(["Empty recently closed"]),
+            contextMenuIcons:new List<string>(["trash"]),
+            explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list."
+        };
+        const recentlyClosed = this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
+
+        const clearAll = (target:string) => `getProto(${target}).data = new List([])`;
+        const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, 
+            title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton,  buttonText: "Empty", icon: "trash", system: true,
+            toolTip: "Empty recently closed",};
+        const clearBtnsScripts = {onClick: clearAll("self.target")}
+        const clearDocsButton = this.AssignScripts(
+                this.AssignOpts(DocCast(recentlyClosed?.clearDocsBtn), clearBtnsOpts) ?? (recentlyClosed.clearDocsBtn = Docs.Create.FontIconDocument(clearBtnsOpts)),
+                clearBtnsScripts);
+
+        if (recentlyClosed.buttonMenuDoc !== clearDocsButton) Doc.GetProto(recentlyClosed).buttonMenuDoc = clearDocsButton;
+        
+        if (!Cast(recentlyClosed.contextMenuScripts, listSpec(ScriptField),null)?.find((script) => script.script.originalScript === clearAll("self"))) {
+            recentlyClosed.contextMenuScripts = new List<ScriptField>([ScriptField.MakeScript(clearAll("self"))!])
         }
     }
 
-    static setupSidebarContainer(doc: Doc) {
-        if (doc.sidebar === undefined) {
-            const sidebarContainer = new Doc();
-            sidebarContainer.system = true;
-            doc.sidebar = new PrefetchProxy(sidebarContainer);
-        }
-        return doc.sidebar as Doc;
+    /// creates a new, empty filter doc
+    static createFilterDoc() {
+        const clearAll = `getProto(self).data = new List([])`;
+        const reqdOpts:DocumentOptions = {
+            _lockedPosition: true,  _autoHeight: true, _fitWidth: true, _height: 150, _xPadding: 5, _yPadding: 5, _gridGap: 5, _forceActive: true, 
+            title: "Unnamed Filter", filterBoolean: "AND", boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, system: true,
+            childDropAction: "none", treeViewHideTitle: true, treeViewTruncateTitleWidth: 150, 
+            childContextMenuLabels: new List<string>(["Clear All"]),
+            childContextMenuScripts: new List<ScriptField>([ScriptField.MakeFunction(clearAll)!]),
+        };
+        return Docs.Create.FilterDocument(reqdOpts);
     }
 
-    // setup the list of sidebar mode buttons which determine what is displayed in the sidebar
-    static async setupSidebarButtons(doc: Doc) {
-        CurrentUserUtils.setupSidebarContainer(doc);
-        CurrentUserUtils.setupToolsBtnPanel(doc);
-        CurrentUserUtils.setupImportSidebar(doc);
-        CurrentUserUtils.setupDashboards(doc);
-        CurrentUserUtils.setupPresentations(doc);
-        CurrentUserUtils.setupFilesystem(doc);
-        CurrentUserUtils.setupRecentlyClosedDocs(doc);
-        CurrentUserUtils.setupUserDoc(doc);
+    /// initializes the left sidebar panel view of the UserDoc
+    static setupUserDocView(doc: Doc, field:string) {
+        const reqdOpts:DocumentOptions = {
+            _lockedPosition: true, _gridGap: 5, _forceActive: true, title: Doc.CurrentUserEmail +"-view",
+            boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, system: true,
+            treeViewHideTitle: true, treeViewTruncateTitleWidth: 150
+        };
+        if (!doc[field]) this.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" })
+        this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([doc], reqdOpts));
     }
 
-    static linearButtonList = (opts: DocumentOptions, docs: Doc[]) => new PrefetchProxy(Docs.Create.LinearDocument(docs, {
-        ...opts, _gridGap: 0, _xMargin: 5, _yMargin: 5, boxShadow: "0 0", _forceActive: true,
+    static linearButtonList = (title: string, opts: DocumentOptions, docs: Doc[]) => Docs.Create.LinearDocument(docs, {
+        title, ...opts, _gridGap: 0, _xMargin: 5, _yMargin: 5, boxShadow: "0 0", _forceActive: true,
         dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
         _lockedPosition: true, system: true, flexDirection: "row"
-    })) as any as Doc
+    })
 
-    static createToolButton = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({
+    static createToolButton = (opts: DocumentOptions) => Docs.Create.FontIconDocument({
         btnType: ButtonType.ToolButton, _forceActive: true, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List<string>(["_dropAction", "_hideContextMenu", "stayInCollection"]), _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true, ...opts,
-    })) as any as Doc
-
-    /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window
-    static setupDockedButtons(doc: Doc) {
-        if (doc.dockedBtns === undefined) {
-            const dockBtn = (opts: DocumentOptions) => CurrentUserUtils.createToolButton({ _width: 30, _height: 30, dontUndo: true, _stayInCollection: true, ...opts });
-            const btnDescs = [
-                { title: "undo", opts: () => ({ icon: "undo-alt", onClick: ScriptField.MakeScript("undo()"), toolTip: "Click to undo" }) },
-                { title: "redo", opts: () => ({ icon: "redo-alt", onClick: ScriptField.MakeScript("redo()"), toolTip: "Click to redo" }) }
-            ];
-            doc.dockedBtns = CurrentUserUtils.linearButtonList({
-                title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true,
-                childDontRegisterViews: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true
-            }, btnDescs.map(desc => doc[`dockedBtn-${desc.title}`] as Doc ?? (doc[`dockedBtn-${desc.title}`] = dockBtn({ title: desc.title, ...desc.opts() }))));
+    })
+
+    /// initializes the required buttons in the expanding button menu at the bottom of the Dash window
+    static setupDockedButtons(doc: Doc, field="myDockedBtns") {
+        const dockedBtns = DocCast(doc[field]);
+        const dockBtn = (title:string, onClick: string, opts: DocumentOptions) => {
+            const btn = this.AssignOpts(DocListCast(dockedBtns?.data)?.find(doc => doc.title === title), opts) ?? 
+                            CurrentUserUtils.createToolButton({title, ...opts});
+            this.AssignScripts(btn, {onClick})
+            return btn;
         }
+        const btnDescs = [// setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet
+            { title: "undo", click: "undo()", opts: { icon: "undo-alt", toolTip: "Click to undo" }},
+            { title: "redo", click:"redo()", opts: { icon: "redo-alt", toolTip: "Click to redo" }}
+        ];
+        const btns = btnDescs.map(desc => dockBtn(desc.title, desc.click, {_width: 30, _height: 30, dontUndo: true, _stayInCollection: true, ...desc.opts}));
+        const dockBtnsReqdOpts = {
+            title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true,
+            childDontRegisterViews: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true
+        };
+        reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
+        reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "undo")!).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true });
+        return this.AssignOpts(dockedBtns, dockBtnsReqdOpts, btns) ?? (doc[field] = CurrentUserUtils.linearButtonList("dockedBtns", dockBtnsReqdOpts, btns));
     }
 
-    static textTools(doc: Doc) {
-        const tools: Button[] =
-            [
-                {
-                    title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true,
-                    list: ["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia",
-                        "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"],
-                    script: 'setFont(value, _readOnly_)'
-                },
-                { title: "Size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'setFontSize(value, _readOnly_)' },
-                { title: "Color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script: 'setFontColor(value, _readOnly_)' },
-                { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold(_readOnly_)' },
-                { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic(_readOnly_)' },
-                { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline(_readOnly_)' },
-                { title: "Bullets", toolTip: "Bullet List", btnType: ButtonType.ToggleButton, icon: "list", click: 'setBulletList("bullet", _readOnly_)' },
-                { title: "#", toolTip: "Number List", btnType: ButtonType.ToggleButton, icon: "list-ol", click: 'setBulletList("decimal", _readOnly_)' },
-
-                // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", click: 'toggleStrikethrough()'},
-                // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", click: 'toggleSuperscript()'},
-                // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", click: 'toggleSubscript()'},
-                { title: "Left", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", click: 'setAlignment("left", _readOnly_)' },
-                { title: "Center", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", click: 'setAlignment("center", _readOnly_)' },
-                { title: "Right", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", click: 'setAlignment("right", _readOnly_)' },
-                { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", click: 'toggleNoAutoLinkAnchor(_readOnly_)' },
-            ];
-        return tools;
+    static textTools():Button[] {
+        return [
+            {
+                title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true,
+                btnList: new List<string>(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]),
+                scripts :{script : 'setFont(value, _readOnly_)'}
+            },
+            { title: "Size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, scripts: {script: '{ return setFontSize(value, _readOnly_);}'} },
+            { title: "Color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, scripts:{script: '{ return setFontColor(value, _readOnly_); }'}},
+            { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", scripts: {onClick: '{ return toggleBold(_readOnly_); }'} },
+            { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", scripts: {onClick: '{ return toggleItalic(_readOnly_);}'} },
+            { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline",scripts: {onClick:'{ return toggleUnderline(_readOnly_);}'} },
+            { title: "Bullets", toolTip: "Bullet List", btnType: ButtonType.ToggleButton, icon: "list", scripts: {onClick: '{ return setBulletList("bullet", _readOnly_);}'} },
+            { title: "#", toolTip: "Number List", btnType: ButtonType.ToggleButton, icon: "list-ol", scripts: {onClick: '{ return setBulletList("decimal", _readOnly_);}'} },
+
+            // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", scripts: {onClick:: 'toggleStrikethrough()'}},
+            // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", scripts: {onClick:: 'toggleSuperscript()'}},
+            // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", scripts: {onClick:: 'toggleSubscript()'}},
+            { title: "Left", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", scripts: {onClick:'{ return setAlignment("left", _readOnly_);}' }},
+            { title: "Center", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", scripts: {onClick:'{ return setAlignment("center", _readOnly_);}'} },
+            { title: "Right", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", scripts: {onClick:'{ return setAlignment("right", _readOnly_);}'} },
+            { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", scripts: {onClick:'{ return toggleNoAutoLinkAnchor(_readOnly_);}'}},
+        ];
     }
 
-    static inkTools(doc: Doc) {
-        const tools: Button[] = [
-            { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", click: 'setActiveInkTool("pen", _readOnly_)' },
-            { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("write", _readOnly_)' },
-            { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", click: 'setActiveInkTool("eraser", _readOnly_)' },
-            // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")' },
-            { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle", _readOnly_)' },
+    static inkTools():Button[] {
+        return [
+            { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", scripts:{onClick:'{ return setActiveInkTool("pen", _readOnly_);}' }},
+            { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", scripts:{onClick:'{ return setActiveInkTool("write", _readOnly_);}'} },
+            { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", scripts:{onClick:'{ return setActiveInkTool("eraser", _readOnly_);}' }},
+            // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", scripts:{onClick: 'setActiveInkTool("highlighter")'} },
+            { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", scripts:{onClick:'{ return setActiveInkTool("circle", _readOnly_);}'} },
             // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")' },
-            { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", click: 'setActiveInkTool("line", _readOnly_)' },
-            { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setFillColor(value, _readOnly_)" },
-            { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, script: 'setStrokeWidth(value, _readOnly_)' },
-            { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, script: 'setStrokeColor(value, _readOnly_)' },
+            { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", scripts:{onClick: '{ return setActiveInkTool("line", _readOnly_);}' }},
+            { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", scripts: {script: "{ return setFillColor(value, _readOnly_);}"} },
+            { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, scripts: {script: '{ return setStrokeWidth(value, _readOnly_);}'} },
+            { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, scripts: {script: '{ return setStrokeColor(value, _readOnly_);}'} },
         ];
-        return tools;
     }
 
-    static schemaTools(doc: Doc) {
-        const tools: Button[] =
-            [{ title: "Show preview", toolTip: "Show preview of selected document", btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", click: 'toggleSchemaPreview(_readOnly_)', }];
-        return tools;
+    static schemaTools():Button[] {
+        return [{ title: "Show preview", toolTip: "Show preview of selected document", btnType: ButtonType.ToggleButton, buttonText: "Show Preview", icon: "eye", scripts:{onClick:'toggleSchemaPreview(_readOnly_)'}, }];
     }
 
-    static webTools(doc: Doc) {
-        const tools: Button[] =
-            [
-                { title: "Back", toolTip: "Go back", btnType: ButtonType.ClickButton, icon: "arrow-left", click: 'webBack(_readOnly_)' },
-                { title: "Forward", toolTip: "Go forward", btnType: ButtonType.ClickButton, icon: "arrow-right", click: 'webForward(_readOnly_)' },
-                //{ title: "Reload", toolTip: "Reload webpage", btnType: ButtonType.ClickButton, icon: "redo-alt", click: 'webReload()' },
-                { title: "URL", toolTip: "URL", width: 250, btnType: ButtonType.EditableText, icon: "lock", ignoreClick: true, script: 'webSetURL(value, _readOnly_)' },
-            ];
-
-        return tools;
+    static webTools() {
+        return [
+            { title: "Back", toolTip: "Go back", btnType: ButtonType.ClickButton, icon: "arrow-left", click: 'webBack(_readOnly_)' },
+            { title: "Forward", toolTip: "Go forward", btnType: ButtonType.ClickButton, icon: "arrow-right", click: 'webForward(_readOnly_)' },
+            //{ title: "Reload", toolTip: "Reload webpage", btnType: ButtonType.ClickButton, icon: "redo-alt", click: 'webReload()' },
+            { title: "URL", toolTip: "URL", width: 250, btnType: ButtonType.EditableText, icon: "lock", ignoreClick: true, script: 'webSetURL(value, _readOnly_)' },
+        ];
     }
 
-    static contextMenuTools(doc: Doc) {
+    static contextMenuTools():Button[] {
         return [
             {
                 title: "Perspective", toolTip: "View", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true,
-                list: [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Tree,
-                CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Multicolumn,
-                CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel,
-                CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map,
-                CollectionViewType.Grid],
-                script: 'setView(value, _readOnly_)',
+                btnList: new List<string>([CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Tree,
+                    CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Multicolumn,
+                    CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel,
+                    CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map,
+                    CollectionViewType.Grid]),
+                scripts: {script: 'setView(value, _readOnly_)'},
             }, // Always show
-            { title: "Back", toolTip: "Prev Animation Frame", width: 20, btnType: ButtonType.ClickButton, click: 'prevKeyFrame(_readOnly_)', icon: "chevron-left", hidden: 'IsNoviceMode()' },
-            { title: "Fwd", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, click: 'nextKeyFrame(_readOnly_)', icon: "chevron-right", hidden: 'IsNoviceMode()' },
-            { title: "Fill", toolTip: "Background Fill Color", width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", script: "setBackgroundColor(value, _readOnly_)", hidden: 'selectedDocumentType()' }, // Only when a document is selected
-            { title: "Header", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading", script: "setHeaderColor(value, _readOnly_)", hidden: 'selectedDocumentType()', },
-            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay(_readOnly_)', hidden: 'selectedDocumentType(undefined, "freeform", true)' }, // Only when floating document is selected in freeform
+            { title: "Back", toolTip: "Prev Animation Frame", width: 20, btnType: ButtonType.ClickButton,icon: "chevron-left",  scripts:{onClick: 'prevKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'} },
+            { title: "Fwd", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton, icon: "chevron-right", scripts:{onClick: 'nextKeyFrame(_readOnly_)'}, funcs: {hidden: 'IsNoviceMode()'}},
+            { title: "Fill", toolTip: "Background Fill Color", width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", scripts: { script: "setBackgroundColor(value, _readOnly_)"}, funcs:{ hidden: 'selectedDocumentType()' }}, // Only when a document is selected
+            { title: "Header", toolTip: "Header Color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "heading", scripts: {script: "setHeaderColor(value, _readOnly_)"}, funcs : {hidden: 'selectedDocumentType()'} },
+            { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", scripts: {onClick : 'toggleOverlay(_readOnly_)'}, funcs: {hidden: 'selectedDocumentType(undefined, "freeform", true)'} }, // Only when floating document is selected in freeform
             // { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden: 'selectedDocumentType()' }, // Only when a document is selected 
-            { title: "Text", type: "textTools", subMenu: CurrentUserUtils.textTools(doc), expanded: 'selectedDocumentType("rtf")' }, // Always available
-            { title: "Ink", type: "inkTools", subMenu: CurrentUserUtils.inkTools(doc), expanded: 'selectedDocumentType("ink")' }, // Always available
-            { title: "Web", type: "webTools", subMenu: CurrentUserUtils.webTools(doc), hidden: 'selectedDocumentType("web")' }, // Only when Web is selected
-            { title: "Schema", type: "schemaTools", subMenu: CurrentUserUtils.schemaTools(doc), hidden: 'selectedDocumentType(undefined, "schema")' } // Only when Schema is selected
+            { title: "Text", icon: "text",  subMenu: CurrentUserUtils.textTools(), funcs: { linearViewIsExpanded: `selectedDocumentType("${DocumentType.RTF}")`} }, // Always available
+            { title: "Ink", icon: "ink", subMenu: CurrentUserUtils.inkTools(), funcs: { linearViewIsExpanded: `selectedDocumentType("${DocumentType.INK}")`} }, // Always available
+            { title: "Web", icon: "web", subMenu: CurrentUserUtils.webTools(), funcs: { linearViewIsExpanded: `selectedDocumentType("${DocumentType.WEB}")`, hidden: `!selectedDocumentType("${DocumentType.WEB}")`} }, // Only when Web is selected
+            { title: "Schema", icon: "schema", subMenu: CurrentUserUtils.schemaTools(), funcs: { linearViewIsExpanded: `selectedDocumentType(undefined, "${CollectionViewType.Schema}")`, hidden: `!selectedDocumentType(undefined, "${CollectionViewType.Schema}")`} } // Only when Schema is selected
         ];
     }
 
-    // Sets up the default context menu buttons
-    static setupContextMenuButtons(doc: Doc) {
-        const btnFunc = (params: Button) => Docs.Create.FontIconDocument({
-            title: params.title, icon: params.icon, toolTip: params.toolTip, color: Colors.WHITE, system: true, dontUndo: true, ignoreClick: params.ignoreClick,
+    /// initializes a context menu button for the top bar context menu
+    static setupContextMenuButton(params:Button, btnDoc?:Doc) {
+        const reqdOpts:DocumentOptions = {
+            title: params.title, btnType: params.btnType, icon: params.icon, toolTip: params.toolTip, ignoreClick: params.ignoreClick, 
+            numBtnType: params.numBtnType, numBtnMin: params.numBtnMin, numBtnMax: params.numBtnMax,_nativeHeight: 30, btnList: params.btnList,
+            backgroundColor: params.scripts?.onClick ? undefined: "transparent", ///  a bit hacky. if an onClick is specified, then we assume we assume a toggle use onClick to get the backgroundColor (see below).  Otherwise, assume a transparent background
             _nativeWidth: params.width ? params.width : 30,
-            _nativeHeight: 30,
             _width: params.width ? params.width : 30,
             _height: 30,
-            btnType: params.btnType,
-            numBtnType: params.numBtnType, numBtnMin: params.numBtnMin, numBtnMax: params.numBtnMax,
-            btnList: new List<string>(params.list),
+            color: Colors.WHITE, system: true, dontUndo: true, 
             _stayInCollection: true,
             _hideContextMenu: true,
             _lockedPosition: true,
             _dropAction: "alias",
             _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
-            script: params.script ? ScriptField.MakeScript(params.script, { value: "any" }) : undefined,
-            backgroundColor: params.click ? ComputedField.MakeFunction(params.click) as any : "transparent",
-            onClick: params.click ? ScriptField.MakeScript(params.click, { scriptContext: "any" }, { _readOnly_: false }) : undefined,
-            hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined,
-        });
-        if (doc.contextMenuBtns === undefined) {
-            doc.contextMenuBtns = CurrentUserUtils.linearButtonList(
-                { title: "menu buttons", flexGap: 0, childDontRegisterViews: true,  linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 },
-                CurrentUserUtils.contextMenuTools(doc).map(params =>
-                    !params.subMenu ?
-                        btnFunc(params) :
-                        CurrentUserUtils.linearButtonList({
-                            title: params.title,
-                            childDontRegisterViews: true,
-                            linearViewSubMenu: true, flexGap: 0, ignoreClick: true,
-                            linearViewExpandable: true, icon: params.title, _height: 30,
-                            linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined,
-                            hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined,
-                        }, params.subMenu.map(btnFunc))));
-        } else {
-            const menuBtnList = DocListCast((doc.contextMenuBtns as Doc).data);
-            let prev = "";
-            CurrentUserUtils.contextMenuTools(doc).forEach(params => {
-                const menuBtnDoc = menuBtnList.find(doc => doc.title === params.title);
-                if (!menuBtnDoc) {
-                    const newMenuBtnDoc = !params.subMenu ?
-                        btnFunc(params) :
-                        CurrentUserUtils.linearButtonList({
-                            title: params.title,
-                            childDontRegisterViews: true,
-                            linearViewSubMenu: true, flexGap: 0, ignoreClick: true,
-                            linearViewExpandable: true, icon: params.title, _height: 30,
-                            linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined,
-                            hidden: params.hidden ? ComputedField.MakeFunction(params.hidden) as any : undefined,
-                        }, params.subMenu.map(btnFunc));
-                    const after = menuBtnList.find(doc => doc.title === prev);
-                    Doc.AddDocToList(doc.contextMenuBtns as Doc, "data", newMenuBtnDoc, after, false, !after);
-                }
-                const subMenuBtnList = menuBtnDoc?.data ? DocListCast(menuBtnDoc.data) : undefined;
-                if (menuBtnDoc && subMenuBtnList && params.subMenu && DocListCast(doc.data).length !== subMenuBtnList.length) {
-                    let prevSub = "";
-                    params.subMenu.forEach(sub => {
-                        if (!subMenuBtnList.find(doc => doc.title === sub.title)) {
-                            const newSubMenuBtnDoc = btnFunc(sub);
-                            const after = subMenuBtnList.find(doc => doc.title === prevSub);
-                            Doc.AddDocToList(menuBtnDoc, "data", newSubMenuBtnDoc, after, false, !prevSub);
-                        }
-                        prevSub = params.title;
-                    });
-                }
-                prev = params.title;
-            });
+        };
+        const reqdFuncs:{[key:string]:any} = {
+            ...params.funcs,
+            backgroundColor: params.scripts?.onClick /// a bit hacky.  if onClick is set, then we assume it returns a color value when queried with '_readOnly_'.  This will be true for toggle buttons, but not generally
         }
+        return this.AssignScripts(this.AssignOpts(btnDoc, reqdOpts) ?? Docs.Create.FontIconDocument(reqdOpts), params.scripts, reqdFuncs);
     }
 
-    // sets up the default set of documents to be shown in the Overlay layer
-    static setupOverlays(doc: Doc) {
-        if (doc.myOverlayDocs === undefined) {
-            doc.myOverlayDocs = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "overlay documents", backgroundColor: "#aca3a6", system: true }));
-        }
+    /// Initializes all the default buttons for the top bar context menu
+    static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") {
+        const ctxtMenuBtnsDoc = DocCast(doc[field]);
+        const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => {
+            const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title);
+            if (!params.subMenu) {
+                return this.setupContextMenuButton(params, menuBtnDoc);
+            } else {
+                const reqdSubMenuOpts = { title: params.title, icon: params.icon, childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: true,
+                    linearViewSubMenu: true, linearViewExpandable: true, };
+                const reqdSubMenuFuncs:{[key:string]:any} = { ...params.funcs};
+                return this.AssignScripts(this.AssignOpts(menuBtnDoc, reqdSubMenuOpts) ??
+                        CurrentUserUtils.linearButtonList("submenu", reqdSubMenuOpts, params.subMenu.map(sub =>
+                            this.setupContextMenuButton(sub, DocListCast(menuBtnDoc?.data).find(doc => doc.title === sub.title))
+                        )), undefined, reqdSubMenuFuncs);
+            }
+        });
+        const reqdCtxtOpts = { title: "menu buttons", flexGap: 0, childDontRegisterViews: true,  linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 };
+        return this.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts) ?? (doc[field] = CurrentUserUtils.linearButtonList("contextMenuButtons", reqdCtxtOpts, ctxtMenuBtns));
     }
 
-    // the initial presentation Doc to use
-    static setupDefaultPresentation(doc: Doc) {
-        if (doc["template-presentation"] === undefined) {
-            doc["template-presentation"] = new PrefetchProxy(Docs.Create.PresElementBoxDocument({
-                title: "pres element template", backgroundColor: "transparent", _xMargin: 5, _fitWidth: true, _height: 46, isTemplateDoc: true, isTemplateForField: "data", system: true
-            }));
-        }
+    /// collection of documents rendered in the overlay layer above all tabs and other UI
+    static setupOverlays(doc: Doc, field = "myOverlayDocs") {
+        const reqdOpts = { title: "overlay documents", backgroundColor: "#aca3a6", system: true };
+        return this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.FreeformDocument([], reqdOpts));
     }
-
-    // Sharing sidebar is where shared documents are contained
+    
+    /// The database of all links on all documents
     static async setupLinkDocs(doc: Doc, linkDatabaseId: string) {
         if (doc.myLinkDatabase === undefined) {
             let linkDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(linkDatabaseId);
@@ -915,6 +842,8 @@ export class CurrentUserUtils {
             doc.myLinkDatabase = new PrefetchProxy(linkDocs);
         }
     }
+
+    /// Shared documents option on the left side button panel
     // A user's sharing document is where all documents that are shared to that user are placed.  
     // When the user views one of these documents, it will be added to the sharing documents 'viewed' list field
     // The sharing document also stores the user's color value which helps distinguish shared documents from personal documents
@@ -941,37 +870,26 @@ export class CurrentUserUtils {
         };
         
         const sharedDocs = Docs.newAccount ? undefined : DocCast(doc.mySharedDocs) ?? DocCast(await DocServer.GetRefField(sharingDocumentId + "outer"));
-        if (!(sharedDocs instanceof Doc)) {
-            doc.mySharedDocs = new PrefetchProxy(
-                Docs.Create.TreeDocument([], {...sharedDocOpts, ...sharedRequiredDocOpts}, sharingDocumentId + "outer", sharingDocumentId));
-        } else {
-            Object.entries(sharedRequiredDocOpts).forEach(pair => {
-                const targetDoc = pair[0].startsWith("_") ? sharedDocs as Doc : Doc.GetProto(sharedDocs as Doc);
-                targetDoc[pair[0]] = pair[1];
-            });
-        }
+        return this.AssignOpts(DocCast(sharedDocs), sharedRequiredDocOpts) ?? 
+            (doc.mySharedDocs = Docs.Create.TreeDocument([], {...sharedDocOpts, ...sharedRequiredDocOpts}, sharingDocumentId + "outer", sharingDocumentId));
     }
 
-    // Import sidebar is where shared documents are contained
-    static setupImportSidebar(doc: Doc) {
-        if (doc.myImportDocs === undefined) {
-            const newImportButton: Doc = Docs.Create.FontIconDocument({ onClick: ScriptField.MakeScript("importDocument()"), _forceActive: true, toolTip: "Import from computer", _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, buttonText: "Import", icon: "upload", system: true });
-            doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([], {
-                title: "My Imports", _forceActive: true, buttonMenu: true, buttonMenuDoc: newImportButton, ignoreClick: true, _showTitle: "title", _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0,
-                childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true,
-                dontRegisterView: true, explainer: "This is where documents that are Imported into Dash will go."
-            }));
-        }
-    }
-
-    // Search sidebar is where searches within the document are performed
-    static setupSearchSidebar(doc: Doc) {
-        if (doc.mySearchPanel === undefined) {
-            doc.mySearchPanel = new PrefetchProxy(Docs.Create.SearchDocument({
-                dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, _searchDoc: true,
-                childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "Search Panel", system: true
-            })) as any as Doc;
-        }
+    /// Import option on the left side button panel 
+    static setupImportSidebar(doc: Doc, field:string) {  
+        const reqdOpts:DocumentOptions = {
+            title: "My Imports", _forceActive: true, buttonMenu: true, ignoreClick: true, _showTitle: "title", 
+            _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0,
+            childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true,
+            dontRegisterView: true, explainer: "This is where documents that are Imported into Dash will go."
+        };
+        const myImports = this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.StackingDocument([], reqdOpts));
+    
+        const reqdBtnOpts:DocumentOptions = { _forceActive: true, toolTip: "Import from computer",
+            _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, 
+            buttonText: "Import", icon: "upload", system: true };
+        const reqdBtnScripts = { onClick: "importDocument()" };
+        const newImportBtn = this.AssignOpts(DocCast(myImports.buttonMenuDoc), reqdBtnOpts) ?? (myImports.buttonMenuDoc = Docs.Create.FontIconDocument(reqdBtnOpts));
+        this.AssignScripts(newImportBtn, reqdBtnScripts);
     }
 
     static setupClickEditorTemplates(doc: Doc) {
@@ -1033,8 +951,8 @@ export class CurrentUserUtils {
             }, { fireImmediately: true });
         // Document properties on load
         doc.system = true;
-        doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode;
         doc.title = Doc.CurrentUserEmail;
+        doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode;
         doc._raiseWhenDragged = true;
         doc._showLabel = true;
         doc._showMenuLabel = true;
@@ -1053,29 +971,22 @@ export class CurrentUserUtils {
         doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, false);
         doc.activeCollectionNestedBackground = Cast(doc.activeCollectionNestedBackground, "string", null);
         doc.noviceMode = BoolCast(doc.noviceMode, true);
-        doc.savedFilters = new List<Doc>();
+        !doc.savedFilters && (doc.savedFilters = new List<Doc>());
         doc.filterDocCount = 0;
         doc.freezeChildren = "remove|add";
         doc.myPublishedDocs = doc.myPublishedDocs ?? new List<Doc>();
-        doc.myHeaderBarDoc = doc.myHeaderBarDoc ?? Docs.Create.MulticolumnDocument([], { title: "header bar", system: true });
+        doc.myHeaderBar = doc.myHeaderBar?? Docs.Create.MulticolumnDocument([], { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents
+        await this.setupLinkDocs(doc, linkDatabaseId);
+        await this.setupSharedDocs(doc, sharingDocumentId);  // sets up the right sidebar collection for mobile upload documents and sharing
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
         this.setupDocTemplates(doc); // sets up the template menu of templates
-        this.setupImportSidebar(doc); // sets up the import sidebar
-        this.setupSearchSidebar(doc); // sets up the search sidebar
         this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile
-        this.setupOverlays(doc);  // documents in overlay layer
-        this.setupContextMenuButtons(doc); // set up context menu buttons
+        this.setupOverlays(doc);  // sets up the overlay panel where documents and other widgets can be added to float over the rest of the dashboard
+        this.setupContextMenuButtons(doc); // set up the row of buttons at the top of the dashboard that change depending on what is selected
         this.setupDockedButtons(doc);  // the bottom bar of font icons
-        await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels
-        await this.setupMenuPanel(doc, sharingDocumentId, linkDatabaseId);
+        this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left
         if (!doc.globalScriptDatabase) doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument();
 
-        setTimeout(() => this.setupDefaultPresentation(doc), 0); // presentation that's initially triggered
-
-        // setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet
-        doc["dockedBtn-undo"] && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc["dockedBtn-undo"] as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true });
-        doc["dockedBtn-redo"] && reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(doc["dockedBtn-redo"] as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
-
         setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500);
         doc.fieldInfos = await Docs.setupFieldInfos();
         if (doc.activeDashboard instanceof Doc) {
@@ -1227,7 +1138,7 @@ export class CurrentUserUtils {
 
     public static async snapshotDashboard(userDoc: Doc) {
         const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard);
-        Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy);
+        Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", copy);
         CurrentUserUtils.openDashboard(userDoc, copy);
     }
 
@@ -1277,15 +1188,24 @@ export class CurrentUserUtils {
         return tbox;
     }
 
-    public static get DockedBtns() { return Cast(Doc.UserDoc().dockedBtns, Doc, null); }
-    public static get MySearchPanelDoc() { return Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null); }
-    public static get ActiveDashboard() { return Cast(Doc.UserDoc().activeDashboard, Doc, null); }
-    public static get MyHeaderBarDoc() { return Cast(Doc.UserDoc().myHeaderBarDoc, Doc, null); }
-    public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); }
-    public static get MyRecentlyClosed() { return Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); }
-    public static get MyDashboards() { return Cast(Doc.UserDoc().myDashboards, Doc, null); }
-    public static get EmptyPane() { return Cast(Doc.UserDoc().emptyPane, Doc, null); }
-    public static get OverlayDocs() { return DocListCast((Doc.UserDoc().myOverlayDocs as Doc)?.data); }
+    public static get MyUserDocView() { return DocCast(Doc.UserDoc().myUserDocView); }
+    public static get MyDockedBtns() { return DocCast(Doc.UserDoc().myDockedBtns); }
+    public static get MySearcher() { return DocCast(Doc.UserDoc().mySearcher); }
+    public static get MyFilesystem() { return DocCast(Doc.UserDoc().myFilesystem); }
+    public static get MyHeaderBar() { return DocCast(Doc.UserDoc().myHeaderBar); }
+    public static get MyTools() { return DocCast(Doc.UserDoc().myTools); }
+    public static get MyDashboards() { return DocCast(Doc.UserDoc().myDashboards); }
+    public static get MyLeftSidebarMenu() { return DocCast(Doc.UserDoc().myLeftSidebarMenu); }
+    public static get MyLeftSidebarPanel() { return DocCast(Doc.UserDoc().myLeftSidebarPanel); }
+    public static get MySharedDocs() { return DocCast(Doc.UserDoc().mySharedDocs); }
+    public static get MyTrails() { return DocCast(Doc.UserDoc().myTrails); }
+    public static get MyImports() { return DocCast(Doc.UserDoc().myImports); }
+    public static get MyContextMenuBtns() { return DocCast(Doc.UserDoc().myContextMenuBtns); }
+    public static get MyRecentlyClosed() { return DocCast(Doc.UserDoc().myRecentlyClosed); }
+    public static get MyOverlayDocs() { return DocListCast(DocCast(Doc.UserDoc().myOverlayDocs)?.data); }
+    public static get ActiveDashboard() { return DocCast(Doc.UserDoc().activeDashboard); }
+    public static get ActivePresentation() { return DocCast(Doc.UserDoc().activePresentation); }
+    public static get EmptyPane() { return DocCast(Doc.UserDoc().emptyPane); }
     public static set SelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; }
     @computed public static get SelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; }
 }
@@ -1298,7 +1218,7 @@ ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) {
         view && SelectionManager.SelectView(view, false);
     }
 });
-ScriptingGlobals.add(function MySharedDocs() { return Doc.SharingDoc(); }, "document containing all shared Docs");
+ScriptingGlobals.add(function MySharedDocs() { return CurrentUserUtils.MySharedDocs; }, "document containing all shared Docs");
 ScriptingGlobals.add(function IsNoviceMode() { return Doc.UserDoc().noviceMode; }, "is Dash in novice mode");
 ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering");
 ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); }, "creates a snapshot copy of a dashboard");
@@ -1329,15 +1249,15 @@ ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colTy
         const parentDoc: Doc = Cast(selected.context, Doc, null);
         selected = parentDoc;
     }
-    if (selected && docType && selected.type === docType) return false;
-    else if (selected && colType && selected.viewType === colType) return false;
-    else if (selected && !colType && !docType) return false;
-    else return true;
+    if (selected && docType && selected.type === docType) return true;
+    else if (selected && colType && selected.viewType === colType) return true;
+    else if (selected && !colType && !docType) return true;
+    else return false;
 });
 ScriptingGlobals.add(function makeTopLevelFolder() {
     const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true });
     TreeView._editTitleOnLoad = { id: folder[Id], parent: undefined };
-    return Doc.AddDocToList(Doc.UserDoc().myFilesystem as Doc, "data", folder);
+    return Doc.AddDocToList(CurrentUserUtils.MyFilesystem, "data", folder);
 });
 ScriptingGlobals.add(function nextKeyFrame(readOnly: boolean) {
     if (readOnly) return;
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index a82f99d5a..0435cd535 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -10,6 +10,7 @@ import { CollectionView } from '../views/collections/CollectionView';
 import { LightboxView } from '../views/LightboxView';
 import { DocumentView, ViewAdjustment } from '../views/nodes/DocumentView';
 import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox';
+import { CurrentUserUtils } from './CurrentUserUtils';
 import { ScriptingGlobals } from './ScriptingGlobals';
 import { SelectionManager } from './SelectionManager';
 
@@ -296,7 +297,7 @@ export function DocFocusOrOpen(doc: Doc, collectionDoc?: Doc) {
         Doc.linkFollowHighlight(dv?.props.Document, false);
     }
     else {
-        const context = doc.context !== Doc.UserDoc().myFilesystem && Cast(doc.context, Doc, null);
+        const context = doc.context !== CurrentUserUtils.MyFilesystem && Cast(doc.context, Doc, null);
         const showDoc = context || doc;
         const bestAlias = showDoc === Doc.GetProto(showDoc) ? DocListCast(showDoc.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail) : showDoc;
 
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 223b0268c..3791bec73 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -223,7 +223,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
         paramList.push(`${key}: ${typeof val === "object" ? Object.getPrototypeOf(val).constructor.name : typeof val}`);
     }
     const paramString = paramList.join(", ");
-    const body = addReturn ? `return ${script};` : script;
+    const body = addReturn && !script.startsWith("{ return") ? `return ${script};` : script;
     const reqTypes = requiredType ? `: ${requiredType}` : '';
     const funcScript = `(function(${paramString})${reqTypes} { ${body} })`;
     host.writeFile("file.ts", funcScript);
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 8486b3d62..be5927497 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -141,7 +141,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                 const toRemove = value.filter(v => docs.includes(v));
 
                 if (toRemove.length !== 0) {
-                    const recent = Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc) as Doc;
+                    const recent = CurrentUserUtils.MyRecentlyClosed;
                     toRemove.forEach(doc => {
                         leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey);
                         Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 767efdbc2..16d7c9a23 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -235,7 +235,7 @@ export class KeyManager {
                 if (SelectionManager.Views().length === 1 && SelectionManager.Views()[0].ComponentView?.search) {
                     SelectionManager.Views()[0].ComponentView?.search?.("", false, false);
                 } else {
-                    const searchBtn = Doc.UserDoc().searchBtn as Doc;
+                    const searchBtn = CurrentUserUtils.MySearcher;
                     if (searchBtn) {
                         MainView.Instance.selectMenu(searchBtn);
                     }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 5fd76c388..7e3916b3f 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -10,7 +10,7 @@ import * as ReactDOM from 'react-dom';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { PrefetchProxy } from '../../fields/Proxy';
 import { ScriptField } from '../../fields/ScriptField';
-import { PromiseValue, StrCast } from '../../fields/Types';
+import { DocCast, PromiseValue, StrCast } from '../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
@@ -79,7 +79,7 @@ export class MainView extends React.Component {
     @observable private _dashUIWidth: number = 0;   // width of entire main dashboard region including left menu buttons and properties panel (but not including the dashboard selector button row)
     @observable private _dashUIHeight: number = 0;  // height of entire main dashboard region including top menu buttons
     @observable private _panelContent: string = "none";
-    @observable private _sidebarContent: any = this.userDoc?.sidebar;
+    @observable private _sidebarContent: any = CurrentUserUtils.MyLeftSidebarPanel;
     @observable private _leftMenuFlyoutWidth: number = 0;
 
     @computed private get dashboardTabHeight() { return 27; } //  27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js
@@ -92,7 +92,7 @@ export class MainView extends React.Component {
     @computed private get userDoc() { return Doc.UserDoc(); }
     @computed private get colorScheme() { return StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme); }
     @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; }
-    @computed private get headerBarDoc() { return this.userDoc ? CurrentUserUtils.MyHeaderBarDoc : CurrentUserUtils.MyHeaderBarDoc; }
+    @computed private get headerBarDoc() { return CurrentUserUtils.MyHeaderBar; }
     @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs?.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
 
     headerBarDocWidth = () => this.mainDocViewWidth();
@@ -249,24 +249,8 @@ export class MainView extends React.Component {
 
     @action
     createNewFolder = async () => {
-        if (!await this.userDoc.myFilesystem) {
-            this.userDoc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
-            const newFolder = ScriptField.MakeFunction(`createNewFolder()`, { scriptContext: "any" })!;
-            const newFolderButton: Doc = Docs.Create.FontIconDocument({
-                onClick: newFolder, _forceActive: true, toolTip: "New folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder",
-                btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true
-            });
-            this.userDoc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([this.userDoc.myFileOrphans as Doc], {
-                title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100,
-                treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias",
-                treeViewTruncateTitleWidth: 150, ignoreClick: true,
-                isFolder: true, treeViewType: TreeViewType.fileSystem, childHideLinkButton: true,
-                _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true,
-                explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
-            }));
-        }
         const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true });
-        Doc.AddDocToList(this.userDoc.myFilesystem as Doc, "data", folder);
+        Doc.AddDocToList(CurrentUserUtils.MyFilesystem, "data", folder);
     }
 
     @observable _exploreMode = false;
@@ -397,7 +381,7 @@ export class MainView extends React.Component {
                         addDocTab={this.addDocTabFunc}
                         pinToPres={emptyFunction}
                         docViewPath={returnEmptyDoclist}
-                        styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards || this._sidebarContent.proto === Doc.UserDoc().myFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
+                        styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards || this._sidebarContent.proto === CurrentUserUtils.MyFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
                         rootSelected={returnTrue}
                         removeDocument={returnFalse}
                         ScreenToLocalTransform={this.mainContainerXf}
@@ -423,7 +407,7 @@ export class MainView extends React.Component {
     @computed get leftMenuPanel() {
         return <div key="menu" className="mainView-leftMenuPanel">
             <DocumentView
-                Document={Doc.UserDoc().menuStack as Doc}
+                Document={CurrentUserUtils.MyLeftSidebarMenu}
                 DataDoc={undefined}
                 addDocument={undefined}
                 addDocTab={this.addDocTabFunc}
@@ -531,9 +515,9 @@ export class MainView extends React.Component {
         this._leftMenuFlyoutWidth = 0;
     });
 
-    remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true);
+    remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(CurrentUserUtils.MyDockedBtns, "data", doc), true);
     moveButtonDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => this.remButtonDoc(doc) && addDocument(doc);
-    addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true);
+    addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(CurrentUserUtils.MyDockedBtns, "data", doc), true);
 
     buttonBarXf = () => {
         if (!this._docBtnRef.current) return Transform.Identity();
@@ -543,9 +527,9 @@ export class MainView extends React.Component {
 
     @computed get docButtons() {
         return !(this.userDoc.dockedBtns instanceof Doc) ? (null) :
-            <div className="mainView-docButtons" ref={this._docBtnRef} style={{ height: !this.userDoc.dockedBtns.linearViewIsExpanded ? "42px" : undefined }} >
+            <div className="mainView-docButtons" ref={this._docBtnRef} style={{ height: !CurrentUserUtils.MyDockedBtns.linearViewIsExpanded ? "42px" : undefined }} >
                 <CollectionLinearView
-                    Document={this.userDoc.dockedBtns}
+                    Document={CurrentUserUtils.MyDockedBtns}
                     DataDoc={undefined}
                     fieldKey={"data"}
                     dropAction={"alias"}
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index ab5dc74c9..bdb3bb857 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -158,7 +158,7 @@ export class OverlayView extends React.Component {
     }.bind(this));
 
     @computed get overlayDocs() {
-        return CurrentUserUtils.OverlayDocs?.map(d => {
+        return CurrentUserUtils.MyOverlayDocs?.map(d => {
             let offsetx = 0, offsety = 0;
             const dref = React.createRef<HTMLDivElement>();
             const onPointerMove = action((e: PointerEvent, down: number[]) => {
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 16eb95cf4..2cf334ae1 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -931,7 +931,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      * If it doesn't exist, it creates it.
      */
     checkFilterDoc() {
-        if (!this.selectedDoc.currentFilter) CurrentUserUtils.setupFilterDocs(this.selectedDoc);
+        if (!this.selectedDoc.currentFilter) this.selectedDoc.currentFilter = CurrentUserUtils.createFilterDoc();
     }
 
     /**
@@ -944,8 +944,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         this.selectedDoc._docRangeFilters = new List<string>();
         (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters;
         (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters;
-        this.selectedDoc.currentFilter = undefined;
-        CurrentUserUtils.setupFilterDocs(this.selectedDoc);
+        this.selectedDoc.currentFilter = CurrentUserUtils.createFilterDoc();
     }
 
     /**
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index d2687df17..1ebbe08ef 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -416,7 +416,7 @@ export class CollectionDockingView extends CollectionSubView() {
     }
 
     tabDestroyed = (tab: any) => {
-        Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", tab.DashDoc);
+        Doc.AddDocToList(CurrentUserUtils.MyHeaderBar, "data", tab.DashDoc);
         Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true);
         const dview = CollectionDockingView.Instance.props.Document;
         const fieldKey = CollectionDockingView.Instance.props.fieldKey;
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 39f6466d6..1c25421f5 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -99,7 +99,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
     }
 
     @computed get contMenuButtons() {
-        const selDoc = Doc.UserDoc().contextMenuBtns;
+        const selDoc = CurrentUserUtils.MyContextMenuBtns;
         return !(selDoc instanceof Doc) ? (null) : <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: this.props.panelHeight() }} >
             <CollectionLinearView
                 Document={selDoc}
@@ -268,8 +268,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
                  self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`,
         immediate: undoBatch((source: Doc[]) => { this.target._docFilters = undefined; this.target._searchFilterDocs = undefined; }),
         initialize: (button: Doc) => {
-            button['target-docFilters'] = (Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null)._docFilters || Cast(Doc.UserDoc().activeDashboard, Doc, null)._docFilters) instanceof ObjectField ?
-                ObjectField.MakeCopy((Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null)._docFilters || Cast(Doc.UserDoc().activeDashboard, Doc, null)._docFilters) as any as ObjectField) : undefined;
+            button['target-docFilters'] = (CurrentUserUtils.MySearcher._docFilters || CurrentUserUtils.ActiveDashboard._docFilters) instanceof ObjectField ?
+                ObjectField.MakeCopy((CurrentUserUtils.MySearcher._docFilters || CurrentUserUtils.ActiveDashboard._docFilters) as any as ObjectField) : undefined;
             button['target-searchFilterDocs'] = CurrentUserUtils.ActiveDashboard._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(CurrentUserUtils.ActiveDashboard._searchFilterDocs as any as ObjectField) : undefined;
         },
     };
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 52e99f26b..c07e44fcc 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -985,7 +985,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || CurrentUserUtils.OverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
+        if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || CurrentUserUtils.MyOverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
         if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming
             e.stopPropagation();
         }
@@ -1031,7 +1031,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 else if (ranges.yrange.max <= (panY - panelDim[1] / 2)) panY = ranges.yrange.min - panelDim[1] / 2;
             }
         }
-        if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || CurrentUserUtils.OverlayDocs.includes(this.Document)) {
+        if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || CurrentUserUtils.MyOverlayDocs.includes(this.Document)) {
             this._viewTransition = panTime;
             const scale = this.getLocalTransform().inverse().Scale;
             const minScale = NumCast(this.rootDoc._viewScaleMin, 1);
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 9b1cb1601..c31267e87 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -207,7 +207,7 @@ export class CollectionLinearView extends CollectionSubView() {
                     }}>
                     {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
                 </div>
-                {!DocumentLinksButton.StartLink || this.layoutDoc !== CurrentUserUtils.DockedBtns ? null :
+                {!DocumentLinksButton.StartLink || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? null :
                     <span className="bottomPopup-background" style={{ pointerEvents: "all" }}
                         onPointerDown={e => e.stopPropagation()} >
                         <span className="bottomPopup-text" >
@@ -226,7 +226,7 @@ export class CollectionLinearView extends CollectionSubView() {
                             </span>
                         </Tooltip>
                     </span>}
-                {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== CurrentUserUtils.DockedBtns ? (null) :
+                {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? (null) :
                     <span className="bottomPopup-background">
                         <span className="bottomPopup-text">
                             Currently playing:
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
index 4b33ef8ae..a6f6bd35f 100644
--- a/src/client/views/linking/LinkPopup.tsx
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -68,8 +68,8 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
                         className="linkPopup-searchBox searchBox-input" /> */}
 
                     <SearchBox
-                        Document={CurrentUserUtils.MySearchPanelDoc}
-                        DataDoc={CurrentUserUtils.MySearchPanelDoc}
+                        Document={CurrentUserUtils.MySearcher}
+                        DataDoc={CurrentUserUtils.MySearcher}
                         linkFrom={linkDoc}
                         linkSearch={true}
                         fieldKey="data"
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 60d16f044..e2b37d2b4 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -327,7 +327,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             this._downX = touch.clientX;
             this._downY = touch.clientY;
             if (!e.nativeEvent.cancelBubble) {
-                if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) e.stopPropagation();
+                if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc._lockedPosition && !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) e.stopPropagation();
                 this.removeMoveListeners();
                 this.addMoveListeners();
                 this.removeEndListeners();
@@ -341,7 +341,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (e.cancelBubble && this.props.isDocumentActive?.()) {
             this.removeMoveListeners();
         }
-        else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
+        else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) {
             const touch = me.touchEvent.changedTouches.item(0);
             if (touch && (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3)) {
                 if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler)) {
@@ -563,7 +563,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 !this.Document.ignoreClick &&
                 !e.ctrlKey &&
                 (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) &&
-                !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
+                !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) {
                 e.stopPropagation();
                 // don't preventDefault anymore.  Goldenlayout, PDF text selection and RTF text selection all need it to go though
                 //if (this.props.isSelected(true) && this.rootDoc.type !== DocumentType.PDF && this.layoutDoc._viewType !== CollectionViewType.Docking) e.preventDefault();
@@ -581,7 +581,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (e.cancelBubble) return;
         if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool))) return;
 
-        if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
+        if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) {
             if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
                 if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) {
                     document.removeEventListener("pointermove", this.onPointerMove);
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index e3708d7b9..5add09653 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -35,7 +35,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
     constructor(props: Readonly<FieldViewProps>) {
         super(props);
         const targetDoc = FilterBox.targetDoc;
-        if (targetDoc && !targetDoc.currentFilter) CurrentUserUtils.setupFilterDocs(targetDoc);
+        if (targetDoc && !targetDoc.currentFilter) targetDoc.currentFilter = CurrentUserUtils.createFilterDoc();
     }
     public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FilterBox, fieldKey); }
 
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 10974ca03..967158cbf 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -156,20 +156,22 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
         runInAction(() => {
             this._annotationKeySuffix = () => this._urlHash + "-annotations";
+            const reqdFuncs:{[key:string]: string} = {};
             // bcz: need to make sure that doc.data-annotations points to the currently active web page's annotations (this could/should be when the doc is created)
-            this.dataDoc[this.fieldKey + "-annotations"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`);
-            this.dataDoc[this.fieldKey + "-sidebar"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`);
+            reqdFuncs[this.fieldKey + "-annotations"] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`;
+            reqdFuncs[this.fieldKey + "-sidebar"] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`;
+            CurrentUserUtils.AssignScripts(this.dataDoc, {}, reqdFuncs);
         });
-        reaction(() => this.props.isSelected() || this.isAnyChildContentActive() || Doc.isBrushedHighlightedDegree(this.props.Document),
+        reaction(() => this.props.isSelected(true) || this.isAnyChildContentActive() || Doc.isBrushedHighlightedDegree(this.props.Document),
             async (selected) => {
                 if (selected) {
                     this._webPageHasBeenRendered = true;
-                } else if ((!this.props.isContentActive() || SnappingManager.GetIsDragging()) &&   // update thumnail when unselected AND (no child annotation is active OR we've started dragging the document in which case no additional deselect will occur so this is the only chance to update the thumbnail)
+                } else if ((!this.props.isContentActive(true) || SnappingManager.GetIsDragging()) &&   // update thumnail when unselected AND (no child annotation is active OR we've started dragging the document in which case no additional deselect will occur so this is the only chance to update the thumbnail)
                     !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && // don't create a thumbnail when double-clicking to enter lightbox because thumbnail will be empty
                     LightboxView.LightboxDoc !== this.rootDoc) { // don't create a thumbnail if entering Lightbox from maximize either, since thumb will be empty.
                     this.updateThumb();
                 }
-            }, { fireImmediately: this.props.isSelected() || this.isAnyChildContentActive() || (Doc.isBrushedHighlightedDegree(this.props.Document) ? true : false) });
+            }, { fireImmediately: this.props.isSelected(true) || this.isAnyChildContentActive() || (Doc.isBrushedHighlightedDegreeUnmemoized(this.props.Document) ? true : false) });
 
         this._disposers.autoHeight = reaction(() => this.layoutDoc._autoHeight,
             autoHeight => {
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 97e6eddfe..f29dfe489 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -521,7 +521,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                     <div className={`menuButton ${this.type}`} style={{ color, backgroundColor }}>
                         {this.icon === "pres-trail" ? trailsIcon : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />}
                         {menuLabel}
-                        <FontIconBadge value={ScriptCast(this.Document.badgeValue,null)?.script.run({self:this.Document}).result} />
+                        <FontIconBadge value={Cast(this.Document.badgeValue,"string",null)} />
                     </div >
                 );
                 break;
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 1dd6fef9b..52b03b0a5 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -623,7 +623,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     @action
     updateMinimize = async () => {
-        if (CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) {
+        if (CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) {
             this.layoutDoc.presStatus = PresStatus.Edit;
             Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
             CollectionDockingView.AddSplit(this.rootDoc, "right");
@@ -868,7 +868,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 }
                 break;
             case "Escape":
-                if (CurrentUserUtils.OverlayDocs.includes(this.layoutDoc)) { this.updateMinimize(); }
+                if (CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) { this.updateMinimize(); }
                 else if (this.layoutDoc.presStatus === "edit") { this._selectedArray.clear(); this._eleArray.length = this._dragArray.length = 0; }
                 else this.layoutDoc.presStatus = "edit";
                 if (this._presTimer) clearTimeout(this._presTimer);
@@ -2511,7 +2511,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.UserDoc().activePresentation);
         const presEnd: boolean = !this.layoutDoc.presLoop && (this.itemIndex === this.childDocs.length - 1);
         const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0);
-        return CurrentUserUtils.OverlayDocs.includes(this.rootDoc) ?
+        return CurrentUserUtils.MyOverlayDocs.includes(this.rootDoc) ?
             <div className="miniPres" onClick={e => e.stopPropagation()}>
                 <div className="presPanelOverlay" style={{ display: "inline-flex", height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}>
                     <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : undefined }}
@@ -2536,7 +2536,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 </div>
             </div >
             :
-            <div className="presBox-cont" style={{ minWidth: CurrentUserUtils.OverlayDocs.includes(this.layoutDoc) ? 240 : undefined }} >
+            <div className="presBox-cont" style={{ minWidth: CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc) ? 240 : undefined }} >
                 {this.topPanel}
                 {this.toolbar}
                 {this.newDocumentToolbarDropdown}
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 7b72787d1..94be286f5 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -941,6 +941,9 @@ export namespace Doc {
     export function isBrushedHighlightedDegree(doc: Doc) {
         return Doc.IsHighlighted(doc) ? DocBrushStatus.highlighted : Doc.IsBrushedDegree(doc);
     }
+    export function isBrushedHighlightedDegreeUnmemoized(doc: Doc) {
+        return Doc.IsHighlighted(doc) ? DocBrushStatus.highlighted : Doc.IsBrushedDegreeUnmemoized(doc);
+    }
 
     export class DocBrush {
         BrushedDoc: ObservableMap<Doc, boolean> = new ObservableMap();
@@ -980,7 +983,7 @@ export namespace Doc {
     export function SearchQuery(): string { return manager._searchQuery; }
     export function SetSearchQuery(query: string) { runInAction(() => manager._searchQuery = query); }
     export function UserDoc(): Doc { return manager._user_doc; }
-    export function SharingDoc(): Doc { return Cast(Doc.UserDoc().mySharedDocs, Doc, null); }
+    export function SharingDoc(): Doc { return CurrentUserUtils.MySharedDocs; }
     export function LinkDBDoc(): Doc { return Cast(Doc.UserDoc().myLinkDatabase, Doc, null); }
     export function SetUserDoc(doc: Doc) { return (manager._user_doc = doc); }
 
diff --git a/src/fields/util.ts b/src/fields/util.ts
index d1e565774..37b9be31b 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -88,6 +88,9 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
             delete target.__fields[prop];
         } else {
             target.__fieldKeys && (target.__fieldKeys[prop] = true);
+            // if (target.__fields[prop] !== value) {
+            //     console.log("ASSIGN " + prop + " " + value);
+            // }
             target.__fields[prop] = value;
         }
         //if (typeof value === "object" && !(value instanceof ObjectField)) debugger;
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index 78ec706d7..c5c6fb688 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -68,7 +68,7 @@ export class MobileInterface extends React.Component {
 
     constructor(props: Readonly<{}>) {
         super(props);
-        this._library = CurrentUserUtils.setupLibrary(Doc.UserDoc()); // to access documents in Dash Web
+        this._library = CurrentUserUtils.setupDashboards(Doc.UserDoc()); // to access documents in Dash Web
         MobileInterface.Instance = this;
     }
 
-- 
cgit v1.2.3-70-g09d2


From b292a055401af6236e0537cfad603016d77a535a Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 20 Jun 2022 13:40:07 -0400
Subject: a bunch of changes to make some userDoc() field accesses more
 typesafe by going through CurrentUserUtils.  Some normalization of
 naming/functionality in CurrentUserUtils

---
 src/client/apis/youtube/YoutubeBox.tsx             |   2 +-
 src/client/documents/Documents.ts                  |   6 +-
 src/client/util/CurrentUserUtils.ts                | 118 +++++++++++----------
 src/client/util/SettingsManager.tsx                |  16 +--
 src/client/util/SharingManager.tsx                 |   4 +-
 src/client/views/DashboardView.tsx                 |   4 +-
 src/client/views/DocComponent.tsx                  |   6 +-
 src/client/views/DocumentButtonBar.tsx             |   2 +-
 src/client/views/GestureOverlay.tsx                |  12 +--
 src/client/views/GlobalKeyHandler.ts               |   8 +-
 src/client/views/InkStrokeProperties.ts            |   4 +-
 src/client/views/InkTranscription.tsx              |   2 +-
 src/client/views/InkingStroke.tsx                  |   2 +-
 src/client/views/MainView.tsx                      |   4 +-
 src/client/views/OverlayView.tsx                   |   8 +-
 src/client/views/PropertiesButtons.tsx             |   4 +-
 src/client/views/PropertiesView.tsx                |  86 ++++++++-------
 src/client/views/TemplateMenu.tsx                  |  11 +-
 .../views/collections/CollectionDockingView.tsx    |  22 ++--
 src/client/views/collections/CollectionMenu.tsx    |  35 +++---
 .../views/collections/CollectionStackingView.tsx   |   2 +-
 .../CollectionStackingViewFieldColumn.tsx          |   4 +-
 .../views/collections/CollectionTreeView.tsx       |   8 +-
 src/client/views/collections/CollectionView.tsx    |  14 +--
 src/client/views/collections/TabDocView.tsx        |   2 +-
 src/client/views/collections/TreeView.tsx          |   6 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  30 +++---
 .../collections/collectionFreeForm/MarqueeView.tsx |   6 +-
 .../collectionSchema/CollectionSchemaHeaders.tsx   |   2 +-
 src/client/views/nodes/AudioBox.tsx                |   5 +-
 src/client/views/nodes/ColorBox.tsx                |   3 +-
 src/client/views/nodes/DocumentView.tsx            |  40 +++----
 src/client/views/nodes/FilterBox.tsx               |  47 ++++----
 src/client/views/nodes/ImageBox.tsx                |   4 +-
 src/client/views/nodes/LabelBox.tsx                |   2 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |   4 +-
 src/client/views/nodes/VideoBox.tsx                |   6 +-
 src/client/views/nodes/WebBox.tsx                  |   6 +-
 src/client/views/nodes/button/FontIconBox.tsx      |  35 +++---
 .../views/nodes/formattedText/FormattedTextBox.tsx |  12 +--
 .../views/nodes/formattedText/RichTextMenu.tsx     |   1 -
 src/client/views/nodes/trails/PresBox.tsx          |  30 +++---
 src/client/views/nodes/trails/PresElementBox.tsx   |  16 +--
 src/client/views/pdf/PDFViewer.tsx                 |   2 +-
 src/client/views/topbar/TopBar.tsx                 |  10 +-
 src/client/views/webcam/DashWebRTCVideo.tsx        |   2 +-
 src/fields/Doc.ts                                  |  18 ++--
 src/fields/util.ts                                 |   2 +-
 src/mobile/MobileInterface.tsx                     |  20 ++--
 49 files changed, 360 insertions(+), 335 deletions(-)

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

diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx
index c5ff2db68..e14dc60b4 100644
--- a/src/client/apis/youtube/YoutubeBox.tsx
+++ b/src/client/apis/youtube/YoutubeBox.tsx
@@ -351,7 +351,7 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
 
         const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
 
-        const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
+        const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.ActiveTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
         return (
             <>
                 <div className={classname}  >
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 927996af2..f96bffb3c 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -687,7 +687,7 @@ export namespace Docs {
             ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc);
 
             !Doc.IsSystem(dataDoc) && ![DocumentType.MARKER, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(proto.type as any) &&
-                !dataDoc.isFolder && !dataProps.annotationOn && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", dataDoc);
+                !dataDoc.isFolder && !dataProps.annotationOn && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, undefined, dataDoc);
 
             updateCachedAcls(dataDoc);
             updateCachedAcls(viewDoc);
@@ -802,7 +802,7 @@ export namespace Docs {
             I.rotation = 0;
             I.data = new InkField(points);
             I.creationDate = new DateField;
-            I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            I["acl-Public"] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
             I["acl-Override"] = "None";
             I.links = ComputedField.MakeFunction("links(self)");
             I[Initializing] = false;
@@ -1204,7 +1204,7 @@ export namespace DocUtils {
             created = Docs.Create.RecordingDocument((field).url.href, resolved);
             layout = RecordingBox.LayoutString;
         } else if (field instanceof InkField) {
-            created = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), (field).inkData, resolved);
+            created = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), (field).inkData, resolved);
             layout = InkingStroke.LayoutString;
         } else if (field instanceof List && field[0] instanceof Doc) {
             created = Docs.Create.StackingDocument(DocListCast(field), resolved);
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index afb1442bf..7a38886fe 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -74,16 +74,16 @@ export class CurrentUserUtils {
     @observable public static headerBarHeight: number = 0;
     @observable public static searchPanelWidth: number = 0;
 
-    static AssignScripts(doc:Doc, scripts?:{ [key: string]: string;}, functions?:{[key:string]: string}) {
+    static AssignScripts(doc:Doc, scripts?:{ [key: string]: string;}, funcs?:{[key:string]: string}) {
         scripts && Object.keys(scripts).map(key => {
             if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && scripts[key]) {
                 doc[key] = ScriptField.MakeScript(scripts[key], { dragData: DragManager.DocumentDragData.name,  value:"any", scriptContext: "any" }, {"_readOnly_": true});
             }
         }); 
-        functions && Object.keys(functions).map(key => {
+        funcs && Object.keys(funcs).map(key => {
             const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
-            if (ScriptCast(cfield)?.script.originalScript !== functions[key] && functions[key]) {
-                doc[key] = ComputedField.MakeFunction(functions[key], { dragData: DragManager.DocumentDragData.name }, {"_readOnly_": true});
+            if (ScriptCast(cfield)?.script.originalScript !== funcs[key] && funcs[key]) {
+                doc[key] = ComputedField.MakeFunction(funcs[key], { dragData: DragManager.DocumentDragData.name }, {"_readOnly_": true});
             }
         });
         return doc;
@@ -180,14 +180,14 @@ export class CurrentUserUtils {
     }
 
     /// Initializes collection of templates for notes and click functions
-    static setupDocTemplates(doc: Doc) {
+    static setupDocTemplates(doc: Doc, field="myTemplates") {
         const templates = [
             CurrentUserUtils.setupNoteTemplates(doc),
             CurrentUserUtils.setupClickEditorTemplates(doc)
         ];
         const reqdOpts = { title: "template layouts", _xMargin: 0, system: true,  };
         const reqdScripts = {  dropConverter: "convertToButtons(dragData)"  };
-        return this.AssignScripts(this.AssignOpts(DocCast(doc.templateDocs), reqdOpts, templates) ?? (doc.templateDocs = Docs.Create.TreeDocument(templates, reqdOpts)), reqdScripts);
+        return this.AssignScripts(this.AssignOpts(DocCast(doc[field]), reqdOpts, templates) ?? (doc[field] = Docs.Create.TreeDocument(templates, reqdOpts)), reqdScripts);
     }
 
     // setup templates for different document types when they are iconified from Document Decorations
@@ -329,7 +329,7 @@ export class CurrentUserUtils {
     static setupCreatorButtons(doc: Doc, dragCreatorDoc?:Doc):Doc {
         const creatorBtns = CurrentUserUtils.creatorBtnDescriptors(doc).map((reqdOpts) => {
             const btn = dragCreatorDoc ? DocListCast(dragCreatorDoc.data).find(doc => doc.title === reqdOpts.title): undefined;
-            const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["backgroundColor"]).omit,
+            const opts:DocumentOptions = {...OmitKeys(reqdOpts, ["funcs", "scripts", "backgroundColor"]).omit,
                 _nativeWidth: 50, _nativeHeight: 50, _width: 35, _height: 35, _hideContextMenu: true, _stayInCollection: true, _dropAction: "alias",
                 btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, system: true,
                 _removeDropProperties: new List<string>(["_stayInCollection"]),
@@ -562,6 +562,7 @@ export class CurrentUserUtils {
         if (Cast(myDashboards.childContextMenuFilters, listSpec(ScriptField), null)?.length !== childContextMenuFilters.length) {
             myDashboards.childContextMenuFilters = new List<ScriptField>(childContextMenuFilters.map(script => !script ? script: ScriptField.MakeFunction(script)!));
         }
+        return myDashboards;
     }
 
     /// initializes the left sidebar Trails pane
@@ -728,13 +729,13 @@ export class CurrentUserUtils {
 
     static inkTools():Button[] {
         return [
-            { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", scripts:{onClick:'{ return setActiveInkTool("pen", _readOnly_);}' }},
-            { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", scripts:{onClick:'{ return setActiveInkTool("write", _readOnly_);}'} },
-            { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", scripts:{onClick:'{ return setActiveInkTool("eraser", _readOnly_);}' }},
-            // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", scripts:{onClick: 'setActiveInkTool("highlighter")'} },
-            { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", scripts:{onClick:'{ return setActiveInkTool("circle", _readOnly_);}'} },
-            // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")' },
-            { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", scripts:{onClick: '{ return setActiveInkTool("line", _readOnly_);}' }},
+            { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen-nib", scripts:{onClick:'{ return setActiveTool("pen", _readOnly_);}' }},
+            { title: "Write", toolTip: "Write (Ctrl+Shift+P)", btnType: ButtonType.ToggleButton, icon: "pen", scripts:{onClick:'{ return setActiveTool("write", _readOnly_);}'} },
+            { title: "Eraser", toolTip: "Eraser (Ctrl+E)", btnType: ButtonType.ToggleButton, icon: "eraser", scripts:{onClick:'{ return setActiveTool("eraser", _readOnly_);}' }},
+            // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", scripts:{onClick: 'setActiveTool("highlighter")'} },
+            { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", scripts:{onClick:'{ return setActiveTool("circle", _readOnly_);}'} },
+            // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveTool("square")' },
+            { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", scripts:{onClick: '{ return setActiveTool("line", _readOnly_);}' }},
             { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", scripts: {script: "{ return setFillColor(value, _readOnly_);}"} },
             { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, numBtnMin: 1, ignoreClick: true, scripts: {script: '{ return setStrokeWidth(value, _readOnly_);}'} },
             { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, scripts: {script: '{ return setStrokeColor(value, _readOnly_);}'} },
@@ -941,41 +942,39 @@ export class CurrentUserUtils {
     }
 
     static async updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
-        if (!doc.globalGroupDatabase) doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument();
-        await DocListCastAsync((doc.globalGroupDatabase as Doc).data);
-        reaction(() => DateCast((doc.globalGroupDatabase as Doc)["data-lastModified"]),
+        doc.globalGroupDatabase ?? (doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument());
+        await DocListCastAsync(DocCast(doc.globalGroupDatabase).data);
+        reaction(() => DateCast(DocCast(doc.globalGroupDatabase)["data-lastModified"]),
             async () => {
-                const groups = await DocListCastAsync((doc.globalGroupDatabase as Doc).data);
+                const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase).data);
                 const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(Doc.CurrentUserEmail)) || [];
                 SnappingManager.SetCachedGroups(["Public", ...mygroups?.map(g => StrCast(g.title))]);
             }, { fireImmediately: true });
         // Document properties on load
-        doc.system = true;
-        doc.title = Doc.CurrentUserEmail;
-        doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode;
-        doc._raiseWhenDragged = true;
-        doc._showLabel = true;
-        doc._showMenuLabel = true;
-        doc.textAlign = StrCast(doc.textAlign, "left");
-        doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)");
-        doc.activeInkWidth = Number(StrCast(doc.activeInkWidth, "1"));
-        doc.activeInkBezier = StrCast(doc.activeInkBezier, "0");
-        doc.activeFillColor = StrCast(doc.activeFillColor, "");
-        doc.activeArrowStart = StrCast(doc.activeArrowStart, "");
-        doc.activeArrowEnd = StrCast(doc.activeArrowEnd, "");
-        doc.activeDash = StrCast(doc.activeDash, "0");
-        doc.fontSize = StrCast(doc.fontSize, "12px");
-        doc.fontFamily = StrCast(doc.fontFamily, "Arial");
-        doc.fontColor = StrCast(doc.fontColor, "black");
-        doc.fontHighlight = StrCast(doc.fontHighlight, "");
-        doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, false);
-        doc.activeCollectionNestedBackground = Cast(doc.activeCollectionNestedBackground, "string", null);
-        doc.noviceMode = BoolCast(doc.noviceMode, true);
-        !doc.savedFilters && (doc.savedFilters = new List<Doc>());
+        doc.system ?? (doc.system = true);
+        doc.title ?? (doc.title = Doc.CurrentUserEmail);
+        Doc.noviceMode ?? (Doc.noviceMode = true);
+        doc._raiseWhenDragged ?? (doc._raiseWhenDragged = true);
+        doc._showLabel ?? (doc._showLabel = true);
+        doc._showMenuLabel ?? (doc._showMenuLabel = true);
+        doc.textAlign ?? (doc.textAlign = "left");
+        doc.activeInkColor ?? (doc.activeInkColor = "rgb(0, 0, 0)");;
+        doc.activeInkWidth ?? (doc.activeInkWidth = 1);
+        doc.activeInkBezier ?? (doc.activeInkBezier = "0");
+        doc.activeFillColor ?? (doc.activeFillColor = "");
+        doc.activeArrowStart ?? (doc.activeArrowStart = "");
+        doc.activeArrowEnd ?? (doc.activeArrowEnd = "");
+        doc.activeDash ?? (doc.activeDash == "0");
+        doc.fontSize ?? (doc.fontSize = "12px");
+        doc.fontFamily ?? (doc.fontFamily = "Arial");
+        doc.fontColor ?? (doc.fontColor = "black");
+        doc.fontHighlight ?? (doc.fontHighlight = "");
+        doc.defaultAclPrivate ?? (doc.defaultAclPrivate = false);
+        doc.savedFilters ?? (doc.savedFilters = new List<Doc>());
         doc.filterDocCount = 0;
         doc.freezeChildren = "remove|add";
-        doc.myPublishedDocs = doc.myPublishedDocs ?? new List<Doc>();
-        doc.myHeaderBar = doc.myHeaderBar?? Docs.Create.MulticolumnDocument([], { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents
+        doc.myPublishedDocs ?? (doc.myPublishedDocs = new List<Doc>());
+        doc.myHeaderBar ?? (doc.myHeaderBar = Docs.Create.MulticolumnDocument([], { title: "header bar", system: true })); // drop down panel at top of dashboard for stashing documents
         await this.setupLinkDocs(doc, linkDatabaseId);
         await this.setupSharedDocs(doc, sharingDocumentId);  // sets up the right sidebar collection for mobile upload documents and sharing
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
@@ -985,7 +984,7 @@ export class CurrentUserUtils {
         this.setupContextMenuButtons(doc); // set up the row of buttons at the top of the dashboard that change depending on what is selected
         this.setupDockedButtons(doc);  // the bottom bar of font icons
         this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left
-        if (!doc.globalScriptDatabase) doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument();
+        doc.globalScriptDatabase ?? ( doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument());
 
         setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500);
         doc.fieldInfos = await Docs.setupFieldInfos();
@@ -1022,7 +1021,7 @@ export class CurrentUserUtils {
                     const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc;
                     Docs.newAccount &&(userDoc.activePage = "home");
                     const updated = this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId);
-                    (await DocListCastAsync(Cast(Doc.UserDoc().myLinkDatabase, Doc, null)?.data))?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager
+                    (await DocListCastAsync(Doc.LinkDBDoc()?.data))?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager
                         const a1 = await Cast(link?.anchor1, Doc, null);
                         const a2 = await Cast(link?.anchor2, Doc, null);
                     });
@@ -1097,7 +1096,7 @@ export class CurrentUserUtils {
                     }
                 }
             } else if (input.files && input.files.length !== 0) {
-                const importDocs = Cast(Doc.UserDoc().myImportDocs, Doc, null);
+                const importDocs = CurrentUserUtils.MyImports;
                 const disposer = OverlayView.ShowSpinner();
                 DocListCastAsync(importDocs.data).then(async list => {
                     const results = await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {});
@@ -1115,9 +1114,10 @@ export class CurrentUserUtils {
     }
 
     public static CaptureDashboardThumbnail() {
+        const activeDashboard = CurrentUserUtils.ActiveDashboard;
         const docView = CollectionDockingView.Instance.props.DocumentView?.();
         const content = docView?.ContentDiv;
-        if (docView && content) {
+        if (docView && content && activeDashboard) {
             const _width = Number(getComputedStyle(content).width.replace("px",""));
             const _height = Number(getComputedStyle(content).height.replace("px",""));
             return CollectionFreeFormView.UpdateIcon(
@@ -1130,20 +1130,22 @@ export class CurrentUserUtils {
                     const proto = Cast(img.proto, Doc, null)!;
                     proto["data-nativeWidth"] = _width;
                     proto["data-nativeHeight"] = _height;
-                    Doc.GetProto(CurrentUserUtils.ActiveDashboard).thumb = img;
+                    Doc.GetProto(activeDashboard).thumb = img;
                 });
         }
 
     }
 
     public static async snapshotDashboard(userDoc: Doc) {
-        const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard);
-        Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", copy);
-        CurrentUserUtils.openDashboard(userDoc, copy);
+        if (CurrentUserUtils.ActiveDashboard) {
+            const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard);
+            Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", copy);
+            CurrentUserUtils.openDashboard(userDoc, copy);
+        }
     }
 
     public static closeActiveDashboard = () => {
-          Doc.UserDoc().activeDashboard = undefined;
+        CurrentUserUtils.ActiveDashboard = undefined;
     }
 
     public static createNewDashboard = async (userDoc: Doc, id?: string) => {
@@ -1195,6 +1197,8 @@ export class CurrentUserUtils {
     public static get MyHeaderBar() { return DocCast(Doc.UserDoc().myHeaderBar); }
     public static get MyTools() { return DocCast(Doc.UserDoc().myTools); }
     public static get MyDashboards() { return DocCast(Doc.UserDoc().myDashboards); }
+    public static get MyFileOrphans() { return DocCast(Doc.UserDoc().myFileOrphans); }
+    public static get MyTemplates() { return DocCast(Doc.UserDoc().myTemplates); }
     public static get MyLeftSidebarMenu() { return DocCast(Doc.UserDoc().myLeftSidebarMenu); }
     public static get MyLeftSidebarPanel() { return DocCast(Doc.UserDoc().myLeftSidebarPanel); }
     public static get MySharedDocs() { return DocCast(Doc.UserDoc().mySharedDocs); }
@@ -1202,12 +1206,16 @@ export class CurrentUserUtils {
     public static get MyImports() { return DocCast(Doc.UserDoc().myImports); }
     public static get MyContextMenuBtns() { return DocCast(Doc.UserDoc().myContextMenuBtns); }
     public static get MyRecentlyClosed() { return DocCast(Doc.UserDoc().myRecentlyClosed); }
-    public static get MyOverlayDocs() { return DocListCast(DocCast(Doc.UserDoc().myOverlayDocs)?.data); }
+    public static get MyOverlayDocs() { return DocCast(Doc.UserDoc().myOverlayDocs); }
     public static get ActiveDashboard() { return DocCast(Doc.UserDoc().activeDashboard); }
+    public static set ActiveDashboard(val:Doc|undefined) { Doc.UserDoc().activeDashboard = val; }
     public static get ActivePresentation() { return DocCast(Doc.UserDoc().activePresentation); }
+    public static set ActivePresentation(val) { Doc.UserDoc().activePresentation = val; }
+    public static get ActivePage() { return StrCast(Doc.UserDoc().activePage); }
+    public static set ActivePage(val) { Doc.UserDoc().activePage = val; }
     public static get EmptyPane() { return DocCast(Doc.UserDoc().emptyPane); }
-    public static set SelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; }
-    @computed public static get SelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; }
+    public static set ActiveTool(tool: InkTool) { Doc.UserDoc().activeTool = tool; }
+    public static get ActiveTool(): InkTool { return StrCast(Doc.UserDoc().activeTool, InkTool.None) as InkTool; }
 }
 
 ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) {
@@ -1219,7 +1227,7 @@ ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) {
     }
 });
 ScriptingGlobals.add(function MySharedDocs() { return CurrentUserUtils.MySharedDocs; }, "document containing all shared Docs");
-ScriptingGlobals.add(function IsNoviceMode() { return Doc.UserDoc().noviceMode; }, "is Dash in novice mode");
+ScriptingGlobals.add(function IsNoviceMode() { return Doc.noviceMode; }, "is Dash in novice mode");
 ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering");
 ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); }, "creates a snapshot copy of a dashboard");
 ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); }, "creates a new dashboard when called");
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 6a26dfdc7..382274462 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -38,7 +38,7 @@ export class SettingsManager extends React.Component<{}> {
     @observable activeTab = "Accounts";
 
     @computed get backgroundColor() { return Doc.UserDoc().activeCollectionBackground; }
-    @computed get colorScheme() { return CurrentUserUtils.ActiveDashboard.colorScheme; }
+    @computed get colorScheme() { return CurrentUserUtils.ActiveDashboard?.colorScheme; }
 
     constructor(props: {}) {
         super(props);
@@ -59,7 +59,7 @@ export class SettingsManager extends React.Component<{}> {
         }
     }
 
-    @undoBatch selectUserMode = action((e: React.ChangeEvent) => Doc.UserDoc().noviceMode = (e.currentTarget as any)?.value === "Novice");
+    @undoBatch selectUserMode = action((e: React.ChangeEvent) => Doc.noviceMode = (e.currentTarget as any)?.value === "Novice");
     @undoBatch changeShowTitle = action((e: React.ChangeEvent) => Doc.UserDoc().showTitle = (e.currentTarget as any).value ? "title" : undefined);
     @undoBatch changeFontFamily = action((e: React.ChangeEvent) => Doc.UserDoc().fontFamily = (e.currentTarget as any).value);
     @undoBatch changeFontSize = action((e: React.ChangeEvent) => Doc.UserDoc().fontSize = (e.currentTarget as any).value);
@@ -78,19 +78,21 @@ export class SettingsManager extends React.Component<{}> {
     @undoBatch
     @action
     changeColorScheme = action((e: React.ChangeEvent) => {
+        const activeDashboard= CurrentUserUtils.ActiveDashboard;
+        if (!activeDashboard) return;
         const scheme: ColorScheme = (e.currentTarget as any).value;
         switch (scheme) {
             case ColorScheme.Light:
-                CurrentUserUtils.ActiveDashboard.colorScheme = undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
+                activeDashboard.colorScheme = undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
                 addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "#d3d3d3 !important" });
                 break;
             case ColorScheme.Dark:
-                CurrentUserUtils.ActiveDashboard.colorScheme = ColorScheme.Dark;
+                activeDashboard.colorScheme = ColorScheme.Dark;
                 addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "black !important" });
                 break;
             case ColorScheme.System: default:
                 window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
-                    CurrentUserUtils.ActiveDashboard.colorScheme = e.matches ? ColorScheme.Dark : undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
+                    activeDashboard.colorScheme = e.matches ? ColorScheme.Dark : undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
                 });
                 break;
         }
@@ -254,7 +256,7 @@ export class SettingsManager extends React.Component<{}> {
             <div className="tab-column">
                 <div className="tab-column-title">Modes</div>
                 <div className="tab-column-content">
-                    <select className="modes-select" onChange={this.selectUserMode} defaultValue={Doc.UserDoc().noviceMode ? "Novice" : "Developer"}>
+                    <select className="modes-select" onChange={this.selectUserMode} defaultValue={Doc.noviceMode ? "Novice" : "Developer"}>
                         <option key={"Novice"} value={"Novice"}> Novice </option>
                         <option key={"Developer"} value={"Developer"}> Developer</option>
                     </select>
@@ -270,7 +272,7 @@ export class SettingsManager extends React.Component<{}> {
                     <button onClick={() => GroupManager.Instance?.open()}>Manage groups</button>
                     <div className="default-acl">
                         <input className="acl-check" type="checkbox" checked={BoolCast(Doc.UserDoc()?.defaultAclPrivate)}
-                            onChange={action(() => Doc.UserDoc().defaultAclPrivate = !Doc.UserDoc().defaultAclPrivate)} />
+                            onChange={action(() => Doc.defaultAclPrivate = !Doc.defaultAclPrivate)} />
                         <div className="acl-text">Default access private</div>
                     </div>
                 </div>
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index f439d4488..d77633b8d 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -369,7 +369,7 @@ export class SharingManager extends React.Component<{}> {
         const dropdownValues: string[] = Object.values(SharingPermissions);
         if (!uniform) dropdownValues.unshift("-multiple-");
         if (override) dropdownValues.unshift("None");
-        return dropdownValues.filter(permission => !Doc.UserDoc().noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
+        return dropdownValues.filter(permission => !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
         (
             <option key={permission} value={permission}>
                 {permission}
@@ -680,7 +680,7 @@ export class SharingManager extends React.Component<{}> {
                         </div>
 
                         <div className="acl-container">
-                            {Doc.UserDoc().noviceMode ? (null) :
+                            {Doc.noviceMode ? (null) :
                                 <div className="layoutDoc-acls">
                                     <input type="checkbox" onChange={action(() => this.layoutDocAcls = !this.layoutDocAcls)} checked={this.layoutDocAcls} /> <label>Layout</label>
                                 </div>}
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index efc1644fe..9a0f25fe3 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -34,8 +34,8 @@ export class DashboardView extends React.Component {
 
       clickDashboard = async (e: React.MouseEvent, dashboard: Doc) => {
             if (e.detail === 2) {
-                  Doc.UserDoc().activeDashboard = dashboard;
-                  Doc.UserDoc().activePage = "dashboard";
+                  CurrentUserUtils.ActiveDashboard = dashboard;
+                  CurrentUserUtils.ActivePage = "dashboard";
             }
       }
 
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index be5927497..169bd3873 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -65,7 +65,7 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() {
 
         isContentActive = (outsideReaction?: boolean) => (
             this.props.isContentActive?.() === false ? false :
-                (CurrentUserUtils.SelectedTool !== InkTool.None ||
+                (CurrentUserUtils.ActiveTool !== InkTool.None ||
                     (this.props.isContentActive?.() || this.props.Document.forceActive ||
                         this.props.isSelected(outsideReaction) ||
                         this.props.rootSelected(outsideReaction)) ? true : undefined))
@@ -200,7 +200,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
 
                     if (effectiveAcl === AclAugment) {
                         added.map(doc => {
-                            if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
+                            if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)) && CurrentUserUtils.ActiveDashboard) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
                             doc.context = this.props.Document;
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
                             Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
@@ -213,7 +213,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                             doc.context = this.props.Document;
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
 
-                            inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
+                            CurrentUserUtils.ActiveDashboard && inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
                         });
                         const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
                         if (annoDocs instanceof List) annoDocs.push(...added);
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 0bbe473d7..9b8f7238d 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -347,7 +347,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <div className="documentButtonBar-button">
                 {this.menuButton}
             </div>
-            {/* {Doc.UserDoc().noviceMode ? (null) : <div className="documentButtonBar-button">
+            {/* {Doc.noviceMode ? (null) : <div className="documentButtonBar-button">
                 {this.moreButton}
             </div>} */}
         </div>;
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 6afe64868..e960f5cca 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -129,7 +129,7 @@ export class GestureOverlay extends Touchable {
             // and this seems to be the only way of differentiating pen and touch on touch events
             if (pt.radiusX > 1 && pt.radiusY > 1) {
                 InkTranscription.Instance.createInkGroup();
-                Doc.UserDoc().activeInkTool = InkTool.None;
+                CurrentUserUtils.ActiveTool = InkTool.None;
                 this.prevPoints.set(pt.identifier, pt);
             }
         }
@@ -498,18 +498,18 @@ export class GestureOverlay extends Touchable {
             setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => {
                 if (doubleTap) {
                     InkTranscription.Instance.createInkGroup();
-                    CurrentUserUtils.SelectedTool = InkTool.None;
+                    CurrentUserUtils.ActiveTool = InkTool.None;
                     return;
                 }
             }));
         }
-        if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+        if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
             if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
-                CurrentUserUtils.SelectedTool = InkTool.Write;
+                CurrentUserUtils.ActiveTool = InkTool.Write;
             }
             this._points.push({ X: e.clientX, Y: e.clientY });
             setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
-            // if (CurrentUserUtils.SelectedTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)");
+            // if (CurrentUserUtils.ActiveTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)");
         }
     }
 
@@ -606,7 +606,7 @@ export class GestureOverlay extends Touchable {
                 this._points = [];
                 if (!CollectionFreeFormViewChrome.Instance?._keepPrimitiveMode) {
                     this.InkShape = "";
-                    Doc.UserDoc().activeInkTool = InkTool.None;
+                    CurrentUserUtils.ActiveTool = InkTool.None;
                 }
             }
             // if we're not drawing in a toolglass try to recognize as gesture
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 16d7c9a23..f5122df3f 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -57,7 +57,7 @@ export class KeyManager {
 
     public handle = action((e: KeyboardEvent) => {
         if (e.key?.toLowerCase() === "shift") DocumentDecorations.Instance.AddToSelection = true;
-        //if (!Doc.UserDoc().noviceMode && e.key.toLocaleLowerCase() === "shift") DocServer.UPDATE_SERVER_CACHE(true);
+        //if (!Doc.noviceMode && e.key.toLocaleLowerCase() === "shift") DocServer.UPDATE_SERVER_CACHE(true);
         const keyname = e.key && e.key.toLowerCase();
         this.handleGreedy(keyname);
 
@@ -112,7 +112,7 @@ export class KeyManager {
                 DocumentLinksButton.StartLink = undefined;
                 DocumentLinksButton.StartLinkView = undefined;
                 InkStrokeProperties.Instance._controlButton = false;
-                CurrentUserUtils.SelectedTool = InkTool.None;
+                CurrentUserUtils.ActiveTool = InkTool.None;
                 DragManager.CompleteWindowDrag?.(true);
                 var doDeselect = true;
                 if (SnappingManager.GetIsDragging()) {
@@ -241,9 +241,9 @@ export class KeyManager {
                     }
                 }
                 break;
-            case "e": CurrentUserUtils.SelectedTool = InkTool.Eraser;
+            case "e": CurrentUserUtils.ActiveTool = InkTool.Eraser;
                 break;
-            case "p": CurrentUserUtils.SelectedTool = InkTool.Pen;
+            case "p": CurrentUserUtils.ActiveTool = InkTool.Pen;
                 break;
             case "o":
                 const target = SelectionManager.Docs().lastElement();
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 0449da483..471ad09e9 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -25,8 +25,8 @@ export class InkStrokeProperties {
 
     constructor() {
         InkStrokeProperties._Instance = this;
-        reaction(() => this._controlButton, button => button && (CurrentUserUtils.SelectedTool = InkTool.None));
-        reaction(() => CurrentUserUtils.SelectedTool, tool => (tool !== InkTool.None) && (this._controlButton = false));
+        reaction(() => this._controlButton, button => button && (CurrentUserUtils.ActiveTool = InkTool.None));
+        reaction(() => CurrentUserUtils.ActiveTool, tool => (tool !== InkTool.None) && (this._controlButton = false));
     }
 
     /**
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index 8ab54918c..487bbcd00 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -254,7 +254,7 @@ export class InkTranscription extends React.Component {
  */
 createInkGroup() {
     // TODO nda - if document being added to is a inkGrouping then we can just add to that group
-    if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+    if (CurrentUserUtils.ActiveTool === InkTool.Write) {
         CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
             // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
             const selected = ffView.unprocessedDocs;
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index aa5a815ac..35d89d2b1 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -345,7 +345,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps>() {
             onClick: (e: React.MouseEvent) => this._handledClick && e.stopPropagation(),
             onContextMenu: () => {
                 const cm = ContextMenu.Instance;
-                !Doc.UserDoc().noviceMode && cm?.addItem({ description: "Recognize Writing", event: this.analyzeStrokes, icon: "paint-brush" });
+                !Doc.noviceMode && cm?.addItem({ description: "Recognize Writing", event: this.analyzeStrokes, icon: "paint-brush" });
                 cm?.addItem({ description: "Toggle Mask", event: () => InkingStroke.toggleMask(this.rootDoc), icon: "paint-brush" });
                 cm?.addItem({ description: "Edit Points", event: action(() => InkStrokeProperties.Instance._controlButton = !InkStrokeProperties.Instance._controlButton), icon: "paint-brush" });
             }
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 7e3916b3f..791c64630 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -381,7 +381,7 @@ export class MainView extends React.Component {
                         addDocTab={this.addDocTabFunc}
                         pinToPres={emptyFunction}
                         docViewPath={returnEmptyDoclist}
-                        styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards || this._sidebarContent.proto === CurrentUserUtils.MyFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
+                        styleProvider={this._sidebarContent.proto === CurrentUserUtils.MyDashboards || this._sidebarContent.proto === CurrentUserUtils.MyFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
                         rootSelected={returnTrue}
                         removeDocument={returnFalse}
                         ScreenToLocalTransform={this.mainContainerXf}
@@ -656,7 +656,7 @@ export class MainView extends React.Component {
                             <GestureOverlay> {this.mainDashboardArea}   </GestureOverlay>
                         </>;
                     case "home":  return <DashboardView/>;
-                } })(StrCast(Doc.UserDoc().activePage))
+                } })(CurrentUserUtils.ActivePage)
             }
             
             <PreviewCursor />
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index bdb3bb857..598fff29a 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -4,7 +4,7 @@ import { observer } from "mobx-react";
 import { computedFn } from "mobx-utils";
 import * as React from "react";
 import ReactLoading from 'react-loading';
-import { Doc, WidthSym, HeightSym } from "../../fields/Doc";
+import { Doc, WidthSym, HeightSym, DocListCast } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { Cast, NumCast } from "../../fields/Types";
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils } from "../../Utils";
@@ -149,7 +149,7 @@ export class OverlayView extends React.Component {
     }
 
     removeOverlayDoc = (doc: Doc | Doc[]) => {
-        (doc instanceof Doc ? [doc] : doc).forEach(doc => Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc));
+        (doc instanceof Doc ? [doc] : doc).forEach(doc => Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, doc));
         return true;
     }
     
@@ -158,7 +158,7 @@ export class OverlayView extends React.Component {
     }.bind(this));
 
     @computed get overlayDocs() {
-        return CurrentUserUtils.MyOverlayDocs?.map(d => {
+        return DocListCast(CurrentUserUtils.MyOverlayDocs?.data).map(d => {
             let offsetx = 0, offsety = 0;
             const dref = React.createRef<HTMLDivElement>();
             const onPointerMove = action((e: PointerEvent, down: number[]) => {
@@ -172,7 +172,7 @@ export class OverlayView extends React.Component {
                     dragData.dropAction = "move";
                     dragData.removeDocument = (doc: Doc | Doc[]) => {
                         const docs = (doc instanceof Doc) ? [doc] : doc;
-                        docs.forEach(d => Doc.RemoveDocFromList(Cast(Doc.UserDoc().myOverlayDocs, Doc, null), "data", d));
+                        docs.forEach(d => Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, d));
                         return true;
                     };
                     dragData.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => {
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 74055f057..9c6d9a108 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -200,7 +200,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
                     {list}
                 </div>
             </div>
-            {Doc.UserDoc().noviceMode ? (null) : <div onPointerDown={this.editOnClickScript} className="onClickFlyout-editScript"> Edit onClick Script</div>}
+            {Doc.noviceMode ? (null) : <div onPointerDown={this.editOnClickScript} className="onClickFlyout-editScript"> Edit onClick Script</div>}
         </div>;
     }
     @computed
@@ -229,7 +229,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         const isTree = this.selectedDoc?._viewType === CollectionViewType.Tree;
         const isTabView = this.selectedTabView;
         const toggle = (ele: JSX.Element | null, style?: React.CSSProperties) => <div className="propertiesButtons-button" style={style}> {ele} </div>;
-        const isNovice = Doc.UserDoc().noviceMode;
+        const isNovice = Doc.noviceMode;
         return !this.selectedDoc ? (null) :
             <div className="propertiesButtons">
                 {toggle(this.titleButton)}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 2cf334ae1..90c86fa18 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -11,7 +11,7 @@ import { Id } from "../../fields/FieldSymbols";
 import { InkField } from "../../fields/InkField";
 import { List } from "../../fields/List";
 import { ComputedField } from "../../fields/ScriptField";
-import { BoolCast, Cast, NumCast, StrCast } from "../../fields/Types";
+import { Cast, NumCast, StrCast, DocCast } from "../../fields/Types";
 import { denormalizeEmail, GetEffectiveAcl, SharingPermissions } from "../../fields/util";
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from "../../Utils";
 import { DocumentType } from "../documents/DocumentTypes";
@@ -340,7 +340,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      */
     @undoBatch
     changePermissions = (e: any, user: string) => {
-        const docs = SelectionManager.Views().length < 2 ? [this.selectedDoc] : SelectionManager.Views().map(docView => docView.props.Document);
+        const docs = SelectionManager.Views().length < 2 ? (this.selectedDoc ? [this.selectedDoc]:[]) : SelectionManager.Views().map(docView => docView.props.Document);
         SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
     }
 
@@ -355,7 +355,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             value={permission}
             onChange={e => this.changePermissions(e, user)}>
             {dropdownValues.filter(permission =>
-                !Doc.UserDoc().noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
+                !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
                     <option key={permission} value={permission}>  {permission}  </option>)}
         </select>;
     }
@@ -419,7 +419,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
         // all selected docs
         const docs = SelectionManager.Views().length < 2 ?
-            [this.layoutDocAcls ? this.selectedDoc : this.selectedDoc[DataSym]]
+            [this.layoutDocAcls ? this.selectedDoc : this.selectedDoc?.[DataSym]]
             : SelectionManager.Views().map(docView => this.layoutDocAcls ? docView.props.Document : docView.props.Document[DataSym]);
 
         const target = docs[0];
@@ -907,7 +907,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             {!this.openSharing ? (null) :
                 <div className="propertiesView-sharing-content">
                     <div className="propertiesView-buttonContainer">
-                        {!Doc.UserDoc().noviceMode ? (<div className="propertiesView-acls-checkbox">
+                        {!Doc.noviceMode ? (<div className="propertiesView-acls-checkbox">
                             <Checkbox
                                 color="primary"
                                 onChange={action(() => this.layoutDocAcls = !this.layoutDocAcls)}
@@ -931,42 +931,46 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      * If it doesn't exist, it creates it.
      */
     checkFilterDoc() {
-        if (!this.selectedDoc.currentFilter) this.selectedDoc.currentFilter = CurrentUserUtils.createFilterDoc();
+        if (!this.selectedDoc?.currentFilter) this.selectedDoc!.currentFilter = CurrentUserUtils.createFilterDoc();
     }
 
     /**
      * Creates a new currentFilter for this.filterDoc, 
      */
     createNewFilterDoc = () => {
-        const currentDocFilters = this.selectedDoc._docFilters;
-        const currentDocRangeFilters = this.selectedDoc._docRangeFilters;
-        this.selectedDoc._docFilters = new List<string>();
-        this.selectedDoc._docRangeFilters = new List<string>();
-        (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters;
-        (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters;
-        this.selectedDoc.currentFilter = CurrentUserUtils.createFilterDoc();
+        if (this.selectedDoc) {
+            const currentDocFilters = this.selectedDoc._docFilters;
+            const currentDocRangeFilters = this.selectedDoc._docRangeFilters;
+            this.selectedDoc._docFilters = new List<string>();
+            this.selectedDoc._docRangeFilters = new List<string>();
+            (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters;
+            (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters;
+            this.selectedDoc.currentFilter = CurrentUserUtils.createFilterDoc();
+        }
     }
 
     /**
      * Updates this.filterDoc's currentFilter and saves the docFilters on the currentFilter
      */
     updateFilterDoc = (doc: Doc) => {
-        if (doc === this.selectedDoc.currentFilter) return; // causes problems if you try to reapply the same doc
-        const savedDocFilters = doc._docFiltersList;
-        const currentDocFilters = this.selectedDoc._docFilters;
-        this.selectedDoc._docFilters = new List<string>();
-        (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters;
-        this.selectedDoc.currentFilter = doc;
-        doc._docFiltersList = new List<string>();
-        this.selectedDoc._docFilters = savedDocFilters;
-
-        const savedDocRangeFilters = doc._docRangeFiltersList;
-        const currentDocRangeFilters = this.selectedDoc._docRangeFilters;
-        this.selectedDoc._docRangeFilters = new List<string>();
-        (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters;
-        this.selectedDoc.currentFilter = doc;
-        doc._docRangeFiltersList = new List<string>();
-        this.selectedDoc._docRangeFilters = savedDocRangeFilters;
+        if (this.selectedDoc) {
+            if (doc === this.selectedDoc.currentFilter) return; // causes problems if you try to reapply the same doc
+            const savedDocFilters = doc._docFiltersList;
+            const currentDocFilters = this.selectedDoc._docFilters;
+            this.selectedDoc._docFilters = new List<string>();
+            (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters;
+            this.selectedDoc.currentFilter = doc;
+            doc._docFiltersList = new List<string>();
+            this.selectedDoc._docFilters = savedDocFilters;
+
+            const savedDocRangeFilters = doc._docRangeFiltersList;
+            const currentDocRangeFilters = this.selectedDoc._docRangeFilters;
+            this.selectedDoc._docRangeFilters = new List<string>();
+            (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters;
+            this.selectedDoc.currentFilter = doc;
+            doc._docRangeFiltersList = new List<string>();
+            this.selectedDoc._docRangeFilters = savedDocRangeFilters;
+        }
     }
 
     @computed get filtersSubMenu() {
@@ -1059,13 +1063,13 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     <FontAwesomeIcon icon={this.openFields ? "caret-down" : "caret-right"} size="lg" color="white" />
                 </div>
             </div>
-            {!Doc.UserDoc().noviceMode && this.openFields ? <div className="propertiesView-fields-checkbox">
+            {!Doc.noviceMode && this.openFields ? <div className="propertiesView-fields-checkbox">
                 {this.fieldsCheckbox}
                 <div className="propertiesView-fields-checkbox-text">Layout</div>
             </div> : null}
             {!this.openFields ? (null) :
                 <div className="propertiesView-fields-content">
-                    {Doc.UserDoc().noviceMode ? this.noviceFields : this.expandedField}
+                    {Doc.noviceMode ? this.noviceFields : this.expandedField}
                 </div>}
         </div>;
     }
@@ -1122,7 +1126,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @undoBatch
     handleDescriptionChange = action((value: string) => {
-        if (LinkManager.currentLink) {
+        if (LinkManager.currentLink && this.selectedDoc) {
             this.selectedDoc.description = value;
             this.description = value;
             return true;
@@ -1131,7 +1135,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @undoBatch
     handleLinkRelationshipChange = action((value: string) => {
-        if (LinkManager.currentLink) {
+        if (LinkManager.currentLink && this.selectedDoc) {
             this.selectedDoc.linkRelationship = value;
             this.relationship = value;
             return true;
@@ -1188,7 +1192,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
 
     @undoBatch
     changeFollowBehavior = action((follow: string) => {
-        if (LinkManager.currentLink) {
+        if (LinkManager.currentLink && this.selectedDoc) {
             this.selectedDoc.followLinkLocation = follow;
             return true;
         }
@@ -1219,18 +1223,18 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     }
 
     toggleAnchor = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc.linkAutoMove = !this.selectedDoc.linkAutoMove)));
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (this.selectedDoc.linkAutoMove = !this.selectedDoc.linkAutoMove))));
     }
 
     toggleArrow = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc.displayArrow = !this.selectedDoc.displayArrow)));
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (this.selectedDoc.displayArrow = !this.selectedDoc.displayArrow))));
     }
 
     toggleZoomToTarget1 = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => Cast(this.selectedDoc.anchor1, Doc, null).followLinkZoom = !Cast(this.selectedDoc.anchor1, Doc, null).followLinkZoom)));
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (DocCast(this.selectedDoc.anchor1).followLinkZoom = !DocCast(this.selectedDoc.anchor1).followLinkZoom))));
     }
     toggleZoomToTarget2 = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => Cast(this.selectedDoc.anchor2, Doc, null).followLinkZoom = !Cast(this.selectedDoc.anchor2, Doc, null).followLinkZoom)));
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (DocCast(this.selectedDoc.anchor2).followLinkZoom = !DocCast(this.selectedDoc.anchor2).followLinkZoom))));
     }
 
     @computed
@@ -1238,7 +1242,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return <input
             autoComplete={"off"}
             id="link_relationship_input"
-            value={StrCast(this.selectedDoc.linkRelationship)}
+            value={StrCast(this.selectedDoc?.linkRelationship)}
             onKeyDown={this.onRelationshipKey}
             onBlur={this.onSelectOutRelationship}
             onChange={e => this.handleLinkRelationshipChange(e.currentTarget.value)}
@@ -1252,7 +1256,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         return <input
             autoComplete={"off"}
             id="link_description_input"
-            value={StrCast(this.selectedDoc.description)}
+            value={StrCast(this.selectedDoc?.description)}
             onKeyDown={this.onDescriptionKey}
             onBlur={this.onSelectOutDesc}
             onChange={e => this.handleDescriptionChange(e.currentTarget.value)}
@@ -1273,7 +1277,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     // }
 
     render() {
-        const isNovice = BoolCast(Doc.UserDoc().noviceMode);
+        const isNovice = Doc.noviceMode;
         if (!this.selectedDoc && !this.isPres) {
             return <div className="propertiesView" style={{ width: this.props.width }}>
                 <div className="propertiesView-title" style={{ width: this.props.width }}>
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 636f7042f..689ee4fc1 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -15,6 +15,7 @@ import { DocumentView } from "./nodes/DocumentView";
 import { DefaultStyleProvider } from "./StyleProvider";
 import './TemplateMenu.scss';
 import React = require("react");
+import { CurrentUserUtils } from "../util/CurrentUserUtils";
 
 @observer
 class TemplateToggle extends React.Component<{ template: string, checked: boolean, toggle: (event: React.ChangeEvent<HTMLInputElement>, template: string) => void }> {
@@ -111,22 +112,22 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
         const firstDoc = this.props.docViews[0].props.Document;
         const templateName = StrCast(firstDoc.layoutKey, "layout").replace("layout_", "");
         const noteTypes = DocListCast(Cast(Doc.UserDoc()["template-notes"], Doc, null)?.data);
-        const addedTypes = Doc.UserDoc().noviceMode ? [] : DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data);
+        const addedTypes = Doc.noviceMode ? [] : DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data);
         const layout = Doc.Layout(firstDoc);
         const templateMenu: Array<JSX.Element> = [];
         this.props.templates?.forEach((checked, template) =>
             templateMenu.push(<TemplateToggle key={template} template={template} checked={checked} toggle={this.toggleTemplate} />));
         templateMenu.push(<OtherToggle key={"audio"} name={"Audio"} checked={firstDoc._showAudio ? true : false} toggle={this.toggleAudio} />);
         templateMenu.push(<OtherToggle key={"default"} name={"Default"} checked={templateName === "layout"} toggle={this.toggleDefault} />);
-        !Doc.UserDoc().noviceMode && templateMenu.push(<OtherToggle key={"chrome"} name={"Chrome"} checked={!layout._chromeHidden} toggle={this.toggleChrome} />);
+        !Doc.noviceMode && templateMenu.push(<OtherToggle key={"chrome"} name={"Chrome"} checked={!layout._chromeHidden} toggle={this.toggleChrome} />);
         addedTypes.concat(noteTypes).map(template => template.treeViewChecked = this.templateIsUsed(firstDoc, template));
         this._addedKeys && Array.from(this._addedKeys).filter(key => !noteTypes.some(nt => nt.title === key)).forEach(template => templateMenu.push(
             <OtherToggle key={template} name={template} checked={templateName === template} toggle={e => this.toggleLayout(e, template)} />));
         return <ul className="template-list" style={{ display: "block" }}>
-            {Doc.UserDoc().noviceMode ? (null) : <input placeholder="+ layout" ref={this._customRef} onKeyPress={this.onCustomKeypress} />}
+            {Doc.noviceMode ? (null) : <input placeholder="+ layout" ref={this._customRef} onKeyPress={this.onCustomKeypress} />}
             {templateMenu}
-            {Doc.UserDoc().noviceMode ? (null) : <CollectionTreeView
-                Document={Doc.UserDoc().templateDocs as Doc}
+            {Doc.noviceMode ? (null) : <CollectionTreeView
+                Document={CurrentUserUtils.MyTemplates}
                 CollectionView={undefined}
                 ContainingCollectionDoc={undefined}
                 ContainingCollectionView={undefined}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 1ebbe08ef..5f36a7a51 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -368,7 +368,7 @@ export class CollectionDockingView extends CollectionSubView() {
             }
         }
         if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) &&
-            ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+            ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
             e.stopPropagation();
         }
     }
@@ -433,8 +433,8 @@ export class CollectionDockingView extends CollectionSubView() {
 
     stackCreated = (stack: any) => {
         stack.header?.element.on('mousedown', (e: any) => {
-            if (e.target === stack.header?.element[0] && e.button === 2) {
-                const dashboard= CurrentUserUtils.ActiveDashboard;
+            const dashboard = CurrentUserUtils.ActiveDashboard;
+            if (dashboard && e.target === stack.header?.element[0] && e.button === 2) {
                 dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1;
                 const docToAdd = Docs.Create.FreeformDocument([], {
                     _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _backgroundGridShow: true, _fitWidth: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}`,
@@ -462,12 +462,14 @@ export class CollectionDockingView extends CollectionSubView() {
             .click(action(() => {
                 // stack.config.fixed = !stack.config.fixed;  // force the stack to have a fixed size
                 const dashboard = CurrentUserUtils.ActiveDashboard;
-                dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1;
-                const docToAdd = Docs.Create.FreeformDocument([], {
-                    _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, _backgroundGridShow: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}`
-                });
-                this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
-                CollectionDockingView.AddSplit(docToAdd, "", stack);
+                if (dashboard) {
+                    dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1;
+                    const docToAdd = Docs.Create.FreeformDocument([], {
+                        _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, _backgroundGridShow: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}`
+                    });
+                    this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
+                    CollectionDockingView.AddSplit(docToAdd, "", stack);
+                }
             }));
     }
 
@@ -480,6 +482,6 @@ ScriptingGlobals.add(function openInLightbox(doc: any) { LightboxView.AddDocTab(
     "opens up document in a lightbox", "(doc: any)");
 ScriptingGlobals.add(function openOnRight(doc: any) { return CollectionDockingView.AddSplit(doc, "right"); },
     "opens up document in tab on right side of the screen", "(doc: any)");
-ScriptingGlobals.add(function openInOverlay(doc: any) { return Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc); },
+ScriptingGlobals.add(function openInOverlay(doc: any) { return Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, doc); },
     "opens up document in screen overlay layer", "(doc: any)");
 ScriptingGlobals.add(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); });
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 1c25421f5..9b1bb5b97 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -268,17 +268,20 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
                  self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`,
         immediate: undoBatch((source: Doc[]) => { this.target._docFilters = undefined; this.target._searchFilterDocs = undefined; }),
         initialize: (button: Doc) => {
-            button['target-docFilters'] = (CurrentUserUtils.MySearcher._docFilters || CurrentUserUtils.ActiveDashboard._docFilters) instanceof ObjectField ?
-                ObjectField.MakeCopy((CurrentUserUtils.MySearcher._docFilters || CurrentUserUtils.ActiveDashboard._docFilters) as any as ObjectField) : undefined;
-            button['target-searchFilterDocs'] = CurrentUserUtils.ActiveDashboard._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(CurrentUserUtils.ActiveDashboard._searchFilterDocs as any as ObjectField) : undefined;
+            const activeDash = CurrentUserUtils.ActiveDashboard;
+            if (activeDash) {
+                button['target-docFilters'] = (CurrentUserUtils.MySearcher._docFilters || activeDash._docFilters) instanceof ObjectField ?
+                    ObjectField.MakeCopy((CurrentUserUtils.MySearcher._docFilters || activeDash._docFilters) as any as ObjectField) : undefined;
+                button['target-searchFilterDocs'] = activeDash._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(activeDash._searchFilterDocs as any as ObjectField) : undefined;
+            }
         },
     };
 
-    @computed get _freeform_commands() { return Doc.UserDoc().noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; }
-    @computed get _stacking_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
-    @computed get _masonry_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
-    @computed get _schema_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._templateCommand, this._narrativeCommand]; }
-    @computed get _doc_commands() { return Doc.UserDoc().noviceMode ? undefined : [this._openLinkInCommand, this._onClickCommand]; }
+    @computed get _freeform_commands() { return Doc.noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; }
+    @computed get _stacking_commands() { return Doc.noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
+    @computed get _masonry_commands() { return Doc.noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
+    @computed get _schema_commands() { return Doc.noviceMode ? undefined : [this._templateCommand, this._narrativeCommand]; }
+    @computed get _doc_commands() { return Doc.noviceMode ? undefined : [this._openLinkInCommand, this._onClickCommand]; }
     @computed get _tree_commands() { return undefined; }
     private get _buttonizableCommands() {
         switch (this.props.type) {
@@ -467,7 +470,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     @action
     startRecording = () => {
         const doc = Docs.Create.ScreenshotDocument({ title: "screen recording", _fitWidth: true, _width: 400, _height: 200, mediaState: "pendingRecording" });
-        //Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
+        //Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, doc);
         CollectionDockingView.AddSplit(doc, "right");
     }
 
@@ -691,8 +694,8 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
     @action clearKeepPrimitiveMode() { this._selectedPrimitive = this._shapePrims.length; }
     @action primCreated() {
         if (!this._keepPrimitiveMode) { //get out of ink mode after each stroke=
-            if (CurrentUserUtils.SelectedTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor);
-            CurrentUserUtils.SelectedTool = InkTool.None;
+            if (CurrentUserUtils.ActiveTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor);
+            CurrentUserUtils.ActiveTool = InkTool.None;
             this._selectedPrimitive = this._shapePrims.length;
             SetActiveArrowStart("none");
             SetActiveArrowEnd("none");
@@ -734,11 +737,11 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
             if (this._selectedPrimitive !== i) {
                 this._selectedPrimitive = i;
                 if (this._title[i] === "highlighter") {
-                    CurrentUserUtils.SelectedTool = InkTool.Highlighter;
+                    CurrentUserUtils.ActiveTool = InkTool.Highlighter;
                     GestureOverlay.Instance.SavedColor = ActiveInkColor();
                     SetActiveInkColor("rgba(245, 230, 95, 0.75)");
                 } else {
-                    CurrentUserUtils.SelectedTool = InkTool.Pen;
+                    CurrentUserUtils.ActiveTool = InkTool.Pen;
                 }
                 SetActiveArrowStart(this._head[i]);
                 SetActiveArrowEnd(this._end[i]);
@@ -747,7 +750,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
                 GestureOverlay.Instance.InkShape = this._shapePrims[i];
             } else {
                 this._selectedPrimitive = this._shapePrims.length;
-                CurrentUserUtils.SelectedTool = InkTool.None;
+                CurrentUserUtils.ActiveTool = InkTool.None;
                 SetActiveArrowStart("");
                 SetActiveArrowEnd("");
                 GestureOverlay.Instance.InkShape = "";
@@ -840,7 +843,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
                         {this.widthPicker}
                         {this.colorPicker}
                         {this.fillPicker}
-                        {Doc.UserDoc().noviceMode || this.props.isDoc ? (null) :
+                        {Doc.noviceMode || this.props.isDoc ? (null) :
                             <>
                                 <Tooltip key="back" title={<div className="dash-tooltip">Back Frame</div>} placement="bottom">
                                     <div className="backKeyframe" onClick={this.prevKeyframe}>
@@ -879,7 +882,7 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
         const val = value.toLowerCase();
         const docs = DocListCast(this.document[this.props.fieldKey]);
 
-        if (Doc.UserDoc().noviceMode) {
+        if (Doc.noviceMode) {
             if (docs instanceof Doc) {
                 const keys = Object.keys(docs).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
                     key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index dddae4a34..277fcd59c 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -598,7 +598,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             <>
                 {buttonMenu || noviceExplainer ? <div className="documentButtonMenu">
                     {buttonMenu ? this.buttonMenu : null}
-                    {Doc.UserDoc().noviceMode && noviceExplainer ?
+                    {Doc.noviceMode && noviceExplainer ?
                         <div className="documentExplanation">
                             {noviceExplainer}
                         </div>
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 7f96217b8..f3a798143 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -224,8 +224,8 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
                     }
                 }, icon: "compress-arrows-alt"
             }));
-        !Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({ description: "Doc Fields ...", subitems: docItems, icon: "eye" });
-        !Doc.UserDoc().noviceMode && ContextMenu.Instance.addItem({ description: "Containers ...", subitems: layoutItems, icon: "eye" });
+        !Doc.noviceMode && ContextMenu.Instance.addItem({ description: "Doc Fields ...", subitems: docItems, icon: "eye" });
+        !Doc.noviceMode && ContextMenu.Instance.addItem({ description: "Containers ...", subitems: layoutItems, icon: "eye" });
         ContextMenu.Instance.setDefaultItem("::", (name: string): void => {
             Doc.GetProto(this.props.Document)[name] = "";
             const created = Docs.Create.TextDocument("", { title: name, _width: 250, _autoHeight: true });
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index e809bfbce..ba72fb7b9 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -67,7 +67,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     @computed get treeChildren() { TraceMobx(); return this.props.childDocuments || this.childDocs; }
     @computed get outlineMode() { return this.doc.treeViewType === TreeViewType.outline; }
     @computed get fileSysMode() { return this.doc.treeViewType === TreeViewType.fileSystem; }
-    @computed get dashboardMode() { return this.doc === Doc.UserDoc().myDashboards; }
+    @computed get dashboardMode() { return this.doc === CurrentUserUtils.MyDashboards; }
 
     @observable _explainerHeight = 0; // height of the description of the tree view
 
@@ -76,7 +76,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     // these should stay in synch with counterparts in DocComponent.ts ViewBoxAnnotatableComponent
     @observable _isAnyChildContentActive = false;
     whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive));
-    isContentActive = (outsideReaction?: boolean) => (CurrentUserUtils.SelectedTool !== InkTool.None ||
+    isContentActive = (outsideReaction?: boolean) => (CurrentUserUtils.ActiveTool !== InkTool.None ||
         (this.props.isContentActive?.() || this.props.Document.forceActive ||
             this.props.isSelected(outsideReaction) || this._isAnyChildContentActive ||
             this.props.rootSelected(outsideReaction)) ? true : false)
@@ -164,7 +164,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     }
     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 (!Doc.UserDoc().noviceMode) {
+        if (!Doc.noviceMode) {
             const layoutItems: ContextMenuProps[] = [];
             layoutItems.push({ description: "Make tree state " + (this.doc.treeViewOpenIsTransient ? "persistent" : "transient"), event: () => this.doc.treeViewOpenIsTransient = !this.doc.treeViewOpenIsTransient, icon: "paint-brush" });
             layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields, icon: "paint-brush" });
@@ -286,7 +286,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     }
 
     @computed get noviceExplainer() {
-        return !Doc.UserDoc().noviceMode || !this.rootDoc.explainer ? (null) :
+        return !Doc.noviceMode || !this.rootDoc.explainer ? (null) :
             <div className="documentExplanation"> {this.rootDoc.explainer} </div>;
     }
 
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 965f0a352..4b5c5e3fb 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -160,8 +160,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
         subItems.push({ description: "Masonry", event: () => func(CollectionViewType.Masonry), icon: "columns" });
         subItems.push({ description: "Carousel", event: () => func(CollectionViewType.Carousel), icon: "columns" });
         subItems.push({ description: "3D Carousel", event: () => func(CollectionViewType.Carousel3D), icon: "columns" });
-        !Doc.UserDoc().noviceMode && subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" });
-        !Doc.UserDoc().noviceMode && subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
+        !Doc.noviceMode && subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" });
+        !Doc.noviceMode && subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
         subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" });
 
         if (!Doc.IsSystem(this.rootDoc) && !this.rootDoc.isGroup && !this.rootDoc.annotationOn) {
@@ -184,16 +184,16 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
 
             const options = cm.findByDescription("Options...");
             const optionItems = options && "subitems" in options ? options.subitems : [];
-            !Doc.UserDoc().noviceMode ? optionItems.splice(0, 0, { description: `${this.rootDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.rootDoc.forceActive = !this.rootDoc.forceActive, icon: "project-diagram" }) : null;
+            !Doc.noviceMode ? optionItems.splice(0, 0, { description: `${this.rootDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.rootDoc.forceActive = !this.rootDoc.forceActive, icon: "project-diagram" }) : null;
             if (this.rootDoc.childLayout instanceof Doc) {
                 optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.rootDoc.childLayout as Doc, "add:right"), icon: "project-diagram" });
             }
             if (this.rootDoc.childClickedOpenTemplateView instanceof Doc) {
                 optionItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.rootDoc.childClickedOpenTemplateView as Doc, "add:right"), icon: "project-diagram" });
             }
-            !Doc.UserDoc().noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer, icon: "project-diagram" });
+            !Doc.noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer, icon: "project-diagram" });
 
-            if (!Doc.UserDoc().noviceMode) {
+            if (!Doc.noviceMode) {
                 optionItems.push({
                     description: "Create Branch", event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), "add:right"), icon: "project-diagram"
                 });
@@ -210,7 +210,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
 
             !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "hand-point-right" });
 
-            if (!Doc.UserDoc().noviceMode && !this.rootDoc.annotationOn) {
+            if (!Doc.noviceMode && !this.rootDoc.annotationOn) {
                 const existingOnClick = cm.findByDescription("OnClick...");
                 const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
                 const funcs = [{ key: "onChildClick", name: "On Child Clicked" }, { key: "onChildDoubleClick", name: "On Child Double Clicked" }];
@@ -230,7 +230,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
                 !Doc.IsSystem(this.rootDoc) && !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
             }
 
-            if (!Doc.UserDoc().noviceMode) {
+            if (!Doc.noviceMode) {
                 const more = cm.findByDescription("More...");
                 const moreItems = more && "subitems" in more ? more.subitems : [];
                 moreItems.push({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.rootDoc) });
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 022b9fa24..70db121d1 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -277,7 +277,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
         });
         if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) {
-            const docs = Cast(Cast(Doc.UserDoc().myOverlayDocs, Doc, null).data, listSpec(Doc), []);
+            const docs = Cast(CurrentUserUtils.MyOverlayDocs.data, listSpec(Doc), []);
             if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1);
             CollectionDockingView.AddSplit(curPres, "right");
             setTimeout(() => DocumentManager.Instance.jumpToDocument(docList.lastElement(), false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 59dc5671b..704b8989a 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -114,7 +114,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         return this.doc.viewType === CollectionViewType.Docking ? this.fieldKey :
             this.props.treeView.dashboardMode ? this.fieldKey :
                 this.props.treeView.fileSysMode ? (this.doc.isFolder ? this.fieldKey : "aliases") : // for displaying 
-                    this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.UserDoc().noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields");
+                    this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields");
     }
 
     @computed get doc() { return this.props.document; }
@@ -560,8 +560,8 @@ export class TreeView extends React.Component<TreeViewProps> {
         const links = () => DocListCast(this.doc.links).length && !this.props.treeView.dashboardMode ? "links" : "";
         const data = () => this.childDocs || this.props.treeView.dashboardMode ? this.fieldKey : "";
         const aliases = () => this.props.treeView.dashboardMode ? "" : "aliases";
-        const fields = () => Doc.UserDoc().noviceMode ? "" : "fields";
-        const layout = (Doc.UserDoc().noviceMode) || this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"];
+        const fields = () => Doc.noviceMode ? "" : "fields";
+        const layout = (Doc.noviceMode) || this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"];
         return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()].filter(m => m);
     }
     @action
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c07e44fcc..ffe146ae4 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -458,7 +458,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 !this.props.Document._isGroup && // group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag
                 !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) &&
                 !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
-                switch (CurrentUserUtils.SelectedTool) {
+                switch (CurrentUserUtils.ActiveTool) {
                     case InkTool.Highlighter:
                         break;
                     // TODO: nda - this where we want to create the new "writingDoc" collection that we add strokes to
@@ -497,7 +497,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     this.addMoveListeners();
                     this.removeEndListeners();
                     this.addEndListeners();
-                    if (CurrentUserUtils.SelectedTool === InkTool.None) {
+                    if (CurrentUserUtils.ActiveTool === InkTool.None) {
                         this._lastX = pt.pageX;
                         this._lastY = pt.pageY;
                         e.preventDefault();
@@ -519,9 +519,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             case GestureUtils.Gestures.Stroke:
                 const points = ge.points;
                 const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
-                const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points,
+                const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points,
                     { title: "ink stroke", x: B.x - ActiveInkWidth() / 2, y: B.y - ActiveInkWidth() / 2, _width: B.width + ActiveInkWidth(), _height: B.height + ActiveInkWidth() });
-                if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+                if (CurrentUserUtils.ActiveTool === InkTool.Write) {
                     this.unprocessedDocs.push(inkDoc);
                     CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
                 }
@@ -711,7 +711,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     onPointerMove = (e: PointerEvent): void => {
         if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return;
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
-            Doc.UserDoc().activeInkTool = InkTool.None;
+            CurrentUserUtils.ActiveTool = InkTool.None;
             if (this.props.isContentActive(true)) e.stopPropagation();
         } else if (!e.cancelBubble) {
             if (this.tryDragCluster(e, this._hitCluster)) {
@@ -834,7 +834,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (!e.cancelBubble) {
             const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
             if (myTouches[0]) {
-                if (CurrentUserUtils.SelectedTool === InkTool.None) {
+                if (CurrentUserUtils.ActiveTool === InkTool.None) {
                     if (this.tryDragCluster(e, this._hitCluster)) {
                         e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
                         e.preventDefault();
@@ -985,7 +985,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || CurrentUserUtils.MyOverlayDocs.includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
+        if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
         if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) { // things that can scroll vertically should do that instead of zooming
             e.stopPropagation();
         }
@@ -1031,7 +1031,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 else if (ranges.yrange.max <= (panY - panelDim[1] / 2)) panY = ranges.yrange.min - panelDim[1] / 2;
             }
         }
-        if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || CurrentUserUtils.MyOverlayDocs.includes(this.Document)) {
+        if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.Document)) {
             this._viewTransition = panTime;
             const scale = this.getLocalTransform().inverse().Scale;
             const minScale = NumCast(this.rootDoc._viewScaleMin, 1);
@@ -1629,7 +1629,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const appearance = ContextMenu.Instance.findByDescription("Appearance...");
         const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
         appearanceItems.push({ description: "Reset View", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" });
-        !Doc.UserDoc().noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
+        !Doc.noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
         appearanceItems.push({ description: `${this.fitContentsToBox ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitContentsToBox = !this.fitContentsToBox, icon: !this.fitContentsToBox ? "expand-arrows-alt" : "compress-arrows-alt" });
         appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, {pinDocView:true, panelWidth: this.props.PanelWidth(), panelHeight:this.props.PanelHeight()}), icon: "map-pin" });
         //appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" });
@@ -1640,27 +1640,27 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
         // this.props.Document._isGroup && this.childDocs.filter(s => s.type === DocumentType.INK).length > 0 && appearanceItems.push({ description: "Ink to math", event: () => this.transcribeStrokes(true), icon: "square-root-alt" });
 
-        !Doc.UserDoc().noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null;
+        !Doc.noviceMode ? appearanceItems.push({ description: "Arrange contents in grid", event: this.layoutDocsInGrid, icon: "table" }) : null;
         !appearance && ContextMenu.Instance.addItem({ description: "Appearance...", subitems: appearanceItems, icon: "eye" });
 
         const viewctrls = ContextMenu.Instance.findByDescription("UI Controls...");
         const viewCtrlItems = viewctrls && "subitems" in viewctrls ? viewctrls.subitems : [];
-        !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null;
-        !Doc.UserDoc().noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._useClusters), icon: "braille" }) : null;
+        !Doc.noviceMode ? viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null;
+        !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._useClusters), icon: "braille" }) : null;
         !viewctrls && ContextMenu.Instance.addItem({ description: "UI Controls...", subitems: viewCtrlItems, icon: "eye" });
 
         const options = ContextMenu.Instance.findByDescription("Options...");
         const optionItems = options && "subitems" in options ? options.subitems : [];
-        !this.props.isAnnotationOverlay && !Doc.UserDoc().noviceMode &&
+        !this.props.isAnnotationOverlay && !Doc.noviceMode &&
             optionItems.push({ description: (this._showAnimTimeline ? "Close" : "Open") + " Animation Timeline", event: action(() => this._showAnimTimeline = !this._showAnimTimeline), icon: "eye" });
         this.props.renderDepth && optionItems.push({ description: "Use Background Color as Default", event: () => Cast(Doc.UserDoc().emptyCollection, Doc, null)._backgroundColor = StrCast(this.layoutDoc._backgroundColor), icon: "palette" });
-        if (!Doc.UserDoc().noviceMode) {
+        if (!Doc.noviceMode) {
             optionItems.push({ description: (!Doc.NativeWidth(this.layoutDoc) || !Doc.NativeHeight(this.layoutDoc) ? "Freeze" : "Unfreeze") + " Aspect", event: this.toggleNativeDimensions, icon: "snowflake" });
         }
         !options && ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
         const mores = ContextMenu.Instance.findByDescription("More...");
         const moreItems = mores && "subitems" in mores ? mores.subitems : [];
-        if (!Doc.UserDoc().noviceMode) {
+        if (!Doc.noviceMode) {
             moreItems.push({ description: "Export collection", icon: "download", event: async () => Doc.Zip(this.props.Document) });
             moreItems.push({ description: "Import exported collection", icon: "upload", event: ({ x, y }) => this.importDocument(x, y) });
         }
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 051da795f..b62020a04 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -330,7 +330,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     onClick = (e: React.MouseEvent): void => {
         if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
             Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
-            if (CurrentUserUtils.SelectedTool === InkTool.None) {
+            if (CurrentUserUtils.ActiveTool === InkTool.None) {
                 if (!(e.nativeEvent as any).marqueeHit) {
                     (e.nativeEvent as any).marqueeHit = true;
                     if (!this.props.trySelectCluster(e.shiftKey)) {
@@ -367,7 +367,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         const newCollection = creator ? creator(selected, { title: "nested stack", }) : ((doc: Doc) => {
             Doc.GetProto(doc).data = new List<Doc>(selected);
             Doc.GetProto(doc).title = makeGroup ? "grouping" : "nested freeform";
-            !this.props.isAnnotationOverlay && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", Doc.GetProto(doc));
+            !this.props.isAnnotationOverlay && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, undefined, Doc.GetProto(doc));
             doc._panX = doc._panY = 0;
             return doc;
         })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
@@ -643,7 +643,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         return <div className="marqueeView"
             style={{
                 overflow: StrCast(this.props.Document._overflow),
-                cursor: [InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool) || this._visible || PresBox.startMarquee ? "crosshair" : "pointer"
+                cursor: [InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool) || this._visible || PresBox.startMarquee ? "crosshair" : "pointer"
             }}
 
             onDragOver={e => e.preventDefault()}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
index 0875c80b3..9653f2808 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
@@ -321,7 +321,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
         const whitelistKeys = ["context", "author", "*lastModified", "text", "data", "tags", "creationDate"];
         const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
         const showKeys = new Set<string>();
-        [...keyOptions, ...whitelistKeys].forEach(key => (!Doc.UserDoc().noviceMode ||
+        [...keyOptions, ...whitelistKeys].forEach(key => (!Doc.noviceMode ||
             whitelistKeys.includes(key)
             || ((!key.startsWith("_") && key[0] === key[0].toUpperCase()) || key[0] === "#")) ? showKeys.add(key) : null);
         return Array.from(showKeys.keys()).filter(key => !this._searchTerm || key.includes(this._searchTerm));
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 669622455..8e24fce11 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -348,11 +348,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             Doc.GetProto(newDoc).recordingSource = this.dataDoc;
             Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.fieldKey}-recordingStart"]`);
             Doc.GetProto(newDoc).mediaState = ComputedField.MakeFunction("self.recordingSource.mediaState");
-            const overlayDoc = Doc.UserDoc().myOverlayDocs as Doc;
-            if (DocListCast(overlayDoc[Doc.LayoutFieldKey(overlayDoc)]).includes(this.rootDoc)) {
+            if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.rootDoc)) {
                 newDoc.x = this.rootDoc.x;
                 newDoc.y = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
-                Doc.AddDocToList(overlayDoc, undefined, newDoc);
+                Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, newDoc);
             } else {
                 this.props.addDocument?.(newDoc);
             }
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index d975baf9b..6d05fca5e 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -22,7 +22,6 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     @action
     static switchColor(color: ColorState) {
-        // Doc.UserDoc().backgroundColor = Utils.colorString(color); // bcz: this can't go here ... needs a proper home in the settings panel
         SetActiveInkColor(color.hex);
 
         SelectionManager.Views().map(view => {
@@ -49,7 +48,7 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
             style={{ transform: `scale(${scaling})`, width: `${100 * scaling}%`, height: `${100 * scaling}%` }} >
 
             <SketchPicker
-                onChange={c => CurrentUserUtils.SelectedTool === InkTool.None && ColorBox.switchColor(c)}
+                onChange={c => CurrentUserUtils.ActiveTool === InkTool.None && ColorBox.switchColor(c)}
                 color={StrCast(SelectionManager.Views()?.[0]?.rootDoc?._backgroundColor, ActiveInkColor())}
                 presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986',
                     '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index e2b37d2b4..4d84a8ad2 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -327,7 +327,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             this._downX = touch.clientX;
             this._downY = touch.clientY;
             if (!e.nativeEvent.cancelBubble) {
-                if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc._lockedPosition && !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) e.stopPropagation();
+                if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc._lockedPosition && !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) e.stopPropagation();
                 this.removeMoveListeners();
                 this.addMoveListeners();
                 this.removeEndListeners();
@@ -341,7 +341,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (e.cancelBubble && this.props.isDocumentActive?.()) {
             this.removeMoveListeners();
         }
-        else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) {
+        else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
             const touch = me.touchEvent.changedTouches.item(0);
             if (touch && (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3)) {
                 if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler)) {
@@ -543,9 +543,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     });
 
     onPointerDown = (e: React.PointerEvent): void => {
-        if (this.rootDoc.type === DocumentType.INK && CurrentUserUtils.SelectedTool === InkTool.Eraser) return;
+        if (this.rootDoc.type === DocumentType.INK && CurrentUserUtils.ActiveTool === InkTool.Eraser) return;
         // continue if the event hasn't been canceled AND we are using a mouse or this has an onClick or onDragStart function (meaning it is a button document)
-        if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool))) {
+        if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool))) {
             if (!InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
                 e.stopPropagation();
                 if (SelectionManager.IsSelected(this.props.DocumentView(), true) && this.props.Document._viewType !== CollectionViewType.Docking) e.preventDefault(); // goldenlayout needs to be able to move its tabs, so can't preventDefault for it
@@ -563,7 +563,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 !this.Document.ignoreClick &&
                 !e.ctrlKey &&
                 (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) &&
-                !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) {
+                !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
                 e.stopPropagation();
                 // don't preventDefault anymore.  Goldenlayout, PDF text selection and RTF text selection all need it to go though
                 //if (this.props.isSelected(true) && this.rootDoc.type !== DocumentType.PDF && this.layoutDoc._viewType !== CollectionViewType.Docking) e.preventDefault();
@@ -579,9 +579,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     onPointerMove = (e: PointerEvent): void => {
         if (e.cancelBubble) return;
-        if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool))) return;
+        if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool))) return;
 
-        if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) {
+        if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
             if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
                 if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) {
                     document.removeEventListener("pointermove", this.onPointerMove);
@@ -697,7 +697,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     @action
     onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => {
-        if (e && this.rootDoc._hideContextMenu && Doc.UserDoc().noviceMode) {
+        if (e && this.rootDoc._hideContextMenu && Doc.noviceMode) {
             e.preventDefault();
             e.stopPropagation();
             //!this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false);
@@ -747,8 +747,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
             const appearance = cm.findByDescription("UI Controls...");
             const appearanceItems: ContextMenuProps[] = appearance && "subitems" in appearance ? appearance.subitems : [];
-            !Doc.UserDoc().noviceMode && templateDoc && appearanceItems.push({ description: "Open Template   ", event: () => this.props.addDocTab(templateDoc, "add:right"), icon: "eye" });
-            !Doc.UserDoc().noviceMode && appearanceItems.push({
+            !Doc.noviceMode && templateDoc && appearanceItems.push({ description: "Open Template   ", event: () => this.props.addDocTab(templateDoc, "add:right"), icon: "eye" });
+            !Doc.noviceMode && appearanceItems.push({
                 description: "Add a Field", event: () => {
                     const alias = Doc.MakeAlias(this.rootDoc);
                     alias.layout = FormattedTextBox.LayoutString("newfield");
@@ -765,7 +765,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             !appearance && cm.addItem({ description: "UI Controls...", subitems: appearanceItems, icon: "compass" });
 
             if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
-                !Doc.UserDoc().noviceMode && appearanceItems.splice(0, 0, { description: `${!this.layoutDoc._showAudio ? "Show" : "Hide"} Audio Button`, event: action(() => this.layoutDoc._showAudio = !this.layoutDoc._showAudio), icon: "microphone" });
+                !Doc.noviceMode && appearanceItems.splice(0, 0, { description: `${!this.layoutDoc._showAudio ? "Show" : "Hide"} Audio Button`, event: action(() => this.layoutDoc._showAudio = !this.layoutDoc._showAudio), icon: "microphone" });
                 const existingOnClick = cm.findByDescription("OnClick...");
                 const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
 
@@ -778,8 +778,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 }
                 !zorders && cm.addItem({ description: "ZOrder...", noexpand: true, subitems: zorderItems, icon: "compass" });
 
-                !Doc.UserDoc().noviceMode && onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
-                !Doc.UserDoc().noviceMode && onClicks.push({ description: "Toggle Detail", event: this.setToggleDetail, icon: "concierge-bell" });
+                !Doc.noviceMode && onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
+                !Doc.noviceMode && onClicks.push({ description: "Toggle Detail", event: this.setToggleDetail, icon: "concierge-bell" });
                 this.props.CollectionFreeFormDocumentView && onClicks.push({ description: (this.Document.followLinkZoom ? "Don't" : "") + " zoom following link", event: () => this.Document.followLinkZoom = !this.Document.followLinkZoom, icon: this.Document.ignoreClick ? "unlock" : "lock" });
 
                 if (!this.Document.annotationOn) {
@@ -803,7 +803,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             }
 
             const funcs: ContextMenuProps[] = [];
-            if (!Doc.UserDoc().noviceMode && this.layoutDoc.onDragStart) {
+            if (!Doc.noviceMode && this.layoutDoc.onDragStart) {
                 funcs.push({ description: "Drag an Alias", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) });
                 funcs.push({ description: "Drag a Copy", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) });
                 funcs.push({ description: "Drag Document", icon: "edit", event: () => this.layoutDoc.onDragStart = undefined });
@@ -813,8 +813,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             const more = cm.findByDescription("More...");
             const moreItems = more && "subitems" in more ? more.subitems : [];
             if (!Doc.IsSystem(this.rootDoc)) {
-                (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.UserDoc().noviceMode) && moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: "users" });
-                if (!Doc.UserDoc().noviceMode) {
+                (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: "users" });
+                if (!Doc.noviceMode) {
                     moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
                     moreItems.push({ description: `${this.Document._chromeHidden ? "Show" : "Hide"} Chrome`, event: () => this.Document._chromeHidden = !this.Document._chromeHidden, icon: "project-diagram" });
 
@@ -835,9 +835,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             const help = cm.findByDescription("Help...");
             const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
             helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
-            !Doc.UserDoc().noviceMode && helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
-            !Doc.UserDoc().noviceMode && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
-            !Doc.UserDoc().noviceMode && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" });
+            !Doc.noviceMode && helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
+            !Doc.noviceMode && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
+            !Doc.noviceMode && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" });
             cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
         }
 
@@ -860,7 +860,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     setContentView = action((view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view);
     isContentActive = (outsideReaction?: boolean) => {
         return this.props.isContentActive() === false ? false : (
-            CurrentUserUtils.SelectedTool !== InkTool.None ||
+            CurrentUserUtils.ActiveTool !== InkTool.None ||
             SnappingManager.GetIsDragging() ||
             this.rootSelected() ||
             this.props.Document.forceActive ||
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 5add09653..17b57cb3b 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -8,7 +8,7 @@ import { List } from "../../../fields/List";
 import { RichTextField } from "../../../fields/RichTextField";
 import { listSpec } from "../../../fields/Schema";
 import { ComputedField, ScriptField } from "../../../fields/ScriptField";
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
+import { Cast, NumCast, StrCast, DocCast } from "../../../fields/Types";
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../../Utils";
 import { Docs } from "../../documents/Documents";
 import { DocumentType } from "../../documents/DocumentTypes";
@@ -84,7 +84,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return "data";
     }
     @computed static get targetDocChildren() {
-        return DocListCast(FilterBox.targetDoc?.[FilterBox.targetDocChildKey] || CurrentUserUtils.ActiveDashboard.data);
+        return DocListCast(FilterBox.targetDoc?.[FilterBox.targetDocChildKey] || CurrentUserUtils.ActiveDashboard?.data);
     }
 
     @observable _loaded = false;
@@ -115,7 +115,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
         const keys = new Set<string>(noviceFields);
         this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key)));
-        return Array.from(keys.keys()).filter(key => key[0]).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_")) || noviceFields.includes(key) || !Doc.UserDoc().noviceMode).sort();
+        return Array.from(keys.keys()).filter(key => key[0]).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_")) || noviceFields.includes(key) || !Doc.noviceMode).sort();
     }
 
 
@@ -167,23 +167,25 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
     removeFilterDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).map(doc => this.removeFilter(StrCast(doc.title))).length ? true : false;
     public removeFilter = (filterName: string) => {
         const targetDoc = FilterBox.targetDoc;
-        const filterDoc = targetDoc.currentFilter as Doc;
-        const attributes = DocListCast(filterDoc.data);
-        const found = attributes.findIndex(doc => doc.title === filterName);
-        if (found !== -1) {
-            (filterDoc.data as List<Doc>).splice(found, 1);
-            const docFilter = Cast(targetDoc._docFilters, listSpec("string"));
-            if (docFilter) {
-                let index: number;
-                while ((index = docFilter.findIndex(item => item.split(":")[0] === filterName)) !== -1) {
-                    docFilter.splice(index, 1);
+        if (targetDoc) {
+            const filterDoc = targetDoc.currentFilter as Doc;
+            const attributes = DocListCast(filterDoc.data);
+            const found = attributes.findIndex(doc => doc.title === filterName);
+            if (found !== -1) {
+                (filterDoc.data as List<Doc>).splice(found, 1);
+                const docFilter = Cast(targetDoc._docFilters, listSpec("string"));
+                if (docFilter) {
+                    let index: number;
+                    while ((index = docFilter.findIndex(item => item.split(":")[0] === filterName)) !== -1) {
+                        docFilter.splice(index, 1);
+                    }
                 }
-            }
-            const docRangeFilters = Cast(targetDoc._docRangeFilters, listSpec("string"));
-            if (docRangeFilters) {
-                let index: number;
-                while ((index = docRangeFilters.findIndex(item => item.split(":")[0] === filterName)) !== -1) {
-                    docRangeFilters.splice(index, 3);
+                const docRangeFilters = Cast(targetDoc._docRangeFilters, listSpec("string"));
+                if (docRangeFilters) {
+                    let index: number;
+                    while ((index = docRangeFilters.findIndex(item => item.split(":")[0] === filterName)) !== -1) {
+                        docRangeFilters.splice(index, 3);
+                    }
                 }
             }
         }
@@ -194,6 +196,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     facetClick = (facetHeader: string) => {
         const { targetDoc, targetDocChildren } = FilterBox;
+        if (!targetDoc) return;
         const found = this.activeAttributes.findIndex(doc => doc.title === facetHeader);
         if (found !== -1) {
             this.removeFilter(facetHeader);
@@ -267,7 +270,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     @action
     changeBool = (e: any) => {
-        (FilterBox.targetDoc.currentFilter as Doc).filterBoolean = e.currentTarget.value;
+        FilterBox.targetDoc && (DocCast(FilterBox.targetDoc.currentFilter).filterBoolean = e.currentTarget.value);
     }
 
     /**
@@ -322,7 +325,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
      * Changes the title of the filterDoc
      */
     onTitleValueChange = (val: string) => {
-        this.props.Document.title = val || `FilterDoc for ${FilterBox.targetDoc.title}`;
+        this.props.Document.title = val || `FilterDoc for ${FilterBox.targetDoc?.title}`;
         return true;
     }
 
@@ -373,7 +376,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
             </div>
 
             <div className="filterBox-select-bool">
-                <select className="filterBox-selection" onChange={this.changeBool} defaultValue={StrCast((FilterBox.targetDoc.currentFilter as Doc)?.filterBoolean)}>
+                <select className="filterBox-selection" onChange={this.changeBool} defaultValue={StrCast((FilterBox.targetDoc?.currentFilter as Doc)?.filterBoolean)}>
                     <option value="AND" key="AND">AND</option>
                     <option value="OR" key="OR">OR</option>
                 </select>
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 8c27b3508..60eb48114 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -179,7 +179,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             funcs.push({ description: "Rotate Clockwise 90", event: this.rotate, icon: "expand-arrows-alt" });
             funcs.push({ description: `Show ${this.layoutDoc._showFullRes ? "Dynamic Res" : "Full Res"}`, event: this.resolution, icon: "expand-arrows-alt" });
             funcs.push({ description: "Copy path", event: () => Utils.CopyText(this.choosePath(field.url)), icon: "expand-arrows-alt" });
-            if (!Doc.UserDoc().noviceMode) {
+            if (!Doc.noviceMode) {
                 funcs.push({ description: "Export to Google Photos", event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" });
 
                 const existingAnalyze = ContextMenu.Instance?.findByDescription("Analyzers...");
@@ -357,7 +357,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return <div className="imageBox-annotationLayer" style={{ height: this.props.PanelHeight() }} ref={this._annotationLayer} />;
     }
     marqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._viewScale,1) <= NumCast(this.rootDoc.viewScaleMin,1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._viewScale,1) <= NumCast(this.rootDoc.viewScaleMin,1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
             setupMoveUpEvents(this, e, action(e => {
                 MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                 this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index b0b050cea..b58a9affb 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -52,7 +52,7 @@ export class LabelBox extends ViewBoxBaseComponent<(FieldViewProps & LabelBoxPro
     get paramsDoc() { return Doc.AreProtosEqual(this.layoutDoc, this.dataDoc) ? this.dataDoc : this.layoutDoc; }
     specificContextMenu = (e: React.MouseEvent): void => {
         const funcs: ContextMenuProps[] = [];
-        !Doc.UserDoc().noviceMode && funcs.push({
+        !Doc.noviceMode && funcs.push({
             description: "Clear Script Params", event: () => {
                 const params = Cast(this.paramsDoc["onClick-paramFieldKeys"], listSpec("string"), []);
                 params?.map(p => this.paramsDoc[p] = undefined);
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 5f4c17ee6..0b7264d79 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -471,7 +471,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     onMarqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
             setupMoveUpEvents(this, e, action(e => {
                 MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                 this._marqueeing = [e.clientX, e.clientY];
@@ -570,7 +570,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         //     moveDocument={this.moveDocument}
         //     addDocument={this.sidebarAddDocument}
         //     childPointerEvents={true}
-        //     pointerEvents={CurrentUserUtils.SelectedTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
+        //     pointerEvents={CurrentUserUtils.ActiveTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
         return <div className="mapBox" ref={this._ref}>
             {/*console.log(apiKey)*/}
             {/* <LoadScript
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index b14a1f0f6..ddfbf50df 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -428,7 +428,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
       // renders the video and audio
       @computed get content() {
             const field = Cast(this.dataDoc[this.fieldKey], VideoField);
-            const interactive = CurrentUserUtils.SelectedTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive";
+            const interactive = CurrentUserUtils.ActiveTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive";
             const classname = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
             return !field ? <div key="loading">Loading</div> :
                   <div className="videoBox-contentContainer" key="container" style={{ mixBlendMode: "multiply" }}>
@@ -484,7 +484,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                   this._disposers.youtubeReactionDisposer?.();
                   this._disposers.reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek(NumCast(this.layoutDoc._currentTimecode)));
                   this._disposers.youtubeReactionDisposer = reaction(
-                        () => CurrentUserUtils.SelectedTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
+                        () => CurrentUserUtils.ActiveTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
                         (interactive) => iframe.style.pointerEvents = interactive ? "all" : "none", { fireImmediately: true });
             };
             if (typeof (YT) === undefined) setTimeout(() => this.loadYouTube(iframe), 100);
@@ -707,7 +707,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
       // starts marquee selection
       marqueeDown = (e: React.PointerEvent) => {
-            if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+            if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.ActiveTool)) {
                   setupMoveUpEvents(this, e, action(e => {
                         MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                         this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 967158cbf..d14af49ea 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -536,7 +536,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const cm = ContextMenu.Instance;
         const funcs: ContextMenuProps[] = [];
         if (!cm.findByDescription("Options...")) {
-            !Doc.UserDoc().noviceMode && funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" });
+            !Doc.noviceMode && funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" });
             funcs.push({
                 description: (this.layoutDoc.allowScripts ? "Prevent" : "Allow") + " Scripts", event: () => {
                     this.layoutDoc.allowScripts = !this.layoutDoc.allowScripts;
@@ -561,7 +561,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     onMarqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
             setupMoveUpEvents(this, e, action(e => {
                 MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                 this._marqueeing = [e.clientX, e.clientY];
@@ -678,7 +678,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth))
 
     @computed get content() {
-        const interactive = !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance?.Interacting;
+        const interactive = !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && CurrentUserUtils.ActiveTool === InkTool.None && !DocumentDecorations.Instance?.Interacting;
         return <div className={"webBox-cont" + (interactive ? "-interactive" : "")}
             onKeyDown={e => e.stopPropagation()}
             style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) || `100%` : "100%", }}>
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index f29dfe489..3af6a3d51 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -1,6 +1,7 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
+import { StringIterator } from 'lodash';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -70,7 +71,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
     useAsPrototype = (): void => { this.layoutDoc.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)'); };
 
     specificContextMenu = (): void => {
-        if (!Doc.UserDoc().noviceMode) {
+        if (!Doc.noviceMode) {
             const cm = ContextMenu.Instance;
             cm.addItem({ description: "Show Template", event: this.showTemplate, icon: "tag" });
             cm.addItem({ description: "Use as Render Template", event: this.dragAsTemplate, icon: "tag" });
@@ -269,7 +270,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
 
         // Get items to place into the list
         const list = this.buttonList.map((value) => {
-            if (Doc.UserDoc().noviceMode && !noviceList.includes(value)) {
+            if (Doc.noviceMode && !noviceList.includes(value)) {
                 return;
             }
             return <div className="list-item" key={`${value}`}
@@ -567,8 +568,8 @@ ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boole
 // toggle: Set overlay status of selected document
 ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
     const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
-    if (checkResult && selected) {
-        if (NumCast(selected.Document.z) >= 1) return Colors.MEDIUM_BLUE;
+    if (checkResult) {
+        if (NumCast(selected?.Document.z) >= 1) return Colors.MEDIUM_BLUE;
         return "transparent";
     }
     selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("[FontIconBox.tsx] toggleOverlay failed");
@@ -710,7 +711,7 @@ ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
 
 export function checkInksToGroup() {
     // console.log("getting here to inks group");
-    if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+    if (CurrentUserUtils.ActiveTool === InkTool.Write) {
         CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
             // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
             // find all inkDocs in ffView.unprocessedDocs that are within 200 pixels of each other
@@ -723,7 +724,7 @@ export function checkInksToGroup() {
 
 export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
     // TODO nda - if document being added to is a inkGrouping then we can just add to that group
-    if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+    if (CurrentUserUtils.ActiveTool === InkTool.Write) {
         CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
             // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
             const selected = ffView.unprocessedDocs;
@@ -785,38 +786,38 @@ export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
 
 
 /** INK
- *      setActiveInkTool
+ *      setActiveTool
  *      setStrokeWidth
  *      setStrokeColor
  **/
 
-ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boolean) {
+ScriptingGlobals.add(function setActiveTool(tool: string, checkResult?: boolean) {
     InkTranscription.Instance?.createInkGroup();
     if (checkResult) {
-        return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ?
+        return ((CurrentUserUtils.ActiveTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ?
             Colors.MEDIUM_BLUE : "transparent";
     }
     if (["circle", "square", "line"].includes(tool)) {
         if (GestureOverlay.Instance.InkShape === tool) {
-            Doc.UserDoc().activeInkTool = InkTool.None;
+            CurrentUserUtils.ActiveTool = InkTool.None;
             GestureOverlay.Instance.InkShape = InkTool.None;
         } else {
-            Doc.UserDoc().activeInkTool = InkTool.Pen;
+            CurrentUserUtils.ActiveTool = InkTool.Pen;
             GestureOverlay.Instance.InkShape = tool;
         }
     } else if (tool) { // pen or eraser
-        if (Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance.InkShape) {
-            Doc.UserDoc().activeInkTool = InkTool.None;
-        } else if (tool == "write") {
+        if (CurrentUserUtils.ActiveTool === tool && !GestureOverlay.Instance.InkShape) {
+            CurrentUserUtils.ActiveTool = InkTool.None;
+        } else if (tool == InkTool.Write) {
             // console.log("write mode selected - create groupDoc here!", tool)
-            Doc.UserDoc().activeInkTool = tool;
+            CurrentUserUtils.ActiveTool = tool;
             GestureOverlay.Instance.InkShape = "";
         } else {
-            Doc.UserDoc().activeInkTool = tool;
+            CurrentUserUtils.ActiveTool = tool as any;
             GestureOverlay.Instance.InkShape = "";
         }
     } else {
-        Doc.UserDoc().activeInkTool = InkTool.None;
+        CurrentUserUtils.ActiveTool = InkTool.None;
     }
 });
 
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 16a523b40..90199618b 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -648,7 +648,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const highlighting: ContextMenuProps[] = [];
         const noviceHighlighting = ["Audio Tags", "My Text", "Text from Others", "Bold Text"];
         const expertHighlighting = [...noviceHighlighting, "Important Items", "Ignore Items", "Disagree Items", "By Recent Minute", "By Recent Hour"];
-        (Doc.UserDoc().noviceMode ? noviceHighlighting : expertHighlighting).forEach(option =>
+        (Doc.noviceMode ? noviceHighlighting : expertHighlighting).forEach(option =>
             highlighting.push({
                 description: (FormattedTextBox._globalHighlights.indexOf(option) === -1 ? "Highlight " : "Unhighlight ") + option, event: () => {
                     e.stopPropagation();
@@ -663,11 +663,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }));
 
         const uicontrols: ContextMenuProps[] = [];
-        !Doc.UserDoc().noviceMode && uicontrols.push({ description: `${FormattedTextBox._canAnnotate ? "Don't" : ""} Show Menu on Selections`, event: () => FormattedTextBox._canAnnotate = !FormattedTextBox._canAnnotate, icon: "expand-arrows-alt" });
+        !Doc.noviceMode && uicontrols.push({ description: `${FormattedTextBox._canAnnotate ? "Don't" : ""} Show Menu on Selections`, event: () => FormattedTextBox._canAnnotate = !FormattedTextBox._canAnnotate, icon: "expand-arrows-alt" });
         uicontrols.push({ description: !this.Document._noSidebar ? "Hide Sidebar Handle" : "Show Sidebar Handle", event: () => this.layoutDoc._noSidebar = !this.layoutDoc._noSidebar, icon: "expand-arrows-alt" });
         uicontrols.push({ description: `${this.layoutDoc._showAudio ? "Hide" : "Show"} Dictation Icon`, event: () => this.layoutDoc._showAudio = !this.layoutDoc._showAudio, icon: "expand-arrows-alt" });
         uicontrols.push({ description: "Show Highlights...", noexpand: true, subitems: highlighting, icon: "hand-point-right" });
-        !Doc.UserDoc().noviceMode && uicontrols.push({
+        !Doc.noviceMode && uicontrols.push({
             description: "Broadcast Message", event: () => DocServer.GetRefField("rtfProto").then(proto =>
                 proto instanceof Doc && (proto.BROADCAST_MESSAGE = Cast(this.rootDoc[this.fieldKey], RichTextField)?.Text)), icon: "expand-arrows-alt"
         });
@@ -677,7 +677,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
         appearanceItems.push({ description: "Change Perspective...", noexpand: true, subitems: changeItems, icon: "external-link-alt" });
         // this.rootDoc.isTemplateDoc && appearanceItems.push({ description: "Make Default Layout", event: async () => Doc.UserDoc().defaultTextLayout = new PrefetchProxy(this.rootDoc), icon: "eye" });
-        !Doc.UserDoc().noviceMode && appearanceItems.push({
+        !Doc.noviceMode && appearanceItems.push({
             description: "Make Default Layout", event: () => {
                 if (!this.layoutDoc.isTemplateDoc) {
                     const title = StrCast(this.rootDoc.title);
@@ -1636,7 +1636,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     noSidebar={true}
                     fieldKey={this.layoutDoc.sidebarViewType === "translation" ? `${this.fieldKey}-translation` : `${this.fieldKey}-annotations`} />;
         };
-        return <div className={"formattedTextBox-sidebar" + (CurrentUserUtils.SelectedTool !== InkTool.None ? "-inking" : "")}
+        return <div className={"formattedTextBox-sidebar" + (CurrentUserUtils.ActiveTool !== InkTool.None ? "-inking" : "")}
             style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
             {renderComponent(StrCast(this.layoutDoc.sidebarViewType))}
         </div>;
@@ -1647,7 +1647,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         const active = this.props.isContentActive();
         const scale = (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
         const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : "";
-        const interactive = (CurrentUserUtils.SelectedTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition);
+        const interactive = (CurrentUserUtils.ActiveTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition);
         if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
         const minimal = this.props.ignoreAutoHeight;
         const paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0);
diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx
index 9bc2e5628..98343a261 100644
--- a/src/client/views/nodes/formattedText/RichTextMenu.tsx
+++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx
@@ -67,7 +67,6 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps>   {
         runInAction(() => {
             RichTextMenu.Instance = this;
             this._canFade = false;
-            //this.Pinned = BoolCast(Doc.UserDoc()["menuRichText-pinned"]);
             this.Pinned = true;
         });
     }
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 52b03b0a5..0d2cffc2c 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -134,7 +134,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get presElement() { return Cast(Doc.UserDoc().presElement, Doc, null); }
     constructor(props: any) {
         super(props);
-        if (Doc.UserDoc().activePresentation = this.rootDoc) runInAction(() => PresBox.Instance = this);
+        if (CurrentUserUtils.ActivePresentation = this.rootDoc) runInAction(() => PresBox.Instance = this);
         if (!this.presElement) { // create exactly one presElmentBox template to use by any and all presentations.
             Doc.UserDoc().presElement = new PrefetchProxy(Docs.Create.PresElementBoxDocument({
                 title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"
@@ -178,16 +178,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.layoutDoc._gridGap = 0;
         this.layoutDoc._yMargin = 0;
         this.turnOffEdit(true);
-        DocListCastAsync((Doc.UserDoc().myTrails as Doc).data).then(pres =>
-            !pres?.includes(this.rootDoc) && Doc.AddDocToList(Doc.UserDoc().myTrails as Doc, "data", this.rootDoc));
+        DocListCastAsync(CurrentUserUtils.MyTrails.data).then(pres =>
+            !pres?.includes(this.rootDoc) && Doc.AddDocToList(CurrentUserUtils.MyTrails, "data", this.rootDoc));
         this._disposers.selection = reaction(() => SelectionManager.Views(),
             views => views.some(view => view.props.Document === this.rootDoc) && this.updateCurrentPresentation());
     }
 
     @action
     updateCurrentPresentation = (pres?: Doc) => {
-        if (pres) Doc.UserDoc().activePresentation = pres;
-        else Doc.UserDoc().activePresentation = this.rootDoc;
+        if (pres) CurrentUserUtils.ActivePresentation = pres;
+        else CurrentUserUtils.ActivePresentation = this.rootDoc;
         document.removeEventListener("keydown", PresBox.keyEventsWrapper, true);
         document.addEventListener("keydown", PresBox.keyEventsWrapper, true);
         this._presKeyEventsActive = true;
@@ -623,9 +623,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     @action
     updateMinimize = async () => {
-        if (CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) {
+        if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
             this.layoutDoc.presStatus = PresStatus.Edit;
-            Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
+            Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, this.rootDoc);
             CollectionDockingView.AddSplit(this.rootDoc, "right");
         } else {
             this.layoutDoc.presStatus = PresStatus.Edit;
@@ -635,7 +635,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.rootDoc.y = pt[1] + 10;
             this.rootDoc._height = 30;
             this.rootDoc._width = 248;
-            Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, this.rootDoc);
+            Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, this.rootDoc);
             this.props.removeDocument?.(this.layoutDoc);
         }
     }
@@ -732,7 +732,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc);
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
-    isContentActive = (outsideReaction?: boolean) => ((CurrentUserUtils.SelectedTool === InkTool.None && !this.layoutDoc._lockedPosition) &&
+    isContentActive = (outsideReaction?: boolean) => ((CurrentUserUtils.ActiveTool === InkTool.None && !this.layoutDoc._lockedPosition) &&
         (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
 
     /**
@@ -868,7 +868,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 }
                 break;
             case "Escape":
-                if (CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc)) { this.updateMinimize(); }
+                if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) { this.updateMinimize(); }
                 else if (this.layoutDoc.presStatus === "edit") { this._selectedArray.clear(); this._eleArray.length = this._dragArray.length = 0; }
                 else this.layoutDoc.presStatus = "edit";
                 if (this._presTimer) clearTimeout(this._presTimer);
@@ -2247,7 +2247,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const propTitle = CurrentUserUtils.propertiesWidth > 0 ? "Close Presentation Panel" : "Open Presentation Panel";
         const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
         const isMini: boolean = this.toolbarWidth <= 100;
-        const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.UserDoc().activePresentation);
+        const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === CurrentUserUtils.ActivePresentation);
         const activeColor = Colors.LIGHT_BLUE;
         const inactiveColor = Colors.WHITE;
         return (mode === CollectionViewType.Carousel3D) ? (null) : (
@@ -2305,7 +2305,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     value={mode}>
                     <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Stacking}>List</option>
                     <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Tree}>Tree</option>
-                    {Doc.UserDoc().noviceMode ? (null) : <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel3D}>3D Carousel</option>}
+                    {Doc.noviceMode ? (null) : <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel3D}>3D Carousel</option>}
                 </select>}
                 <div className="presBox-presentPanel" style={{ opacity: this.childDocs.length ? 1 : 0.3 }}>
                     <span className={`presBox-button ${this.layoutDoc.presStatus === "edit" ? "present" : ""}`}>
@@ -2508,10 +2508,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         // needed to ensure that the childDocs are loaded for looking up fields
         this.childDocs.slice();
         const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
-        const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.UserDoc().activePresentation);
+        const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === CurrentUserUtils.ActivePresentation);
         const presEnd: boolean = !this.layoutDoc.presLoop && (this.itemIndex === this.childDocs.length - 1);
         const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0);
-        return CurrentUserUtils.MyOverlayDocs.includes(this.rootDoc) ?
+        return DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.rootDoc) ?
             <div className="miniPres" onClick={e => e.stopPropagation()}>
                 <div className="presPanelOverlay" style={{ display: "inline-flex", height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}>
                     <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : undefined }}
@@ -2536,7 +2536,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 </div>
             </div >
             :
-            <div className="presBox-cont" style={{ minWidth: CurrentUserUtils.MyOverlayDocs.includes(this.layoutDoc) ? 240 : undefined }} >
+            <div className="presBox-cont" style={{ minWidth: DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc) ? 240 : undefined }} >
                 {this.topPanel}
                 {this.toolbar}
                 {this.newDocumentToolbarDropdown}
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 50df00612..1a2f4b93f 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -309,7 +309,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
       @computed get recordingIsInOverlay() {
             let isInOverlay = false
-            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
+            DocListCast(CurrentUserUtils.MyOverlayDocs.data).forEach((doc) => {
                   if (doc.slides === this.rootDoc) {
                         isInOverlay = true
                         return
@@ -319,9 +319,9 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
       }
 
       removeAllRecordingInOverlay = () => {
-            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
+            DocListCast(CurrentUserUtils.MyOverlayDocs.data).forEach((doc) => {
                   if (doc.slides === this.rootDoc) {
-                        Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
+                        Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, doc);
                   }
             })
       }
@@ -339,7 +339,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.removeAllRecordingInOverlay()
             if (activeItem.recording) {
                   // if we already have an existing recording
-                  Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, Cast(activeItem.recording, Doc, null));
+                  Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, Cast(activeItem.recording, Doc, null));
 
             }
       }
@@ -348,15 +348,15 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
       @action
       startRecording = (activeItem: Doc) => {
             // Remove every recording that already exists in overlay view
-            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
+            DocListCast(CurrentUserUtils.MyOverlayDocs.data).forEach((doc) => {
                   if (doc.slides !== null) {
-                        Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
+                        Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, doc);
                   }
             })
 
             if (activeItem.recording) {
                   // if we already have an existing recording
-                  Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, Cast(activeItem.recording, Doc, null));
+                  Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, Cast(activeItem.recording, Doc, null));
 
             } else {
                   // if we dont have any recording
@@ -376,7 +376,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                   // make recording box appear in the bottom right corner of the screen
                   recording.x = window.innerWidth - recording[WidthSym]() - 20;
                   recording.y = window.innerHeight - recording[HeightSym]() - 20;
-                  Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, recording);
+                  Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, recording);
             }
       }
 
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 0ded1bb3c..2869d4f2d 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -340,7 +340,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         if ((e.button !== 0 || e.altKey) && this.props.isContentActive(true)) {
             this._setPreviewCursor?.(e.clientX, e.clientY, true, false);
         }
-        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
             this.props.select(false);
             MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
             this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 0db3950a2..1de0c0b10 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -27,12 +27,12 @@ import "./TopBar.scss";
 export class TopBar extends React.Component {
       navigateToHome = () => {
             CurrentUserUtils.CaptureDashboardThumbnail()?.then(() => {
-                Doc.UserDoc().activePage = "home";
+                CurrentUserUtils.ActivePage = "home";
                 CurrentUserUtils.closeActiveDashboard(); // bcz: if we do this, we need some other way to keep track, for user convenience, of the last dashboard in use
             });
       }
       render() {
-            const activeDashboard = Cast(Doc.UserDoc().activeDashboard, Doc, null)
+            const activeDashboard = CurrentUserUtils.ActiveDashboard;
             return (
                   //TODO:glr Add support for light / dark mode
                   <div style={{ pointerEvents: "all", background: Colors.DARK_GRAY, borderBottom: Borders.STANDARD }} className="topbar-container">
@@ -44,11 +44,11 @@ export class TopBar extends React.Component {
                                     }}>{Doc.CurrentUserEmail}</div> : (null)}
                               </div>
                               <div className="topbar-center" >
-                                    <div className="topbar-title" onClick={() => SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(activeDashboard)!, false)}>
+                                    <div className="topbar-title" onClick={() => activeDashboard && SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(activeDashboard)!, false)}>
                                           {activeDashboard ? StrCast(activeDashboard.title) : "Dash"}
                                     </div>
                                     <div className="topbar-button-icon" onClick={e => {
-                                        const dashView = DocumentManager.Instance.getDocumentView(activeDashboard);
+                                        const dashView = activeDashboard && DocumentManager.Instance.getDocumentView(activeDashboard);
                                         ContextMenu.Instance.addItem({ description: "Open Dashboard View", event: this.navigateToHome, icon: "edit" });
                                         ContextMenu.Instance.addItem({ description: "Snapshot Dashboard", event: async () => {
                                             const batch = UndoManager.StartBatch("snapshot");
@@ -69,7 +69,7 @@ export class TopBar extends React.Component {
                                     <div className="topbar-button-text" onClick={() => {SharingManager.Instance.open(undefined, activeDashboard)}}>
                                         {/* TODO: if this is my dashboard, display share
                                         if this is a shared dashboard, display "view original or view annotated" */}
-                                        { Doc.GetProto(CurrentUserUtils.ActiveDashboard)?.author ===  Doc.CurrentUserEmail ?  "Share": "view original" }
+                                        { CurrentUserUtils.ActiveDashboard && (Doc.GetProto(CurrentUserUtils.ActiveDashboard)?.author ===  Doc.CurrentUserEmail ?  "Share": "view original") }
                                     </div>
                                     <div className="topbar-button-icon" onClick={() => window.open(
                                           "https://brown-dash.github.io/Dash-Documentation/", "_blank")}>
diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx
index e0d328c89..6c0c9b301 100644
--- a/src/client/views/webcam/DashWebRTCVideo.tsx
+++ b/src/client/views/webcam/DashWebRTCVideo.tsx
@@ -71,7 +71,7 @@ export class DashWebRTCVideo extends React.Component<CollectionFreeFormDocumentV
             </div >;
 
         const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
-        const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.SelectedTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
+        const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.ActiveTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
 
         return (
             <>
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 94be286f5..74213652b 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -21,7 +21,7 @@ import { FieldId, RefField } from "./RefField";
 import { RichTextField } from "./RichTextField";
 import { listSpec } from "./Schema";
 import { ComputedField, ScriptField } from "./ScriptField";
-import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types";
+import { BoolCast, Cast, FieldValue, NumCast, StrCast, ToConstructor } from "./Types";
 import { AudioField, ImageField, MapField, PdfField, VideoField, WebField } from "./URLField";
 import { deleteProperty, GetEffectiveAcl, getField, getter, inheritParentAcls, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from "./util";
 import JSZip = require("jszip");
@@ -233,6 +233,10 @@ export class Doc extends RefField {
     }
 
     private [CachedUpdates]: { [key: string]: () => void | Promise<any> } = {};
+    public static get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode); }
+    public static set noviceMode(val) { Doc.UserDoc().noviceMode = val; }
+    public static get defaultAclPrivate() { return Doc.UserDoc().defaultAclPrivate; }
+    public static set defaultAclPrivate(val) { Doc.UserDoc().defaultAclPrivate = val; }
     public static CurrentUserEmail: string = "";
     public static get CurrentUserEmailNormalized() { return normalizeEmail(Doc.CurrentUserEmail); }
     public async [HandleUpdate](diff: any) {
@@ -805,7 +809,7 @@ export namespace Doc {
             Doc.AddDocToList(Doc.GetProto(copy)[DataSym], "aliases", copy);
         }
         copy.context = undefined;
-        Doc.UserDoc().defaultAclPrivate && (copy["acl-Public"] = "Not Shared");
+        Doc.defaultAclPrivate && (copy["acl-Public"] = "Not Shared");
         if (retitle) {
             copy.title = incrementTitleCopy(StrCast(copy.title));
         }
@@ -861,7 +865,7 @@ export namespace Doc {
             const applied = ApplyTemplateTo(templateDoc, target, targetKey, templateDoc.title + "(..." + _applyCount++ + ")");
             target.layoutKey = targetKey;
             applied && (Doc.GetProto(applied).type = templateDoc.type);
-            Doc.UserDoc().defaultAclPrivate && (applied["acl-Public"] = "Not Shared");
+            Doc.defaultAclPrivate && (applied["acl-Public"] = "Not Shared");
             return applied;
         }
         return undefined;
@@ -1234,19 +1238,19 @@ export namespace Doc {
 
     export function isDocPinned(doc: Doc) {
         //add this new doc to props.Document
-        const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
+        const curPres = CurrentUserUtils.ActivePresentation;
         return !curPres ? false : DocListCast(curPres.data).findIndex((val) => Doc.AreProtosEqual(val, doc)) !== -1;
     }
 
     export function copyDragFactory(dragFactory: Doc) {
         const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true);
-        ndoc && Doc.AddDocToList(Cast(Doc.UserDoc().myFileOrphans, Doc, null), "data", Doc.GetProto(ndoc));
+        ndoc && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, "data", Doc.GetProto(ndoc));
         if (ndoc && dragFactory["dragFactory-count"] !== undefined) {
             dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1;
             Doc.SetInPlace(ndoc, "title", ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(), true);
         }
 
-        if (ndoc) inheritParentAcls(CurrentUserUtils.ActiveDashboard, ndoc);
+        if (ndoc && CurrentUserUtils.ActiveDashboard) inheritParentAcls(CurrentUserUtils.ActiveDashboard, ndoc);
 
         return ndoc;
     }
@@ -1448,7 +1452,7 @@ ScriptingGlobals.add(function DOC(id: string) { console.log("Can't parse a docum
 ScriptingGlobals.add(function assignDoc(doc: Doc, field: string, id: string) { return Doc.assignDocToField(doc, field, id); });
 ScriptingGlobals.add(function docCast(doc: FieldResult): any { return DocCastAsync(doc); });
 ScriptingGlobals.add(function activePresentationItem() {
-    const curPres = Doc.UserDoc().activePresentation as Doc;
+    const curPres = CurrentUserUtils.ActivePresentation;
     return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)];
 });
 ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 37b9be31b..8fb35981b 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -145,7 +145,7 @@ export function inheritParentAcls(parent: Doc, child: Doc) {
     const dataDoc = parent[DataSym];
     for (const key of Object.keys(dataDoc)) {
         // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
-        const permission = (key === "acl-Public" && Doc.UserDoc().defaultAclPrivate) ? AclPrivate : dataDoc[key];
+        const permission = (key === "acl-Public" && Doc.defaultAclPrivate) ? AclPrivate : dataDoc[key];
         key.startsWith("acl") && distributeAcls(key, permission, child);
     }
 }
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index c5c6fb688..fe8100997 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -51,7 +51,7 @@ library.add(...[faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, fa
 @observer
 export class MobileInterface extends React.Component {
     static Instance: MobileInterface;
-    private _library: Promise<Doc>;
+    private _library: Doc;
     private _mainDoc: any = CurrentUserUtils.setupActiveMobileMenu(Doc.UserDoc());
     @observable private _sidebarActive: boolean = false; //to toggle sidebar display
     @observable private _imageUploadActive: boolean = false; //to toggle image upload
@@ -68,7 +68,7 @@ export class MobileInterface extends React.Component {
 
     constructor(props: Readonly<{}>) {
         super(props);
-        this._library = CurrentUserUtils.setupDashboards(Doc.UserDoc()); // to access documents in Dash Web
+        this._library = CurrentUserUtils.setupDashboards(Doc.UserDoc(), "myDashboards"); // to access documents in Dash Web
         MobileInterface.Instance = this;
     }
 
@@ -76,7 +76,7 @@ export class MobileInterface extends React.Component {
     componentDidMount = () => {
         // if the home menu is in list view -> adjust the menu toggle appropriately
         this._menuListView = this._homeDoc._viewType === "stacking" ? true : false;
-        CurrentUserUtils.SelectedTool = InkTool.None; // ink should intially be set to none
+        CurrentUserUtils.ActiveTool = InkTool.None; // ink should intially be set to none
         Doc.UserDoc().activeMobile = this._homeDoc; // active mobile set to home
         AudioBox.Enabled = true;
 
@@ -125,7 +125,7 @@ export class MobileInterface extends React.Component {
      * Method called when 'Library' button is pressed on the home screen
      */
     switchToLibrary = async () => {
-        this._library.then(library => this.switchCurrentView(library));
+        this.switchCurrentView(this._library);
         runInAction(() => this._homeMenu = false);
         this.toggleSidebar();
     }
@@ -140,7 +140,7 @@ export class MobileInterface extends React.Component {
         // Case 1: Parent document is 'dashboards'
         if (doc === Cast(this._library, Doc) as Doc) {
             this.dashboards = null;
-            this._library.then(library => this.switchCurrentView(library));
+            this.switchCurrentView(this._library);
             // Case 2: Parent document is the 'home' menu (root node)
         } else if (doc === Cast(this._homeDoc, Doc) as Doc) {
             this._homeMenu = true;
@@ -179,7 +179,7 @@ export class MobileInterface extends React.Component {
     @action
     returnMain = () => {
         this._parents = [this._homeDoc];
-        this._library.then(library => this.switchCurrentView(library));
+        this.switchCurrentView(this._library);
         this._homeMenu = false;
         this.dashboards = null;
     }
@@ -419,10 +419,10 @@ export class MobileInterface extends React.Component {
         button.style.color = this._ink ? "black" : "white";
 
         if (!this._ink) {
-            CurrentUserUtils.SelectedTool = InkTool.Pen;
+            CurrentUserUtils.ActiveTool = InkTool.Pen;
             this._ink = true;
         } else {
-            CurrentUserUtils.SelectedTool = InkTool.None;
+            CurrentUserUtils.ActiveTool = InkTool.None;
             this._ink = false;
         }
     }
@@ -514,7 +514,7 @@ export class MobileInterface extends React.Component {
             return <div className="docButton"
                 title={Doc.isDocPinned(this._activeDoc) ? "Unpin from presentation" : "Pin to presentation"}
                 style={{ backgroundColor: isPinned ? "black" : "white", color: isPinned ? "white" : "black" }}
-                onClick={e => TabDocView.PinDoc(this._activeDoc, { unpin: isPinned })}>
+                onClick={e => TabDocView.PinDoc(this._activeDoc)}>
                 <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
             </div>;
         } else return (null);
@@ -573,7 +573,7 @@ export class MobileInterface extends React.Component {
     // For setting up the presentation document for the home menu
     @action
     setupDefaultPresentation = () => {
-        const presentation = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
+        const presentation = CurrentUserUtils.ActivePresentation;
 
         if (presentation) {
             this.switchCurrentView(presentation);
-- 
cgit v1.2.3-70-g09d2


From 3415f672292bb349c7d9ec66564933a746ee3b25 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Mon, 20 Jun 2022 18:27:29 -0400
Subject: Revert "Merge branch 'master' into temporalmedia-mehek"

This reverts commit 145117365b2708ef6b365c6f0f10c38b85a87307, reversing
changes made to 7eedde332010c8896be636f0b5c6a7b2c8043e48.
---
 .eslintrc.json                                     |   13 -
 .prettierrc.json                                   |    6 -
 .vscode/settings.json                              |    2 +-
 package-lock.json                                  | 7245 +++++++++++---------
 package.json                                       |    3 -
 src/Utils.ts                                       |   12 +-
 src/client/Network.ts                              |   74 +-
 src/client/documents/DocumentTypes.ts              |    1 -
 src/client/documents/Documents.ts                  |   47 +-
 src/client/goldenLayout.js                         |    7 +-
 src/client/util/CurrentUserUtils.ts                |  134 +-
 src/client/util/DragManager.ts                     |    2 +-
 src/client/util/History.ts                         |    6 +-
 src/client/util/RecordingApi.ts                    |  269 -
 src/client/util/Scripting.ts                       |    2 +-
 src/client/views/DashboardView.scss                |   48 -
 src/client/views/DashboardView.tsx                 |   69 -
 src/client/views/DocComponent.tsx                  |    6 +-
 src/client/views/DocumentButtonBar.tsx             |   52 +-
 src/client/views/DocumentDecorations.tsx           | 1049 ++-
 src/client/views/GestureOverlay.tsx                |    9 +-
 src/client/views/InkTangentHandles.tsx             |    3 +-
 src/client/views/InkTranscription.tsx              |  184 +-
 src/client/views/LightboxView.tsx                  |    2 +
 src/client/views/Main.tsx                          |    3 -
 src/client/views/MainView.scss                     |   15 +-
 src/client/views/MainView.tsx                      |   52 +-
 src/client/views/MarqueeAnnotator.tsx              |   10 +-
 src/client/views/OverlayView.scss                  |   12 -
 src/client/views/OverlayView.tsx                   |   14 +-
 src/client/views/PropertiesButtons.tsx             |   14 +-
 src/client/views/PropertiesView.tsx                |    2 +-
 src/client/views/ScriptingRepl.tsx                 |    3 +-
 .../views/collections/CollectionDockingView.tsx    |  135 +-
 .../collections/CollectionDockingViewMenu.scss     |   34 +
 .../collections/CollectionDockingViewMenu.tsx      |   48 +
 src/client/views/collections/CollectionMenu.tsx    |    7 +-
 .../collections/CollectionStackedTimeline.tsx      |    1 -
 .../views/collections/CollectionStackingView.scss  |    5 +-
 .../views/collections/CollectionTimeView.tsx       |    2 +-
 src/client/views/collections/TabDocView.tsx        |  146 +-
 src/client/views/collections/TreeView.tsx          |   15 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  158 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |  124 +-
 .../collectionLinear/CollectionLinearView.tsx      |    1 -
 .../CollectionMulticolumnView.tsx                  |    3 +-
 .../CollectionMultirowView.tsx                     |    3 +-
 .../collectionSchema/CollectionSchemaView.tsx      |    2 +-
 src/client/views/linking/LinkMenuItem.tsx          |    4 -
 src/client/views/nodes/AudioBox.tsx                |   85 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |    4 +-
 src/client/views/nodes/DocumentContentsView.tsx    |    3 +-
 src/client/views/nodes/DocumentView.tsx            |   21 +-
 src/client/views/nodes/FieldView.tsx               |    1 -
 src/client/views/nodes/FilterBox.tsx               |    2 +-
 src/client/views/nodes/ImageBox.tsx                |    2 +-
 src/client/views/nodes/LinkDocPreview.tsx          |    2 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |    6 +-
 src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx |   14 +-
 src/client/views/nodes/PDFBox.tsx                  |   44 +-
 .../views/nodes/RecordingBox/ProgressBar.scss      |   26 -
 .../views/nodes/RecordingBox/ProgressBar.tsx       |   45 -
 .../views/nodes/RecordingBox/RecordingBox.tsx      |   61 -
 .../views/nodes/RecordingBox/RecordingView.scss    |  207 -
 .../views/nodes/RecordingBox/RecordingView.tsx     |  282 -
 src/client/views/nodes/RecordingBox/index.ts       |    2 -
 src/client/views/nodes/ScreenshotBox.tsx           |  429 +-
 src/client/views/nodes/VideoBox.tsx                | 2173 +++---
 src/client/views/nodes/WebBoxRenderer.js           |    4 +-
 src/client/views/nodes/button/FontIconBadge.tsx    |   32 +-
 src/client/views/nodes/button/FontIconBox.tsx      |    7 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   22 +-
 .../formattedText/ProsemirrorExampleTransfer.ts    |    2 +-
 src/client/views/nodes/trails/PresBox.tsx          |  152 +-
 src/client/views/nodes/trails/PresElementBox.scss  |  328 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |  438 +-
 src/client/views/pdf/PDFViewer.tsx                 |    7 -
 src/client/views/topbar/TopBar.scss                |  276 +-
 src/client/views/topbar/TopBar.tsx                 |  126 +-
 src/fields/Doc.ts                                  |    1 -
 src/fields/Types.ts                                |    4 -
 src/fields/URLField.ts                             |    1 -
 src/fields/documentSchemas.ts                      |    6 +-
 src/mobile/AudioUpload.tsx                         |    4 +-
 src/server/ApiManagers/UploadManager.ts            |  462 +-
 tslint.json                                        |   32 +
 86 files changed, 7194 insertions(+), 8177 deletions(-)
 delete mode 100644 .eslintrc.json
 delete mode 100644 .prettierrc.json
 delete mode 100644 src/client/util/RecordingApi.ts
 delete mode 100644 src/client/views/DashboardView.scss
 delete mode 100644 src/client/views/DashboardView.tsx
 create mode 100644 src/client/views/collections/CollectionDockingViewMenu.scss
 create mode 100644 src/client/views/collections/CollectionDockingViewMenu.tsx
 delete mode 100644 src/client/views/nodes/RecordingBox/ProgressBar.scss
 delete mode 100644 src/client/views/nodes/RecordingBox/ProgressBar.tsx
 delete mode 100644 src/client/views/nodes/RecordingBox/RecordingBox.tsx
 delete mode 100644 src/client/views/nodes/RecordingBox/RecordingView.scss
 delete mode 100644 src/client/views/nodes/RecordingBox/RecordingView.tsx
 delete mode 100644 src/client/views/nodes/RecordingBox/index.ts
 create mode 100644 tslint.json

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

diff --git a/.eslintrc.json b/.eslintrc.json
deleted file mode 100644
index 5cc0ab6dc..000000000
--- a/.eslintrc.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "extends": ["airbnb", "prettier", "plugin:node/recommended"],
-  "plugins": ["prettier"],
-  "rules": {
-    "prettier/prettier": "error",
-    "no-unused-vars": "warn",
-    "no-console": "off",
-    "func-names": "off",
-    "no-process-exit": "off",
-    "object-shorthand": "off",
-    "class-methods-use-this": "off"
-  }
-}
diff --git a/.prettierrc.json b/.prettierrc.json
deleted file mode 100644
index a07280e32..000000000
--- a/.prettierrc.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-  "trailingComma": "es5",
-  "tabWidth": 4,
-  "semi": false,
-  "singleQuote": false
-}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index a6e6ad360..27acfd1a2 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -6,7 +6,7 @@
         "**/CVS": true,
         "**/.DS_Store": true,
     },
-    "editor.formatOnSave": false,
+    "editor.formatOnSave": true,
     "editor.detectIndentation": false,
     "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false,
     "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": true,
diff --git a/package-lock.json b/package-lock.json
index 9dc7f1b04..6683f979e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5,167 +5,220 @@
     "requires": true,
     "dependencies": {
         "@babel/code-frame": {
-            "version": "7.16.7",
-            "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz",
-            "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==",
+            "version": "7.8.3",
+            "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
+            "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
             "requires": {
-                "@babel/highlight": "^7.16.7"
+                "@babel/highlight": "^7.8.3"
             }
         },
         "@babel/generator": {
-            "version": "7.18.2",
-            "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.2.tgz",
-            "integrity": "sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==",
-            "requires": {
-                "@babel/types": "^7.18.2",
-                "@jridgewell/gen-mapping": "^0.3.0",
-                "jsesc": "^2.5.1"
+            "version": "7.9.6",
+            "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.6.tgz",
+            "integrity": "sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ==",
+            "requires": {
+                "@babel/types": "^7.9.6",
+                "jsesc": "^2.5.1",
+                "lodash": "^4.17.13",
+                "source-map": "^0.5.0"
+            },
+            "dependencies": {
+                "@babel/types": {
+                    "version": "7.9.6",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.6.tgz",
+                    "integrity": "sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==",
+                    "requires": {
+                        "@babel/helper-validator-identifier": "^7.9.5",
+                        "lodash": "^4.17.13",
+                        "to-fast-properties": "^2.0.0"
+                    }
+                }
             }
         },
         "@babel/helper-annotate-as-pure": {
-            "version": "7.16.7",
-            "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz",
-            "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==",
+            "version": "7.10.4",
+            "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz",
+            "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==",
             "requires": {
-                "@babel/types": "^7.16.7"
+                "@babel/types": "^7.10.4"
+            },
+            "dependencies": {
+                "@babel/helper-validator-identifier": {
+                    "version": "7.10.4",
+                    "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+                    "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw=="
+                },
+                "@babel/types": {
+                    "version": "7.10.5",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz",
+                    "integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==",
+                    "requires": {
+                        "@babel/helper-validator-identifier": "^7.10.4",
+                        "lodash": "^4.17.19",
+                        "to-fast-properties": "^2.0.0"
+                    }
+                },
+                "lodash": {
+                    "version": "4.17.19",
+                    "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
+                    "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
+                }
             }
         },
-        "@babel/helper-environment-visitor": {
-            "version": "7.18.2",
-            "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.2.tgz",
-            "integrity": "sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ=="
-        },
         "@babel/helper-function-name": {
-            "version": "7.17.9",
-            "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz",
-            "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==",
+            "version": "7.9.5",
+            "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz",
+            "integrity": "sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==",
             "requires": {
-                "@babel/template": "^7.16.7",
-                "@babel/types": "^7.17.0"
+                "@babel/helper-get-function-arity": "^7.8.3",
+                "@babel/template": "^7.8.3",
+                "@babel/types": "^7.9.5"
             }
         },
-        "@babel/helper-hoist-variables": {
-            "version": "7.16.7",
-            "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz",
-            "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==",
+        "@babel/helper-get-function-arity": {
+            "version": "7.8.3",
+            "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz",
+            "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==",
             "requires": {
-                "@babel/types": "^7.16.7"
+                "@babel/types": "^7.8.3"
             }
         },
         "@babel/helper-module-imports": {
-            "version": "7.16.7",
-            "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz",
-            "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==",
+            "version": "7.10.3",
+            "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.3.tgz",
+            "integrity": "sha512-Jtqw5M9pahLSUWA+76nhK9OG8nwYXzhQzVIGFoNaHnXF/r4l7kz4Fl0UAW7B6mqC5myoJiBP5/YQlXQTMfHI9w==",
             "requires": {
-                "@babel/types": "^7.16.7"
+                "@babel/types": "^7.10.3"
+            },
+            "dependencies": {
+                "@babel/helper-validator-identifier": {
+                    "version": "7.10.3",
+                    "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.3.tgz",
+                    "integrity": "sha512-bU8JvtlYpJSBPuj1VUmKpFGaDZuLxASky3LhaKj3bmpSTY6VWooSM8msk+Z0CZoErFye2tlABF6yDkT3FOPAXw=="
+                },
+                "@babel/types": {
+                    "version": "7.10.3",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.3.tgz",
+                    "integrity": "sha512-nZxaJhBXBQ8HVoIcGsf9qWep3Oh3jCENK54V4mRF7qaJabVsAYdbTtmSD8WmAp1R6ytPiu5apMwSXyxB1WlaBA==",
+                    "requires": {
+                        "@babel/helper-validator-identifier": "^7.10.3",
+                        "lodash": "^4.17.13",
+                        "to-fast-properties": "^2.0.0"
+                    }
+                }
             }
         },
         "@babel/helper-plugin-utils": {
-            "version": "7.17.12",
-            "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz",
-            "integrity": "sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA=="
+            "version": "7.16.7",
+            "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz",
+            "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA=="
         },
         "@babel/helper-split-export-declaration": {
-            "version": "7.16.7",
-            "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz",
-            "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==",
+            "version": "7.8.3",
+            "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz",
+            "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==",
             "requires": {
-                "@babel/types": "^7.16.7"
+                "@babel/types": "^7.8.3"
             }
         },
         "@babel/helper-validator-identifier": {
-            "version": "7.16.7",
-            "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz",
-            "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw=="
+            "version": "7.9.5",
+            "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz",
+            "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g=="
         },
         "@babel/highlight": {
-            "version": "7.17.12",
-            "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.12.tgz",
-            "integrity": "sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==",
+            "version": "7.9.0",
+            "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
+            "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
             "requires": {
-                "@babel/helper-validator-identifier": "^7.16.7",
+                "@babel/helper-validator-identifier": "^7.9.0",
                 "chalk": "^2.0.0",
                 "js-tokens": "^4.0.0"
             }
         },
         "@babel/parser": {
-            "version": "7.18.4",
-            "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.4.tgz",
-            "integrity": "sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow=="
+            "version": "7.9.6",
+            "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.6.tgz",
+            "integrity": "sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q=="
         },
         "@babel/plugin-syntax-jsx": {
-            "version": "7.17.12",
-            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.17.12.tgz",
-            "integrity": "sha512-spyY3E3AURfxh/RHtjx5j6hs8am5NbUBGfcZ2vB3uShSpZdQyXSf5rR5Mk76vbtlAZOelyVQ71Fg0x9SG4fsog==",
+            "version": "7.16.7",
+            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz",
+            "integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==",
             "requires": {
-                "@babel/helper-plugin-utils": "^7.17.12"
+                "@babel/helper-plugin-utils": "^7.16.7"
             }
         },
         "@babel/runtime": {
-            "version": "7.18.3",
-            "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.3.tgz",
-            "integrity": "sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==",
+            "version": "7.9.2",
+            "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
+            "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
             "requires": {
                 "regenerator-runtime": "^0.13.4"
             }
         },
         "@babel/runtime-corejs3": {
-            "version": "7.18.3",
-            "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.18.3.tgz",
-            "integrity": "sha512-l4ddFwrc9rnR+EJsHsh+TJ4A35YqQz/UqcjtlX2ov53hlJYG5CxtQmNZxyajwDVmCxwy++rtvGU5HazCK4W41Q==",
+            "version": "7.17.7",
+            "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.7.tgz",
+            "integrity": "sha512-TvliGJjhxis5m7xIMvlXH/xG8Oa/LK0SCUCyfKD6nLi42n5fB4WibDJ0g9trmmBB6hwpMNx+Lzbxy9/4gpMaVw==",
             "requires": {
                 "core-js-pure": "^3.20.2",
                 "regenerator-runtime": "^0.13.4"
             }
         },
         "@babel/template": {
-            "version": "7.16.7",
-            "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz",
-            "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==",
+            "version": "7.8.6",
+            "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
+            "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
             "requires": {
-                "@babel/code-frame": "^7.16.7",
-                "@babel/parser": "^7.16.7",
-                "@babel/types": "^7.16.7"
+                "@babel/code-frame": "^7.8.3",
+                "@babel/parser": "^7.8.6",
+                "@babel/types": "^7.8.6"
             }
         },
         "@babel/traverse": {
-            "version": "7.18.2",
-            "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.2.tgz",
-            "integrity": "sha512-9eNwoeovJ6KH9zcCNnENY7DMFwTU9JdGCFtqNLfUAqtUHRCOsTOqWoffosP8vKmNYeSBUv3yVJXjfd8ucwOjUA==",
-            "requires": {
-                "@babel/code-frame": "^7.16.7",
-                "@babel/generator": "^7.18.2",
-                "@babel/helper-environment-visitor": "^7.18.2",
-                "@babel/helper-function-name": "^7.17.9",
-                "@babel/helper-hoist-variables": "^7.16.7",
-                "@babel/helper-split-export-declaration": "^7.16.7",
-                "@babel/parser": "^7.18.0",
-                "@babel/types": "^7.18.2",
+            "version": "7.9.6",
+            "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.6.tgz",
+            "integrity": "sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg==",
+            "requires": {
+                "@babel/code-frame": "^7.8.3",
+                "@babel/generator": "^7.9.6",
+                "@babel/helper-function-name": "^7.9.5",
+                "@babel/helper-split-export-declaration": "^7.8.3",
+                "@babel/parser": "^7.9.6",
+                "@babel/types": "^7.9.6",
                 "debug": "^4.1.0",
-                "globals": "^11.1.0"
+                "globals": "^11.1.0",
+                "lodash": "^4.17.13"
             },
             "dependencies": {
-                "debug": {
-                    "version": "4.3.4",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
-                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+                "@babel/types": {
+                    "version": "7.9.6",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.6.tgz",
+                    "integrity": "sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==",
                     "requires": {
-                        "ms": "2.1.2"
+                        "@babel/helper-validator-identifier": "^7.9.5",
+                        "lodash": "^4.17.13",
+                        "to-fast-properties": "^2.0.0"
                     }
                 },
-                "ms": {
-                    "version": "2.1.2",
-                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+                "debug": {
+                    "version": "4.1.1",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+                    "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+                    "requires": {
+                        "ms": "^2.1.1"
+                    }
                 }
             }
         },
         "@babel/types": {
-            "version": "7.18.4",
-            "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.4.tgz",
-            "integrity": "sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==",
+            "version": "7.9.5",
+            "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.5.tgz",
+            "integrity": "sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg==",
             "requires": {
-                "@babel/helper-validator-identifier": "^7.16.7",
+                "@babel/helper-validator-identifier": "^7.9.5",
+                "lodash": "^4.17.13",
                 "to-fast-properties": "^2.0.0"
             }
         },
@@ -175,9 +228,9 @@
             "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw=="
         },
         "@emotion/babel-plugin": {
-            "version": "11.9.2",
-            "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.9.2.tgz",
-            "integrity": "sha512-Pr/7HGH6H6yKgnVFNEj2MVlreu3ADqftqjqwUvDy/OJzKFgxKeTQ+eeUf20FOTuHVkDON2iNa25rAXVYtWJCjw==",
+            "version": "11.7.2",
+            "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.7.2.tgz",
+            "integrity": "sha512-6mGSCWi9UzXut/ZAN6lGFu33wGR3SJisNl3c0tvlmb8XChH1b2SUvxvnOh7hvLpqyRdHHU9AiazV3Cwbk5SXKQ==",
             "requires": {
                 "@babel/helper-module-imports": "^7.12.13",
                 "@babel/plugin-syntax-jsx": "^7.12.13",
@@ -193,15 +246,45 @@
                 "stylis": "4.0.13"
             },
             "dependencies": {
+                "@babel/helper-module-imports": {
+                    "version": "7.16.7",
+                    "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz",
+                    "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==",
+                    "requires": {
+                        "@babel/types": "^7.16.7"
+                    }
+                },
+                "@babel/helper-validator-identifier": {
+                    "version": "7.16.7",
+                    "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz",
+                    "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw=="
+                },
+                "@babel/runtime": {
+                    "version": "7.17.7",
+                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz",
+                    "integrity": "sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==",
+                    "requires": {
+                        "regenerator-runtime": "^0.13.4"
+                    }
+                },
+                "@babel/types": {
+                    "version": "7.17.0",
+                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz",
+                    "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==",
+                    "requires": {
+                        "@babel/helper-validator-identifier": "^7.16.7",
+                        "to-fast-properties": "^2.0.0"
+                    }
+                },
                 "@emotion/memoize": {
                     "version": "0.7.5",
                     "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz",
                     "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ=="
                 },
                 "@emotion/serialize": {
-                    "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.3.tgz",
-                    "integrity": "sha512-2mSSvgLfyV3q+iVh3YWgNlUc2a9ZlDU7DjuP5MjK3AXRR0dYigCrP99aeFtaB2L/hjfEZdSThn5dsZ0ufqbvsA==",
+                    "version": "1.0.2",
+                    "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz",
+                    "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==",
                     "requires": {
                         "@emotion/hash": "^0.8.0",
                         "@emotion/memoize": "^0.7.4",
@@ -216,14 +299,19 @@
                     "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
                 },
                 "csstype": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
+                    "version": "3.0.11",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
+                    "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
                 },
                 "escape-string-regexp": {
                     "version": "4.0.0",
                     "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
                     "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
+                },
+                "stylis": {
+                    "version": "4.0.13",
+                    "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
+                    "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
                 }
             }
         },
@@ -264,13 +352,13 @@
             }
         },
         "@emotion/css": {
-            "version": "11.9.0",
-            "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.9.0.tgz",
-            "integrity": "sha512-S9UjCxSrxEHawOLnWw4upTwfYKb0gVQdatHejn3W9kPyXxmKv3HmjVfJ84kDLmdX8jR20OuDQwaJ4Um24qD9vA==",
+            "version": "11.7.1",
+            "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.7.1.tgz",
+            "integrity": "sha512-RUUgPlMZunlc7SE5A6Hg+VWRzb2cU6O9xlV78KCFgcnl25s7Qz/20oQg71iKudpLqk7xj0vhbJlwcJJMT0BOZg==",
             "requires": {
                 "@emotion/babel-plugin": "^11.7.1",
                 "@emotion/cache": "^11.7.1",
-                "@emotion/serialize": "^1.0.3",
+                "@emotion/serialize": "^1.0.0",
                 "@emotion/sheet": "^1.0.3",
                 "@emotion/utils": "^1.0.0"
             },
@@ -288,9 +376,9 @@
                     }
                 },
                 "@emotion/serialize": {
-                    "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.3.tgz",
-                    "integrity": "sha512-2mSSvgLfyV3q+iVh3YWgNlUc2a9ZlDU7DjuP5MjK3AXRR0dYigCrP99aeFtaB2L/hjfEZdSThn5dsZ0ufqbvsA==",
+                    "version": "1.0.2",
+                    "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz",
+                    "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==",
                     "requires": {
                         "@emotion/hash": "^0.8.0",
                         "@emotion/memoize": "^0.7.4",
@@ -310,9 +398,14 @@
                     "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
                 },
                 "csstype": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
+                    "version": "3.0.11",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
+                    "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
+                },
+                "stylis": {
+                    "version": "4.0.13",
+                    "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
+                    "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
                 }
             }
         },
@@ -322,11 +415,11 @@
             "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
         },
         "@emotion/is-prop-valid": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.2.tgz",
-            "integrity": "sha512-3QnhqeL+WW88YjYbQL5gUIkthuMw7a0NGbZ7wfFVk2kg/CK5w8w5FFa0RzWjyY1+sujN0NWbtSHH6OJmWHtJpQ==",
+            "version": "0.8.8",
+            "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
+            "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
             "requires": {
-                "@emotion/memoize": "^0.7.4"
+                "@emotion/memoize": "0.7.4"
             }
         },
         "@emotion/memoize": {
@@ -335,19 +428,27 @@
             "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw=="
         },
         "@emotion/react": {
-            "version": "11.9.0",
-            "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.9.0.tgz",
-            "integrity": "sha512-lBVSF5d0ceKtfKCDQJveNAtkC7ayxpVlgOohLgXqRwqWr9bOf4TZAFFyIcNngnV6xK6X4x2ZeXq7vliHkoVkxQ==",
+            "version": "11.8.2",
+            "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.8.2.tgz",
+            "integrity": "sha512-+1bcHBaNJv5nkIIgnGKVsie3otS0wF9f1T1hteF3WeVvMNQEtfZ4YyFpnphGoot3ilU/wWMgP2SgIDuHLE/wAA==",
             "requires": {
                 "@babel/runtime": "^7.13.10",
                 "@emotion/babel-plugin": "^11.7.1",
                 "@emotion/cache": "^11.7.1",
-                "@emotion/serialize": "^1.0.3",
+                "@emotion/serialize": "^1.0.2",
                 "@emotion/utils": "^1.1.0",
                 "@emotion/weak-memoize": "^0.2.5",
                 "hoist-non-react-statics": "^3.3.1"
             },
             "dependencies": {
+                "@babel/runtime": {
+                    "version": "7.17.7",
+                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz",
+                    "integrity": "sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==",
+                    "requires": {
+                        "regenerator-runtime": "^0.13.4"
+                    }
+                },
                 "@emotion/cache": {
                     "version": "11.7.1",
                     "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz",
@@ -361,9 +462,9 @@
                     }
                 },
                 "@emotion/serialize": {
-                    "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.3.tgz",
-                    "integrity": "sha512-2mSSvgLfyV3q+iVh3YWgNlUc2a9ZlDU7DjuP5MjK3AXRR0dYigCrP99aeFtaB2L/hjfEZdSThn5dsZ0ufqbvsA==",
+                    "version": "1.0.2",
+                    "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz",
+                    "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==",
                     "requires": {
                         "@emotion/hash": "^0.8.0",
                         "@emotion/memoize": "^0.7.4",
@@ -383,9 +484,14 @@
                     "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
                 },
                 "csstype": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
+                    "version": "3.0.11",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
+                    "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
+                },
+                "stylis": {
+                    "version": "4.0.13",
+                    "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
+                    "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
                 }
             }
         },
@@ -418,10 +524,26 @@
                 "@emotion/utils": "^1.1.0"
             },
             "dependencies": {
+                "@babel/runtime": {
+                    "version": "7.17.7",
+                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz",
+                    "integrity": "sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==",
+                    "requires": {
+                        "regenerator-runtime": "^0.13.4"
+                    }
+                },
+                "@emotion/is-prop-valid": {
+                    "version": "1.1.2",
+                    "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.2.tgz",
+                    "integrity": "sha512-3QnhqeL+WW88YjYbQL5gUIkthuMw7a0NGbZ7wfFVk2kg/CK5w8w5FFa0RzWjyY1+sujN0NWbtSHH6OJmWHtJpQ==",
+                    "requires": {
+                        "@emotion/memoize": "^0.7.4"
+                    }
+                },
                 "@emotion/serialize": {
-                    "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.3.tgz",
-                    "integrity": "sha512-2mSSvgLfyV3q+iVh3YWgNlUc2a9ZlDU7DjuP5MjK3AXRR0dYigCrP99aeFtaB2L/hjfEZdSThn5dsZ0ufqbvsA==",
+                    "version": "1.0.2",
+                    "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz",
+                    "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==",
                     "requires": {
                         "@emotion/hash": "^0.8.0",
                         "@emotion/memoize": "^0.7.4",
@@ -436,9 +558,9 @@
                     "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
                 },
                 "csstype": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
+                    "version": "3.0.11",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
+                    "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
                 }
             }
         },
@@ -526,23 +648,51 @@
             "integrity": "sha512-RwLIB4TZw0M9gvy5u+TusAA0afbwM4JQIimNH/j3ygd6aIvYPQLqXMhC9ErY26J23rDPyDZldIfPq/HpTTJ/tQ==",
             "requires": {
                 "prop-types": "^15.8.1"
+            },
+            "dependencies": {
+                "prop-types": {
+                    "version": "15.8.1",
+                    "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+                    "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+                    "requires": {
+                        "loose-envify": "^1.4.0",
+                        "object-assign": "^4.1.1",
+                        "react-is": "^16.13.1"
+                    }
+                }
             }
         },
         "@googlemaps/js-api-loader": {
-            "version": "1.14.3",
-            "resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.14.3.tgz",
-            "integrity": "sha512-6iIb+qpGgQpgIHmIFO44WhE1rDUxPVHuezNFL30wRJnkvhwFm94tD291UvNg9L05hLDSoL16jd0lbqqmdy4C5g==",
+            "version": "1.12.8",
+            "resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.12.8.tgz",
+            "integrity": "sha512-h1RNVIJkHWL4UrdJo8fwECJwSRg8CSIfTEtPdzvThcji7tAPvVBDVBPzvPKhvl8e8mWEGPhnYOt6kPfSxJL+vQ==",
             "requires": {
                 "fast-deep-equal": "^3.1.3"
+            },
+            "dependencies": {
+                "fast-deep-equal": {
+                    "version": "3.1.3",
+                    "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+                    "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+                }
             }
         },
         "@googlemaps/markerclusterer": {
-            "version": "2.0.7",
-            "resolved": "https://registry.npmjs.org/@googlemaps/markerclusterer/-/markerclusterer-2.0.7.tgz",
-            "integrity": "sha512-/xADL31ERrNoVsF+O2A+H+obxOki4DHPQkU/STS5gFG6ZZVpvNV4WLisW2aOlOsH3rUXSq3MFjqFOEDXSQcvog==",
+            "version": "1.0.12",
+            "resolved": "https://registry.npmjs.org/@googlemaps/markerclusterer/-/markerclusterer-1.0.12.tgz",
+            "integrity": "sha512-DSmN2w1ZoI+PBWfNCKYcr+zalP0uaaKIU08+UgebJYZ96X+J6CdUpD+xOVwBV1OJDiIXkneHquOZnT+V6dm+Sg==",
             "requires": {
+                "@turf/clusters-dbscan": "^6.4.0",
+                "@turf/clusters-kmeans": "^6.4.0",
                 "fast-deep-equal": "^3.1.3",
                 "supercluster": "^7.1.3"
+            },
+            "dependencies": {
+                "fast-deep-equal": {
+                    "version": "3.1.3",
+                    "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+                    "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+                }
             }
         },
         "@hig/flyout": {
@@ -566,9 +716,9 @@
             }
         },
         "@hig/theme-data": {
-            "version": "2.26.0",
-            "resolved": "https://registry.npmjs.org/@hig/theme-data/-/theme-data-2.26.0.tgz",
-            "integrity": "sha512-jEiCieyafBePgFLlIPRPIhNPulWELeOgWJUY4wxdvozRdFUEq50ydx0ysuYPQOh4ucy0jI7Q8FcsBd+aEe7WXQ==",
+            "version": "2.24.0",
+            "resolved": "https://registry.npmjs.org/@hig/theme-data/-/theme-data-2.24.0.tgz",
+            "integrity": "sha512-/71T3kezuom7z60Bxyxwxo0oHqb2B59jCbdWrgRgUYorK0vO6zjFmQvRmPSgxq2i1bQxQLx12iuzW9cM494pMQ==",
             "requires": {
                 "tinycolor2": "^1.4.1"
             }
@@ -606,63 +756,20 @@
             "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz",
             "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw=="
         },
-        "@jridgewell/gen-mapping": {
-            "version": "0.3.1",
-            "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz",
-            "integrity": "sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==",
-            "requires": {
-                "@jridgewell/set-array": "^1.0.0",
-                "@jridgewell/sourcemap-codec": "^1.4.10",
-                "@jridgewell/trace-mapping": "^0.3.9"
-            }
-        },
-        "@jridgewell/resolve-uri": {
-            "version": "3.0.7",
-            "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz",
-            "integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA=="
-        },
-        "@jridgewell/set-array": {
-            "version": "1.1.1",
-            "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.1.tgz",
-            "integrity": "sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ=="
-        },
-        "@jridgewell/source-map": {
-            "version": "0.3.2",
-            "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
-            "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
-            "requires": {
-                "@jridgewell/gen-mapping": "^0.3.0",
-                "@jridgewell/trace-mapping": "^0.3.9"
-            }
-        },
-        "@jridgewell/sourcemap-codec": {
-            "version": "1.4.13",
-            "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz",
-            "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w=="
-        },
-        "@jridgewell/trace-mapping": {
-            "version": "0.3.13",
-            "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz",
-            "integrity": "sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==",
-            "requires": {
-                "@jridgewell/resolve-uri": "^3.0.3",
-                "@jridgewell/sourcemap-codec": "^1.4.10"
-            }
-        },
         "@log4js-node/log4js-api": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/@log4js-node/log4js-api/-/log4js-api-1.0.2.tgz",
             "integrity": "sha512-6SJfx949YEWooh/CUPpJ+F491y4BYJmknz4hUN1+RHvKoUEynKbRmhnwbk/VLmh4OthLLDNCyWXfbh4DG1cTXA=="
         },
         "@mapbox/node-pre-gyp": {
-            "version": "1.0.9",
-            "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz",
-            "integrity": "sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==",
+            "version": "1.0.8",
+            "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz",
+            "integrity": "sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg==",
             "requires": {
-                "detect-libc": "^2.0.0",
+                "detect-libc": "^1.0.3",
                 "https-proxy-agent": "^5.0.0",
                 "make-dir": "^3.1.0",
-                "node-fetch": "^2.6.7",
+                "node-fetch": "^2.6.5",
                 "nopt": "^5.0.0",
                 "npmlog": "^5.0.1",
                 "rimraf": "^3.0.2",
@@ -670,6 +777,69 @@
                 "tar": "^6.1.11"
             },
             "dependencies": {
+                "ansi-regex": {
+                    "version": "5.0.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                    "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+                },
+                "are-we-there-yet": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
+                    "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
+                    "requires": {
+                        "delegates": "^1.0.0",
+                        "readable-stream": "^3.6.0"
+                    }
+                },
+                "emoji-regex": {
+                    "version": "8.0.0",
+                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+                    "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+                },
+                "gauge": {
+                    "version": "3.0.2",
+                    "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
+                    "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
+                    "requires": {
+                        "aproba": "^1.0.3 || ^2.0.0",
+                        "color-support": "^1.1.2",
+                        "console-control-strings": "^1.0.0",
+                        "has-unicode": "^2.0.1",
+                        "object-assign": "^4.1.1",
+                        "signal-exit": "^3.0.0",
+                        "string-width": "^4.2.3",
+                        "strip-ansi": "^6.0.1",
+                        "wide-align": "^1.1.2"
+                    }
+                },
+                "is-fullwidth-code-point": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+                    "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+                },
+                "lru-cache": {
+                    "version": "6.0.0",
+                    "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+                    "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+                    "requires": {
+                        "yallist": "^4.0.0"
+                    }
+                },
+                "make-dir": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+                    "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+                    "requires": {
+                        "semver": "^6.0.0"
+                    },
+                    "dependencies": {
+                        "semver": {
+                            "version": "6.3.0",
+                            "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+                            "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
+                        }
+                    }
+                },
                 "node-fetch": {
                     "version": "2.6.7",
                     "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
@@ -678,26 +848,79 @@
                         "whatwg-url": "^5.0.0"
                     }
                 },
+                "npmlog": {
+                    "version": "5.0.1",
+                    "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
+                    "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
+                    "requires": {
+                        "are-we-there-yet": "^2.0.0",
+                        "console-control-strings": "^1.1.0",
+                        "gauge": "^3.0.0",
+                        "set-blocking": "^2.0.0"
+                    }
+                },
                 "semver": {
-                    "version": "7.3.7",
-                    "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
-                    "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
+                    "version": "7.3.5",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+                    "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
                     "requires": {
                         "lru-cache": "^6.0.0"
                     }
+                },
+                "string-width": {
+                    "version": "4.2.3",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+                    "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+                    "requires": {
+                        "emoji-regex": "^8.0.0",
+                        "is-fullwidth-code-point": "^3.0.0",
+                        "strip-ansi": "^6.0.1"
+                    }
+                },
+                "strip-ansi": {
+                    "version": "6.0.1",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                    "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                    "requires": {
+                        "ansi-regex": "^5.0.1"
+                    }
+                },
+                "tr46": {
+                    "version": "0.0.3",
+                    "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+                    "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
+                },
+                "webidl-conversions": {
+                    "version": "3.0.1",
+                    "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+                    "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
+                },
+                "whatwg-url": {
+                    "version": "5.0.0",
+                    "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+                    "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
+                    "requires": {
+                        "tr46": "~0.0.3",
+                        "webidl-conversions": "^3.0.0"
+                    }
+                },
+                "yallist": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+                    "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
                 }
             }
         },
         "@material-ui/core": {
-            "version": "4.12.4",
-            "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.4.tgz",
-            "integrity": "sha512-tr7xekNlM9LjA6pagJmL8QCgZXaubWUwkJnoYcMKd4gw/t4XiyvnTkjdGrUVicyB2BsdaAv1tvow45bPM4sSwQ==",
+            "version": "4.12.3",
+            "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.3.tgz",
+            "integrity": "sha512-sdpgI/PL56QVsEJldwEe4FFaFTLUqN+rd7sSZiRCdx2E/C7z5yK0y/khAWVBH24tXwto7I1hCzNWfJGZIYJKnw==",
             "requires": {
                 "@babel/runtime": "^7.4.4",
-                "@material-ui/styles": "^4.11.5",
-                "@material-ui/system": "^4.12.2",
+                "@material-ui/styles": "^4.11.4",
+                "@material-ui/system": "^4.12.1",
                 "@material-ui/types": "5.1.0",
-                "@material-ui/utils": "^4.11.3",
+                "@material-ui/utils": "^4.11.2",
                 "@types/react-transition-group": "^4.2.0",
                 "clsx": "^1.0.4",
                 "hoist-non-react-statics": "^3.3.2",
@@ -708,9 +931,9 @@
             },
             "dependencies": {
                 "csstype": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
+                    "version": "3.0.11",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
+                    "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
                 },
                 "dom-helpers": {
                     "version": "5.2.1",
@@ -735,14 +958,14 @@
             }
         },
         "@material-ui/styles": {
-            "version": "4.11.5",
-            "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.5.tgz",
-            "integrity": "sha512-o/41ot5JJiUsIETME9wVLAJrmIWL3j0R0Bj2kCOLbSfqEkKf0fmaPt+5vtblUh5eXr2S+J/8J3DaCb10+CzPGA==",
+            "version": "4.11.4",
+            "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.4.tgz",
+            "integrity": "sha512-KNTIZcnj/zprG5LW0Sao7zw+yG3O35pviHzejMdcSGCdWbiO8qzRgOYL8JAxAsWBKOKYwVZxXtHWaB5T2Kvxew==",
             "requires": {
                 "@babel/runtime": "^7.4.4",
                 "@emotion/hash": "^0.8.0",
                 "@material-ui/types": "5.1.0",
-                "@material-ui/utils": "^4.11.3",
+                "@material-ui/utils": "^4.11.2",
                 "clsx": "^1.0.4",
                 "csstype": "^2.5.2",
                 "hoist-non-react-statics": "^3.3.2",
@@ -758,12 +981,12 @@
             }
         },
         "@material-ui/system": {
-            "version": "4.12.2",
-            "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.2.tgz",
-            "integrity": "sha512-6CSKu2MtmiJgcCGf6nBQpM8fLkuB9F55EKfbdTC80NND5wpTmKzwdhLYLH3zL4cLlK0gVaaltW7/wMuyTnN0Lw==",
+            "version": "4.12.1",
+            "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.1.tgz",
+            "integrity": "sha512-lUdzs4q9kEXZGhbN7BptyiS1rLNHe6kG9o8Y307HCvF4sQxbCgpL2qi+gUk+yI8a2DNk48gISEQxoxpgph0xIw==",
             "requires": {
                 "@babel/runtime": "^7.4.4",
-                "@material-ui/utils": "^4.11.3",
+                "@material-ui/utils": "^4.11.2",
                 "csstype": "^2.5.2",
                 "prop-types": "^15.7.2"
             }
@@ -774,9 +997,9 @@
             "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A=="
         },
         "@material-ui/utils": {
-            "version": "4.11.3",
-            "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.3.tgz",
-            "integrity": "sha512-ZuQPV4rBK/V1j2dIkSSEcH5uT6AaHuKWFfotADHsC0wVL1NLd2WkFCm4ZZbX33iO4ydl6V0GPngKm8HZQ2oujg==",
+            "version": "4.11.2",
+            "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.2.tgz",
+            "integrity": "sha512-Uul8w38u+PICe2Fg2pDKCaIG7kOyhowZ9vjiC1FsVwPABTW8vPPKfF6OvxRq3IiBaI1faOJmgdvMG7rMJARBhA==",
             "requires": {
                 "@babel/runtime": "^7.4.4",
                 "prop-types": "^15.7.2",
@@ -784,27 +1007,27 @@
             }
         },
         "@react-google-maps/api": {
-            "version": "2.12.0",
-            "resolved": "https://registry.npmjs.org/@react-google-maps/api/-/api-2.12.0.tgz",
-            "integrity": "sha512-BqsclJ9p2I6yZa++0R3oEcneECcH7PFsULhn8UDlWQ9uNAQR/aqvqGb9SXYvdpIAOJdk3E737iTrGhFg4j5Ucw==",
-            "requires": {
-                "@googlemaps/js-api-loader": "1.14.3",
-                "@googlemaps/markerclusterer": "2.0.7",
-                "@react-google-maps/infobox": "2.11.8",
-                "@react-google-maps/marker-clusterer": "2.11.8",
-                "@types/google.maps": "3.49.1",
+            "version": "2.7.0",
+            "resolved": "https://registry.npmjs.org/@react-google-maps/api/-/api-2.7.0.tgz",
+            "integrity": "sha512-Fb/l7aR69ObYvzhpt1d1931454ylRkTPKF1C8GfLvXtg19wc36zTev2Afbahd+qAdyKuQyDdf5/SQyMjYBHI/A==",
+            "requires": {
+                "@googlemaps/js-api-loader": "1.12.8",
+                "@googlemaps/markerclusterer": "1.0.12",
+                "@react-google-maps/infobox": "2.6.0",
+                "@react-google-maps/marker-clusterer": "2.6.0",
+                "@types/google.maps": "3.46.1",
                 "invariant": "2.2.4"
             }
         },
         "@react-google-maps/infobox": {
-            "version": "2.11.8",
-            "resolved": "https://registry.npmjs.org/@react-google-maps/infobox/-/infobox-2.11.8.tgz",
-            "integrity": "sha512-15y3dniys1Op5HREyLwiJI2pAdeeHRJT5b4WPrS+vyL5KF6iXK1P3y/9bXtnxHiKe2v6iPreaEhR3VgvfRA50Q=="
+            "version": "2.6.0",
+            "resolved": "https://registry.npmjs.org/@react-google-maps/infobox/-/infobox-2.6.0.tgz",
+            "integrity": "sha512-2dXVZsuZ2PjelVg+slpUSYPnvoSIySwf4P/wScGijjqu6bpvhyX9AivitsF7zHfbwnZ0gAPX1Q8xtQhKgawkEg=="
         },
         "@react-google-maps/marker-clusterer": {
-            "version": "2.11.8",
-            "resolved": "https://registry.npmjs.org/@react-google-maps/marker-clusterer/-/marker-clusterer-2.11.8.tgz",
-            "integrity": "sha512-OEcdbTXEgPwdpGvVuSrhIhjcZPhlzPz2TvaKCBLraf1PKiDyLOPhlTVRlbIziLiu0xN89pJcill1w9UVIVFF+Q=="
+            "version": "2.6.0",
+            "resolved": "https://registry.npmjs.org/@react-google-maps/marker-clusterer/-/marker-clusterer-2.6.0.tgz",
+            "integrity": "sha512-EjeoCM+U/6W9qoGWqurcDbCubqTEDTFET7Jd3XcLHqTLvFAE473x2YbQg98mJXKNgPKcPTNEcTiYYGiIz9tEjA=="
         },
         "@react-three/fiber": {
             "version": "6.2.3",
@@ -821,6 +1044,16 @@
                 "use-asset": "^1.0.4",
                 "utility-types": "^3.10.0",
                 "zustand": "^3.5.1"
+            },
+            "dependencies": {
+                "@babel/runtime": {
+                    "version": "7.17.7",
+                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz",
+                    "integrity": "sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==",
+                    "requires": {
+                        "regenerator-runtime": "^0.13.4"
+                    }
+                }
             }
         },
         "@rkusa/linebreak": {
@@ -844,6 +1077,68 @@
                 "defer-to-connect": "^2.0.1"
             }
         },
+        "@turf/clone": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-6.5.0.tgz",
+            "integrity": "sha512-mzVtTFj/QycXOn6ig+annKrM6ZlimreKYz6f/GSERytOpgzodbQyOgkfwru100O1KQhhjSudKK4DsQ0oyi9cTw==",
+            "requires": {
+                "@turf/helpers": "^6.5.0"
+            }
+        },
+        "@turf/clusters-dbscan": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/clusters-dbscan/-/clusters-dbscan-6.5.0.tgz",
+            "integrity": "sha512-SxZEE4kADU9DqLRiT53QZBBhu8EP9skviSyl+FGj08Y01xfICM/RR9ACUdM0aEQimhpu+ZpRVcUK+2jtiCGrYQ==",
+            "requires": {
+                "@turf/clone": "^6.5.0",
+                "@turf/distance": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "density-clustering": "1.3.0"
+            }
+        },
+        "@turf/clusters-kmeans": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/clusters-kmeans/-/clusters-kmeans-6.5.0.tgz",
+            "integrity": "sha512-DwacD5+YO8kwDPKaXwT9DV46tMBVNsbi1IzdajZu1JDSWoN7yc7N9Qt88oi+p30583O0UPVkAK+A10WAQv4mUw==",
+            "requires": {
+                "@turf/clone": "^6.5.0",
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0",
+                "@turf/meta": "^6.5.0",
+                "skmeans": "0.9.7"
+            }
+        },
+        "@turf/distance": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-6.5.0.tgz",
+            "integrity": "sha512-xzykSLfoURec5qvQJcfifw/1mJa+5UwByZZ5TZ8iaqjGYN0vomhV9aiSLeYdUGtYRESZ+DYC/OzY+4RclZYgMg==",
+            "requires": {
+                "@turf/helpers": "^6.5.0",
+                "@turf/invariant": "^6.5.0"
+            }
+        },
+        "@turf/helpers": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.5.0.tgz",
+            "integrity": "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw=="
+        },
+        "@turf/invariant": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.5.0.tgz",
+            "integrity": "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg==",
+            "requires": {
+                "@turf/helpers": "^6.5.0"
+            }
+        },
+        "@turf/meta": {
+            "version": "6.5.0",
+            "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.5.0.tgz",
+            "integrity": "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA==",
+            "requires": {
+                "@turf/helpers": "^6.5.0"
+            }
+        },
         "@types/adm-zip": {
             "version": "0.4.34",
             "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.4.34.tgz",
@@ -880,14 +1175,14 @@
             "dev": true
         },
         "@types/babel-types": {
-            "version": "7.0.11",
-            "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.11.tgz",
-            "integrity": "sha512-pkPtJUUY+Vwv6B1inAz55rQvivClHJxc9aVEPPmaq2cbyeMLCiDpbKpcKyX4LAwpNGi+SHBv0tHv6+0gXv0P2A=="
+            "version": "7.0.7",
+            "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.7.tgz",
+            "integrity": "sha512-dBtBbrc+qTHy1WdfHYjBwRln4+LWqASWakLHsWHR2NWHIFkv4W3O070IGoGLEBrJBvct3r0L1BUPuvURi7kYUQ=="
         },
         "@types/babylon": {
-            "version": "6.16.6",
-            "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.6.tgz",
-            "integrity": "sha512-G4yqdVlhr6YhzLXFKy5F7HtRBU8Y23+iWy7UKthMq/OSQnL1hbsoeXESQ2LY8zEDlknipDG3nRGhUC9tkwvy/w==",
+            "version": "6.16.5",
+            "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.5.tgz",
+            "integrity": "sha512-xH2e58elpj1X4ynnKp9qSnWlsRTIs6n3tgLGNfwAGHwePw0mulHQllV34n0T25uYSu1k0hRKkWXF890B1yS47w==",
             "requires": {
                 "@types/babel-types": "*"
             }
@@ -895,7 +1190,7 @@
         "@types/bcrypt-nodejs": {
             "version": "0.0.30",
             "resolved": "https://registry.npmjs.org/@types/bcrypt-nodejs/-/bcrypt-nodejs-0.0.30.tgz",
-            "integrity": "sha512-gSWCu7EOXhcM0FYM8tanV0deaX1VM07sgBS8dSUmgnAQ5/3Xn6k+mWF+ZfE+c/OCW14IVnNdTOKcHgvL78oDFQ==",
+            "integrity": "sha1-TN2WtJKTs5MhIuS34pVD415rrlg=",
             "dev": true
         },
         "@types/bezier-js": {
@@ -920,18 +1215,11 @@
             }
         },
         "@types/bson": {
-            "version": "4.0.5",
-            "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.5.tgz",
-            "integrity": "sha512-vVLwMUqhYJSQ/WKcE60eFqcyuWse5fGH+NMAXHuKrUAPoryq3ATxk5o4bgYNtg5aOM4APVg7Hnb3ASqUYG0PKg==",
+            "version": "4.0.2",
+            "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.2.tgz",
+            "integrity": "sha512-+uWmsejEHfmSjyyM/LkrP0orfE2m5Mx9Xel4tXNeqi1ldK5XMQcDsFkBmLDtuyKUbxj2jGDo0H240fbCRJZo7Q==",
             "requires": {
                 "@types/node": "*"
-            },
-            "dependencies": {
-                "@types/node": {
-                    "version": "17.0.41",
-                    "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.41.tgz",
-                    "integrity": "sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw=="
-                }
             }
         },
         "@types/cacheable-request": {
@@ -943,13 +1231,6 @@
                 "@types/keyv": "*",
                 "@types/node": "*",
                 "@types/responselike": "*"
-            },
-            "dependencies": {
-                "@types/node": {
-                    "version": "17.0.41",
-                    "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.41.tgz",
-                    "integrity": "sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw=="
-                }
             }
         },
         "@types/caseless": {
@@ -959,9 +1240,9 @@
             "dev": true
         },
         "@types/chai": {
-            "version": "4.3.1",
-            "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.1.tgz",
-            "integrity": "sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ==",
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz",
+            "integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==",
             "dev": true
         },
         "@types/color": {
@@ -985,8 +1266,7 @@
         "@types/color-name": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
-            "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
-            "dev": true
+            "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
         },
         "@types/connect": {
             "version": "3.4.35",
@@ -1007,9 +1287,9 @@
             }
         },
         "@types/cookie-parser": {
-            "version": "1.4.3",
-            "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.3.tgz",
-            "integrity": "sha512-CqSKwFwefj4PzZ5n/iwad/bow2hTCh0FlNAeWLtQM3JA/NX/iYagIpWG2cf1bQKQ2c9gU2log5VUCrn7LDOs0w==",
+            "version": "1.4.2",
+            "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.2.tgz",
+            "integrity": "sha512-uwcY8m6SDQqciHsqcKDGbo10GdasYsPCYkH3hVegj9qAah6pX5HivOnOuI3WYmyQMnOATV39zv/Ybs0bC/6iVg==",
             "dev": true,
             "requires": {
                 "@types/express": "*"
@@ -1085,9 +1365,9 @@
             }
         },
         "@types/eslint": {
-            "version": "8.4.3",
-            "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.3.tgz",
-            "integrity": "sha512-YP1S7YJRMPs+7KZKDb9G63n8YejIwW9BALq7a5j2+H4yl6iOv9CB29edho+cuFRrvmJbbaH2yiVChKLJVysDGw==",
+            "version": "8.4.1",
+            "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz",
+            "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==",
             "dev": true,
             "requires": {
                 "@types/estree": "*",
@@ -1188,13 +1468,6 @@
             "requires": {
                 "@types/events": "*",
                 "@types/node": "*"
-            },
-            "dependencies": {
-                "@types/node": {
-                    "version": "17.0.41",
-                    "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.41.tgz",
-                    "integrity": "sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw=="
-                }
             }
         },
         "@types/geojson": {
@@ -1230,9 +1503,9 @@
             }
         },
         "@types/google.maps": {
-            "version": "3.49.1",
-            "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.49.1.tgz",
-            "integrity": "sha512-Sbl5anucT7LUcUxXsRxkCozHdXIkUiY+Tyru+OVl5rot0+VIZuuulmABC7X+nF7rL7BRTAguSBSAD/e/AfIkkA=="
+            "version": "3.46.1",
+            "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.46.1.tgz",
+            "integrity": "sha512-GAa5ZWYgXG50yLXybb7A824esGm/L0LKHS7qD0qkP0IA/Qp5r922P9tmYcbCkGEf3Zgf7Ukbp7l08/IGIJuQwQ=="
         },
         "@types/hoist-non-react-statics": {
             "version": "3.3.1",
@@ -1241,23 +1514,6 @@
             "requires": {
                 "@types/react": "*",
                 "hoist-non-react-statics": "^3.3.0"
-            },
-            "dependencies": {
-                "@types/react": {
-                    "version": "18.0.12",
-                    "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.12.tgz",
-                    "integrity": "sha512-duF1OTASSBQtcigUvhuiTB1Ya3OvSy+xORCiEf20H0P0lzx+/KeVsA99U5UjLXSbyo1DRJDlLKqTeM1ngosqtg==",
-                    "requires": {
-                        "@types/prop-types": "*",
-                        "@types/scheduler": "*",
-                        "csstype": "^3.0.2"
-                    }
-                },
-                "csstype": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
-                }
             }
         },
         "@types/html-minifier-terser": {
@@ -1287,24 +1543,12 @@
                 "@types/node": "*",
                 "@types/parse5": "*",
                 "@types/tough-cookie": "*"
-            },
-            "dependencies": {
-                "@types/node": {
-                    "version": "17.0.41",
-                    "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.41.tgz",
-                    "integrity": "sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw=="
-                }
             }
         },
-        "@types/json-buffer": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/@types/json-buffer/-/json-buffer-3.0.0.tgz",
-            "integrity": "sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ=="
-        },
         "@types/json-schema": {
-            "version": "7.0.11",
-            "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
-            "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ=="
+            "version": "7.0.9",
+            "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
+            "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ=="
         },
         "@types/keygrip": {
             "version": "1.0.2",
@@ -1313,18 +1557,11 @@
             "dev": true
         },
         "@types/keyv": {
-            "version": "3.1.4",
-            "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
-            "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
+            "version": "3.1.3",
+            "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz",
+            "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==",
             "requires": {
                 "@types/node": "*"
-            },
-            "dependencies": {
-                "@types/node": {
-                    "version": "17.0.41",
-                    "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.41.tgz",
-                    "integrity": "sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw=="
-                }
             }
         },
         "@types/libxmljs": {
@@ -1337,9 +1574,9 @@
             }
         },
         "@types/lodash": {
-            "version": "4.14.182",
-            "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz",
-            "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==",
+            "version": "4.14.179",
+            "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.179.tgz",
+            "integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w==",
             "dev": true
         },
         "@types/mime": {
@@ -1373,16 +1610,10 @@
             "version": "3.6.20",
             "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz",
             "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==",
+            "dev": true,
             "requires": {
                 "@types/bson": "*",
                 "@types/node": "*"
-            },
-            "dependencies": {
-                "@types/node": {
-                    "version": "17.0.41",
-                    "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.41.tgz",
-                    "integrity": "sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw=="
-                }
             }
         },
         "@types/mongoose": {
@@ -1397,8 +1628,7 @@
         "@types/node": {
             "version": "10.17.60",
             "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
-            "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==",
-            "dev": true
+            "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="
         },
         "@types/nodemailer": {
             "version": "4.6.8",
@@ -1435,9 +1665,9 @@
             "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g=="
         },
         "@types/passport": {
-            "version": "1.0.8",
-            "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.8.tgz",
-            "integrity": "sha512-Gdcvis7+7G/Mobm+25AeFi+oe5teBhHzpbCOFWeN10Bj8tnoEE1L5lkraQjzmDEKkJQuM7xSJUGIFGl/giyRfQ==",
+            "version": "1.0.7",
+            "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.7.tgz",
+            "integrity": "sha512-JtswU8N3kxBYgo+n9of7C97YQBT+AYPP2aBfNGTzABqPAZnK/WOAaKfh3XesUYMZRrXFuoPc2Hv0/G/nQFveHw==",
             "dev": true,
             "requires": {
                 "@types/express": "*"
@@ -1496,9 +1726,9 @@
             }
         },
         "@types/prop-types": {
-            "version": "15.7.5",
-            "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
-            "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
+            "version": "15.7.4",
+            "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
+            "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ=="
         },
         "@types/prosemirror-commands": {
             "version": "1.0.4",
@@ -1565,9 +1795,9 @@
             }
         },
         "@types/prosemirror-model": {
-            "version": "1.16.2",
-            "resolved": "https://registry.npmjs.org/@types/prosemirror-model/-/prosemirror-model-1.16.2.tgz",
-            "integrity": "sha512-1XPJopkKP3oHSBP61uuSuW13DIDZPWvAzP6Pv2/6mixk8EBPUeRGIW548DjJTicMo23gEg1zvCZy9asblQdWag==",
+            "version": "1.16.1",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-model/-/prosemirror-model-1.16.1.tgz",
+            "integrity": "sha512-SrrCe2cHlYrQ9o55e2i/c3wt1yRajTTpRLvzfmB+2DWjWEbBLTByVWyjrdpKtQTxAaTeU2aeDGo1iuwl/jF27w==",
             "dev": true,
             "requires": {
                 "@types/orderedmap": "*"
@@ -1585,9 +1815,9 @@
             }
         },
         "@types/prosemirror-state": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@types/prosemirror-state/-/prosemirror-state-1.3.0.tgz",
-            "integrity": "sha512-nMdUF6w8B++NH4V54X+4GvDty7M02UfuHQW0s1AS25Z4ZrOW4RSY2+s57doXBbeMSjzYV/QoMxCY2sT3KQ2VdQ==",
+            "version": "1.2.8",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-state/-/prosemirror-state-1.2.8.tgz",
+            "integrity": "sha512-mq9uyQWcpu8jeamO6Callrdvf/e1H/aRLR2kZWSpZrPHctEsxWHBbluD/wqVjXBRIOoMHLf6ZvOkrkmGLoCHVA==",
             "dev": true,
             "requires": {
                 "@types/prosemirror-model": "*",
@@ -1596,18 +1826,18 @@
             }
         },
         "@types/prosemirror-transform": {
-            "version": "1.4.2",
-            "resolved": "https://registry.npmjs.org/@types/prosemirror-transform/-/prosemirror-transform-1.4.2.tgz",
-            "integrity": "sha512-FZNzjYm6YUkb1XXOrw2193TiFzwM92ui1nycNaRSd5JDbugf9yBLkXm4Rq3HGJJxBBkRcUE8niqUW5aWlXQQiQ==",
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-transform/-/prosemirror-transform-1.1.5.tgz",
+            "integrity": "sha512-Wr2HXaEF4JPklWpC17RTxE6PxyU54Taqk5FMhK1ojgcN93J+GpkYW8s0mD3rl7KfTmlhVwZPCHE9o0cYf2Go5A==",
             "dev": true,
             "requires": {
                 "@types/prosemirror-model": "*"
             }
         },
         "@types/prosemirror-view": {
-            "version": "1.23.3",
-            "resolved": "https://registry.npmjs.org/@types/prosemirror-view/-/prosemirror-view-1.23.3.tgz",
-            "integrity": "sha512-T5dPDmZiXAazJVSvnx55D6h4mcpiH2q2wTyO9zIeOdox5zx964+zcDl9dFNaXG3qCGlERwMPckhBZL1HCxyygw==",
+            "version": "1.23.1",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-view/-/prosemirror-view-1.23.1.tgz",
+            "integrity": "sha512-6e1B2oKUnhmZPUrsVvYjDqeVjE6jGezygjtoHsAK4ZENAxHzHqy5NT4jUvdPTWjCYeH0t2Y7pSfRPNrPIyQX4A==",
             "dev": true,
             "requires": {
                 "@types/prosemirror-model": "*",
@@ -1637,10 +1867,9 @@
             }
         },
         "@types/react": {
-            "version": "16.14.26",
-            "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.26.tgz",
-            "integrity": "sha512-c/5CYyciOO4XdFcNhZW1O2woVx86k4T+DO2RorHZL7EhitkNQgSD/SgpdZJAUJa/qjVgOmTM44gHkAdZSXeQuQ==",
-            "dev": true,
+            "version": "16.14.23",
+            "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.23.tgz",
+            "integrity": "sha512-WngBZLuSkP4IAgPi0HOsGCHo6dn3CcuLQnCfC17VbA7YBgipZiZoTOhObwl/93DsFW0Y2a/ZXeonpW4DxirEJg==",
             "requires": {
                 "@types/prop-types": "*",
                 "@types/scheduler": "*",
@@ -1648,10 +1877,9 @@
             },
             "dependencies": {
                 "csstype": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
-                    "dev": true
+                    "version": "3.0.10",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
+                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
                 }
             }
         },
@@ -1694,9 +1922,9 @@
             }
         },
         "@types/react-dom": {
-            "version": "16.9.16",
-            "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.16.tgz",
-            "integrity": "sha512-Oqc0RY4fggGA3ltEgyPLc3IV9T73IGoWjkONbsyJ3ZBn+UPPCYpU2ec0i3cEbJuEdZtkqcCF2l1zf2pBdgUGSg==",
+            "version": "16.9.14",
+            "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.14.tgz",
+            "integrity": "sha512-FIX2AVmPTGP30OUJ+0vadeIFJJ07Mh1m+U0rxfgyW34p3rTlXI+nlenvAxNn4BP36YyI9IJ/+UJ7Wu22N1pI7A==",
             "dev": true,
             "requires": {
                 "@types/react": "^16"
@@ -1711,14 +1939,6 @@
                 "@types/react": "*"
             }
         },
-        "@types/react-icons": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/@types/react-icons/-/react-icons-3.0.0.tgz",
-            "integrity": "sha512-Vefs6LkLqF61vfV7AiAqls+vpR94q67gunhMueDznG+msAkrYgRxl7gYjNem/kZ+as2l2mNChmF1jRZzzQQtMg==",
-            "requires": {
-                "react-icons": "*"
-            }
-        },
         "@types/react-measure": {
             "version": "2.0.8",
             "resolved": "https://registry.npmjs.org/@types/react-measure/-/react-measure-2.0.8.tgz",
@@ -1729,56 +1949,22 @@
             }
         },
         "@types/react-reconciler": {
-            "version": "0.26.7",
-            "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.7.tgz",
-            "integrity": "sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==",
+            "version": "0.26.4",
+            "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.4.tgz",
+            "integrity": "sha512-bdx4aIBkQRDAnzc23JBFeZmVpmfLJHfHikmQukEt9qs4bQtq9f+PDbNwhR9u74FkIUyIDz1I1qJ8OF6RwadKpw==",
             "requires": {
                 "@types/react": "*"
-            },
-            "dependencies": {
-                "@types/react": {
-                    "version": "18.0.12",
-                    "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.12.tgz",
-                    "integrity": "sha512-duF1OTASSBQtcigUvhuiTB1Ya3OvSy+xORCiEf20H0P0lzx+/KeVsA99U5UjLXSbyo1DRJDlLKqTeM1ngosqtg==",
-                    "requires": {
-                        "@types/prop-types": "*",
-                        "@types/scheduler": "*",
-                        "csstype": "^3.0.2"
-                    }
-                },
-                "csstype": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
-                }
             }
         },
         "@types/react-redux": {
-            "version": "7.1.24",
-            "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.24.tgz",
-            "integrity": "sha512-7FkurKcS1k0FHZEtdbbgN8Oc6b+stGSfZYjQGicofJ0j4U0qIn/jaSvnP2pLwZKiai3/17xqqxkkrxTgN8UNbQ==",
+            "version": "7.1.22",
+            "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.22.tgz",
+            "integrity": "sha512-GxIA1kM7ClU73I6wg9IRTVwSO9GS+SAKZKe0Enj+82HMU6aoESFU2HNAdNi3+J53IaOHPiUfT3kSG4L828joDQ==",
             "requires": {
                 "@types/hoist-non-react-statics": "^3.3.0",
                 "@types/react": "*",
                 "hoist-non-react-statics": "^3.3.0",
                 "redux": "^4.0.0"
-            },
-            "dependencies": {
-                "@types/react": {
-                    "version": "18.0.12",
-                    "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.12.tgz",
-                    "integrity": "sha512-duF1OTASSBQtcigUvhuiTB1Ya3OvSy+xORCiEf20H0P0lzx+/KeVsA99U5UjLXSbyo1DRJDlLKqTeM1ngosqtg==",
-                    "requires": {
-                        "@types/prop-types": "*",
-                        "@types/scheduler": "*",
-                        "csstype": "^3.0.2"
-                    }
-                },
-                "csstype": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
-                }
             }
         },
         "@types/react-select": {
@@ -1798,23 +1984,6 @@
             "integrity": "sha512-fVQXjy/EYDbgraScgjDONA291McKqGrw0R0NeK639fx2bS4T19TnXMjg3FjOPlkI3qYTQtFTPADlRYysaQIMpA==",
             "requires": {
                 "@types/react": "*"
-            },
-            "dependencies": {
-                "@types/react": {
-                    "version": "18.0.12",
-                    "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.12.tgz",
-                    "integrity": "sha512-duF1OTASSBQtcigUvhuiTB1Ya3OvSy+xORCiEf20H0P0lzx+/KeVsA99U5UjLXSbyo1DRJDlLKqTeM1ngosqtg==",
-                    "requires": {
-                        "@types/prop-types": "*",
-                        "@types/scheduler": "*",
-                        "csstype": "^3.0.2"
-                    }
-                },
-                "csstype": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
-                }
             }
         },
         "@types/react-transition-group": {
@@ -1823,23 +1992,6 @@
             "integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==",
             "requires": {
                 "@types/react": "*"
-            },
-            "dependencies": {
-                "@types/react": {
-                    "version": "18.0.12",
-                    "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.12.tgz",
-                    "integrity": "sha512-duF1OTASSBQtcigUvhuiTB1Ya3OvSy+xORCiEf20H0P0lzx+/KeVsA99U5UjLXSbyo1DRJDlLKqTeM1ngosqtg==",
-                    "requires": {
-                        "@types/prop-types": "*",
-                        "@types/scheduler": "*",
-                        "csstype": "^3.0.2"
-                    }
-                },
-                "csstype": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
-                }
             }
         },
         "@types/reactcss": {
@@ -1861,19 +2013,6 @@
                 "@types/node": "*",
                 "@types/tough-cookie": "*",
                 "form-data": "^2.5.0"
-            },
-            "dependencies": {
-                "form-data": {
-                    "version": "2.5.1",
-                    "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
-                    "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
-                    "dev": true,
-                    "requires": {
-                        "asynckit": "^0.4.0",
-                        "combined-stream": "^1.0.6",
-                        "mime-types": "^2.1.12"
-                    }
-                }
             }
         },
         "@types/request-promise": {
@@ -1892,13 +2031,6 @@
             "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
             "requires": {
                 "@types/node": "*"
-            },
-            "dependencies": {
-                "@types/node": {
-                    "version": "17.0.41",
-                    "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.41.tgz",
-                    "integrity": "sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw=="
-                }
             }
         },
         "@types/reveal": {
@@ -1991,7 +2123,7 @@
         "@types/strip-bom": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz",
-            "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==",
+            "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=",
             "dev": true
         },
         "@types/strip-json-comments": {
@@ -2020,23 +2152,23 @@
             "integrity": "sha512-6JqTgijtfXcTJik8NtiNxr2L90ex6ElM00qilOGeUcrEsJLOdzLJSIkXHUYS+KPAYQYtRJQKD6XaXds3HjS+gg=="
         },
         "@types/tough-cookie": {
-            "version": "4.0.2",
-            "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz",
-            "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw=="
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz",
+            "integrity": "sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg=="
         },
         "@types/typescript": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/@types/typescript/-/typescript-2.0.0.tgz",
-            "integrity": "sha512-WMEWfMISiJ2QKyk5/dSdgL0ZwP//PZj0jmDU0hMh51FmLq4WIYzjlngsUQZXejQL+QtkXJUOGjb3G3UCvgZuSQ==",
+            "integrity": "sha1-xDNTnJi64oaCswfqp6D9IRW4PCg=",
             "dev": true,
             "requires": {
                 "typescript": "*"
             }
         },
         "@types/uglify-js": {
-            "version": "3.13.3",
-            "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.3.tgz",
-            "integrity": "sha512-9dmBYXt/rKxedUXfCvXSxyiPvpDXLkiRlv17DnqdhS+pRustL1967rI1jZVt1xysTO+xJGMoZzcy3cWC9+b6Tw==",
+            "version": "3.13.1",
+            "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.1.tgz",
+            "integrity": "sha512-O3MmRAk6ZuAKa9CHgg0Pr0+lUOqoMLpc9AS4R8ano2auvsg7IE8syF3Xh/NPr26TWklxYcqoEEFdzLLs1fV9PQ==",
             "dev": true,
             "requires": {
                 "source-map": "^0.6.1"
@@ -2108,6 +2240,14 @@
                 "@types/connect": "*",
                 "tapable": "^2.2.0",
                 "webpack": "^5"
+            },
+            "dependencies": {
+                "tapable": {
+                    "version": "2.2.1",
+                    "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+                    "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+                    "dev": true
+                }
             }
         },
         "@types/webpack-sources": {
@@ -2122,9 +2262,9 @@
             },
             "dependencies": {
                 "source-map": {
-                    "version": "0.7.4",
-                    "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
-                    "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+                    "version": "0.7.3",
+                    "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+                    "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
                     "dev": true
                 }
             }
@@ -2135,23 +2275,6 @@
             "integrity": "sha512-e1DZGD+eH19BnllTWCGXAdrMa2kI53wEMuhn/d+wUmnu8//ZI6BiuK/EPdw07fI4+tlyo5qdPZdXdpkoXHJVOw==",
             "requires": {
                 "@types/react": "*"
-            },
-            "dependencies": {
-                "@types/react": {
-                    "version": "18.0.12",
-                    "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.12.tgz",
-                    "integrity": "sha512-duF1OTASSBQtcigUvhuiTB1Ya3OvSy+xORCiEf20H0P0lzx+/KeVsA99U5UjLXSbyo1DRJDlLKqTeM1ngosqtg==",
-                    "requires": {
-                        "@types/prop-types": "*",
-                        "@types/scheduler": "*",
-                        "csstype": "^3.0.2"
-                    }
-                },
-                "csstype": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
-                }
             }
         },
         "@types/xregexp": {
@@ -2164,20 +2287,12 @@
             }
         },
         "@types/yauzl": {
-            "version": "2.10.0",
-            "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz",
-            "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==",
+            "version": "2.9.1",
+            "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz",
+            "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==",
             "optional": true,
             "requires": {
                 "@types/node": "*"
-            },
-            "dependencies": {
-                "@types/node": {
-                    "version": "17.0.41",
-                    "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.41.tgz",
-                    "integrity": "sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw==",
-                    "optional": true
-                }
             }
         },
         "@types/youtube": {
@@ -2381,17 +2496,17 @@
         "Base64": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/Base64/-/Base64-0.2.1.tgz",
-            "integrity": "sha512-reGEWshDmTDQDsCec/HduOO9Wyj6yMOupMfhIf3ugN1TDlK2NQW4DDJSqNNtp380SNcvRfXtO8HSCQot0d0SMw=="
+            "integrity": "sha1-ujpCMHCOGGcFBl5mur3Uw1z2ACg="
         },
         "D": {
             "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
-            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA=="
+            "resolved": "https://registry.npmjs.org/D/-/D-1.0.0.tgz",
+            "integrity": "sha512-nQvrCBu7K2pSSEtIM0EEF03FVjcczCXInMt3moLNFbjlWx6bZrX72uT6/1uAXDbnzGUAx9gTyDiQ+vrFi663oA=="
         },
         "abab": {
-            "version": "2.0.6",
-            "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
-            "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz",
+            "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==",
             "dev": true
         },
         "abbrev": {
@@ -2414,17 +2529,32 @@
             "requires": {
                 "mime-types": "~2.1.34",
                 "negotiator": "0.6.3"
+            },
+            "dependencies": {
+                "mime-db": {
+                    "version": "1.51.0",
+                    "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
+                    "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g=="
+                },
+                "mime-types": {
+                    "version": "2.1.34",
+                    "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
+                    "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
+                    "requires": {
+                        "mime-db": "1.51.0"
+                    }
+                }
             }
         },
         "acorn": {
-            "version": "8.7.1",
-            "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
-            "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A=="
+            "version": "3.3.0",
+            "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
+            "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo="
         },
         "acorn-globals": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz",
-            "integrity": "sha512-uWttZCk96+7itPxK8xCzY86PnxKTMrReKDqrHzv42VQY0K30PUO8WY13WMOuI+cOdX4EIdzdvQ8k6jkuGRFMYw==",
+            "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=",
             "requires": {
                 "acorn": "^4.0.4"
             },
@@ -2432,7 +2562,7 @@
                 "acorn": {
                     "version": "4.0.13",
                     "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
-                    "integrity": "sha512-fu2ygVGuMmlzG8ZeRJ0bvR41nsAkxxhbyk8bZ1SS521Z7vmgJFTQQlfz/Mp/nJexGBz+v8sC9bM6+lNgskt4Ug=="
+                    "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c="
                 }
             }
         },
@@ -2461,35 +2591,30 @@
         "after": {
             "version": "0.8.2",
             "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
-            "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA=="
+            "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
         },
         "agent-base": {
-            "version": "6.0.2",
-            "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
-            "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz",
+            "integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==",
             "requires": {
                 "debug": "4"
             },
             "dependencies": {
                 "debug": {
-                    "version": "4.3.4",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
-                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+                    "version": "4.1.1",
+                    "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": {
-                    "version": "2.1.2",
-                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
                 }
             }
         },
         "ajv": {
-            "version": "6.12.6",
-            "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
-            "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+            "version": "6.12.2",
+            "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
+            "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==",
             "requires": {
                 "fast-deep-equal": "^3.1.1",
                 "fast-json-stable-stringify": "^2.0.0",
@@ -2511,9 +2636,9 @@
             },
             "dependencies": {
                 "ajv": {
-                    "version": "8.11.0",
-                    "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
-                    "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
+                    "version": "8.10.0",
+                    "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz",
+                    "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==",
                     "requires": {
                         "fast-deep-equal": "^3.1.1",
                         "json-schema-traverse": "^1.0.0",
@@ -2529,14 +2654,14 @@
             }
         },
         "ajv-keywords": {
-            "version": "3.5.2",
-            "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
-            "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="
+            "version": "3.4.1",
+            "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz",
+            "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ=="
         },
         "align-text": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
-            "integrity": "sha512-GrTZLRpmp6wIC2ztrWW9MjjTgSKccffgFagbNDOX95/dcjEcYZibYTeaOntySQLcdw1ztBoFkviiUvTMbb9MYg==",
+            "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
             "requires": {
                 "kind-of": "^3.0.2",
                 "longest": "^1.0.1",
@@ -2551,7 +2676,7 @@
                 "kind-of": {
                     "version": "3.2.2",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                    "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+                    "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -2561,12 +2686,12 @@
         "amdefine": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
-            "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg=="
+            "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU="
         },
         "ansi-align": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz",
-            "integrity": "sha512-TdlOggdA/zURfMYa7ABC66j+oqfMew58KpJMbUlH3bcZP1b+cBHIHDDn5uH9INsxrHBPjsqM0tDB4jPTF/vgJA==",
+            "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=",
             "requires": {
                 "string-width": "^2.0.0"
             }
@@ -2598,9 +2723,9 @@
             "dev": true
         },
         "ansi-regex": {
-            "version": "3.0.1",
-            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
-            "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw=="
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+            "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
         },
         "ansi-styles": {
             "version": "3.2.1",
@@ -2613,7 +2738,7 @@
         "any-promise": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
-            "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
+            "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
         },
         "anymatch": {
             "version": "2.0.0",
@@ -2627,7 +2752,7 @@
                 "normalize-path": {
                     "version": "2.1.1",
                     "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
-                    "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
+                    "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
                     "requires": {
                         "remove-trailing-separator": "^1.0.1"
                     }
@@ -2635,9 +2760,9 @@
             }
         },
         "aproba": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
-            "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+            "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
         },
         "archiver": {
             "version": "3.1.1",
@@ -2687,12 +2812,28 @@
             }
         },
         "are-we-there-yet": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
-            "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
+            "version": "1.1.5",
+            "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": "^3.6.0"
+                "readable-stream": "^2.0.6"
+            },
+            "dependencies": {
+                "readable-stream": {
+                    "version": "2.3.7",
+                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+                    "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+                    "requires": {
+                        "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"
+                    }
+                }
             }
         },
         "arg": {
@@ -2712,7 +2853,7 @@
         "arr-diff": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-            "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA=="
+            "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA="
         },
         "arr-flatten": {
             "version": "1.1.0",
@@ -2722,7 +2863,7 @@
         "arr-union": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
-            "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q=="
+            "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ="
         },
         "array-back": {
             "version": "2.0.0",
@@ -2745,9 +2886,9 @@
             },
             "dependencies": {
                 "@types/node": {
-                    "version": "12.20.55",
-                    "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
-                    "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="
+                    "version": "12.12.37",
+                    "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.37.tgz",
+                    "integrity": "sha512-4mXKoDptrXAwZErQHrLzpe0FN/0Wmf5JRniSVIdwUrtDf9wnmEV1teCNLBo/TwuXhkK/bVegoEn/wmb+x0AuPg=="
                 },
                 "glob": {
                     "version": "7.1.3",
@@ -2762,14 +2903,6 @@
                         "path-is-absolute": "^1.0.0"
                     }
                 },
-                "minimatch": {
-                    "version": "3.0.4",
-                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
-                    "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
-                    "requires": {
-                        "brace-expansion": "^1.1.7"
-                    }
-                },
                 "mocha": {
                     "version": "6.2.3",
                     "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.3.tgz",
@@ -2813,23 +2946,23 @@
         "array-equal": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
-            "integrity": "sha512-H3LU5RLiSsGXPhN+Nipar0iR0IofH+8r89G2y1tBKxQ/agagKyAjhkAFDRBfodP2caPrNKHpAWNIM/c9yeL7uA==",
+            "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
             "dev": true
         },
         "array-find-index": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
-            "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw=="
+            "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E="
         },
         "array-flatten": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
-            "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
+            "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
         },
         "array-union": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
-            "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
+            "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
             "dev": true,
             "requires": {
                 "array-uniq": "^1.0.1"
@@ -2838,25 +2971,13 @@
         "array-uniq": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
-            "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
+            "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
             "dev": true
         },
         "array-unique": {
             "version": "0.3.2",
             "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-            "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ=="
-        },
-        "array.prototype.reduce": {
-            "version": "1.0.4",
-            "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz",
-            "integrity": "sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw==",
-            "requires": {
-                "call-bind": "^1.0.2",
-                "define-properties": "^1.1.3",
-                "es-abstract": "^1.19.2",
-                "es-array-method-boxes-properly": "^1.0.0",
-                "is-string": "^1.0.7"
-            }
+            "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="
         },
         "arraybuffer.slice": {
             "version": "0.0.7",
@@ -2871,7 +2992,7 @@
         "asap": {
             "version": "2.0.6",
             "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
-            "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="
+            "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
         },
         "asn1": {
             "version": "0.2.6",
@@ -2884,7 +3005,7 @@
         "assert-plus": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-            "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="
+            "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
         },
         "assertion-error": {
             "version": "1.1.0",
@@ -2894,12 +3015,12 @@
         "assign-symbols": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
-            "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw=="
+            "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c="
         },
         "async": {
-            "version": "2.6.4",
-            "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
-            "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
+            "version": "2.6.3",
+            "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
+            "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
             "requires": {
                 "lodash": "^4.17.14"
             }
@@ -2912,7 +3033,7 @@
         "async-foreach": {
             "version": "0.1.3",
             "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz",
-            "integrity": "sha512-VUeSMD8nEGBWaZK4lizI1sf3yEC7pnAQ/mrI7pC2fBz2s/tq5jWWEngTwaf0Gruu/OoXRGLGg1XFqpYBiGTYJA=="
+            "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI="
         },
         "async-limiter": {
             "version": "1.0.1",
@@ -2923,7 +3044,7 @@
         "asynckit": {
             "version": "0.4.0",
             "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-            "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+            "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
         },
         "atob": {
             "version": "2.1.2",
@@ -2949,31 +3070,12 @@
                 "mkdirp": "^0.5.1",
                 "source-map-support": "^0.5.3",
                 "webpack-log": "^1.2.0"
-            },
-            "dependencies": {
-                "enhanced-resolve": {
-                    "version": "4.5.0",
-                    "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz",
-                    "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==",
-                    "dev": true,
-                    "requires": {
-                        "graceful-fs": "^4.1.2",
-                        "memory-fs": "^0.5.0",
-                        "tapable": "^1.0.0"
-                    }
-                },
-                "tapable": {
-                    "version": "1.1.3",
-                    "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
-                    "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
-                    "dev": true
-                }
             }
         },
         "aws-sign2": {
             "version": "0.7.0",
             "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
-            "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA=="
+            "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
         },
         "aws4": {
             "version": "1.11.0",
@@ -2986,17 +3088,40 @@
             "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
             "requires": {
                 "follow-redirects": "1.5.10"
+            },
+            "dependencies": {
+                "debug": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+                    "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+                    "requires": {
+                        "ms": "2.0.0"
+                    }
+                },
+                "follow-redirects": {
+                    "version": "1.5.10",
+                    "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
+                    "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
+                    "requires": {
+                        "debug": "=3.1.0"
+                    }
+                },
+                "ms": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                }
             }
         },
         "babel": {
             "version": "6.23.0",
             "resolved": "https://registry.npmjs.org/babel/-/babel-6.23.0.tgz",
-            "integrity": "sha512-ZDcCaI8Vlct8PJ3DvmyqUz+5X2Ylz3ZuuItBe/74yXosk2dwyVo/aN7MCJ8HJzhnnJ+6yP4o+lDgG9MBe91DLA=="
+            "integrity": "sha1-0NHn2APpdHZb7qMjLU4VPA77kPQ="
         },
         "babel-code-frame": {
             "version": "6.26.0",
             "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
-            "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==",
+            "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
             "dev": true,
             "requires": {
                 "chalk": "^1.1.3",
@@ -3007,19 +3132,19 @@
                 "ansi-regex": {
                     "version": "2.1.1",
                     "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-                    "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+                    "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
                     "dev": true
                 },
                 "ansi-styles": {
                     "version": "2.2.1",
                     "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-                    "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
+                    "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
                     "dev": true
                 },
                 "chalk": {
                     "version": "1.1.3",
                     "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-                    "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
+                    "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
                     "dev": true,
                     "requires": {
                         "ansi-styles": "^2.2.1",
@@ -3032,7 +3157,7 @@
                 "js-tokens": {
                     "version": "3.0.2",
                     "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
-                    "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==",
+                    "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
                     "dev": true
                 },
                 "strip-ansi": {
@@ -3080,26 +3205,25 @@
             }
         },
         "babel-plugin-styled-components": {
-            "version": "2.0.7",
-            "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.7.tgz",
-            "integrity": "sha512-i7YhvPgVqRKfoQ66toiZ06jPNA3p6ierpfUuEWxNF+fV27Uv5gxBkf8KZLHUCc1nFA9j6+80pYoIpqCeyW3/bA==",
+            "version": "1.10.7",
+            "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.10.7.tgz",
+            "integrity": "sha512-MBMHGcIA22996n9hZRf/UJLVVgkEOITuR2SvjHLb5dSTUyR4ZRGn+ngITapes36FI3WLxZHfRhkA1ffHxihOrg==",
             "requires": {
-                "@babel/helper-annotate-as-pure": "^7.16.0",
-                "@babel/helper-module-imports": "^7.16.0",
+                "@babel/helper-annotate-as-pure": "^7.0.0",
+                "@babel/helper-module-imports": "^7.0.0",
                 "babel-plugin-syntax-jsx": "^6.18.0",
-                "lodash": "^4.17.11",
-                "picomatch": "^2.3.0"
+                "lodash": "^4.17.11"
             }
         },
         "babel-plugin-syntax-jsx": {
             "version": "6.18.0",
             "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
-            "integrity": "sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw=="
+            "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY="
         },
         "babel-runtime": {
             "version": "6.26.0",
             "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
-            "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
+            "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
             "requires": {
                 "core-js": "^2.4.0",
                 "regenerator-runtime": "^0.11.0"
@@ -3120,7 +3244,7 @@
         "babel-types": {
             "version": "6.26.0",
             "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
-            "integrity": "sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g==",
+            "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
             "requires": {
                 "babel-runtime": "^6.26.0",
                 "esutils": "^2.0.2",
@@ -3143,12 +3267,12 @@
         "backo2": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
-            "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA=="
+            "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
         },
         "balanced-match": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
-            "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+            "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
         },
         "base": {
             "version": "0.11.2",
@@ -3167,7 +3291,7 @@
                 "define-property": {
                     "version": "1.0.0",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-                    "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+                    "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
                     "requires": {
                         "is-descriptor": "^1.0.0"
                     }
@@ -3203,17 +3327,17 @@
         "base16": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz",
-            "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ=="
+            "integrity": "sha1-4pf2DX7BAUp6lxo568ipjAtoHnA="
         },
         "base64-arraybuffer": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
-            "integrity": "sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg=="
+            "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
         },
         "base64-js": {
-            "version": "1.5.1",
-            "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
-            "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
+            "version": "1.3.1",
+            "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
+            "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
         },
         "base64id": {
             "version": "2.0.0",
@@ -3228,18 +3352,18 @@
         "batch": {
             "version": "0.6.1",
             "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
-            "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==",
+            "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=",
             "dev": true
         },
         "bcrypt-nodejs": {
             "version": "0.0.3",
             "resolved": "https://registry.npmjs.org/bcrypt-nodejs/-/bcrypt-nodejs-0.0.3.tgz",
-            "integrity": "sha512-NmTbLm867btBHCBZ222FQXkQKzecB0KG6pTXFa6NeTVZaSnLfCsx7EK2PL3J+kX8xJThUquEBbhimRCKKZX9zA=="
+            "integrity": "sha1-xgkX8m3CNWYVZsaBBhwwPCsohCs="
         },
         "bcrypt-pbkdf": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
-            "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
+            "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
             "requires": {
                 "tweetnacl": "^0.14.3"
             }
@@ -3247,7 +3371,7 @@
         "bezier-curve": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/bezier-curve/-/bezier-curve-1.0.0.tgz",
-            "integrity": "sha512-h6uZJ6qdFfswS1rIRericgouhTeiVi/MnH10OKtCu2IZzXa+ZcjaxRLHY4u/evRsJcxYbbiNkgWQj2Z4UIcpEQ=="
+            "integrity": "sha1-o9+v6rEqlMRicw1QeYxSqEBdc3k="
         },
         "bezier-js": {
             "version": "4.1.1",
@@ -3260,28 +3384,19 @@
             "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ=="
         },
         "bignumber.js": {
-            "version": "9.0.2",
-            "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz",
-            "integrity": "sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw=="
+            "version": "7.2.1",
+            "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz",
+            "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ=="
         },
         "binary-extensions": {
             "version": "1.13.1",
             "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
             "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw=="
         },
-        "bindings": {
-            "version": "1.5.0",
-            "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
-            "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
-            "optional": true,
-            "requires": {
-                "file-uri-to-path": "1.0.0"
-            }
-        },
         "bl": {
-            "version": "4.1.0",
-            "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
-            "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+            "version": "4.0.2",
+            "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz",
+            "integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==",
             "requires": {
                 "buffer": "^5.5.0",
                 "inherits": "^2.0.4",
@@ -3296,7 +3411,7 @@
         "block-stream": {
             "version": "0.0.9",
             "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
-            "integrity": "sha512-OorbnJVPII4DuUKbjARAe8u8EfqOmkEEaSFIyoQ7OjTHn6kafxWl0wLgoZ2rXaYd7MyLcDaU4TmhfxtwgcccMQ==",
+            "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
             "requires": {
                 "inherits": "~2.0.0"
             }
@@ -3312,22 +3427,20 @@
             "integrity": "sha512-6sTGh1OiUmuH8ftAYvUzALivoOmcnahinGmjZFI4puZVowXoKTn/bXtth7N1skW5AlezEOfjgFH4lNXHeNRQog=="
         },
         "body-parser": {
-            "version": "1.20.0",
-            "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz",
-            "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==",
+            "version": "1.19.2",
+            "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz",
+            "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==",
             "requires": {
                 "bytes": "3.1.2",
                 "content-type": "~1.0.4",
                 "debug": "2.6.9",
-                "depd": "2.0.0",
-                "destroy": "1.2.0",
-                "http-errors": "2.0.0",
+                "depd": "~1.1.2",
+                "http-errors": "1.8.1",
                 "iconv-lite": "0.4.24",
-                "on-finished": "2.4.1",
-                "qs": "6.10.3",
-                "raw-body": "2.5.1",
-                "type-is": "~1.6.18",
-                "unpipe": "1.0.0"
+                "on-finished": "~2.3.0",
+                "qs": "6.9.7",
+                "raw-body": "2.4.3",
+                "type-is": "~1.6.18"
             },
             "dependencies": {
                 "debug": {
@@ -3338,33 +3451,22 @@
                         "ms": "2.0.0"
                     }
                 },
-                "iconv-lite": {
-                    "version": "0.4.24",
-                    "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
-                    "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
-                    "requires": {
-                        "safer-buffer": ">= 2.1.2 < 3"
-                    }
-                },
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                 },
                 "qs": {
-                    "version": "6.10.3",
-                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
-                    "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
-                    "requires": {
-                        "side-channel": "^1.0.4"
-                    }
+                    "version": "6.9.7",
+                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz",
+                    "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw=="
                 }
             }
         },
         "bonjour": {
             "version": "3.5.0",
             "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
-            "integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==",
+            "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=",
             "dev": true,
             "requires": {
                 "array-flatten": "^2.1.0",
@@ -3386,7 +3488,7 @@
         "boolbase": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
-            "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
+            "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
         },
         "bootstrap": {
             "version": "4.6.1",
@@ -3415,7 +3517,7 @@
                 "camelcase": {
                     "version": "4.1.0",
                     "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
-                    "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw=="
+                    "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
                 }
             }
         },
@@ -3448,17 +3550,22 @@
                 "extend-shallow": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-                    "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+                    "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
                     "requires": {
                         "is-extendable": "^0.1.0"
                     }
+                },
+                "is-extendable": {
+                    "version": "0.1.1",
+                    "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+                    "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
                 }
             }
         },
         "browser-assert": {
             "version": "1.2.1",
             "resolved": "https://registry.npmjs.org/browser-assert/-/browser-assert-1.2.1.tgz",
-            "integrity": "sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ=="
+            "integrity": "sha1-mqpaKox0aFwq4Fv+Ru/WBvBowgA="
         },
         "browser-process-hrtime": {
             "version": "1.0.0",
@@ -3472,48 +3579,48 @@
             "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw=="
         },
         "browserslist": {
-            "version": "4.20.4",
-            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.4.tgz",
-            "integrity": "sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw==",
+            "version": "4.19.3",
+            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.3.tgz",
+            "integrity": "sha512-XK3X4xtKJ+Txj8G5c30B4gsm71s69lqXlkYui4s6EkKxuv49qjYlY6oVd+IFJ73d4YymtM3+djvvt/R/iJwwDg==",
             "requires": {
-                "caniuse-lite": "^1.0.30001349",
-                "electron-to-chromium": "^1.4.147",
+                "caniuse-lite": "^1.0.30001312",
+                "electron-to-chromium": "^1.4.71",
                 "escalade": "^3.1.1",
-                "node-releases": "^2.0.5",
+                "node-releases": "^2.0.2",
                 "picocolors": "^1.0.0"
             }
         },
         "bson": {
-            "version": "4.6.4",
-            "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.4.tgz",
-            "integrity": "sha512-TdQ3FzguAu5HKPPlr0kYQCyrYUYh8tFM+CMTpxjNzVzxeiJY00Rtuj3LXLHSgiGvmaWlZ8PE+4KyM2thqE38pQ==",
+            "version": "4.6.1",
+            "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.1.tgz",
+            "integrity": "sha512-I1LQ7Hz5zgwR4QquilLNZwbhPw0Apx7i7X9kGMBTsqPdml/03Q9NBtD9nt/19ahjlphktQImrnderxqpzeVDjw==",
             "requires": {
                 "buffer": "^5.6.0"
             }
         },
         "buffer": {
-            "version": "5.7.1",
-            "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
-            "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+            "version": "5.6.0",
+            "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
+            "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
             "requires": {
-                "base64-js": "^1.3.1",
-                "ieee754": "^1.1.13"
+                "base64-js": "^1.0.2",
+                "ieee754": "^1.1.4"
             }
         },
         "buffer-crc32": {
             "version": "0.2.13",
             "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
-            "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="
+            "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
         },
         "buffer-equal-constant-time": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
-            "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
+            "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
         },
         "buffer-from": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
-            "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+            "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
         },
         "buffer-indexof": {
             "version": "1.1.1",
@@ -3524,20 +3631,21 @@
         "buffer-shims": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz",
-            "integrity": "sha512-Zy8ZXMyxIT6RMTeY7OP/bDndfj6bwCan7SS98CEndS6deHwWPpseeHlwarNcBim+etXnF9HBc1non5JgDaJU1g=="
+            "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E="
         },
         "built-in-math-eval": {
             "version": "0.3.0",
             "resolved": "https://registry.npmjs.org/built-in-math-eval/-/built-in-math-eval-0.3.0.tgz",
-            "integrity": "sha512-5XD5cujru60ooKJ4sGZqoH5v2Xvgw7ezV54gJX/OnPkgDKoH3BnlMEi8xW6hl8xaEjxKHebgrsawroeZnGwIMA==",
+            "integrity": "sha1-JA3CHLOJQ5WIxhxGDrAHZJfvxBw=",
             "requires": {
                 "math-codegen": "^0.3.5"
             }
         },
         "builtin-modules": {
-            "version": "3.3.0",
-            "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
-            "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw=="
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+            "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+            "dev": true
         },
         "bytes": {
             "version": "3.1.2",
@@ -3565,12 +3673,6 @@
                 "y18n": "^4.0.0"
             },
             "dependencies": {
-                "chownr": {
-                    "version": "1.1.4",
-                    "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
-                    "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
-                    "dev": true
-                },
                 "lru-cache": {
                     "version": "4.1.5",
                     "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
@@ -3660,7 +3762,7 @@
         "caller-callsite": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
-            "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==",
+            "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
             "requires": {
                 "callsites": "^2.0.0"
             },
@@ -3668,14 +3770,14 @@
                 "callsites": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
-                    "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ=="
+                    "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA="
                 }
             }
         },
         "caller-path": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
-            "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==",
+            "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
             "requires": {
                 "caller-callsite": "^2.0.0"
             }
@@ -3692,8 +3794,15 @@
             "requires": {
                 "pascal-case": "^3.1.2",
                 "tslib": "^2.0.3"
-            }
-        },
+            },
+            "dependencies": {
+                "tslib": {
+                    "version": "2.3.1",
+                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+                    "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+                }
+            }
+        },
         "camelcase": {
             "version": "5.3.1",
             "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
@@ -3702,7 +3811,7 @@
         "camelcase-keys": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
-            "integrity": "sha512-bA/Z/DERHKqoEOrp+qeGKw1QlvEQkGZSc0XaY6VnTxZr+Kv1G5zFwttpjv8qxZ/sBPT4nthwZaAcsAZTJlSKXQ==",
+            "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
             "requires": {
                 "camelcase": "^2.0.0",
                 "map-obj": "^1.0.0"
@@ -3711,28 +3820,35 @@
                 "camelcase": {
                     "version": "2.1.1",
                     "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
-                    "integrity": "sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw=="
+                    "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8="
                 }
             }
         },
         "camelize": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz",
-            "integrity": "sha512-W2lPwkBkMZwFlPCXhIlYgxu+7gC/NUlCtdK652DAJ1JdgV0sTrvuPFshNPrFa1TY2JOkLhgdeEBplB4ezEa+xg=="
+            "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
         },
         "caniuse-lite": {
-            "version": "1.0.30001351",
-            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001351.tgz",
-            "integrity": "sha512-u+Ll+RDaiQEproTQjZLjZwyfNgNezA1fERMT7/54npcz+PkbVJUAHXMUz4bkXQYRPWrcFNO0Fbi1mwjfXg6N5g=="
+            "version": "1.0.30001312",
+            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz",
+            "integrity": "sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ=="
         },
         "canvas": {
-            "version": "2.9.1",
-            "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.9.1.tgz",
-            "integrity": "sha512-vSQti1uG/2gjv3x6QLOZw7TctfufaerTWbVe+NSduHxxLGB+qf3kFgQ6n66DSnuoINtVUjrLLIK2R+lxrBG07A==",
+            "version": "2.9.0",
+            "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.9.0.tgz",
+            "integrity": "sha512-0l93g7uxp7rMyr7H+XRQ28A3ud0dKIUTIEkUe1Dxh4rjUYN7B93+SjC3r1PDKA18xcQN87OFGgUnyw7LSgNLSQ==",
             "requires": {
                 "@mapbox/node-pre-gyp": "^1.0.0",
                 "nan": "^2.15.0",
                 "simple-get": "^3.0.3"
+            },
+            "dependencies": {
+                "nan": {
+                    "version": "2.15.0",
+                    "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
+                    "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ=="
+                }
             }
         },
         "capture-stack-trace": {
@@ -3743,12 +3859,12 @@
         "caseless": {
             "version": "0.12.0",
             "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
-            "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="
+            "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
         },
         "center-align": {
             "version": "0.1.3",
             "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
-            "integrity": "sha512-Baz3aNe2gd2LP2qk5U+sDk/m4oSuwSDcBfayTCTBoWpfIGO5XFxPmjILQII4NGiZjD6DoDI6kf7gKaxkf7s3VQ==",
+            "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
             "requires": {
                 "align-text": "^0.1.3",
                 "lazy-cache": "^1.0.3"
@@ -3786,12 +3902,12 @@
         "change-emitter": {
             "version": "0.1.6",
             "resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz",
-            "integrity": "sha512-YXzt1cQ4a2jqazhcuSWEOc1K2q8g9H6eWNsyZgi640LDzRWVQ2eDe+Y/kVdftH+vYdPF2rgDb3dLdpxE1jvAxw=="
+            "integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU="
         },
         "character-parser": {
             "version": "2.2.0",
             "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz",
-            "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==",
+            "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=",
             "requires": {
                 "is-regex": "^1.0.3"
             }
@@ -3799,12 +3915,12 @@
         "check-error": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
-            "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA=="
+            "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII="
         },
         "child_process": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz",
-            "integrity": "sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g=="
+            "integrity": "sha1-sffn/HPSXn/R1FWtyU4UODAYK1o="
         },
         "chokidar": {
             "version": "2.1.8",
@@ -3823,17 +3939,512 @@
                 "path-is-absolute": "^1.0.0",
                 "readdirp": "^2.2.1",
                 "upath": "^1.1.1"
+            },
+            "dependencies": {
+                "bindings": {
+                    "version": "1.5.0",
+                    "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+                    "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+                    "optional": true,
+                    "requires": {
+                        "file-uri-to-path": "1.0.0"
+                    }
+                },
+                "fsevents": {
+                    "version": "1.2.12",
+                    "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz",
+                    "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==",
+                    "optional": true,
+                    "requires": {
+                        "bindings": "^1.5.0",
+                        "nan": "^2.12.1",
+                        "node-pre-gyp": "*"
+                    },
+                    "dependencies": {
+                        "abbrev": {
+                            "version": "1.1.1",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "ansi-regex": {
+                            "version": "2.1.1",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "aproba": {
+                            "version": "1.2.0",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "are-we-there-yet": {
+                            "version": "1.1.5",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "delegates": "^1.0.0",
+                                "readable-stream": "^2.0.6"
+                            }
+                        },
+                        "balanced-match": {
+                            "version": "1.0.0",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "brace-expansion": {
+                            "version": "1.1.11",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "balanced-match": "^1.0.0",
+                                "concat-map": "0.0.1"
+                            }
+                        },
+                        "chownr": {
+                            "version": "1.1.4",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "code-point-at": {
+                            "version": "1.1.0",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "concat-map": {
+                            "version": "0.0.1",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "console-control-strings": {
+                            "version": "1.1.0",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "core-util-is": {
+                            "version": "1.0.2",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "debug": {
+                            "version": "3.2.6",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "ms": "^2.1.1"
+                            }
+                        },
+                        "deep-extend": {
+                            "version": "0.6.0",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "delegates": {
+                            "version": "1.0.0",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "detect-libc": {
+                            "version": "1.0.3",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "fs-minipass": {
+                            "version": "1.2.7",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "minipass": "^2.6.0"
+                            }
+                        },
+                        "fs.realpath": {
+                            "version": "1.0.0",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "gauge": {
+                            "version": "2.7.4",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "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": {
+                            "version": "7.1.6",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "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": {
+                            "version": "2.0.1",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "iconv-lite": {
+                            "version": "0.4.24",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "safer-buffer": ">= 2.1.2 < 3"
+                            }
+                        },
+                        "ignore-walk": {
+                            "version": "3.0.3",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "minimatch": "^3.0.4"
+                            }
+                        },
+                        "inflight": {
+                            "version": "1.0.6",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "once": "^1.3.0",
+                                "wrappy": "1"
+                            }
+                        },
+                        "inherits": {
+                            "version": "2.0.4",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "ini": {
+                            "version": "1.3.5",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "is-fullwidth-code-point": {
+                            "version": "1.0.0",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "number-is-nan": "^1.0.0"
+                            }
+                        },
+                        "isarray": {
+                            "version": "1.0.0",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "minimatch": {
+                            "version": "3.0.4",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "brace-expansion": "^1.1.7"
+                            }
+                        },
+                        "minimist": {
+                            "version": "1.2.5",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "minipass": {
+                            "version": "2.9.0",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "safe-buffer": "^5.1.2",
+                                "yallist": "^3.0.0"
+                            }
+                        },
+                        "minizlib": {
+                            "version": "1.3.3",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "minipass": "^2.9.0"
+                            }
+                        },
+                        "mkdirp": {
+                            "version": "0.5.3",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "minimist": "^1.2.5"
+                            }
+                        },
+                        "ms": {
+                            "version": "2.1.2",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "needle": {
+                            "version": "2.3.3",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "debug": "^3.2.6",
+                                "iconv-lite": "^0.4.4",
+                                "sax": "^1.2.4"
+                            }
+                        },
+                        "node-pre-gyp": {
+                            "version": "0.14.0",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "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.4.2"
+                            }
+                        },
+                        "nopt": {
+                            "version": "4.0.3",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "abbrev": "1",
+                                "osenv": "^0.1.4"
+                            }
+                        },
+                        "npm-bundled": {
+                            "version": "1.1.1",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "npm-normalize-package-bin": "^1.0.1"
+                            }
+                        },
+                        "npm-normalize-package-bin": {
+                            "version": "1.0.1",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "npm-packlist": {
+                            "version": "1.4.8",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "ignore-walk": "^3.0.1",
+                                "npm-bundled": "^1.0.1",
+                                "npm-normalize-package-bin": "^1.0.1"
+                            }
+                        },
+                        "npmlog": {
+                            "version": "4.1.2",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "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,
+                            "optional": true
+                        },
+                        "object-assign": {
+                            "version": "4.1.1",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "once": {
+                            "version": "1.4.0",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "wrappy": "1"
+                            }
+                        },
+                        "os-homedir": {
+                            "version": "1.0.2",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "os-tmpdir": {
+                            "version": "1.0.2",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "osenv": {
+                            "version": "0.1.5",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "os-homedir": "^1.0.0",
+                                "os-tmpdir": "^1.0.0"
+                            }
+                        },
+                        "path-is-absolute": {
+                            "version": "1.0.1",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "process-nextick-args": {
+                            "version": "2.0.1",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "rc": {
+                            "version": "1.2.8",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "deep-extend": "^0.6.0",
+                                "ini": "~1.3.0",
+                                "minimist": "^1.2.0",
+                                "strip-json-comments": "~2.0.1"
+                            }
+                        },
+                        "readable-stream": {
+                            "version": "2.3.7",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "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": {
+                            "version": "2.7.1",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "glob": "^7.1.3"
+                            }
+                        },
+                        "safe-buffer": {
+                            "version": "5.1.2",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "safer-buffer": {
+                            "version": "2.1.2",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "sax": {
+                            "version": "1.2.4",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "semver": {
+                            "version": "5.7.1",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "set-blocking": {
+                            "version": "2.0.0",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "signal-exit": {
+                            "version": "3.0.2",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "string-width": {
+                            "version": "1.0.2",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "code-point-at": "^1.0.0",
+                                "is-fullwidth-code-point": "^1.0.0",
+                                "strip-ansi": "^3.0.0"
+                            }
+                        },
+                        "string_decoder": {
+                            "version": "1.1.1",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "safe-buffer": "~5.1.0"
+                            }
+                        },
+                        "strip-ansi": {
+                            "version": "3.0.1",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "ansi-regex": "^2.0.0"
+                            }
+                        },
+                        "strip-json-comments": {
+                            "version": "2.0.1",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "tar": {
+                            "version": "4.4.13",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "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"
+                            }
+                        },
+                        "util-deprecate": {
+                            "version": "1.0.2",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "wide-align": {
+                            "version": "1.1.3",
+                            "bundled": true,
+                            "optional": true,
+                            "requires": {
+                                "string-width": "^1.0.2 || 2"
+                            }
+                        },
+                        "wrappy": {
+                            "version": "1.0.2",
+                            "bundled": true,
+                            "optional": true
+                        },
+                        "yallist": {
+                            "version": "3.1.1",
+                            "bundled": true,
+                            "optional": true
+                        }
+                    }
+                }
             }
         },
         "chownr": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
-            "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+            "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
         },
         "chrome": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/chrome/-/chrome-0.1.0.tgz",
-            "integrity": "sha512-6KYl20U4Taj6YipylsWr2etUvp9AElJKfGNSBmyGTymYmancnOb041ZNadolEZi2nboLXH7jMSqUmm4kpuTzfg==",
+            "integrity": "sha1-9h2beS/v6MGUxwVt3BAscmqGQyk=",
             "requires": {
                 "exeq": "^2.2.0",
                 "plist": "^1.1.0"
@@ -3853,7 +4464,7 @@
         "clamp": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz",
-            "integrity": "sha512-kgMuFyE78OC6Dyu3Dy7vcx4uy97EIbVxJB/B0eJ3bUNAkwdNcxYzgKltnyADiYwsR7SEqkkUPsEUT//OVS6XMA=="
+            "integrity": "sha1-ZqDmQBGBbjcZaCj9yMjBRzEshjQ="
         },
         "class-transformer": {
             "version": "0.2.3",
@@ -3874,7 +4485,7 @@
                 "define-property": {
                     "version": "0.2.5",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-                    "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+                    "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
                     "requires": {
                         "is-descriptor": "^0.1.0"
                     }
@@ -3882,14 +4493,14 @@
             }
         },
         "classnames": {
-            "version": "2.3.1",
-            "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
-            "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
+            "version": "2.2.6",
+            "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
+            "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
         },
         "clean-css": {
-            "version": "5.3.0",
-            "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.0.tgz",
-            "integrity": "sha512-YYuuxv4H/iNb1Z/5IbMRoxgrzjWGhOEFfd+groZ5dMCVkpENiMZmwspdrzBo9286JjM1gZJPAyL7ZIdzuvu2AQ==",
+            "version": "4.2.3",
+            "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
+            "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==",
             "requires": {
                 "source-map": "~0.6.0"
             },
@@ -3904,12 +4515,12 @@
         "cli-boxes": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
-            "integrity": "sha512-3Fo5wu8Ytle8q9iCzS4D2MWVL2X7JVWRiS1BnXbTFDhS9c/REkM9vd1AmabsoZoY5/dGi5TT9iKL8Kb6DeBRQg=="
+            "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM="
         },
         "clipboard": {
             "version": "1.7.1",
             "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-1.7.1.tgz",
-            "integrity": "sha512-smkaRaIQsrnKN1F3wd1/vY9Q+DeR4L8ZCXKeHCFC2j8RZuSBbuImcLdnIO4GTxmzJxQuDGNKkyfpGoPW7Ua5bQ==",
+            "integrity": "sha1-Ng1taUbpmnof7zleQrqStem1oWs=",
             "requires": {
                 "good-listener": "^1.2.2",
                 "select": "^1.1.2",
@@ -3934,7 +4545,7 @@
                 "camelcase": {
                     "version": "4.1.0",
                     "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
-                    "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw=="
+                    "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
                 },
                 "yargs-parser": {
                     "version": "7.0.0",
@@ -3957,9 +4568,9 @@
             },
             "dependencies": {
                 "ansi-regex": {
-                    "version": "4.1.1",
-                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
-                    "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
+                    "version": "4.1.0",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+                    "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
                 },
                 "string-width": {
                     "version": "3.1.0",
@@ -3984,7 +4595,7 @@
         "clj-fuzzy": {
             "version": "0.3.3",
             "resolved": "https://registry.npmjs.org/clj-fuzzy/-/clj-fuzzy-0.3.3.tgz",
-            "integrity": "sha512-9cyh9A8+OphDZeKIG21MgyDHWDkWxTvagwvFLVjtdi6eToFENF7iDLlKwhHrnBQRSQwprKNhazG053nE/UgwfQ=="
+            "integrity": "sha1-seU0MJHFIC28UlMoY+HEp4RX8D0="
         },
         "clone-deep": {
             "version": "4.0.1",
@@ -3999,7 +4610,7 @@
         "clone-response": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
-            "integrity": "sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==",
+            "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
             "requires": {
                 "mimic-response": "^1.0.0"
             },
@@ -4019,12 +4630,12 @@
         "code-point-at": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
-            "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA=="
+            "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
         },
         "collection-visit": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
-            "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==",
+            "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
             "requires": {
                 "map-visit": "^1.0.0",
                 "object-visit": "^1.0.0"
@@ -4050,12 +4661,12 @@
         "color-name": {
             "version": "1.1.3",
             "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-            "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
+            "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
         },
         "color-string": {
-            "version": "1.9.1",
-            "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
-            "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+            "version": "1.9.0",
+            "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz",
+            "integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==",
             "requires": {
                 "color-name": "^1.0.0",
                 "simple-swizzle": "^0.2.2"
@@ -4067,9 +4678,9 @@
             "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="
         },
         "colorette": {
-            "version": "2.0.17",
-            "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.17.tgz",
-            "integrity": "sha512-hJo+3Bkn0NCHybn9Tu35fIeoOKGOk5OCC32y4Hz2It+qlCO2Q3DeQ1hRn/tDDMQKRYUEzqsl7jbF6dYKjlE60g=="
+            "version": "2.0.16",
+            "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz",
+            "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g=="
         },
         "colors": {
             "version": "1.4.0",
@@ -4101,20 +4712,20 @@
             }
         },
         "commander": {
-            "version": "8.3.0",
-            "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
-            "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="
+            "version": "2.20.3",
+            "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+            "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
         },
         "commondir": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
-            "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
+            "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
             "dev": true
         },
         "component-bind": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
-            "integrity": "sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw=="
+            "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
         },
         "component-emitter": {
             "version": "1.3.0",
@@ -4124,16 +4735,7 @@
         "component-inherit": {
             "version": "0.0.3",
             "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
-            "integrity": "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA=="
-        },
-        "compress-brotli": {
-            "version": "1.3.8",
-            "resolved": "https://registry.npmjs.org/compress-brotli/-/compress-brotli-1.3.8.tgz",
-            "integrity": "sha512-lVcQsjhxhIXsuupfy9fmZUFtAIdBmXA7EGY6GBdgZ++qkM9zG4YFT8iU7FoBxzryNDMOpD1HIFHUSX4D87oqhQ==",
-            "requires": {
-                "@types/json-buffer": "~3.0.0",
-                "json-buffer": "~3.0.1"
-            }
+            "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
         },
         "compress-commons": {
             "version": "2.1.1",
@@ -4189,7 +4791,7 @@
                 "bytes": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
-                    "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==",
+                    "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
                     "dev": true
                 },
                 "debug": {
@@ -4204,7 +4806,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
                     "dev": true
                 }
             }
@@ -4212,7 +4814,7 @@
         "concat-map": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-            "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
+            "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
         },
         "concat-stream": {
             "version": "1.6.2",
@@ -4242,37 +4844,22 @@
             }
         },
         "configstore": {
-            "version": "3.1.5",
-            "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.5.tgz",
-            "integrity": "sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==",
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz",
+            "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==",
             "requires": {
-                "dot-prop": "^4.2.1",
+                "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"
-            },
-            "dependencies": {
-                "make-dir": {
-                    "version": "1.3.0",
-                    "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
-                    "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
-                    "requires": {
-                        "pify": "^3.0.0"
-                    }
-                },
-                "pify": {
-                    "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-                    "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="
-                }
             }
         },
         "connect-flash": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz",
-            "integrity": "sha512-2rcfELQt/ZMP+SM/pG8PyhJRaLKp+6Hk2IUBNkEit09X+vwn3QsAL3ZbYtxUn7NVPzbMTSLRDhqe0B/eh30RYA=="
+            "integrity": "sha1-2GMPJtlaf4UfmVax6MxnMvO2qjA="
         },
         "connect-history-api-fallback": {
             "version": "1.6.0",
@@ -4301,12 +4888,12 @@
                 "process-nextick-args": {
                     "version": "1.0.7",
                     "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
-                    "integrity": "sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw=="
+                    "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
                 },
                 "readable-stream": {
                     "version": "2.2.7",
                     "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz",
-                    "integrity": "sha512-a6ibcfWFhgihuTw/chl+u3fB5ykBZFmnvpyZHebY0MCQE4vvYcsCLpCeaQ1BkH7HdJYavNSqF0WDLeo4IPHQaQ==",
+                    "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=",
                     "requires": {
                         "buffer-shims": "~1.0.0",
                         "core-util-is": "~1.0.0",
@@ -4330,7 +4917,7 @@
         "console-control-strings": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
-            "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
+            "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
         },
         "constantinople": {
             "version": "3.1.2",
@@ -4414,7 +5001,7 @@
         "cookie-signature": {
             "version": "1.0.6",
             "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
-            "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
+            "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
         },
         "cookies": {
             "version": "0.8.0",
@@ -4423,6 +5010,13 @@
             "requires": {
                 "depd": "~2.0.0",
                 "keygrip": "~1.1.0"
+            },
+            "dependencies": {
+                "depd": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+                    "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
+                }
             }
         },
         "copy-concurrently": {
@@ -4439,12 +5033,6 @@
                 "run-queue": "^1.0.0"
             },
             "dependencies": {
-                "aproba": {
-                    "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-                    "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
-                    "dev": true
-                },
                 "rimraf": {
                     "version": "2.7.1",
                     "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@@ -4459,7 +5047,7 @@
         "copy-descriptor": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
-            "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw=="
+            "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="
         },
         "copy-webpack-plugin": {
             "version": "4.6.0",
@@ -4489,13 +5077,7 @@
                 "p-try": {
                     "version": "1.0.0",
                     "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
-                    "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==",
-                    "dev": true
-                },
-                "serialize-javascript": {
-                    "version": "1.9.1",
-                    "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",
-                    "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==",
+                    "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
                     "dev": true
                 }
             }
@@ -4503,17 +5085,17 @@
         "core-js": {
             "version": "1.2.7",
             "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
-            "integrity": "sha512-ZiPp9pZlgxpWRu0M+YWbm6+aQ84XEfH1JRXvfOc/fILWI0VKhLC2LX13X1NYq4fULzLMq7Hfh43CSo2/aIaUPA=="
+            "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
         },
         "core-js-pure": {
-            "version": "3.22.8",
-            "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.22.8.tgz",
-            "integrity": "sha512-bOxbZIy9S5n4OVH63XaLVXZ49QKicjowDx/UELyJ68vxfCRpYsbyh/WNZNfEfAk+ekA8vSjt+gCDpvh672bc3w=="
+            "version": "3.21.1",
+            "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz",
+            "integrity": "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ=="
         },
         "core-util-is": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
-            "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+            "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
         },
         "cors": {
             "version": "2.8.5",
@@ -4567,7 +5149,7 @@
         "create-error-class": {
             "version": "3.0.2",
             "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
-            "integrity": "sha512-gYTKKexFO3kh200H1Nit76sRwRtOY32vQd3jpAQKpLtZqyNsSQNfI4N7o3eP2wUjV35pTWKRYqFUDBvUha/Pkw==",
+            "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
             "requires": {
                 "capture-stack-trace": "^1.0.0"
             }
@@ -4626,13 +5208,32 @@
                     "requires": {
                         "whatwg-url": "^5.0.0"
                     }
+                },
+                "tr46": {
+                    "version": "0.0.3",
+                    "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+                    "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
+                },
+                "webidl-conversions": {
+                    "version": "3.0.1",
+                    "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+                    "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
+                },
+                "whatwg-url": {
+                    "version": "5.0.0",
+                    "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+                    "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
+                    "requires": {
+                        "tr46": "~0.0.3",
+                        "webidl-conversions": "^3.0.0"
+                    }
                 }
             }
         },
         "cross-spawn": {
             "version": "3.0.1",
             "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz",
-            "integrity": "sha512-eZ+m1WNhSZutOa/uRblAc9Ut5MQfukFrFMtPSm3bZCA888NmMd5AWXWdgRZ80zd+pTk1P2JrGjg9pUPTvl2PWQ==",
+            "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=",
             "requires": {
                 "lru-cache": "^4.0.1",
                 "which": "^1.2.9"
@@ -4662,7 +5263,7 @@
         "crypto-random-string": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
-            "integrity": "sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg=="
+            "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4="
         },
         "css-box-model": {
             "version": "1.2.1",
@@ -4675,7 +5276,7 @@
         "css-color-keywords": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
-            "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg=="
+            "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU="
         },
         "css-in-js-utils": {
             "version": "2.0.1",
@@ -4703,24 +5304,37 @@
                 "postcss-modules-values": "^2.0.0",
                 "postcss-value-parser": "^3.3.0",
                 "schema-utils": "^1.0.0"
+            },
+            "dependencies": {
+                "schema-utils": {
+                    "version": "1.0.0",
+                    "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+                    "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+                    "dev": true,
+                    "requires": {
+                        "ajv": "^6.1.0",
+                        "ajv-errors": "^1.0.0",
+                        "ajv-keywords": "^3.1.0"
+                    }
+                }
             }
         },
         "css-select": {
-            "version": "4.3.0",
-            "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
-            "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
+            "version": "4.2.1",
+            "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz",
+            "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==",
             "requires": {
                 "boolbase": "^1.0.0",
-                "css-what": "^6.0.1",
-                "domhandler": "^4.3.1",
+                "css-what": "^5.1.0",
+                "domhandler": "^4.3.0",
                 "domutils": "^2.8.0",
                 "nth-check": "^2.0.1"
             },
             "dependencies": {
                 "dom-serializer": {
-                    "version": "1.4.1",
-                    "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
-                    "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
+                    "version": "1.3.2",
+                    "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
+                    "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
                     "requires": {
                         "domelementtype": "^2.0.1",
                         "domhandler": "^4.2.0",
@@ -4728,14 +5342,14 @@
                     }
                 },
                 "domelementtype": {
-                    "version": "2.3.0",
-                    "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
-                    "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
+                    "version": "2.2.0",
+                    "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
+                    "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
                 },
                 "domhandler": {
-                    "version": "4.3.1",
-                    "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
-                    "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+                    "version": "4.3.0",
+                    "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz",
+                    "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==",
                     "requires": {
                         "domelementtype": "^2.2.0"
                     }
@@ -4777,9 +5391,9 @@
             }
         },
         "css-what": {
-            "version": "6.1.0",
-            "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
-            "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw=="
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
+            "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw=="
         },
         "cssesc": {
             "version": "3.0.0",
@@ -4794,9 +5408,9 @@
             "dev": true
         },
         "cssstyle": {
-            "version": "2.3.0",
-            "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz",
-            "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.2.0.tgz",
+            "integrity": "sha512-sEb3XFPx3jNnCAMtqrXPDeSgQr+jojtCeNf8cvMNMh1cG970+lljssvQDzPq6lmmJu2Vhqood/gtEomBiHOGnA==",
             "dev": true,
             "requires": {
                 "cssom": "~0.3.6"
@@ -4811,14 +5425,14 @@
             }
         },
         "csstype": {
-            "version": "2.6.20",
-            "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz",
-            "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA=="
+            "version": "2.6.10",
+            "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.10.tgz",
+            "integrity": "sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w=="
         },
         "currently-unhandled": {
             "version": "0.4.1",
             "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
-            "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==",
+            "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
             "requires": {
                 "array-find-index": "^1.0.1"
             }
@@ -4826,31 +5440,18 @@
         "custom-event": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
-            "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg=="
+            "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU="
         },
         "cyclist": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
-            "integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==",
+            "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
-        "d": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
-            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
-            "dev": true,
-            "requires": {
-                "es5-ext": "^0.10.50",
-                "type": "^1.0.1"
-            }
-        },
         "d3-array": {
-            "version": "2.12.1",
-            "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
-            "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
-            "requires": {
-                "internmap": "^1.0.0"
-            }
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
+            "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
         },
         "d3-axis": {
             "version": "2.1.0",
@@ -4909,6 +5510,16 @@
                 "d3-interpolate": "1.2.0 - 2",
                 "d3-time": "^2.1.1",
                 "d3-time-format": "2 - 3"
+            },
+            "dependencies": {
+                "d3-array": {
+                    "version": "2.12.1",
+                    "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
+                    "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
+                    "requires": {
+                        "internmap": "^1.0.0"
+                    }
+                }
             }
         },
         "d3-selection": {
@@ -4930,6 +5541,16 @@
             "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==",
             "requires": {
                 "d3-array": "2"
+            },
+            "dependencies": {
+                "d3-array": {
+                    "version": "2.12.1",
+                    "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
+                    "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
+                    "requires": {
+                        "internmap": "^1.0.0"
+                    }
+                }
             }
         },
         "d3-time-format": {
@@ -4972,7 +5593,7 @@
         "dashdash": {
             "version": "1.14.1",
             "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
-            "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
+            "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
             "requires": {
                 "assert-plus": "^1.0.0"
             }
@@ -4986,34 +5607,6 @@
                 "abab": "^2.0.0",
                 "whatwg-mimetype": "^2.2.0",
                 "whatwg-url": "^7.0.0"
-            },
-            "dependencies": {
-                "tr46": {
-                    "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
-                    "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
-                    "dev": true,
-                    "requires": {
-                        "punycode": "^2.1.0"
-                    }
-                },
-                "webidl-conversions": {
-                    "version": "4.0.2",
-                    "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
-                    "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
-                    "dev": true
-                },
-                "whatwg-url": {
-                    "version": "7.1.0",
-                    "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
-                    "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
-                    "dev": true,
-                    "requires": {
-                        "lodash.sortby": "^4.7.0",
-                        "tr46": "^1.0.1",
-                        "webidl-conversions": "^4.0.2"
-                    }
-                }
             }
         },
         "date-fns": {
@@ -5024,7 +5617,7 @@
         "de-indent": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
-            "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="
+            "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0="
         },
         "debounce": {
             "version": "1.2.1",
@@ -5042,12 +5635,12 @@
         "decamelize": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
-            "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="
+            "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
         },
         "decode-uri-component": {
             "version": "0.2.0",
             "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
-            "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og=="
+            "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
         },
         "decompress-response": {
             "version": "4.2.1",
@@ -5084,9 +5677,9 @@
             "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
         },
         "deep-is": {
-            "version": "0.1.4",
-            "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
-            "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+            "version": "0.1.3",
+            "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+            "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
             "dev": true
         },
         "deepmerge": {
@@ -5149,12 +5742,11 @@
             "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="
         },
         "define-properties": {
-            "version": "1.1.4",
-            "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
-            "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+            "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
             "requires": {
-                "has-property-descriptors": "^1.0.0",
-                "object-keys": "^1.1.1"
+                "object-keys": "^1.0.12"
             }
         },
         "define-property": {
@@ -5212,7 +5804,7 @@
                 "globby": {
                     "version": "6.1.0",
                     "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
-                    "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==",
+                    "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
                     "dev": true,
                     "requires": {
                         "array-union": "^1.0.1",
@@ -5225,17 +5817,11 @@
                         "pify": {
                             "version": "2.3.0",
                             "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-                            "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+                            "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
                             "dev": true
                         }
                     }
                 },
-                "pify": {
-                    "version": "4.0.1",
-                    "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
-                    "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
-                    "dev": true
-                },
                 "rimraf": {
                     "version": "2.7.1",
                     "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@@ -5250,7 +5836,7 @@
         "delayed-stream": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-            "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
+            "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
         },
         "delegate": {
             "version": "3.2.0",
@@ -5260,13 +5846,18 @@
         "delegates": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
-            "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
+            "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
         },
         "denque": {
             "version": "1.5.1",
             "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz",
             "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw=="
         },
+        "density-clustering": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/density-clustering/-/density-clustering-1.3.0.tgz",
+            "integrity": "sha1-3J9ZyPCrl+FiSsZJMP0xlIF9ysU="
+        },
         "depcheck": {
             "version": "0.9.2",
             "resolved": "https://registry.npmjs.org/depcheck/-/depcheck-0.9.2.tgz",
@@ -5292,18 +5883,24 @@
             },
             "dependencies": {
                 "ansi-regex": {
-                    "version": "5.0.1",
-                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-                    "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+                    "version": "5.0.0",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+                    "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
                 },
                 "ansi-styles": {
-                    "version": "4.3.0",
-                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-                    "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+                    "version": "4.2.1",
+                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+                    "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
                     "requires": {
+                        "@types/color-name": "^1.1.1",
                         "color-convert": "^2.0.1"
                     }
                 },
+                "builtin-modules": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz",
+                    "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw=="
+                },
                 "cliui": {
                     "version": "6.0.0",
                     "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
@@ -5339,11 +5936,11 @@
                     }
                 },
                 "debug": {
-                    "version": "4.3.4",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
-                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+                    "version": "4.1.1",
+                    "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"
                     }
                 },
                 "emoji-regex": {
@@ -5363,7 +5960,7 @@
                 "import-fresh": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
-                    "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==",
+                    "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
                     "requires": {
                         "caller-path": "^2.0.0",
                         "resolve-from": "^3.0.0"
@@ -5382,11 +5979,6 @@
                         "p-locate": "^4.1.0"
                     }
                 },
-                "ms": {
-                    "version": "2.1.2",
-                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
-                },
                 "p-locate": {
                     "version": "4.1.0",
                     "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
@@ -5398,7 +5990,7 @@
                 "parse-json": {
                     "version": "4.0.0",
                     "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
-                    "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==",
+                    "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
                     "requires": {
                         "error-ex": "^1.3.1",
                         "json-parse-better-errors": "^1.0.1"
@@ -5412,24 +6004,24 @@
                 "resolve-from": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
-                    "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw=="
+                    "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
                 },
                 "string-width": {
-                    "version": "4.2.3",
-                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-                    "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+                    "version": "4.2.0",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+                    "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
                     "requires": {
                         "emoji-regex": "^8.0.0",
                         "is-fullwidth-code-point": "^3.0.0",
-                        "strip-ansi": "^6.0.1"
+                        "strip-ansi": "^6.0.0"
                     }
                 },
                 "strip-ansi": {
-                    "version": "6.0.1",
-                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-                    "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                    "version": "6.0.0",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+                    "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
                     "requires": {
-                        "ansi-regex": "^5.0.1"
+                        "ansi-regex": "^5.0.0"
                     }
                 },
                 "wrap-ansi": {
@@ -5443,9 +6035,9 @@
                     }
                 },
                 "yargs": {
-                    "version": "15.4.1",
-                    "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
-                    "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+                    "version": "15.3.1",
+                    "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz",
+                    "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==",
                     "requires": {
                         "cliui": "^6.0.0",
                         "decamelize": "^1.2.0",
@@ -5457,7 +6049,7 @@
                         "string-width": "^4.2.0",
                         "which-module": "^2.0.0",
                         "y18n": "^4.0.0",
-                        "yargs-parser": "^18.1.2"
+                        "yargs-parser": "^18.1.1"
                     }
                 },
                 "yargs-parser": {
@@ -5472,24 +6064,24 @@
             }
         },
         "depd": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
-            "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+            "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
         },
         "deps-regex": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/deps-regex/-/deps-regex-0.1.4.tgz",
-            "integrity": "sha512-3tzwGYogSJi8HoG93R5x9NrdefZQOXgHgGih/7eivloOq6yC6O+yoFxZnkgP661twvfILONfoKRdF9GQOGx2RA=="
+            "integrity": "sha1-UYZnt2kUYKXn4KNBvnbrfOgJAYQ="
         },
         "destroy": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
-            "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+            "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
         },
         "detect-libc": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
-            "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w=="
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+            "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
         },
         "detect-node": {
             "version": "2.1.0",
@@ -5528,7 +6120,7 @@
                 "pify": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-                    "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
+                    "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
                     "dev": true
                 }
             }
@@ -5536,7 +6128,7 @@
         "dns-equal": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
-            "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==",
+            "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=",
             "dev": true
         },
         "dns-packet": {
@@ -5552,7 +6144,7 @@
         "dns-txt": {
             "version": "2.0.2",
             "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz",
-            "integrity": "sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ==",
+            "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=",
             "dev": true,
             "requires": {
                 "buffer-indexof": "^1.0.0"
@@ -5561,7 +6153,7 @@
         "doctypes": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz",
-            "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ=="
+            "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk="
         },
         "dom-converter": {
             "version": "0.2.0",
@@ -5589,14 +6181,14 @@
             },
             "dependencies": {
                 "domelementtype": {
-                    "version": "2.3.0",
-                    "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
-                    "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz",
+                    "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ=="
                 },
                 "entities": {
-                    "version": "2.2.0",
-                    "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
-                    "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
+                    "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw=="
                 }
             }
         },
@@ -5612,14 +6204,6 @@
             "dev": true,
             "requires": {
                 "webidl-conversions": "^4.0.2"
-            },
-            "dependencies": {
-                "webidl-conversions": {
-                    "version": "4.0.2",
-                    "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
-                    "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
-                    "dev": true
-                }
             }
         },
         "domhandler": {
@@ -5630,11 +6214,6 @@
                 "domelementtype": "1"
             }
         },
-        "dommatrix": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/dommatrix/-/dommatrix-1.0.3.tgz",
-            "integrity": "sha512-l32Xp/TLgWb8ReqbVJAFIvXmY7go4nTxxlWiAFyhoQw9RKEOHBZNnyGvJWqDVSPmq3Y9HlM4npqF/T6VMOXhww=="
-        },
         "domutils": {
             "version": "1.7.0",
             "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
@@ -5651,12 +6230,19 @@
             "requires": {
                 "no-case": "^3.0.4",
                 "tslib": "^2.0.3"
+            },
+            "dependencies": {
+                "tslib": {
+                    "version": "2.3.1",
+                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+                    "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+                }
             }
         },
         "dot-prop": {
-            "version": "4.2.1",
-            "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz",
-            "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==",
+            "version": "4.2.0",
+            "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.0"
             }
@@ -5670,12 +6256,12 @@
         "double-bits": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/double-bits/-/double-bits-1.1.1.tgz",
-            "integrity": "sha512-BCLEIBq0O/DWoA7BsCu/R+RP0ZXiowP8BhtJT3qeuuQEBpnS8LK/Wo6UTJQv6v8mK1fj8n90YziHLwGdM5whSg=="
+            "integrity": "sha1-WKu6RUlNpND6Nrc60RoobJGEscY="
         },
         "duplexer3": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
-            "integrity": "sha512-CEj8FwwNA4cVH2uFCoHUrmojhYh1vmCdOaneKJXwkeY1i9jnlslVo9dx+hQ5Hl9GnH/Bwy/IjxAyOePyPKYnzA=="
+            "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
         },
         "duplexify": {
             "version": "3.7.1",
@@ -5709,7 +6295,7 @@
         "dynamic-dedupe": {
             "version": "0.3.0",
             "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz",
-            "integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==",
+            "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=",
             "dev": true,
             "requires": {
                 "xtend": "^4.0.0"
@@ -5718,7 +6304,7 @@
         "ecc-jsbn": {
             "version": "0.1.2",
             "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
-            "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
+            "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
             "requires": {
                 "jsbn": "~0.1.0",
                 "safer-buffer": "^2.1.0"
@@ -5735,12 +6321,12 @@
         "ee-first": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
-            "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
+            "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
         },
         "electron-to-chromium": {
-            "version": "1.4.149",
-            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.149.tgz",
-            "integrity": "sha512-SR6ZQ8gfXX7LIS3UTDXu1CbFMUnluP3TS+jP7QwDwOnoH5CcHbejFdaaaEnGxR6I76E55eFnUV9mxI8GKufkEg=="
+            "version": "1.4.86",
+            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.86.tgz",
+            "integrity": "sha512-EVTZ+igi8x63pK4bPuA95PXIs2b2Cowi3WQwI9f9qManLiZJOD1Lash1J3W4TvvcUCcIR4o/rgi9o8UicXSO+w=="
         },
         "emoji-regex": {
             "version": "7.0.3",
@@ -5764,14 +6350,14 @@
         "encodeurl": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
-            "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
+            "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
         },
         "encoding": {
-            "version": "0.1.13",
-            "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
-            "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
+            "version": "0.1.12",
+            "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
+            "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
             "requires": {
-                "iconv-lite": "^0.6.2"
+                "iconv-lite": "~0.4.13"
             }
         },
         "end-of-stream": {
@@ -5839,7 +6425,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                 },
                 "ws": {
                     "version": "7.4.6",
@@ -5861,13 +6447,14 @@
             }
         },
         "enhanced-resolve": {
-            "version": "5.9.3",
-            "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz",
-            "integrity": "sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==",
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz",
+            "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==",
             "dev": true,
             "requires": {
-                "graceful-fs": "^4.2.4",
-                "tapable": "^2.2.0"
+                "graceful-fs": "^4.1.2",
+                "memory-fs": "^0.5.0",
+                "tapable": "^1.0.0"
             }
         },
         "entities": {
@@ -5889,9 +6476,9 @@
             }
         },
         "errno": {
-            "version": "0.1.8",
-            "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
-            "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
+            "version": "0.1.7",
+            "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
+            "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
             "dev": true,
             "requires": {
                 "prr": "~1.0.1"
@@ -5906,53 +6493,23 @@
             }
         },
         "es-abstract": {
-            "version": "1.20.1",
-            "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz",
-            "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==",
+            "version": "1.17.5",
+            "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz",
+            "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==",
             "requires": {
-                "call-bind": "^1.0.2",
                 "es-to-primitive": "^1.2.1",
                 "function-bind": "^1.1.1",
-                "function.prototype.name": "^1.1.5",
-                "get-intrinsic": "^1.1.1",
-                "get-symbol-description": "^1.0.0",
                 "has": "^1.0.3",
-                "has-property-descriptors": "^1.0.0",
-                "has-symbols": "^1.0.3",
-                "internal-slot": "^1.0.3",
-                "is-callable": "^1.2.4",
-                "is-negative-zero": "^2.0.2",
-                "is-regex": "^1.1.4",
-                "is-shared-array-buffer": "^1.0.2",
-                "is-string": "^1.0.7",
-                "is-weakref": "^1.0.2",
-                "object-inspect": "^1.12.0",
+                "has-symbols": "^1.0.1",
+                "is-callable": "^1.1.5",
+                "is-regex": "^1.0.5",
+                "object-inspect": "^1.7.0",
                 "object-keys": "^1.1.1",
-                "object.assign": "^4.1.2",
-                "regexp.prototype.flags": "^1.4.3",
-                "string.prototype.trimend": "^1.0.5",
-                "string.prototype.trimstart": "^1.0.5",
-                "unbox-primitive": "^1.0.2"
-            },
-            "dependencies": {
-                "object.assign": {
-                    "version": "4.1.2",
-                    "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
-                    "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
-                    "requires": {
-                        "call-bind": "^1.0.0",
-                        "define-properties": "^1.1.3",
-                        "has-symbols": "^1.0.1",
-                        "object-keys": "^1.1.1"
-                    }
-                }
+                "object.assign": "^4.1.0",
+                "string.prototype.trimleft": "^2.1.1",
+                "string.prototype.trimright": "^2.1.1"
             }
         },
-        "es-array-method-boxes-properly": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz",
-            "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA=="
-        },
         "es-module-lexer": {
             "version": "0.9.3",
             "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
@@ -5969,32 +6526,10 @@
                 "is-symbol": "^1.0.2"
             }
         },
-        "es5-ext": {
-            "version": "0.10.61",
-            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz",
-            "integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==",
-            "dev": true,
-            "requires": {
-                "es6-iterator": "^2.0.3",
-                "es6-symbol": "^3.1.3",
-                "next-tick": "^1.1.0"
-            }
-        },
-        "es6-iterator": {
-            "version": "2.0.3",
-            "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
-            "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
-            "dev": true,
-            "requires": {
-                "d": "1",
-                "es5-ext": "^0.10.35",
-                "es6-symbol": "^3.1.1"
-            }
-        },
         "es6-promise": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
-            "integrity": "sha512-oj4jOSXvWglTsc3wrw86iom3LDPOx1nbipQk+jaG3dy+sMRM6ReSgVr/VlmBuF6lXUrflN9DCcQHeSbAwGUl4g=="
+            "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q="
         },
         "es6-symbol": {
             "version": "3.1.3",
@@ -6002,7 +6537,6 @@
             "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
             "dev": true,
             "requires": {
-                "d": "^1.0.1",
                 "ext": "^1.1.2"
             }
         },
@@ -6014,17 +6548,17 @@
         "escape-html": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
-            "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
+            "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
         },
         "escape-string-regexp": {
             "version": "1.0.5",
             "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-            "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
+            "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
         },
         "escodegen": {
-            "version": "1.14.3",
-            "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
-            "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
+            "version": "1.14.1",
+            "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz",
+            "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==",
             "dev": true,
             "requires": {
                 "esprima": "^4.0.1",
@@ -6089,7 +6623,7 @@
         "etag": {
             "version": "1.8.1",
             "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
-            "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
+            "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
         },
         "event-target-shim": {
             "version": "5.0.1",
@@ -6109,15 +6643,18 @@
             "dev": true
         },
         "eventsource": {
-            "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz",
-            "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==",
-            "dev": true
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz",
+            "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==",
+            "dev": true,
+            "requires": {
+                "original": "^1.0.0"
+            }
         },
         "execa": {
             "version": "0.7.0",
             "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
-            "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==",
+            "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
             "requires": {
                 "cross-spawn": "^5.0.1",
                 "get-stream": "^3.0.0",
@@ -6131,18 +6668,13 @@
                 "cross-spawn": {
                     "version": "5.1.0",
                     "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
-                    "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==",
+                    "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
                     "requires": {
                         "lru-cache": "^4.0.1",
                         "shebang-command": "^1.2.0",
                         "which": "^1.2.9"
                     }
                 },
-                "get-stream": {
-                    "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
-                    "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ=="
-                },
                 "lru-cache": {
                     "version": "4.1.5",
                     "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
@@ -6162,12 +6694,12 @@
         "exenv": {
             "version": "1.2.2",
             "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
-            "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw=="
+            "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50="
         },
         "exeq": {
             "version": "2.4.0",
             "resolved": "https://registry.npmjs.org/exeq/-/exeq-2.4.0.tgz",
-            "integrity": "sha512-B648qbDS00nQZv9UQGLT5RbZm/5dNBX10F8oWeXcgpFHSLm1249u95t/3sn2wXdQjLhlF+edAECdshFtSr1K0Q==",
+            "integrity": "sha1-Td8qaEZIxCeteZNJzzO9dTWPiEo=",
             "requires": {
                 "bluebird": "^3.0.3",
                 "native-or-bluebird": "^1.2.0"
@@ -6176,7 +6708,7 @@
         "exif": {
             "version": "0.6.0",
             "resolved": "https://registry.npmjs.org/exif/-/exif-0.6.0.tgz",
-            "integrity": "sha512-gEwM4uanNMfLnDNKclZ7jPEA99E3rpy4ntoS6QW8u6murZjl1o8qRaPdMoC46Syg3d9/QaET0bYKhWlTwJCPgg==",
+            "integrity": "sha1-YKYmaAdlQst+T1cZnUrG830sX0o=",
             "requires": {
                 "debug": "^2.2"
             },
@@ -6192,7 +6724,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                 }
             }
         },
@@ -6204,7 +6736,7 @@
         "expand-brackets": {
             "version": "2.1.4",
             "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-            "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==",
+            "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
             "requires": {
                 "debug": "^2.3.3",
                 "define-property": "^0.2.5",
@@ -6226,7 +6758,7 @@
                 "define-property": {
                     "version": "0.2.5",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-                    "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+                    "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
                     "requires": {
                         "is-descriptor": "^0.1.0"
                     }
@@ -6234,15 +6766,20 @@
                 "extend-shallow": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-                    "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+                    "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
                     "requires": {
                         "is-extendable": "^0.1.0"
                     }
                 },
+                "is-extendable": {
+                    "version": "0.1.1",
+                    "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+                    "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
+                },
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                 }
             }
         },
@@ -6252,47 +6789,46 @@
             "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
         },
         "express": {
-            "version": "4.18.1",
-            "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz",
-            "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==",
+            "version": "4.17.3",
+            "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz",
+            "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==",
             "requires": {
                 "accepts": "~1.3.8",
                 "array-flatten": "1.1.1",
-                "body-parser": "1.20.0",
+                "body-parser": "1.19.2",
                 "content-disposition": "0.5.4",
                 "content-type": "~1.0.4",
-                "cookie": "0.5.0",
+                "cookie": "0.4.2",
                 "cookie-signature": "1.0.6",
                 "debug": "2.6.9",
-                "depd": "2.0.0",
+                "depd": "~1.1.2",
                 "encodeurl": "~1.0.2",
                 "escape-html": "~1.0.3",
                 "etag": "~1.8.1",
-                "finalhandler": "1.2.0",
+                "finalhandler": "~1.1.2",
                 "fresh": "0.5.2",
-                "http-errors": "2.0.0",
                 "merge-descriptors": "1.0.1",
                 "methods": "~1.1.2",
-                "on-finished": "2.4.1",
+                "on-finished": "~2.3.0",
                 "parseurl": "~1.3.3",
                 "path-to-regexp": "0.1.7",
                 "proxy-addr": "~2.0.7",
-                "qs": "6.10.3",
+                "qs": "6.9.7",
                 "range-parser": "~1.2.1",
                 "safe-buffer": "5.2.1",
-                "send": "0.18.0",
-                "serve-static": "1.15.0",
+                "send": "0.17.2",
+                "serve-static": "1.14.2",
                 "setprototypeof": "1.2.0",
-                "statuses": "2.0.1",
+                "statuses": "~1.5.0",
                 "type-is": "~1.6.18",
                 "utils-merge": "1.0.1",
                 "vary": "~1.1.2"
             },
             "dependencies": {
                 "cookie": {
-                    "version": "0.5.0",
-                    "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
-                    "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
+                    "version": "0.4.2",
+                    "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+                    "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA=="
                 },
                 "debug": {
                     "version": "2.6.9",
@@ -6305,15 +6841,12 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                 },
                 "qs": {
-                    "version": "6.10.3",
-                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
-                    "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
-                    "requires": {
-                        "side-channel": "^1.0.4"
-                    }
+                    "version": "6.9.7",
+                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz",
+                    "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw=="
                 },
                 "safe-buffer": {
                     "version": "5.2.1",
@@ -6325,17 +6858,17 @@
         "express-flash": {
             "version": "0.0.2",
             "resolved": "https://registry.npmjs.org/express-flash/-/express-flash-0.0.2.tgz",
-            "integrity": "sha512-QVUR0ZZRCaa8+iPHoUQaQJrQWcQuK/Q+19M7IUIdIEtvwhrA/ifHT7y1CVJI41YfGiOQnbGtn3uvd2vOdgu58A==",
+            "integrity": "sha1-I9GovPP5DXB5KOSJ+Whp7K0KzaI=",
             "requires": {
                 "connect-flash": "0.1.x"
             }
         },
         "express-session": {
-            "version": "1.17.3",
-            "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz",
-            "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==",
+            "version": "1.17.2",
+            "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz",
+            "integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==",
             "requires": {
-                "cookie": "0.4.2",
+                "cookie": "0.4.1",
                 "cookie-signature": "1.0.6",
                 "debug": "2.6.9",
                 "depd": "~2.0.0",
@@ -6345,11 +6878,6 @@
                 "uid-safe": "~2.1.5"
             },
             "dependencies": {
-                "cookie": {
-                    "version": "0.4.2",
-                    "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
-                    "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA=="
-                },
                 "debug": {
                     "version": "2.6.9",
                     "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -6358,10 +6886,15 @@
                         "ms": "2.0.0"
                     }
                 },
+                "depd": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+                    "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
+                },
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                 },
                 "safe-buffer": {
                     "version": "5.2.1",
@@ -6382,21 +6915,21 @@
         "expressjs": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/expressjs/-/expressjs-1.0.1.tgz",
-            "integrity": "sha512-eFnQ5bMJxTZ29XwRJPV8ee/OURBBMS6Fm+b5rvMMEyz6u2IxPEh2SRzMZt9WvgnV+SMLmnzkALE1DnGG1HxJCw=="
+            "integrity": "sha1-IgMoRpoY31rWFeK3oM6ZXxf7ru8="
         },
         "ext": {
-            "version": "1.6.0",
-            "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz",
-            "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==",
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
+            "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
             "dev": true,
             "requires": {
-                "type": "^2.5.0"
+                "type": "^2.0.0"
             },
             "dependencies": {
                 "type": {
-                    "version": "2.6.0",
-                    "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz",
-                    "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==",
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz",
+                    "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==",
                     "dev": true
                 }
             }
@@ -6409,20 +6942,10 @@
         "extend-shallow": {
             "version": "3.0.2",
             "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-            "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
+            "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
             "requires": {
                 "assign-symbols": "^1.0.0",
                 "is-extendable": "^1.0.1"
-            },
-            "dependencies": {
-                "is-extendable": {
-                    "version": "1.0.1",
-                    "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"
-                    }
-                }
             }
         },
         "extglob": {
@@ -6443,7 +6966,7 @@
                 "define-property": {
                     "version": "1.0.0",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-                    "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+                    "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
                     "requires": {
                         "is-descriptor": "^1.0.0"
                     }
@@ -6451,7 +6974,7 @@
                 "extend-shallow": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-                    "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+                    "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
                     "requires": {
                         "is-extendable": "^0.1.0"
                     }
@@ -6481,6 +7004,11 @@
                         "is-data-descriptor": "^1.0.0",
                         "kind-of": "^6.0.2"
                     }
+                },
+                "is-extendable": {
+                    "version": "0.1.1",
+                    "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+                    "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
                 }
             }
         },
@@ -6496,37 +7024,32 @@
             },
             "dependencies": {
                 "debug": {
-                    "version": "4.3.4",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
-                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+                    "version": "4.1.1",
+                    "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"
                     }
                 },
                 "get-stream": {
-                    "version": "5.2.0",
-                    "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
-                    "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+                    "version": "5.1.0",
+                    "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz",
+                    "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==",
                     "requires": {
-                        "pump": "^3.0.0"
-                    }
-                },
-                "ms": {
-                    "version": "2.1.2",
-                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+                        "pump": "^3.0.0"
+                    }
                 }
             }
         },
         "extsprintf": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
-            "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g=="
+            "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
         },
         "fast-deep-equal": {
-            "version": "3.1.3",
-            "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
-            "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
+            "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA=="
         },
         "fast-json-stable-stringify": {
             "version": "2.1.0",
@@ -6536,13 +7059,13 @@
         "fast-levenshtein": {
             "version": "2.0.6",
             "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
-            "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+            "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
             "dev": true
         },
         "fast-text-encoding": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz",
-            "integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig=="
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.2.tgz",
+            "integrity": "sha512-5rQdinSsycpzvAoHga2EDn+LRX1d5xLFsuNG0Kg61JrAT/tASXcLL0nf/33v+sAxlQcfYmWbTURa1mmAf55jGw=="
         },
         "fastest-levenshtein": {
             "version": "1.0.12",
@@ -6559,9 +7082,9 @@
             }
         },
         "fbjs": {
-            "version": "0.8.18",
-            "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.18.tgz",
-            "integrity": "sha512-EQaWFK+fEPSoibjNy8IxUtaFOMXcWsY0JaVrQoZR9zC8N2Ygf9iDITPWjUTVIax95b6I742JFLqASHfsag/vKA==",
+            "version": "0.8.17",
+            "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
+            "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
             "requires": {
                 "core-js": "^1.0.0",
                 "isomorphic-fetch": "^2.1.1",
@@ -6569,25 +7092,17 @@
                 "object-assign": "^4.1.0",
                 "promise": "^7.1.1",
                 "setimmediate": "^1.0.5",
-                "ua-parser-js": "^0.7.30"
+                "ua-parser-js": "^0.7.18"
             }
         },
         "fd-slicer": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
-            "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+            "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
             "requires": {
                 "pend": "~1.2.0"
             }
         },
-        "ffmpeg": {
-            "version": "0.0.4",
-            "resolved": "https://registry.npmjs.org/ffmpeg/-/ffmpeg-0.0.4.tgz",
-            "integrity": "sha512-3TgWUJJlZGQn+crJFyhsO/oNeRRnGTy6GhgS98oUCIfZrOW5haPPV7DUfOm3xJcHr5q3TJpjk2GudPutrNisRA==",
-            "requires": {
-                "when": ">= 0.0.1"
-            }
-        },
         "file-loader": {
             "version": "3.0.1",
             "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz",
@@ -6596,6 +7111,19 @@
             "requires": {
                 "loader-utils": "^1.0.2",
                 "schema-utils": "^1.0.0"
+            },
+            "dependencies": {
+                "schema-utils": {
+                    "version": "1.0.0",
+                    "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+                    "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+                    "dev": true,
+                    "requires": {
+                        "ajv": "^6.1.0",
+                        "ajv-errors": "^1.0.0",
+                        "ajv-keywords": "^3.1.0"
+                    }
+                }
             }
         },
         "file-saver": {
@@ -6612,7 +7140,7 @@
         "fill-range": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-            "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
+            "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
             "requires": {
                 "extend-shallow": "^2.0.1",
                 "is-number": "^3.0.0",
@@ -6623,29 +7151,34 @@
                 "extend-shallow": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-                    "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+                    "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
                     "requires": {
                         "is-extendable": "^0.1.0"
                     }
+                },
+                "is-extendable": {
+                    "version": "0.1.1",
+                    "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+                    "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
                 }
             }
         },
         "filter-obj": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
-            "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ=="
+            "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs="
         },
         "finalhandler": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
-            "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+            "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
             "requires": {
                 "debug": "2.6.9",
                 "encodeurl": "~1.0.2",
                 "escape-html": "~1.0.3",
-                "on-finished": "2.4.1",
+                "on-finished": "~2.3.0",
                 "parseurl": "~1.3.3",
-                "statuses": "2.0.1",
+                "statuses": "~1.5.0",
                 "unpipe": "~1.0.0"
             },
             "dependencies": {
@@ -6660,14 +7193,14 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                 }
             }
         },
         "find": {
             "version": "0.1.7",
             "resolved": "https://registry.npmjs.org/find/-/find-0.1.7.tgz",
-            "integrity": "sha512-jPrupTOe/pO//3a9Ty2o4NqQCp0L46UG+swUnfFtdmtQVN8pEltKpAqR7Nuf6vWn0GBXx5w+R1MyZzqwjEIqdA==",
+            "integrity": "sha1-yGyHrxqxjyIrvjjeyGy8dg0Wpvs=",
             "requires": {
                 "traverse-chain": "~0.1.0"
             }
@@ -6675,81 +7208,12 @@
         "find-cache-dir": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz",
-            "integrity": "sha512-46TFiBOzX7xq/PcSWfFwkyjpemdRnMe31UQF+os0y+1W3k95f6R4SEt02Hj4p3X0Mir9gfrkmOtshFidS0VPUg==",
+            "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=",
             "dev": true,
             "requires": {
                 "commondir": "^1.0.1",
                 "make-dir": "^1.0.0",
                 "pkg-dir": "^2.0.0"
-            },
-            "dependencies": {
-                "find-up": {
-                    "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
-                    "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==",
-                    "dev": true,
-                    "requires": {
-                        "locate-path": "^2.0.0"
-                    }
-                },
-                "locate-path": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
-                    "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==",
-                    "dev": true,
-                    "requires": {
-                        "p-locate": "^2.0.0",
-                        "path-exists": "^3.0.0"
-                    }
-                },
-                "make-dir": {
-                    "version": "1.3.0",
-                    "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
-                    "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
-                    "dev": true,
-                    "requires": {
-                        "pify": "^3.0.0"
-                    }
-                },
-                "p-limit": {
-                    "version": "1.3.0",
-                    "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
-                    "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
-                    "dev": true,
-                    "requires": {
-                        "p-try": "^1.0.0"
-                    }
-                },
-                "p-locate": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
-                    "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==",
-                    "dev": true,
-                    "requires": {
-                        "p-limit": "^1.1.0"
-                    }
-                },
-                "p-try": {
-                    "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
-                    "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==",
-                    "dev": true
-                },
-                "pify": {
-                    "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-                    "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
-                    "dev": true
-                },
-                "pkg-dir": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
-                    "integrity": "sha512-ojakdnUgL5pzJYWw2AIDEupaQCX5OPbM688ZevubICjdIX01PRSYKqm33fJoCOJBRseYCTUlQRnBNX+Pchaejw==",
-                    "dev": true,
-                    "requires": {
-                        "find-up": "^2.1.0"
-                    }
-                }
             }
         },
         "find-in-files": {
@@ -6762,9 +7226,9 @@
             }
         },
         "find-parent-dir": {
-            "version": "0.3.1",
-            "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.1.tgz",
-            "integrity": "sha512-o4UcykWV/XN9wm+jMEtWLPlV8RXCZnMhQI6F6OdHeSez7iiJWePw8ijOlskJZMsaQoGR/b7dH6lO02HhaTN7+A=="
+            "version": "0.3.0",
+            "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.0.tgz",
+            "integrity": "sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ="
         },
         "find-root": {
             "version": "1.1.0",
@@ -6785,9 +7249,9 @@
             "integrity": "sha512-Md3b3ReA/qJwwYvKXeHpOV1fhPqwhJ9/29zc9lfi8ijyg00J0S9C7+5XMZDFhlDAKbwOvVgBxVIG1EHoMSC3vw=="
         },
         "flat": {
-            "version": "4.1.1",
-            "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz",
-            "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==",
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
+            "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
             "requires": {
                 "is-buffer": "~2.0.3"
             }
@@ -6800,7 +7264,7 @@
         "fluent-ffmpeg": {
             "version": "2.1.2",
             "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.2.tgz",
-            "integrity": "sha512-IZTB4kq5GK0DPp7sGQ0q/BWurGHffRtQQwVkiqDgeO6wYJLLV5ZhgNOQ65loZxxuPMKZKZcICCUnaGtlxBiR0Q==",
+            "integrity": "sha1-yVLeIkD4EuvaCqgAbXd27irPfXQ=",
             "requires": {
                 "async": ">=0.2.9",
                 "which": "^1.1.1"
@@ -6834,40 +7298,14 @@
             }
         },
         "follow-redirects": {
-            "version": "1.5.10",
-            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
-            "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
-            "requires": {
-                "debug": "=3.1.0"
-            },
-            "dependencies": {
-                "debug": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-                    "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
-                    "requires": {
-                        "ms": "2.0.0"
-                    }
-                },
-                "ms": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
-                }
-            }
-        },
-        "for-each": {
-            "version": "0.3.3",
-            "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
-            "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
-            "requires": {
-                "is-callable": "^1.1.3"
-            }
+            "version": "1.12.1",
+            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz",
+            "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg=="
         },
         "for-each-property": {
             "version": "0.0.4",
             "resolved": "https://registry.npmjs.org/for-each-property/-/for-each-property-0.0.4.tgz",
-            "integrity": "sha512-xYs28PM0CKXETFzuGC6ZooH0voZlsSDZwidJcy92flQJi3PK7i3gZx23xHXCPOaD4zmet3bDo+wS7E7SujrlCw==",
+            "integrity": "sha1-z6hXrsFCLh0Sb/CHhPz2Jim8g/Y=",
             "requires": {
                 "get-prototype-chain": "^1.0.1"
             }
@@ -6875,7 +7313,7 @@
         "for-each-property-deep": {
             "version": "0.0.3",
             "resolved": "https://registry.npmjs.org/for-each-property-deep/-/for-each-property-deep-0.0.3.tgz",
-            "integrity": "sha512-qzP8QkODWVVRPpWiBZacSbBl67cTTWoBfxMG0wE46AsS1yl7qv05sGN+dHvD4s4tnvl/goe6Sp4qBI+rlVBgNg==",
+            "integrity": "sha1-MTCaSvw4qcygbxsiP1PWSm0IP60=",
             "requires": {
                 "for-each-property": "0.0.4"
             }
@@ -6883,12 +7321,17 @@
         "for-in": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
-            "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ=="
+            "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA="
+        },
+        "foreach": {
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
+            "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k="
         },
         "forever-agent": {
             "version": "0.6.1",
             "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
-            "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw=="
+            "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
         },
         "fork-ts-checker-webpack-plugin": {
             "version": "1.6.0",
@@ -6904,20 +7347,13 @@
                 "semver": "^5.6.0",
                 "tapable": "^1.0.0",
                 "worker-rpc": "^0.1.0"
-            },
-            "dependencies": {
-                "tapable": {
-                    "version": "1.1.3",
-                    "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
-                    "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
-                    "dev": true
-                }
             }
         },
         "form-data": {
-            "version": "2.3.3",
-            "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
-            "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+            "version": "2.5.1",
+            "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
+            "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
+            "dev": true,
             "requires": {
                 "asynckit": "^0.4.0",
                 "combined-stream": "^1.0.6",
@@ -6942,7 +7378,7 @@
         "fragment-cache": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
-            "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==",
+            "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
             "requires": {
                 "map-cache": "^0.2.2"
             }
@@ -6950,12 +7386,12 @@
         "fresh": {
             "version": "0.5.2",
             "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
-            "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="
+            "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
         },
         "from2": {
             "version": "2.3.0",
             "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
-            "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==",
+            "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
             "dev": true,
             "requires": {
                 "inherits": "^2.0.1",
@@ -6987,7 +7423,7 @@
         "fs-extra": {
             "version": "0.26.7",
             "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz",
-            "integrity": "sha512-waKu+1KumRhYv8D8gMRCKJGAMI9pRnPuEb1mvgYD0f7wBscg+h6bW4FDTmEZhB9VKxvoTtxW+Y7bnIlB7zja6Q==",
+            "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=",
             "requires": {
                 "graceful-fs": "^4.1.2",
                 "jsonfile": "^2.1.0",
@@ -7022,7 +7458,7 @@
         "fs-write-stream-atomic": {
             "version": "1.0.10",
             "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
-            "integrity": "sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==",
+            "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
             "dev": true,
             "requires": {
                 "graceful-fs": "^4.1.2",
@@ -7051,17 +7487,7 @@
         "fs.realpath": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-            "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
-        },
-        "fsevents": {
-            "version": "1.2.13",
-            "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
-            "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
-            "optional": true,
-            "requires": {
-                "bindings": "^1.5.0",
-                "nan": "^2.12.1"
-            }
+            "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
         },
         "fstream": {
             "version": "1.0.12",
@@ -7107,69 +7533,50 @@
                 "interval-arithmetic-eval": "^0.4.7"
             }
         },
-        "function.prototype.name": {
-            "version": "1.1.5",
-            "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
-            "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
-            "requires": {
-                "call-bind": "^1.0.2",
-                "define-properties": "^1.1.3",
-                "es-abstract": "^1.19.0",
-                "functions-have-names": "^1.2.2"
-            }
-        },
-        "functions-have-names": {
-            "version": "1.2.3",
-            "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
-            "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="
-        },
         "gauge": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
-            "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
+            "version": "2.7.4",
+            "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+            "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
             "requires": {
-                "aproba": "^1.0.3 || ^2.0.0",
-                "color-support": "^1.1.2",
+                "aproba": "^1.0.3",
                 "console-control-strings": "^1.0.0",
-                "has-unicode": "^2.0.1",
-                "object-assign": "^4.1.1",
+                "has-unicode": "^2.0.0",
+                "object-assign": "^4.1.0",
                 "signal-exit": "^3.0.0",
-                "string-width": "^4.2.3",
-                "strip-ansi": "^6.0.1",
-                "wide-align": "^1.1.2"
+                "string-width": "^1.0.1",
+                "strip-ansi": "^3.0.1",
+                "wide-align": "^1.1.0"
             },
             "dependencies": {
                 "ansi-regex": {
-                    "version": "5.0.1",
-                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-                    "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
-                },
-                "emoji-regex": {
-                    "version": "8.0.0",
-                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-                    "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+                    "version": "2.1.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+                    "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
                 },
                 "is-fullwidth-code-point": {
-                    "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-                    "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+                    "version": "1.0.0",
+                    "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.0"
+                    }
                 },
                 "string-width": {
-                    "version": "4.2.3",
-                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-                    "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+                    "version": "1.0.2",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+                    "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
                     "requires": {
-                        "emoji-regex": "^8.0.0",
-                        "is-fullwidth-code-point": "^3.0.0",
-                        "strip-ansi": "^6.0.1"
+                        "code-point-at": "^1.0.0",
+                        "is-fullwidth-code-point": "^1.0.0",
+                        "strip-ansi": "^3.0.0"
                     }
                 },
                 "strip-ansi": {
-                    "version": "6.0.1",
-                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-                    "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                    "version": "3.0.1",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+                    "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
                     "requires": {
-                        "ansi-regex": "^5.0.1"
+                        "ansi-regex": "^2.0.0"
                     }
                 }
             }
@@ -7187,17 +7594,14 @@
             },
             "dependencies": {
                 "is-stream": {
-                    "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
-                    "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
+                    "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw=="
                 },
                 "node-fetch": {
-                    "version": "2.6.7",
-                    "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
-                    "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
-                    "requires": {
-                        "whatwg-url": "^5.0.0"
-                    }
+                    "version": "2.6.0",
+                    "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
+                    "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
                 }
             }
         },
@@ -7226,16 +7630,16 @@
         "get-func-name": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
-            "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig=="
+            "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE="
         },
         "get-intrinsic": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz",
-            "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==",
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+            "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
             "requires": {
                 "function-bind": "^1.1.1",
                 "has": "^1.0.3",
-                "has-symbols": "^1.0.3"
+                "has-symbols": "^1.0.1"
             }
         },
         "get-node-dimensions": {
@@ -7246,17 +7650,17 @@
         "get-prototype-chain": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/get-prototype-chain/-/get-prototype-chain-1.0.1.tgz",
-            "integrity": "sha512-2m7WZ0jveIg/dAbCbpUxEToaJ8Dmti5EkgDP8YM3UpHUT6SAORjE2odP8XQGNVGXMHi8q8cCCoy3HTByTaTVTw=="
+            "integrity": "sha1-oXGhFeoeSQbG7ThDofABwYUQQW8="
         },
         "get-stdin": {
             "version": "5.0.1",
             "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz",
-            "integrity": "sha512-jZV7n6jGE3Gt7fgSTJoz91Ak5MuTLwMwkoYdjxuJ/AmjIsE1UC03y/IWkZCQGEvVNS9qoRNwy5BCqxImv0FVeA=="
+            "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g="
         },
         "get-stream": {
-            "version": "6.0.1",
-            "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
-            "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+            "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
         },
         "get-symbol-description": {
             "version": "1.0.0",
@@ -7270,12 +7674,12 @@
         "get-value": {
             "version": "2.0.6",
             "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
-            "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA=="
+            "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg="
         },
         "getpass": {
             "version": "0.1.7",
             "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
-            "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
+            "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
             "requires": {
                 "assert-plus": "^1.0.0"
             }
@@ -7283,17 +7687,17 @@
         "github-from-package": {
             "version": "0.0.0",
             "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
-            "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
+            "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4="
         },
         "glob": {
-            "version": "7.2.3",
-            "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
-            "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+            "version": "7.1.6",
+            "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+            "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
             "requires": {
                 "fs.realpath": "^1.0.0",
                 "inflight": "^1.0.4",
                 "inherits": "2",
-                "minimatch": "^3.1.1",
+                "minimatch": "^3.0.4",
                 "once": "^1.3.0",
                 "path-is-absolute": "^1.0.0"
             }
@@ -7301,7 +7705,7 @@
         "glob-parent": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
-            "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==",
+            "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
             "requires": {
                 "is-glob": "^3.1.0",
                 "path-dirname": "^1.0.0"
@@ -7310,7 +7714,7 @@
                 "is-glob": {
                     "version": "3.1.0",
                     "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
-                    "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==",
+                    "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
                     "requires": {
                         "is-extglob": "^2.1.0"
                     }
@@ -7326,7 +7730,7 @@
         "global-dirs": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
-            "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==",
+            "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
             "requires": {
                 "ini": "^1.3.4"
             }
@@ -7339,7 +7743,7 @@
         "globby": {
             "version": "7.1.1",
             "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz",
-            "integrity": "sha512-yANWAN2DUcBtuus5Cpd+SKROzXHs2iVXFZt/Ykrfz6SAXqacLX25NZpltE+39ceMexYF4TtEadjuSTw8+3wX4g==",
+            "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=",
             "dev": true,
             "requires": {
                 "array-union": "^1.0.1",
@@ -7353,48 +7757,25 @@
                 "pify": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-                    "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
+                    "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
                     "dev": true
                 }
             }
         },
         "globule": {
-            "version": "1.3.3",
-            "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.3.tgz",
-            "integrity": "sha512-mb1aYtDbIjTu4ShMB85m3UzjX9BVKe9WCzsnfMSZk+K5GpIbBOexgg4PPCt5eHDEG5/ZQAUX2Kct02zfiPLsKg==",
+            "version": "1.3.1",
+            "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz",
+            "integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==",
             "requires": {
                 "glob": "~7.1.1",
-                "lodash": "~4.17.10",
+                "lodash": "~4.17.12",
                 "minimatch": "~3.0.2"
-            },
-            "dependencies": {
-                "glob": {
-                    "version": "7.1.7",
-                    "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
-                    "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
-                    "requires": {
-                        "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"
-                    }
-                },
-                "minimatch": {
-                    "version": "3.0.8",
-                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz",
-                    "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==",
-                    "requires": {
-                        "brace-expansion": "^1.1.7"
-                    }
-                }
             }
         },
         "golden-layout": {
             "version": "1.5.9",
             "resolved": "https://registry.npmjs.org/golden-layout/-/golden-layout-1.5.9.tgz",
-            "integrity": "sha512-iBXDQCXOTgUEQJo96zPbjDoy5bRIk9XW5l+q+pDgLnIyReqaa1aiQctNud4epsskyLt952BG521dew5Z1liSxA==",
+            "integrity": "sha1-o5vB9qZ+b4hreXwBbdkk6UJrp38=",
             "requires": {
                 "jquery": "*"
             }
@@ -7402,7 +7783,7 @@
         "good-listener": {
             "version": "1.2.2",
             "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
-            "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
+            "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
             "requires": {
                 "delegate": "^3.1.2"
             }
@@ -7420,21 +7801,6 @@
                 "gtoken": "^3.0.0",
                 "jws": "^3.1.5",
                 "lru-cache": "^5.0.0"
-            },
-            "dependencies": {
-                "lru-cache": {
-                    "version": "5.1.1",
-                    "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
-                    "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
-                    "requires": {
-                        "yallist": "^3.0.2"
-                    }
-                },
-                "yallist": {
-                    "version": "3.1.1",
-                    "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
-                    "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
-                }
             }
         },
         "google-maps-react": {
@@ -7443,11 +7809,11 @@
             "integrity": "sha512-M8Eo9WndfQEfxcmm6yRq03qdJgw1x6rQmJ9DN+a+xPQ3K7yNDGkVDbinrf4/8vcox7nELbeopbm4bpefKewWfQ=="
         },
         "google-p12-pem": {
-            "version": "2.0.5",
-            "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.5.tgz",
-            "integrity": "sha512-7RLkxwSsMsYh9wQ5Vb2zRtkAHvqPvfoMGag+nugl1noYO7gf0844Yr9TIFA5NEBMAeVt2Z+Imu7CQMp3oNatzQ==",
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.4.tgz",
+            "integrity": "sha512-S4blHBQWZRnEW44OcR7TL9WR+QCqByRvhNDZ/uuQfpxywfupikf/miba8js1jZi6ZOGv5slgSuoshCWh6EMDzg==",
             "requires": {
-                "node-forge": "^0.10.0"
+                "node-forge": "^0.9.0"
             }
         },
         "googleapis": {
@@ -7473,12 +7839,9 @@
             },
             "dependencies": {
                 "qs": {
-                    "version": "6.10.5",
-                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.5.tgz",
-                    "integrity": "sha512-O5RlPh0VFtR78y79rgcgKK4wbAI0C5zGVLztOIdpWX6ep368q5Hv6XRxDvXuZ9q3C6v+e3n8UfZZJw7IIG27eQ==",
-                    "requires": {
-                        "side-channel": "^1.0.4"
-                    }
+                    "version": "6.9.3",
+                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz",
+                    "integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw=="
                 }
             }
         },
@@ -7493,9 +7856,9 @@
             }
         },
         "got": {
-            "version": "12.1.0",
-            "resolved": "https://registry.npmjs.org/got/-/got-12.1.0.tgz",
-            "integrity": "sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig==",
+            "version": "12.0.2",
+            "resolved": "https://registry.npmjs.org/got/-/got-12.0.2.tgz",
+            "integrity": "sha512-Zi4yHiqCgaorUbknr/RHFBsC3XqjSodaw0F3qxlqAqyj+OGYZl37/uy01R0qz++KANKQYdY5FHJ0okXZpEzwWQ==",
             "requires": {
                 "@sindresorhus/is": "^4.6.0",
                 "@szmarczak/http-timer": "^5.0.1",
@@ -7520,6 +7883,11 @@
                         "mimic-response": "^3.1.0"
                     }
                 },
+                "get-stream": {
+                    "version": "6.0.1",
+                    "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+                    "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="
+                },
                 "mimic-response": {
                     "version": "3.1.0",
                     "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
@@ -7528,9 +7896,9 @@
             }
         },
         "graceful-fs": {
-            "version": "4.2.10",
-            "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
-            "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
+            "version": "4.2.4",
+            "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+            "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw=="
         },
         "growl": {
             "version": "1.10.5",
@@ -7549,9 +7917,9 @@
             },
             "dependencies": {
                 "mime": {
-                    "version": "2.6.0",
-                    "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
-                    "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="
+                    "version": "2.4.4",
+                    "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
+                    "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA=="
                 }
             }
         },
@@ -7569,14 +7937,14 @@
         "har-schema": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
-            "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q=="
+            "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
         },
         "har-validator": {
-            "version": "5.1.5",
-            "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
-            "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+            "version": "5.1.3",
+            "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
+            "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
             "requires": {
-                "ajv": "^6.12.3",
+                "ajv": "^6.5.5",
                 "har-schema": "^2.0.0"
             }
         },
@@ -7591,7 +7959,7 @@
         "has-ansi": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
-            "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
+            "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
             "requires": {
                 "ansi-regex": "^2.0.0"
             },
@@ -7599,14 +7967,14 @@
                 "ansi-regex": {
                     "version": "2.1.1",
                     "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-                    "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="
+                    "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
                 }
             }
         },
         "has-bigints": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
-            "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ=="
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
+            "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA=="
         },
         "has-binary2": {
             "version": "1.0.3",
@@ -7619,32 +7987,24 @@
                 "isarray": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
-                    "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
+                    "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
                 }
             }
         },
         "has-cors": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
-            "integrity": "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA=="
+            "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
         },
         "has-flag": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-            "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
-        },
-        "has-property-descriptors": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
-            "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
-            "requires": {
-                "get-intrinsic": "^1.1.1"
-            }
+            "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
         },
         "has-symbols": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
-            "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+            "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
         },
         "has-tostringtag": {
             "version": "1.0.0",
@@ -7652,17 +8012,24 @@
             "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
             "requires": {
                 "has-symbols": "^1.0.2"
+            },
+            "dependencies": {
+                "has-symbols": {
+                    "version": "1.0.3",
+                    "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+                    "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
+                }
             }
         },
         "has-unicode": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
-            "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
+            "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
         },
         "has-value": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
-            "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==",
+            "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
             "requires": {
                 "get-value": "^2.0.6",
                 "has-values": "^1.0.0",
@@ -7672,7 +8039,7 @@
         "has-values": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
-            "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==",
+            "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
             "requires": {
                 "is-number": "^3.0.0",
                 "kind-of": "^4.0.0"
@@ -7686,7 +8053,7 @@
                 "kind-of": {
                     "version": "4.0.0",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
-                    "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==",
+                    "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -7707,9 +8074,9 @@
             }
         },
         "hosted-git-info": {
-            "version": "2.8.9",
-            "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
-            "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="
+            "version": "2.8.8",
+            "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
+            "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg=="
         },
         "howler": {
             "version": "2.2.3",
@@ -7719,7 +8086,7 @@
         "hpack.js": {
             "version": "2.1.6",
             "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
-            "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==",
+            "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=",
             "dev": true,
             "requires": {
                 "inherits": "^2.0.1",
@@ -7748,7 +8115,7 @@
         "html": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/html/-/html-1.0.0.tgz",
-            "integrity": "sha512-lw/7YsdKiP3kk5PnR1INY17iJuzdAtJewxr14ozKJWbbR97znovZ0mh+WEMZ8rjc3lgTK+ID/htTjuyGKB52Kw==",
+            "integrity": "sha1-pUT6nqVJK/s6LMqCEKEL57WvH2E=",
             "requires": {
                 "concat-stream": "^1.4.7"
             }
@@ -7780,6 +8147,63 @@
                 "param-case": "^3.0.4",
                 "relateurl": "^0.2.7",
                 "terser": "^5.10.0"
+            },
+            "dependencies": {
+                "acorn": {
+                    "version": "8.7.0",
+                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
+                    "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ=="
+                },
+                "clean-css": {
+                    "version": "5.2.4",
+                    "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.2.4.tgz",
+                    "integrity": "sha512-nKseG8wCzEuji/4yrgM/5cthL9oTDc5UOQyFMvW/Q53oP6gLH690o1NbuTh6Y18nujr7BxlsFuS7gXLnLzKJGg==",
+                    "requires": {
+                        "source-map": "~0.6.0"
+                    }
+                },
+                "commander": {
+                    "version": "8.3.0",
+                    "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+                    "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="
+                },
+                "source-map": {
+                    "version": "0.6.1",
+                    "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+                    "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+                },
+                "source-map-support": {
+                    "version": "0.5.21",
+                    "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+                    "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+                    "requires": {
+                        "buffer-from": "^1.0.0",
+                        "source-map": "^0.6.0"
+                    }
+                },
+                "terser": {
+                    "version": "5.12.1",
+                    "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz",
+                    "integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==",
+                    "requires": {
+                        "acorn": "^8.5.0",
+                        "commander": "^2.20.0",
+                        "source-map": "~0.7.2",
+                        "source-map-support": "~0.5.20"
+                    },
+                    "dependencies": {
+                        "commander": {
+                            "version": "2.20.3",
+                            "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+                            "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+                        },
+                        "source-map": {
+                            "version": "0.7.3",
+                            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+                            "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ=="
+                        }
+                    }
+                }
             }
         },
         "html-to-image": {
@@ -7808,6 +8232,13 @@
                 "lodash": "^4.17.21",
                 "pretty-error": "^4.0.0",
                 "tapable": "^2.0.0"
+            },
+            "dependencies": {
+                "tapable": {
+                    "version": "2.2.1",
+                    "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+                    "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="
+                }
             }
         },
         "htmlparser2": {
@@ -7826,7 +8257,7 @@
         "http-browserify": {
             "version": "1.7.0",
             "resolved": "https://registry.npmjs.org/http-browserify/-/http-browserify-1.7.0.tgz",
-            "integrity": "sha512-Irf/LJXmE3cBzU1eaR4+NEX6bmVLqt1wkmDiA7kBwH7zmb0D8kBAXsDmQ88hhj/qv9iEZKlyGx/hrMcFi8sOHw==",
+            "integrity": "sha1-M3la3nLfiKz7/TZ3PO/tp2RzWyA=",
             "requires": {
                 "Base64": "~0.2.0",
                 "inherits": "~2.0.1"
@@ -7840,18 +8271,18 @@
         "http-deceiver": {
             "version": "1.2.7",
             "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
-            "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==",
+            "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=",
             "dev": true
         },
         "http-errors": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
-            "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+            "version": "1.8.1",
+            "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
+            "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
             "requires": {
-                "depd": "2.0.0",
+                "depd": "~1.1.2",
                 "inherits": "2.0.4",
                 "setprototypeof": "1.2.0",
-                "statuses": "2.0.1",
+                "statuses": ">= 1.5.0 < 2",
                 "toidentifier": "1.0.1"
             }
         },
@@ -7887,7 +8318,7 @@
         "http-signature": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
-            "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
+            "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
             "requires": {
                 "assert-plus": "^1.0.0",
                 "jsprim": "^1.2.2",
@@ -7895,9 +8326,9 @@
             }
         },
         "http2-wrapper": {
-            "version": "2.1.11",
-            "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.1.11.tgz",
-            "integrity": "sha512-aNAk5JzLturWEUiuhAN73Jcbq96R7rTitAoXV54FYMatvihnpD2+6PUgU4ce3D/m5VDbw+F5CsyKSF176ptitQ==",
+            "version": "2.1.10",
+            "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.1.10.tgz",
+            "integrity": "sha512-QHgsdYkieKp+6JbXP25P+tepqiHYd+FVnDwXpxi/BlUcoIB0nsmTOymTNvETuTO+pDuwcSklPE72VR3DqV+Haw==",
             "requires": {
                 "quick-lru": "^5.1.1",
                 "resolve-alpn": "^1.2.0"
@@ -7906,34 +8337,29 @@
         "https": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz",
-            "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg=="
+            "integrity": "sha1-PDfHrhqO65ZpBKKtHpdaGUt+06Q="
         },
         "https-browserify": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
-            "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg=="
+            "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM="
         },
         "https-proxy-agent": {
-            "version": "5.0.1",
-            "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
-            "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
+            "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
             "requires": {
                 "agent-base": "6",
                 "debug": "4"
             },
             "dependencies": {
                 "debug": {
-                    "version": "4.3.4",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
-                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+                    "version": "4.1.1",
+                    "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": {
-                    "version": "2.1.2",
-                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
                 }
             }
         },
@@ -7948,9 +8374,9 @@
             "integrity": "sha512-xjxyDLbVDdLgjPnl4NM+Iu6il3UPmk6PNCBXruQKeuKDc/HtaZx1hk1AtMgw3vsn9YnLZRfoBpPxYMXcoT5KAA=="
         },
         "hyphenate-style-name": {
-            "version": "1.0.4",
-            "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
-            "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz",
+            "integrity": "sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ=="
         },
         "i": {
             "version": "0.3.7",
@@ -7958,17 +8384,17 @@
             "integrity": "sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q=="
         },
         "iconv-lite": {
-            "version": "0.6.3",
-            "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
-            "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+            "version": "0.4.24",
+            "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+            "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
             "requires": {
-                "safer-buffer": ">= 2.1.2 < 3.0.0"
+                "safer-buffer": ">= 2.1.2 < 3"
             }
         },
         "icss-replace-symbols": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz",
-            "integrity": "sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==",
+            "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=",
             "dev": true
         },
         "icss-utils": {
@@ -7981,14 +8407,14 @@
             }
         },
         "ieee754": {
-            "version": "1.2.1",
-            "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
-            "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
+            "version": "1.1.13",
+            "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+            "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
         },
         "iferr": {
             "version": "0.1.5",
             "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
-            "integrity": "sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==",
+            "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
             "dev": true
         },
         "ignore": {
@@ -8000,7 +8426,7 @@
         "ignore-by-default": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
-            "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA=="
+            "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk="
         },
         "iink-js": {
             "version": "1.5.4",
@@ -8044,7 +8470,7 @@
         "image-size-stream": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/image-size-stream/-/image-size-stream-1.1.0.tgz",
-            "integrity": "sha512-N505B5FSy2Xf5l/Haef+99TwfJqTu40hnU560+rC0Cm6cxtwVz2yRFh9WpOk1YEjfv3dI0PgVYAH0hmXQmjDcw==",
+            "integrity": "sha1-Ivou2mbG31AQh0bacUkmSy0l+Gs=",
             "requires": {
                 "image-size": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1",
                 "readable-stream": "^1.0.33",
@@ -8058,12 +8484,12 @@
                 "isarray": {
                     "version": "0.0.1",
                     "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
-                    "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
+                    "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
                 },
                 "readable-stream": {
                     "version": "1.1.14",
                     "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
-                    "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==",
+                    "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
                     "requires": {
                         "core-util-is": "~1.0.0",
                         "inherits": "~2.0.1",
@@ -8081,7 +8507,7 @@
         "immediate": {
             "version": "3.0.6",
             "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
-            "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
+            "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
         },
         "import-fresh": {
             "version": "3.3.0",
@@ -8095,7 +8521,7 @@
         "import-lazy": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
-            "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A=="
+            "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM="
         },
         "import-local": {
             "version": "3.1.0",
@@ -8104,12 +8530,52 @@
             "requires": {
                 "pkg-dir": "^4.2.0",
                 "resolve-cwd": "^3.0.0"
+            },
+            "dependencies": {
+                "find-up": {
+                    "version": "4.1.0",
+                    "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+                    "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+                    "requires": {
+                        "locate-path": "^5.0.0",
+                        "path-exists": "^4.0.0"
+                    }
+                },
+                "locate-path": {
+                    "version": "5.0.0",
+                    "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+                    "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+                    "requires": {
+                        "p-locate": "^4.1.0"
+                    }
+                },
+                "p-locate": {
+                    "version": "4.1.0",
+                    "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+                    "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+                    "requires": {
+                        "p-limit": "^2.2.0"
+                    }
+                },
+                "path-exists": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+                    "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
+                },
+                "pkg-dir": {
+                    "version": "4.2.0",
+                    "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+                    "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+                    "requires": {
+                        "find-up": "^4.0.0"
+                    }
+                }
             }
         },
         "imurmurhash": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
-            "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="
+            "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
         },
         "in-publish": {
             "version": "2.0.1",
@@ -8119,20 +8585,26 @@
         "indent-string": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
-            "integrity": "sha512-aqwDFWSgSgfRaEwao5lg5KEcVd/2a+D1rvoG7NdilmYz0NwRk6StWpWdz/Hpk34MKPpx7s8XxUqimfcQK6gGlg==",
+            "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
             "requires": {
                 "repeating": "^2.0.0"
             }
         },
+        "indexes-of": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
+            "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
+            "dev": true
+        },
         "indexof": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
-            "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg=="
+            "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
         },
         "inflight": {
             "version": "1.0.6",
             "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-            "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+            "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
             "requires": {
                 "once": "^1.3.0",
                 "wrappy": "1"
@@ -8149,7 +8621,7 @@
                 "camelcase": {
                     "version": "4.1.0",
                     "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
-                    "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw=="
+                    "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
                 }
             }
         },
@@ -8159,14 +8631,14 @@
             "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
         },
         "ini": {
-            "version": "1.3.8",
-            "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
-            "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
+            "version": "1.3.5",
+            "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+            "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
         },
         "inline-style-prefixer": {
             "version": "3.0.8",
             "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-3.0.8.tgz",
-            "integrity": "sha512-ne8XIyyqkRaNJ1JfL1NYzNdCNxq+MCBQhC8NgOQlzNm2vv3XxlP0VSLQUbSRCF6KPEoveCVEpayHoHzcMyZsMQ==",
+            "integrity": "sha1-hVG45bTVcyROZqNLBPfTIHaitTQ=",
             "requires": {
                 "bowser": "^1.7.3",
                 "css-in-js-utils": "^2.0.0"
@@ -8175,7 +8647,7 @@
         "inspect-function": {
             "version": "0.2.2",
             "resolved": "https://registry.npmjs.org/inspect-function/-/inspect-function-0.2.2.tgz",
-            "integrity": "sha512-becs5gzcHwPrlHawscYkyQ/ShiOiosrXPhA5RVZ3qyWH4aWdD52RnMfXq/dwQXciHwiieD8aIPwdIWYv6eL+sQ==",
+            "integrity": "sha1-hdoMUli8TDMK4yg7Z0fgdZ2QpjU=",
             "requires": {
                 "split-skip": "0.0.1",
                 "unpack-string": "0.0.2"
@@ -8184,7 +8656,7 @@
                 "split-skip": {
                     "version": "0.0.1",
                     "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.1.tgz",
-                    "integrity": "sha512-7dkvq+gofI4M8zx4iZnEZ3O1s7FP4Y/iaIDHJh5RyWrs8idcPauFi2OZe3TBi36fLvR2j5z3kSzVtz6IhPdncQ=="
+                    "integrity": "sha1-gK2ONumOV2RUzDtmfB3SXYZejwA="
                 }
             }
         },
@@ -8199,15 +8671,10 @@
                 "unpack-string": "0.0.2"
             },
             "dependencies": {
-                "commander": {
-                    "version": "2.20.3",
-                    "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
-                    "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
-                },
                 "magicli": {
                     "version": "0.0.5",
                     "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz",
-                    "integrity": "sha512-wZbMtnl2v1b+Jp3xlqA9FU/O4I6YhGXR8xSY/eU2+gDAvut/F+W3gl4qs61iL4LELC7jqSAE6aAD5668EbmQHA==",
+                    "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=",
                     "requires": {
                         "commander": "^2.9.0",
                         "get-stdin": "^5.0.1",
@@ -8227,11 +8694,6 @@
                 "inspect-function": "^0.3.1"
             },
             "dependencies": {
-                "commander": {
-                    "version": "2.20.3",
-                    "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
-                    "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
-                },
                 "inspect-function": {
                     "version": "0.3.4",
                     "resolved": "https://registry.npmjs.org/inspect-function/-/inspect-function-0.3.4.tgz",
@@ -8258,7 +8720,7 @@
                         "inspect-function": {
                             "version": "0.2.2",
                             "resolved": "https://registry.npmjs.org/inspect-function/-/inspect-function-0.2.2.tgz",
-                            "integrity": "sha512-becs5gzcHwPrlHawscYkyQ/ShiOiosrXPhA5RVZ3qyWH4aWdD52RnMfXq/dwQXciHwiieD8aIPwdIWYv6eL+sQ==",
+                            "integrity": "sha1-hdoMUli8TDMK4yg7Z0fgdZ2QpjU=",
                             "requires": {
                                 "split-skip": "0.0.1",
                                 "unpack-string": "0.0.2"
@@ -8267,14 +8729,14 @@
                                 "split-skip": {
                                     "version": "0.0.1",
                                     "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.1.tgz",
-                                    "integrity": "sha512-7dkvq+gofI4M8zx4iZnEZ3O1s7FP4Y/iaIDHJh5RyWrs8idcPauFi2OZe3TBi36fLvR2j5z3kSzVtz6IhPdncQ=="
+                                    "integrity": "sha1-gK2ONumOV2RUzDtmfB3SXYZejwA="
                                 }
                             }
                         },
                         "magicli": {
                             "version": "0.0.5",
                             "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz",
-                            "integrity": "sha512-wZbMtnl2v1b+Jp3xlqA9FU/O4I6YhGXR8xSY/eU2+gDAvut/F+W3gl4qs61iL4LELC7jqSAE6aAD5668EbmQHA==",
+                            "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=",
                             "requires": {
                                 "commander": "^2.9.0",
                                 "get-stdin": "^5.0.1",
@@ -8285,14 +8747,14 @@
                         "split-skip": {
                             "version": "0.0.2",
                             "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.2.tgz",
-                            "integrity": "sha512-weHOi8BolsDnGIwhhWHbA+wKSuSpvWwjRrdj8SdbIIis2vSwOE37CQP8x3EleuzxanUr3AK8BdUy4MkiOULPZg=="
+                            "integrity": "sha1-2J2Iu9L3Pka1FYqjcKVhIk6A1GE="
                         }
                     }
                 },
                 "split-skip": {
                     "version": "0.0.1",
                     "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.1.tgz",
-                    "integrity": "sha512-7dkvq+gofI4M8zx4iZnEZ3O1s7FP4Y/iaIDHJh5RyWrs8idcPauFi2OZe3TBi36fLvR2j5z3kSzVtz6IhPdncQ=="
+                    "integrity": "sha1-gK2ONumOV2RUzDtmfB3SXYZejwA="
                 }
             }
         },
@@ -8355,15 +8817,15 @@
             }
         },
         "ip": {
-            "version": "1.1.8",
-            "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz",
-            "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==",
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
+            "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
             "dev": true
         },
         "ip-regex": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
-            "integrity": "sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==",
+            "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
             "dev": true
         },
         "ipaddr.js": {
@@ -8380,7 +8842,7 @@
         "is-accessor-descriptor": {
             "version": "0.1.6",
             "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
-            "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+            "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
             "requires": {
                 "kind-of": "^3.0.2"
             },
@@ -8393,7 +8855,7 @@
                 "kind-of": {
                     "version": "3.2.2",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                    "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+                    "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -8412,7 +8874,7 @@
         "is-arrayish": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
-            "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
+            "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
         },
         "is-bigint": {
             "version": "1.0.4",
@@ -8425,7 +8887,7 @@
         "is-binary-path": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
-            "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==",
+            "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
             "requires": {
                 "binary-extensions": "^1.0.0"
             }
@@ -8440,14 +8902,14 @@
             }
         },
         "is-buffer": {
-            "version": "2.0.5",
-            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz",
-            "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ=="
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
+            "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A=="
         },
         "is-callable": {
-            "version": "1.2.4",
-            "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz",
-            "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w=="
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
+            "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q=="
         },
         "is-ci": {
             "version": "1.2.1",
@@ -8457,18 +8919,10 @@
                 "ci-info": "^1.5.0"
             }
         },
-        "is-core-module": {
-            "version": "2.9.0",
-            "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
-            "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
-            "requires": {
-                "has": "^1.0.3"
-            }
-        },
         "is-data-descriptor": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
-            "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+            "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
             "requires": {
                 "kind-of": "^3.0.2"
             },
@@ -8481,7 +8935,7 @@
                 "kind-of": {
                     "version": "3.2.2",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                    "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+                    "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -8489,12 +8943,9 @@
             }
         },
         "is-date-object": {
-            "version": "1.0.5",
-            "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
-            "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
-            "requires": {
-                "has-tostringtag": "^1.0.0"
-            }
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
+            "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g=="
         },
         "is-descriptor": {
             "version": "0.1.6",
@@ -8516,12 +8967,12 @@
         "is-directory": {
             "version": "0.3.1",
             "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
-            "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw=="
+            "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE="
         },
         "is-expression": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz",
-            "integrity": "sha512-vyMeQMq+AiH5uUnoBfMTwf18tO3bM6k1QXBE9D6ueAAquEfCZe3AJPtud9g6qS0+4X8xA7ndpZiDyeb2l2qOBw==",
+            "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=",
             "requires": {
                 "acorn": "~4.0.2",
                 "object-assign": "^4.0.1"
@@ -8530,19 +8981,22 @@
                 "acorn": {
                     "version": "4.0.13",
                     "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
-                    "integrity": "sha512-fu2ygVGuMmlzG8ZeRJ0bvR41nsAkxxhbyk8bZ1SS521Z7vmgJFTQQlfz/Mp/nJexGBz+v8sC9bM6+lNgskt4Ug=="
+                    "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c="
                 }
             }
         },
         "is-extendable": {
-            "version": "0.1.1",
-            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
-            "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="
+            "version": "1.0.1",
+            "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-extglob": {
             "version": "2.1.1",
             "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-            "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
+            "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
         },
         "is-finite": {
             "version": "1.1.0",
@@ -8552,7 +9006,7 @@
         "is-fullwidth-code-point": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-            "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w=="
+            "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
         },
         "is-generator-function": {
             "version": "1.0.10",
@@ -8563,9 +9017,9 @@
             }
         },
         "is-glob": {
-            "version": "4.0.3",
-            "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
-            "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+            "version": "4.0.1",
+            "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"
             }
@@ -8573,12 +9027,12 @@
         "is-in-browser": {
             "version": "1.1.3",
             "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz",
-            "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g=="
+            "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU="
         },
         "is-installed-globally": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz",
-            "integrity": "sha512-ERNhMg+i/XgDwPIPF3u24qpajVreaiSuvpb1Uu0jugw7KKcxGyCX8cgp8P5fwTmAuXku6beDHHECdKArjlg7tw==",
+            "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=",
             "requires": {
                 "global-dirs": "^0.1.0",
                 "is-path-inside": "^1.0.0"
@@ -8592,12 +9046,12 @@
         "is-npm": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz",
-            "integrity": "sha512-9r39FIr3d+KD9SbX0sfMsHzb5PP3uimOiwr3YupUaUFG4W0l1U57Rx3utpttV7qz5U3jmrO5auUa04LU9pyHsg=="
+            "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ="
         },
         "is-number": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-            "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
+            "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
             "requires": {
                 "kind-of": "^3.0.2"
             },
@@ -8610,7 +9064,7 @@
                 "kind-of": {
                     "version": "3.2.2",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                    "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+                    "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -8618,9 +9072,9 @@
             }
         },
         "is-number-object": {
-            "version": "1.0.7",
-            "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
-            "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz",
+            "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==",
             "requires": {
                 "has-tostringtag": "^1.0.0"
             }
@@ -8628,7 +9082,7 @@
         "is-obj": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
-            "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg=="
+            "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8="
         },
         "is-path-cwd": {
             "version": "2.2.0",
@@ -8659,7 +9113,7 @@
         "is-path-inside": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
-            "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==",
+            "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
             "requires": {
                 "path-is-inside": "^1.0.1"
             }
@@ -8680,15 +9134,14 @@
         "is-redirect": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
-            "integrity": "sha512-cr/SlUEe5zOGmzvj9bUyC4LVvkNVAXu4GytXLNMr1pny+a65MpQ9IJzFHD5vi7FyJgb4qt27+eS3TuQnqB+RQw=="
+            "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ="
         },
         "is-regex": {
-            "version": "1.1.4",
-            "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
-            "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
+            "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
             "requires": {
-                "call-bind": "^1.0.2",
-                "has-tostringtag": "^1.0.0"
+                "has": "^1.0.3"
             }
         },
         "is-retry-allowed": {
@@ -8705,17 +9158,14 @@
             }
         },
         "is-shared-array-buffer": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
-            "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
-            "requires": {
-                "call-bind": "^1.0.2"
-            }
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz",
+            "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA=="
         },
         "is-stream": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
-            "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ=="
+            "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
         },
         "is-string": {
             "version": "1.0.7",
@@ -8726,34 +9176,116 @@
             }
         },
         "is-symbol": {
-            "version": "1.0.4",
-            "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
-            "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
+            "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
             "requires": {
-                "has-symbols": "^1.0.2"
+                "has-symbols": "^1.0.1"
             }
         },
         "is-typed-array": {
-            "version": "1.1.9",
-            "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz",
-            "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==",
+            "version": "1.1.8",
+            "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz",
+            "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==",
             "requires": {
                 "available-typed-arrays": "^1.0.5",
                 "call-bind": "^1.0.2",
-                "es-abstract": "^1.20.0",
-                "for-each": "^0.3.3",
+                "es-abstract": "^1.18.5",
+                "foreach": "^2.0.5",
                 "has-tostringtag": "^1.0.0"
+            },
+            "dependencies": {
+                "es-abstract": {
+                    "version": "1.19.1",
+                    "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz",
+                    "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==",
+                    "requires": {
+                        "call-bind": "^1.0.2",
+                        "es-to-primitive": "^1.2.1",
+                        "function-bind": "^1.1.1",
+                        "get-intrinsic": "^1.1.1",
+                        "get-symbol-description": "^1.0.0",
+                        "has": "^1.0.3",
+                        "has-symbols": "^1.0.2",
+                        "internal-slot": "^1.0.3",
+                        "is-callable": "^1.2.4",
+                        "is-negative-zero": "^2.0.1",
+                        "is-regex": "^1.1.4",
+                        "is-shared-array-buffer": "^1.0.1",
+                        "is-string": "^1.0.7",
+                        "is-weakref": "^1.0.1",
+                        "object-inspect": "^1.11.0",
+                        "object-keys": "^1.1.1",
+                        "object.assign": "^4.1.2",
+                        "string.prototype.trimend": "^1.0.4",
+                        "string.prototype.trimstart": "^1.0.4",
+                        "unbox-primitive": "^1.0.1"
+                    }
+                },
+                "has-symbols": {
+                    "version": "1.0.3",
+                    "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+                    "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
+                },
+                "is-callable": {
+                    "version": "1.2.4",
+                    "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz",
+                    "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w=="
+                },
+                "is-regex": {
+                    "version": "1.1.4",
+                    "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+                    "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+                    "requires": {
+                        "call-bind": "^1.0.2",
+                        "has-tostringtag": "^1.0.0"
+                    }
+                },
+                "object-inspect": {
+                    "version": "1.12.0",
+                    "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
+                    "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g=="
+                },
+                "object.assign": {
+                    "version": "4.1.2",
+                    "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+                    "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+                    "requires": {
+                        "call-bind": "^1.0.0",
+                        "define-properties": "^1.1.3",
+                        "has-symbols": "^1.0.1",
+                        "object-keys": "^1.1.1"
+                    }
+                },
+                "string.prototype.trimend": {
+                    "version": "1.0.4",
+                    "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
+                    "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
+                    "requires": {
+                        "call-bind": "^1.0.2",
+                        "define-properties": "^1.1.3"
+                    }
+                },
+                "string.prototype.trimstart": {
+                    "version": "1.0.4",
+                    "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
+                    "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
+                    "requires": {
+                        "call-bind": "^1.0.2",
+                        "define-properties": "^1.1.3"
+                    }
+                }
             }
         },
         "is-typedarray": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-            "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
+            "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
         },
         "is-utf8": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
-            "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q=="
+            "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI="
         },
         "is-weakref": {
             "version": "1.0.2",
@@ -8764,9 +9296,9 @@
             }
         },
         "is-what": {
-            "version": "3.14.1",
-            "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz",
-            "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA=="
+            "version": "3.10.0",
+            "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.10.0.tgz",
+            "integrity": "sha512-U4RYCXNOmATQHlOPlOCHCfXyKEFIPqvyaKDqYRuLbD6EYKcTTfc3YXkAYjzOVxO3zt34L+Wh2feIyWrYiZ7kng=="
         },
         "is-windows": {
             "version": "1.0.2",
@@ -8776,28 +9308,28 @@
         "is-wsl": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
-            "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==",
+            "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
             "dev": true
         },
         "isarray": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-            "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+            "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
         },
         "isexe": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-            "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
+            "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
         },
         "isobject": {
             "version": "3.0.1",
             "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-            "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="
+            "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
         },
         "isomorphic-fetch": {
             "version": "2.2.1",
             "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
-            "integrity": "sha512-9c4TNAKYXM5PRyVcwUZrF3W09nQ+sO7+jydgs4ZGW9dhsLG2VOlISJABombdQqQRXCwuYG3sYV/puGf5rp0qmA==",
+            "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
             "requires": {
                 "node-fetch": "^1.0.1",
                 "whatwg-fetch": ">=0.10.0"
@@ -8806,7 +9338,7 @@
         "isstream": {
             "version": "0.1.2",
             "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
-            "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g=="
+            "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
         },
         "its-set": {
             "version": "1.2.3",
@@ -8846,14 +9378,14 @@
             }
         },
         "jquery": {
-            "version": "3.6.0",
-            "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
-            "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
+            "version": "3.5.1",
+            "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz",
+            "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg=="
         },
         "js-base64": {
-            "version": "2.6.4",
-            "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz",
-            "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ=="
+            "version": "2.5.2",
+            "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz",
+            "integrity": "sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ=="
         },
         "js-datepicker": {
             "version": "4.6.6",
@@ -8863,7 +9395,7 @@
         "js-stringify": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz",
-            "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g=="
+            "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds="
         },
         "js-tokens": {
             "version": "4.0.0",
@@ -8882,7 +9414,7 @@
         "jsbn": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
-            "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg=="
+            "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
         },
         "jsdom": {
             "version": "15.2.1",
@@ -8919,9 +9451,9 @@
             },
             "dependencies": {
                 "acorn": {
-                    "version": "7.4.1",
-                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
-                    "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+                    "version": "7.1.1",
+                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
+                    "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==",
                     "dev": true
                 },
                 "acorn-globals": {
@@ -8935,13 +9467,19 @@
                     },
                     "dependencies": {
                         "acorn": {
-                            "version": "6.4.2",
-                            "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
-                            "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
+                            "version": "6.4.1",
+                            "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
+                            "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
                             "dev": true
                         }
                     }
                 },
+                "parse5": {
+                    "version": "5.1.0",
+                    "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
+                    "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==",
+                    "dev": true
+                },
                 "tough-cookie": {
                     "version": "3.0.1",
                     "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
@@ -8952,32 +9490,6 @@
                         "psl": "^1.1.28",
                         "punycode": "^2.1.1"
                     }
-                },
-                "tr46": {
-                    "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
-                    "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
-                    "dev": true,
-                    "requires": {
-                        "punycode": "^2.1.0"
-                    }
-                },
-                "webidl-conversions": {
-                    "version": "4.0.2",
-                    "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
-                    "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
-                    "dev": true
-                },
-                "whatwg-url": {
-                    "version": "7.1.0",
-                    "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
-                    "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
-                    "dev": true,
-                    "requires": {
-                        "lodash.sortby": "^4.7.0",
-                        "tr46": "^1.0.1",
-                        "webidl-conversions": "^4.0.2"
-                    }
                 }
             }
         },
@@ -8987,11 +9499,11 @@
             "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="
         },
         "json-bigint": {
-            "version": "0.3.1",
-            "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.1.tgz",
-            "integrity": "sha512-DGWnSzmusIreWlEupsUelHrhwmPPE+FiQvg+drKfk2p+bdEYa5mp4PJ8JsCWqae0M2jQNb0HPvnwvf1qOTThzQ==",
+            "version": "0.3.0",
+            "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz",
+            "integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=",
             "requires": {
-                "bignumber.js": "^9.0.0"
+                "bignumber.js": "^7.0.0"
             }
         },
         "json-buffer": {
@@ -9002,7 +9514,7 @@
         "json-css": {
             "version": "1.5.6",
             "resolved": "https://registry.npmjs.org/json-css/-/json-css-1.5.6.tgz",
-            "integrity": "sha512-B/0T0OxZH9tSb93tXV6VOYtXqrPz/Vgz2QrCT/4NXen8HGElYkYr9V+8IrSVTMj/ftxa8cG1kcu7f3iAMlaFlQ=="
+            "integrity": "sha1-65ZPg0ouTqobwvaY/12wB6JsfAA="
         },
         "json-parse-better-errors": {
             "version": "1.0.2",
@@ -9015,9 +9527,9 @@
             "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
         },
         "json-schema": {
-            "version": "0.4.0",
-            "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
-            "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="
+            "version": "0.2.3",
+            "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+            "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
         },
         "json-schema-traverse": {
             "version": "0.4.1",
@@ -9027,15 +9539,7 @@
         "json-stringify-safe": {
             "version": "5.0.1",
             "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
-            "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="
-        },
-        "json5": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
-            "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
-            "requires": {
-                "minimist": "^1.2.0"
-            }
+            "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
         },
         "jsondiffpatch": {
             "version": "0.4.1",
@@ -9049,24 +9553,24 @@
         "jsonfile": {
             "version": "2.4.0",
             "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
-            "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==",
+            "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
             "requires": {
                 "graceful-fs": "^4.1.6"
             }
         },
         "jsonschema": {
-            "version": "1.4.1",
-            "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz",
-            "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ=="
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.0.tgz",
+            "integrity": "sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw=="
         },
         "jsprim": {
-            "version": "1.4.2",
-            "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
-            "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
+            "version": "1.4.1",
+            "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
+            "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
             "requires": {
                 "assert-plus": "1.0.0",
                 "extsprintf": "1.3.0",
-                "json-schema": "0.4.0",
+                "json-schema": "0.2.3",
                 "verror": "1.10.0"
             }
         },
@@ -9082,9 +9586,9 @@
             },
             "dependencies": {
                 "csstype": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
+                    "version": "3.0.11",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
+                    "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
                 }
             }
         },
@@ -9158,21 +9662,21 @@
         "jstransformer": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz",
-            "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==",
+            "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=",
             "requires": {
                 "is-promise": "^2.0.0",
                 "promise": "^7.0.1"
             }
         },
         "jszip": {
-            "version": "3.10.0",
-            "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.0.tgz",
-            "integrity": "sha512-LDfVtOLtOxb9RXkYOwPyNBTQDL4eUbqahtoY6x07GiDJHwSYvn8sHHIw8wINImV3MqbMNve2gSuM1DDqEKk09Q==",
+            "version": "3.7.1",
+            "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz",
+            "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==",
             "requires": {
                 "lie": "~3.3.0",
                 "pako": "~1.0.2",
                 "readable-stream": "~2.3.6",
-                "setimmediate": "^1.0.5"
+                "set-immediate-shim": "~1.0.1"
             },
             "dependencies": {
                 "readable-stream": {
@@ -9221,9 +9725,9 @@
             "integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew=="
         },
         "keycode": {
-            "version": "2.2.1",
-            "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.1.tgz",
-            "integrity": "sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg=="
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz",
+            "integrity": "sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ="
         },
         "keygrip": {
             "version": "1.1.0",
@@ -9234,11 +9738,10 @@
             }
         },
         "keyv": {
-            "version": "4.3.0",
-            "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.3.0.tgz",
-            "integrity": "sha512-C30Un9+63J0CsR7Wka5quXKqYZsT6dcRQ2aOwGcSc3RiQ4HGWpTAHlCA+puNfw2jA/s11EsxA1nCXgZRuRKMQQ==",
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz",
+            "integrity": "sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==",
             "requires": {
-                "compress-brotli": "^1.3.8",
                 "json-buffer": "3.0.1"
             }
         },
@@ -9256,7 +9759,7 @@
         "klaw": {
             "version": "1.3.1",
             "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
-            "integrity": "sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==",
+            "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=",
             "requires": {
                 "graceful-fs": "^4.1.9"
             }
@@ -9264,7 +9767,7 @@
         "latest-version": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz",
-            "integrity": "sha512-Be1YRHWWlZaSsrz2U+VInk+tO0EwLIyV+23RhWLINJYwg/UIikxjlj3MhH37/6/EDCAusjajvMkMMUXRaMWl/w==",
+            "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=",
             "requires": {
                 "package-json": "^4.0.0"
             }
@@ -9272,12 +9775,12 @@
         "lazy-cache": {
             "version": "1.0.4",
             "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
-            "integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ=="
+            "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4="
         },
         "lazystream": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
-            "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz",
+            "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=",
             "requires": {
                 "readable-stream": "^2.0.5"
             },
@@ -9301,7 +9804,7 @@
         "levn": {
             "version": "0.3.0",
             "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
-            "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==",
+            "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
             "dev": true,
             "requires": {
                 "prelude-ls": "~1.1.2",
@@ -9324,7 +9827,7 @@
         "load-json-file": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
-            "integrity": "sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==",
+            "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
             "requires": {
                 "graceful-fs": "^4.1.2",
                 "parse-json": "^2.2.0",
@@ -9336,17 +9839,22 @@
                 "parse-json": {
                     "version": "2.2.0",
                     "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
-                    "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==",
+                    "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
                     "requires": {
                         "error-ex": "^1.2.0"
                     }
+                },
+                "pify": {
+                    "version": "2.3.0",
+                    "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+                    "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
                 }
             }
         },
         "loader-runner": {
-            "version": "4.3.0",
-            "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
-            "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
+            "version": "4.2.0",
+            "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz",
+            "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==",
             "dev": true
         },
         "loader-utils": {
@@ -9357,6 +9865,16 @@
                 "big.js": "^5.2.2",
                 "emojis-list": "^3.0.0",
                 "json5": "^1.0.1"
+            },
+            "dependencies": {
+                "json5": {
+                    "version": "1.0.1",
+                    "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+                    "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+                    "requires": {
+                        "minimist": "^1.2.0"
+                    }
+                }
             }
         },
         "locate-path": {
@@ -9381,22 +9899,22 @@
         "lodash._getnative": {
             "version": "3.9.1",
             "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
-            "integrity": "sha512-RrL9VxMEPyDMHOd9uFbvMe8X55X16/cGM5IgOKgRElQZutpX89iS6vwl64duTV1/16w5JY7tuFNXqoekmh1EmA=="
+            "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U="
         },
         "lodash.chunk": {
             "version": "4.2.0",
             "resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz",
-            "integrity": "sha512-ZzydJKfUHJwHa+hF5X66zLFCBrWn5GeF28OHEr4WVWtNDXlQ/IjWKPBiikqKo2ne0+v6JgCgJ0GzJp8k8bHC7w=="
+            "integrity": "sha1-ZuXOH3btJ7QwPYxlEujRIW6BBrw="
         },
         "lodash.curry": {
             "version": "4.1.1",
             "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz",
-            "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA=="
+            "integrity": "sha1-JI42By7ekGUB11lmIAqG2riyMXA="
         },
         "lodash.debounce": {
             "version": "3.1.1",
             "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-3.1.1.tgz",
-            "integrity": "sha512-lcmJwMpdPAtChA4hfiwxTtgFeNAaow701wWUgVUqeD0XJF7vMXIN+bu/2FJSGxT0NUbZy9g9VFrlOFfPjl+0Ew==",
+            "integrity": "sha1-gSIRw3ipTMKdWqTjNGzwv846ffU=",
             "requires": {
                 "lodash._getnative": "^3.0.0"
             }
@@ -9404,42 +9922,42 @@
         "lodash.defaults": {
             "version": "4.2.0",
             "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
-            "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
+            "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw="
         },
         "lodash.difference": {
             "version": "4.5.0",
             "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
-            "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA=="
+            "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw="
         },
         "lodash.flatten": {
             "version": "4.4.0",
             "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
-            "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g=="
+            "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
         },
         "lodash.flow": {
             "version": "3.5.0",
             "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz",
-            "integrity": "sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw=="
+            "integrity": "sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o="
         },
         "lodash.get": {
             "version": "4.4.2",
             "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
-            "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="
+            "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
         },
         "lodash.isequal": {
             "version": "4.5.0",
             "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
-            "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
+            "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
         },
         "lodash.isplainobject": {
             "version": "4.0.6",
             "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
-            "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
+            "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
         },
         "lodash.memoize": {
             "version": "4.1.2",
             "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
-            "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag=="
+            "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4="
         },
         "lodash.merge": {
             "version": "4.6.2",
@@ -9449,23 +9967,23 @@
         "lodash.padend": {
             "version": "4.6.1",
             "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz",
-            "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw=="
+            "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4="
         },
         "lodash.sortby": {
             "version": "4.7.0",
             "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
-            "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==",
+            "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
             "dev": true
         },
         "lodash.throttle": {
             "version": "4.1.1",
             "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
-            "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="
+            "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
         },
         "lodash.union": {
             "version": "4.6.0",
             "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
-            "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw=="
+            "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg="
         },
         "log-symbols": {
             "version": "2.2.0",
@@ -9493,7 +10011,7 @@
         "longest": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
-            "integrity": "sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg=="
+            "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc="
         },
         "loose-envify": {
             "version": "1.4.0",
@@ -9506,7 +10024,7 @@
         "loud-rejection": {
             "version": "1.6.0",
             "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
-            "integrity": "sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==",
+            "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
             "requires": {
                 "currently-unhandled": "^0.4.1",
                 "signal-exit": "^3.0.0"
@@ -9526,6 +10044,13 @@
             "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
             "requires": {
                 "tslib": "^2.0.3"
+            },
+            "dependencies": {
+                "tslib": {
+                    "version": "2.3.1",
+                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+                    "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+                }
             }
         },
         "lowercase-keys": {
@@ -9534,11 +10059,11 @@
             "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ=="
         },
         "lru-cache": {
-            "version": "6.0.0",
-            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
-            "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+            "version": "5.1.1",
+            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+            "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
             "requires": {
-                "yallist": "^4.0.0"
+                "yallist": "^3.0.2"
             }
         },
         "magicli": {
@@ -9555,7 +10080,7 @@
                 "find-up": {
                     "version": "2.1.0",
                     "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
-                    "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==",
+                    "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
                     "requires": {
                         "locate-path": "^2.0.0"
                     }
@@ -9563,7 +10088,7 @@
                 "locate-path": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
-                    "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==",
+                    "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
                     "requires": {
                         "p-locate": "^2.0.0",
                         "path-exists": "^3.0.0"
@@ -9580,7 +10105,7 @@
                 "p-locate": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
-                    "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==",
+                    "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
                     "requires": {
                         "p-limit": "^1.1.0"
                     }
@@ -9588,22 +10113,22 @@
                 "p-try": {
                     "version": "1.0.0",
                     "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
-                    "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww=="
+                    "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M="
                 }
             }
         },
         "make-dir": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
-            "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
+            "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
             "requires": {
-                "semver": "^6.0.0"
+                "pify": "^3.0.0"
             },
             "dependencies": {
-                "semver": {
-                    "version": "6.3.0",
-                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
-                    "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
+                "pify": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+                    "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
                 }
             }
         },
@@ -9616,17 +10141,17 @@
         "map-cache": {
             "version": "0.2.2",
             "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
-            "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg=="
+            "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8="
         },
         "map-obj": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
-            "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg=="
+            "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0="
         },
         "map-visit": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
-            "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==",
+            "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
             "requires": {
                 "object-visit": "^1.0.0"
             }
@@ -9671,7 +10196,7 @@
         "math-codegen": {
             "version": "0.3.5",
             "resolved": "https://registry.npmjs.org/math-codegen/-/math-codegen-0.3.5.tgz",
-            "integrity": "sha512-SsFYMv33FxMKYxI1PBiaZT+8AeDITK+k/PKhbHNlOPHIz5FIPF4wy78yWqanN6luXdsXENUZgCIC6xH6bfUq1g==",
+            "integrity": "sha1-R5nuRnfe0Ud2bQA8ykt4ee3UDMo=",
             "requires": {
                 "extend": "^3.0.0",
                 "mr-parser": "^0.2.1"
@@ -9680,7 +10205,7 @@
         "mathquill": {
             "version": "0.10.1-a",
             "resolved": "https://registry.npmjs.org/mathquill/-/mathquill-0.10.1-a.tgz",
-            "integrity": "sha512-snSAEwAtwdwBFSor+nVBnWWQtTw67kgAgKMyAIxuz4ZPboy0qkWZmd7BL3lfOXp/INihhRlU1PcfaAtDaRhmzA==",
+            "integrity": "sha1-vyylaQEAY6w0vNXVKa3Ag3zVPD8=",
             "requires": {
                 "jquery": "^1.12.3"
             },
@@ -9688,32 +10213,32 @@
                 "jquery": {
                     "version": "1.12.4",
                     "resolved": "https://registry.npmjs.org/jquery/-/jquery-1.12.4.tgz",
-                    "integrity": "sha512-UEVp7PPK9xXYSk8xqXCJrkXnKZtlgWkd2GsAQbMRFK6S/ePU2JN5G2Zum8hIVjzR3CpdfSqdqAzId/xd4TJHeg=="
+                    "integrity": "sha1-AeHfuikP5z3rp3zurLD5ui/sngw="
                 }
             }
         },
         "max-safe-integer": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/max-safe-integer/-/max-safe-integer-1.0.1.tgz",
-            "integrity": "sha512-CHZ/Nopqh46UtA0YvLclxj9F95qExLmTnMS5fnYlXvX4zj1RUOC3cPaGEOMhdPE8SSudSQ6wkQUJLA5E/WeL4A=="
+            "integrity": "sha1-84BgvixWPYwC5tSK85Ei/YO29BA="
         },
         "media-typer": {
             "version": "0.3.0",
             "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
-            "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="
+            "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
         },
         "memfs": {
-            "version": "3.4.4",
-            "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.4.tgz",
-            "integrity": "sha512-W4gHNUE++1oSJVn8Y68jPXi+mkx3fXR5ITE/Ubz6EQ3xRpCN5k2CQ4AUR8094Z7211F876TyoBACGsIveqgiGA==",
+            "version": "3.4.1",
+            "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz",
+            "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==",
             "requires": {
                 "fs-monkey": "1.0.3"
             }
         },
         "memoize-one": {
-            "version": "5.2.1",
-            "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
-            "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
+            "version": "5.1.1",
+            "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz",
+            "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA=="
         },
         "memory-fs": {
             "version": "0.5.0",
@@ -9751,12 +10276,12 @@
         "memorystream": {
             "version": "0.3.1",
             "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
-            "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw=="
+            "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI="
         },
         "meow": {
             "version": "3.7.0",
             "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
-            "integrity": "sha512-TNdwZs0skRlpPpCUK25StC4VH+tP5GgeY1HQOOGP+lQ2xtdkN2VtT/5tiX9k3IWpkBPV9b3LsAWXn4GGi/PrSA==",
+            "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
             "requires": {
                 "camelcase-keys": "^2.0.0",
                 "decamelize": "^1.1.2",
@@ -9781,7 +10306,7 @@
         "merge-descriptors": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
-            "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
+            "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
         },
         "merge-stream": {
             "version": "2.0.0",
@@ -9791,7 +10316,7 @@
         "methods": {
             "version": "1.1.2",
             "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
-            "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="
+            "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
         },
         "microevent.ts": {
             "version": "0.1.1",
@@ -9825,16 +10350,16 @@
             "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
         },
         "mime-db": {
-            "version": "1.52.0",
-            "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
-            "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+            "version": "1.44.0",
+            "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
+            "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
         },
         "mime-types": {
-            "version": "2.1.35",
-            "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
-            "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+            "version": "2.1.27",
+            "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
+            "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
             "requires": {
-                "mime-db": "1.52.0"
+                "mime-db": "1.44.0"
             }
         },
         "mimic-fn": {
@@ -9854,17 +10379,17 @@
             "dev": true
         },
         "minimatch": {
-            "version": "3.1.2",
-            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
-            "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+            "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
             "requires": {
                 "brace-expansion": "^1.1.7"
             }
         },
         "minimist": {
-            "version": "1.2.6",
-            "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
-            "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
+            "version": "1.2.5",
+            "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+            "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
         },
         "minipass": {
             "version": "3.1.6",
@@ -9872,6 +10397,13 @@
             "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==",
             "requires": {
                 "yallist": "^4.0.0"
+            },
+            "dependencies": {
+                "yallist": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+                    "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+                }
             }
         },
         "minizlib": {
@@ -9881,6 +10413,13 @@
             "requires": {
                 "minipass": "^3.0.0",
                 "yallist": "^4.0.0"
+            },
+            "dependencies": {
+                "yallist": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+                    "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+                }
             }
         },
         "mississippi": {
@@ -9920,16 +10459,6 @@
             "requires": {
                 "for-in": "^1.0.2",
                 "is-extendable": "^1.0.1"
-            },
-            "dependencies": {
-                "is-extendable": {
-                    "version": "1.0.1",
-                    "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"
-                    }
-                }
             }
         },
         "mkdirp": {
@@ -9941,9 +10470,9 @@
             }
         },
         "mkdirp-classic": {
-            "version": "0.5.3",
-            "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
-            "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
+            "version": "0.5.2",
+            "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz",
+            "integrity": "sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g=="
         },
         "mobile-detect": {
             "version": "1.4.5",
@@ -10025,28 +10554,19 @@
                 "he": {
                     "version": "1.1.1",
                     "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
-                    "integrity": "sha512-z/GDPjlRMNOa2XJiB4em8wJpuuBfrFOlYKTZxtpkdr1uPdibHI8rYA3MY0KDObpVyaes0e/aunid/t88ZI2EKA==",
+                    "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
                     "dev": true
                 },
-                "minimatch": {
-                    "version": "3.0.4",
-                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
-                    "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
-                    "dev": true,
-                    "requires": {
-                        "brace-expansion": "^1.1.7"
-                    }
-                },
                 "minimist": {
                     "version": "0.0.8",
                     "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
-                    "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==",
+                    "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
                     "dev": true
                 },
                 "mkdirp": {
                     "version": "0.5.1",
                     "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
-                    "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==",
+                    "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
                     "dev": true,
                     "requires": {
                         "minimist": "0.0.8"
@@ -10055,7 +10575,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
                     "dev": true
                 },
                 "supports-color": {
@@ -10149,6 +10669,15 @@
                 "sliced": "1.0.1"
             },
             "dependencies": {
+                "@types/mongodb": {
+                    "version": "3.6.20",
+                    "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz",
+                    "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==",
+                    "requires": {
+                        "@types/bson": "*",
+                        "@types/node": "*"
+                    }
+                },
                 "bson": {
                     "version": "1.1.6",
                     "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz",
@@ -10179,7 +10708,7 @@
         "move-concurrently": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
-            "integrity": "sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==",
+            "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
             "dev": true,
             "requires": {
                 "aproba": "^1.1.1",
@@ -10190,12 +10719,6 @@
                 "run-queue": "^1.0.3"
             },
             "dependencies": {
-                "aproba": {
-                    "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-                    "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
-                    "dev": true
-                },
                 "rimraf": {
                     "version": "2.7.1",
                     "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@@ -10240,14 +10763,14 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                 }
             }
         },
         "mr-parser": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/mr-parser/-/mr-parser-0.2.1.tgz",
-            "integrity": "sha512-hug+mpbSSKnH13rFqy3zm+XiG+QTStiDAgMTHK355TIstQE0qBkBtSJsa5YHP94AuarVX9b/4dcebdTRZ9YiEw=="
+            "integrity": "sha1-hhi5ukF+KOn0OaQcaVtVTq/u2Sc="
         },
         "ms": {
             "version": "2.1.1",
@@ -10267,13 +10790,13 @@
         "multicast-dns-service-types": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
-            "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==",
+            "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
             "dev": true
         },
         "nan": {
-            "version": "2.16.0",
-            "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz",
-            "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA=="
+            "version": "2.14.1",
+            "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz",
+            "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw=="
         },
         "nanoid": {
             "version": "2.1.11",
@@ -10306,7 +10829,7 @@
         "native-or-bluebird": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/native-or-bluebird/-/native-or-bluebird-1.2.0.tgz",
-            "integrity": "sha512-0SH8UubxDfe382eYiwmd12qxAbiWGzlGZv6CkMA+DPojWa/Y0oH4hE0lRtFfFgJmPQFyKXeB8XxPbZz6TvvKaQ=="
+            "integrity": "sha1-OcR7/Xgl0fuf+tMiEK4l2q3xAck="
         },
         "negotiator": {
             "version": "0.6.3",
@@ -10314,20 +10837,20 @@
             "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
         },
         "neo-async": {
-            "version": "2.6.2",
-            "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
-            "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+            "version": "2.6.1",
+            "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
+            "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==",
             "dev": true
         },
         "next-tick": {
-            "version": "1.1.0",
-            "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
-            "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
+            "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
         },
         "nextafter": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/nextafter/-/nextafter-1.0.0.tgz",
-            "integrity": "sha512-7PO+A89Tll2rSEfyrjtqO0MaI37+nnxBdnQcPypfbEYYuGaJxWGCqaOwQX4a3GHNTS08l1kazuiLEWZniZjMUQ==",
+            "integrity": "sha1-t9d7U1MQ4+CX5gJauwqQNHfsGjo=",
             "requires": {
                 "double-bits": "^1.1.0"
             }
@@ -10345,12 +10868,19 @@
             "requires": {
                 "lower-case": "^2.0.2",
                 "tslib": "^2.0.3"
+            },
+            "dependencies": {
+                "tslib": {
+                    "version": "2.3.1",
+                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+                    "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+                }
             }
         },
         "node-abi": {
-            "version": "2.30.1",
-            "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz",
-            "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==",
+            "version": "2.16.0",
+            "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.16.0.tgz",
+            "integrity": "sha512-+sa0XNlWDA6T+bDLmkCUYn6W5k5W6BPRL6mqzSCs6H/xUgtl4D5x2fORKDzopKiU6wsyn/+wXlRXwXeSp+mtoA==",
             "requires": {
                 "semver": "^5.4.1"
             }
@@ -10358,7 +10888,7 @@
         "node-ensure": {
             "version": "0.0.0",
             "resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz",
-            "integrity": "sha512-DRI60hzo2oKN1ma0ckc6nQWlHU69RH6xN0sjQTjMpChPfTYvKZdcQFfdYK2RWbJcKyUizSIy/l8OTGxMAM1QDw=="
+            "integrity": "sha1-7K52QVDemYYexcgQ/V0Jaxg5Mqc="
         },
         "node-environment-flags": {
             "version": "1.0.5",
@@ -10379,9 +10909,9 @@
             }
         },
         "node-forge": {
-            "version": "0.10.0",
-            "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
-            "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="
+            "version": "0.9.1",
+            "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.1.tgz",
+            "integrity": "sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ=="
         },
         "node-gyp": {
             "version": "3.8.0",
@@ -10402,81 +10932,14 @@
                 "which": "1"
             },
             "dependencies": {
-                "ansi-regex": {
-                    "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-                    "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="
-                },
-                "aproba": {
-                    "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-                    "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
-                },
-                "are-we-there-yet": {
-                    "version": "1.1.7",
-                    "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
-                    "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
-                    "requires": {
-                        "delegates": "^1.0.0",
-                        "readable-stream": "^2.0.6"
-                    }
-                },
-                "gauge": {
-                    "version": "2.7.4",
-                    "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
-                    "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==",
-                    "requires": {
-                        "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"
-                    }
-                },
-                "is-fullwidth-code-point": {
-                    "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
-                    "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
-                    "requires": {
-                        "number-is-nan": "^1.0.0"
-                    }
-                },
                 "nopt": {
                     "version": "3.0.6",
                     "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
-                    "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==",
+                    "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
                     "requires": {
                         "abbrev": "1"
                     }
                 },
-                "npmlog": {
-                    "version": "4.1.2",
-                    "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.2",
-                        "console-control-strings": "~1.1.0",
-                        "gauge": "~2.7.3",
-                        "set-blocking": "~2.0.0"
-                    }
-                },
-                "readable-stream": {
-                    "version": "2.3.7",
-                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
-                    "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
-                    "requires": {
-                        "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": {
                     "version": "2.7.1",
                     "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@@ -10488,25 +10951,7 @@
                 "semver": {
                     "version": "5.3.0",
                     "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
-                    "integrity": "sha512-mfmm3/H9+67MCVix1h+IXTpDwL6710LyHuk7+cWC9T1mE0qz4iHhh6r4hU2wrIT9iTsAAC2XQRvfblL028cpLw=="
-                },
-                "string-width": {
-                    "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-                    "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
-                    "requires": {
-                        "code-point-at": "^1.0.0",
-                        "is-fullwidth-code-point": "^1.0.0",
-                        "strip-ansi": "^3.0.0"
-                    }
-                },
-                "strip-ansi": {
-                    "version": "3.0.1",
-                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-                    "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
-                    "requires": {
-                        "ansi-regex": "^2.0.0"
-                    }
+                    "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8="
                 },
                 "tar": {
                     "version": "2.2.2",
@@ -10521,9 +10966,9 @@
             }
         },
         "node-releases": {
-            "version": "2.0.5",
-            "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz",
-            "integrity": "sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q=="
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz",
+            "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg=="
         },
         "node-sass": {
             "version": "4.14.1",
@@ -10552,31 +10997,17 @@
                 "ansi-regex": {
                     "version": "2.1.1",
                     "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-                    "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="
+                    "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
                 },
                 "ansi-styles": {
                     "version": "2.2.1",
                     "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-                    "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA=="
-                },
-                "aproba": {
-                    "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-                    "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
-                },
-                "are-we-there-yet": {
-                    "version": "1.1.7",
-                    "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
-                    "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
-                    "requires": {
-                        "delegates": "^1.0.0",
-                        "readable-stream": "^2.0.6"
-                    }
+                    "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
                 },
                 "chalk": {
                     "version": "1.1.3",
                     "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-                    "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
+                    "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
                     "requires": {
                         "ansi-styles": "^2.2.1",
                         "escape-string-regexp": "^1.0.2",
@@ -10585,68 +11016,10 @@
                         "supports-color": "^2.0.0"
                     }
                 },
-                "gauge": {
-                    "version": "2.7.4",
-                    "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
-                    "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==",
-                    "requires": {
-                        "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"
-                    }
-                },
                 "get-stdin": {
                     "version": "4.0.1",
                     "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
-                    "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw=="
-                },
-                "is-fullwidth-code-point": {
-                    "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
-                    "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
-                    "requires": {
-                        "number-is-nan": "^1.0.0"
-                    }
-                },
-                "npmlog": {
-                    "version": "4.1.2",
-                    "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.2",
-                        "console-control-strings": "~1.1.0",
-                        "gauge": "~2.7.3",
-                        "set-blocking": "~2.0.0"
-                    }
-                },
-                "readable-stream": {
-                    "version": "2.3.7",
-                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
-                    "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
-                    "requires": {
-                        "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-width": {
-                    "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-                    "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
-                    "requires": {
-                        "code-point-at": "^1.0.0",
-                        "is-fullwidth-code-point": "^1.0.0",
-                        "strip-ansi": "^3.0.0"
-                    }
+                    "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4="
                 },
                 "strip-ansi": {
                     "version": "3.0.1",
@@ -10701,7 +11074,7 @@
         "noop-logger": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
-            "integrity": "sha512-6kM8CLXvuW5crTxsAtva2YLrRrDaiTIkIePWs9moLHqbFWT94WpNFjwS/5dfLfECg5i/lkmw3aoqVidxt23TEQ=="
+            "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI="
         },
         "nopt": {
             "version": "5.0.0",
@@ -10738,9 +11111,9 @@
             "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg=="
         },
         "npm": {
-            "version": "6.14.17",
-            "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.17.tgz",
-            "integrity": "sha512-CxEDn1ydVRPDl4tHrlnq+WevYAhv4GF2AEHzJKQ4prZDZ96IS3Uo6t0Sy6O9kB6XzqkI+J00WfYCqqk0p6IJ1Q==",
+            "version": "6.14.16",
+            "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.16.tgz",
+            "integrity": "sha512-LMiLGYsVNJfVPlQg7v2NYjG7iRIapcLv+oMunlq7fkXVx0BATCjRu7XyWl0G+iuZzHy4CjtM32QB8ox8juTgaw==",
             "requires": {
                 "JSONStream": "^1.3.5",
                 "abbrev": "~1.1.1",
@@ -10869,8 +11242,7 @@
             "dependencies": {
                 "JSONStream": {
                     "version": "1.3.5",
-                    "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
-                    "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+                    "bundled": true,
                     "requires": {
                         "jsonparse": "^1.2.0",
                         "through": ">=2.2.7 <3"
@@ -10878,12 +11250,12 @@
                 },
                 "abbrev": {
                     "version": "1.1.1",
-                    "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
                 },
                 "agent-base": {
                     "version": "4.3.0",
-                    "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
                     "requires": {
                         "es6-promisify": "^5.0.0"
@@ -10891,7 +11263,7 @@
                 },
                 "agentkeepalive": {
                     "version": "3.5.2",
-                    "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==",
                     "requires": {
                         "humanize-ms": "^1.2.1"
@@ -10899,7 +11271,7 @@
                 },
                 "ansi-align": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=",
                     "requires": {
                         "string-width": "^2.0.0"
@@ -10907,12 +11279,12 @@
                 },
                 "ansi-regex": {
                     "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
                 },
                 "ansi-styles": {
                     "version": "3.2.1",
-                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
                     "requires": {
                         "color-convert": "^1.9.0"
@@ -10920,27 +11292,27 @@
                 },
                 "ansicolors": {
                     "version": "0.3.2",
-                    "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk="
                 },
                 "ansistyles": {
                     "version": "0.1.3",
-                    "resolved": "https://registry.npmjs.org/ansistyles/-/ansistyles-0.1.3.tgz",
+                    "resolved": false,
                     "integrity": "sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk="
                 },
                 "aproba": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
                 },
                 "archy": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA="
                 },
                 "are-we-there-yet": {
                     "version": "1.1.4",
-                    "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
+                    "resolved": false,
                     "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
                     "requires": {
                         "delegates": "^1.0.0",
@@ -10949,7 +11321,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+                            "resolved": false,
                             "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
                             "requires": {
                                 "core-util-is": "~1.0.0",
@@ -10963,7 +11335,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+                            "resolved": false,
                             "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
                             "requires": {
                                 "safe-buffer": "~5.1.0"
@@ -10973,12 +11345,12 @@
                 },
                 "asap": {
                     "version": "2.0.6",
-                    "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+                    "resolved": false,
                     "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
                 },
                 "asn1": {
                     "version": "0.2.4",
-                    "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
+                    "resolved": false,
                     "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
                     "requires": {
                         "safer-buffer": "~2.1.0"
@@ -10986,32 +11358,32 @@
                 },
                 "assert-plus": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
                 },
                 "asynckit": {
                     "version": "0.4.0",
-                    "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
                 },
                 "aws-sign2": {
                     "version": "0.7.0",
-                    "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
                 },
                 "aws4": {
                     "version": "1.8.0",
-                    "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
                 },
                 "balanced-match": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
                 },
                 "bcrypt-pbkdf": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
                     "optional": true,
                     "requires": {
@@ -11033,12 +11405,12 @@
                 },
                 "bluebird": {
                     "version": "3.5.5",
-                    "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz",
+                    "resolved": false,
                     "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w=="
                 },
                 "boxen": {
                     "version": "1.3.0",
-                    "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==",
                     "requires": {
                         "ansi-align": "^2.0.0",
@@ -11052,7 +11424,7 @@
                 },
                 "brace-expansion": {
                     "version": "1.1.11",
-                    "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+                    "resolved": false,
                     "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
                     "requires": {
                         "balanced-match": "^1.0.0",
@@ -11061,27 +11433,27 @@
                 },
                 "buffer-from": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA=="
                 },
                 "builtins": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz",
+                    "resolved": false,
                     "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og="
                 },
                 "byline": {
                     "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE="
                 },
                 "byte-size": {
                     "version": "5.0.1",
-                    "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-5.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw=="
                 },
                 "cacache": {
                     "version": "12.0.3",
-                    "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==",
                     "requires": {
                         "bluebird": "^3.5.5",
@@ -11103,27 +11475,27 @@
                 },
                 "call-limit": {
                     "version": "1.1.1",
-                    "resolved": "https://registry.npmjs.org/call-limit/-/call-limit-1.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-5twvci5b9eRBw2wCfPtN0GmlR2/gadZqyFpPhOK6CvMFoFgA+USnZ6Jpu1lhG9h85pQ3Ouil3PfXWRD4EUaRiQ=="
                 },
                 "camelcase": {
                     "version": "4.1.0",
-                    "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
                 },
                 "capture-stack-trace": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0="
                 },
                 "caseless": {
                     "version": "0.12.0",
-                    "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
                 },
                 "chalk": {
                     "version": "2.4.1",
-                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
                     "requires": {
                         "ansi-styles": "^3.2.1",
@@ -11133,17 +11505,17 @@
                 },
                 "chownr": {
                     "version": "1.1.4",
-                    "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+                    "resolved": false,
                     "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
                 },
                 "ci-info": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="
                 },
                 "cidr-regex": {
                     "version": "2.0.10",
-                    "resolved": "https://registry.npmjs.org/cidr-regex/-/cidr-regex-2.0.10.tgz",
+                    "resolved": false,
                     "integrity": "sha512-sB3ogMQXWvreNPbJUZMRApxuRYd+KoIo4RGQ81VatjmMW6WJPo+IJZ2846FGItr9VzKo5w7DXzijPLGtSd0N3Q==",
                     "requires": {
                         "ip-regex": "^2.1.0"
@@ -11151,12 +11523,12 @@
                 },
                 "cli-boxes": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM="
                 },
                 "cli-columns": {
                     "version": "3.1.2",
-                    "resolved": "https://registry.npmjs.org/cli-columns/-/cli-columns-3.1.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-ZzLZcpee/CrkRKHwjgj6E5yWoY4=",
                     "requires": {
                         "string-width": "^2.0.0",
@@ -11165,7 +11537,7 @@
                 },
                 "cli-table3": {
                     "version": "0.5.1",
-                    "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==",
                     "requires": {
                         "colors": "^1.1.2",
@@ -11184,9 +11556,9 @@
                     },
                     "dependencies": {
                         "ansi-regex": {
-                            "version": "4.1.1",
-                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
-                            "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
+                            "version": "4.1.0",
+                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+                            "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
                         },
                         "is-fullwidth-code-point": {
                             "version": "2.0.0",
@@ -11215,12 +11587,12 @@
                 },
                 "clone": {
                     "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+                    "resolved": false,
                     "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4="
                 },
                 "cmd-shim": {
                     "version": "3.0.3",
-                    "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-3.0.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-DtGg+0xiFhQIntSBRzL2fRQBnmtAVwXIDo4Qq46HPpObYquxMaZS4sb82U9nH91qJrlosC1wa9gwr0QyL/HypA==",
                     "requires": {
                         "graceful-fs": "^4.1.2",
@@ -11229,12 +11601,12 @@
                 },
                 "code-point-at": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
                 },
                 "color-convert": {
                     "version": "1.9.1",
-                    "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
                     "requires": {
                         "color-name": "^1.1.1"
@@ -11242,18 +11614,18 @@
                 },
                 "color-name": {
                     "version": "1.1.3",
-                    "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+                    "resolved": false,
                     "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
                 },
                 "colors": {
                     "version": "1.3.3",
-                    "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==",
                     "optional": true
                 },
                 "columnify": {
                     "version": "1.5.4",
-                    "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz",
+                    "resolved": false,
                     "integrity": "sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs=",
                     "requires": {
                         "strip-ansi": "^3.0.0",
@@ -11262,7 +11634,7 @@
                 },
                 "combined-stream": {
                     "version": "1.0.6",
-                    "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
+                    "resolved": false,
                     "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
                     "requires": {
                         "delayed-stream": "~1.0.0"
@@ -11270,12 +11642,12 @@
                 },
                 "concat-map": {
                     "version": "0.0.1",
-                    "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
                 },
                 "concat-stream": {
                     "version": "1.6.2",
-                    "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
                     "requires": {
                         "buffer-from": "^1.0.0",
@@ -11286,7 +11658,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+                            "resolved": false,
                             "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
                             "requires": {
                                 "core-util-is": "~1.0.0",
@@ -11300,7 +11672,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+                            "resolved": false,
                             "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
                             "requires": {
                                 "safe-buffer": "~5.1.0"
@@ -11310,7 +11682,7 @@
                 },
                 "config-chain": {
                     "version": "1.1.12",
-                    "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz",
+                    "resolved": false,
                     "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==",
                     "requires": {
                         "ini": "^1.3.4",
@@ -11332,12 +11704,12 @@
                 },
                 "console-control-strings": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
                 },
                 "copy-concurrently": {
                     "version": "1.0.5",
-                    "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
+                    "resolved": false,
                     "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
                     "requires": {
                         "aproba": "^1.1.1",
@@ -11350,24 +11722,24 @@
                     "dependencies": {
                         "aproba": {
                             "version": "1.2.0",
-                            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+                            "resolved": false,
                             "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
                         },
                         "iferr": {
                             "version": "0.1.5",
-                            "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
+                            "resolved": false,
                             "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE="
                         }
                     }
                 },
                 "core-util-is": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
                 },
                 "create-error-class": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
                     "requires": {
                         "capture-stack-trace": "^1.0.0"
@@ -11375,7 +11747,7 @@
                 },
                 "cross-spawn": {
                     "version": "5.1.0",
-                    "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
                     "requires": {
                         "lru-cache": "^4.0.1",
@@ -11385,7 +11757,7 @@
                     "dependencies": {
                         "lru-cache": {
                             "version": "4.1.5",
-                            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+                            "resolved": false,
                             "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
                             "requires": {
                                 "pseudomap": "^1.0.2",
@@ -11394,24 +11766,24 @@
                         },
                         "yallist": {
                             "version": "2.1.2",
-                            "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+                            "resolved": false,
                             "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
                         }
                     }
                 },
                 "crypto-random-string": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4="
                 },
                 "cyclist": {
                     "version": "0.2.2",
-                    "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA="
                 },
                 "dashdash": {
                     "version": "1.14.1",
-                    "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
                     "requires": {
                         "assert-plus": "^1.0.0"
@@ -11419,7 +11791,7 @@
                 },
                 "debug": {
                     "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
                     "requires": {
                         "ms": "2.0.0"
@@ -11427,34 +11799,34 @@
                     "dependencies": {
                         "ms": {
                             "version": "2.0.0",
-                            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+                            "resolved": false,
                             "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                         }
                     }
                 },
                 "debuglog": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI="
                 },
                 "decamelize": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
                 },
                 "decode-uri-component": {
                     "version": "0.2.0",
-                    "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
                 },
                 "deep-extend": {
                     "version": "0.6.0",
-                    "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
                 },
                 "defaults": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz",
+                    "resolved": false,
                     "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=",
                     "requires": {
                         "clone": "^1.0.2"
@@ -11462,7 +11834,7 @@
                 },
                 "define-properties": {
                     "version": "1.1.3",
-                    "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
                     "requires": {
                         "object-keys": "^1.0.12"
@@ -11470,27 +11842,27 @@
                 },
                 "delayed-stream": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
                 },
                 "delegates": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
                 },
                 "detect-indent": {
                     "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50="
                 },
                 "detect-newline": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I="
                 },
                 "dezalgo": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz",
+                    "resolved": false,
                     "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=",
                     "requires": {
                         "asap": "^2.0.0",
@@ -11507,17 +11879,17 @@
                 },
                 "dotenv": {
                     "version": "5.0.1",
-                    "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow=="
                 },
                 "duplexer3": {
                     "version": "0.1.4",
-                    "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
+                    "resolved": false,
                     "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
                 },
                 "duplexify": {
                     "version": "3.6.0",
-                    "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==",
                     "requires": {
                         "end-of-stream": "^1.0.0",
@@ -11528,7 +11900,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+                            "resolved": false,
                             "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
                             "requires": {
                                 "core-util-is": "~1.0.0",
@@ -11542,7 +11914,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+                            "resolved": false,
                             "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
                             "requires": {
                                 "safe-buffer": "~5.1.0"
@@ -11552,7 +11924,7 @@
                 },
                 "ecc-jsbn": {
                     "version": "0.1.2",
-                    "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
                     "optional": true,
                     "requires": {
@@ -11562,7 +11934,7 @@
                 },
                 "editor": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/editor/-/editor-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-YMf4e9YrzGqJT6jM1q+3gjok90I="
                 },
                 "emoji-regex": {
@@ -11572,7 +11944,7 @@
                 },
                 "encoding": {
                     "version": "0.1.12",
-                    "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
+                    "resolved": false,
                     "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
                     "requires": {
                         "iconv-lite": "~0.4.13"
@@ -11580,7 +11952,7 @@
                 },
                 "end-of-stream": {
                     "version": "1.4.1",
-                    "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
                     "requires": {
                         "once": "^1.4.0"
@@ -11588,17 +11960,17 @@
                 },
                 "env-paths": {
                     "version": "2.2.0",
-                    "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA=="
                 },
                 "err-code": {
                     "version": "1.1.2",
-                    "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA="
                 },
                 "errno": {
                     "version": "0.1.7",
-                    "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
+                    "resolved": false,
                     "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
                     "requires": {
                         "prr": "~1.0.1"
@@ -11606,7 +11978,7 @@
                 },
                 "es-abstract": {
                     "version": "1.12.0",
-                    "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==",
                     "requires": {
                         "es-to-primitive": "^1.1.1",
@@ -11618,7 +11990,7 @@
                 },
                 "es-to-primitive": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
                     "requires": {
                         "is-callable": "^1.1.4",
@@ -11628,12 +12000,12 @@
                 },
                 "es6-promise": {
                     "version": "4.2.8",
-                    "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
+                    "resolved": false,
                     "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
                 },
                 "es6-promisify": {
                     "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
                     "requires": {
                         "es6-promise": "^4.0.3"
@@ -11641,12 +12013,12 @@
                 },
                 "escape-string-regexp": {
                     "version": "1.0.5",
-                    "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+                    "resolved": false,
                     "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
                 },
                 "execa": {
                     "version": "0.7.0",
-                    "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
                     "requires": {
                         "cross-spawn": "^5.0.1",
@@ -11660,39 +12032,39 @@
                     "dependencies": {
                         "get-stream": {
                             "version": "3.0.0",
-                            "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+                            "resolved": false,
                             "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
                         }
                     }
                 },
                 "extend": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
                 },
                 "extsprintf": {
                     "version": "1.3.0",
-                    "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
                 },
                 "fast-json-stable-stringify": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
                 },
                 "figgy-pudding": {
                     "version": "3.5.1",
-                    "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w=="
                 },
                 "find-npm-prefix": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/find-npm-prefix/-/find-npm-prefix-1.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-KEftzJ+H90x6pcKtdXZEPsQse8/y/UnvzRKrOSQFprnrGaFuJ62fVkP34Iu2IYuMvyauCyoLTNkJZgrrGA2wkA=="
                 },
                 "flush-write-stream": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==",
                     "requires": {
                         "inherits": "^2.0.1",
@@ -11701,7 +12073,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+                            "resolved": false,
                             "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
                             "requires": {
                                 "core-util-is": "~1.0.0",
@@ -11715,7 +12087,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+                            "resolved": false,
                             "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
                             "requires": {
                                 "safe-buffer": "~5.1.0"
@@ -11725,12 +12097,12 @@
                 },
                 "forever-agent": {
                     "version": "0.6.1",
-                    "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
                 },
                 "form-data": {
                     "version": "2.3.2",
-                    "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
                     "requires": {
                         "asynckit": "^0.4.0",
@@ -11740,7 +12112,7 @@
                 },
                 "from2": {
                     "version": "2.3.0",
-                    "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
                     "requires": {
                         "inherits": "^2.0.1",
@@ -11749,7 +12121,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+                            "resolved": false,
                             "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
                             "requires": {
                                 "core-util-is": "~1.0.0",
@@ -11763,7 +12135,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+                            "resolved": false,
                             "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
                             "requires": {
                                 "safe-buffer": "~5.1.0"
@@ -11773,7 +12145,7 @@
                 },
                 "fs-minipass": {
                     "version": "1.2.7",
-                    "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
+                    "resolved": false,
                     "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
                     "requires": {
                         "minipass": "^2.6.0"
@@ -11781,7 +12153,7 @@
                     "dependencies": {
                         "minipass": {
                             "version": "2.9.0",
-                            "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
+                            "resolved": false,
                             "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
                             "requires": {
                                 "safe-buffer": "^5.1.2",
@@ -11792,7 +12164,7 @@
                 },
                 "fs-vacuum": {
                     "version": "1.2.10",
-                    "resolved": "https://registry.npmjs.org/fs-vacuum/-/fs-vacuum-1.2.10.tgz",
+                    "resolved": false,
                     "integrity": "sha1-t2Kb7AekAxolSP35n17PHMizHjY=",
                     "requires": {
                         "graceful-fs": "^4.1.2",
@@ -11802,7 +12174,7 @@
                 },
                 "fs-write-stream-atomic": {
                     "version": "1.0.10",
-                    "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
+                    "resolved": false,
                     "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
                     "requires": {
                         "graceful-fs": "^4.1.2",
@@ -11813,12 +12185,12 @@
                     "dependencies": {
                         "iferr": {
                             "version": "0.1.5",
-                            "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
+                            "resolved": false,
                             "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE="
                         },
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+                            "resolved": false,
                             "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
                             "requires": {
                                 "core-util-is": "~1.0.0",
@@ -11832,7 +12204,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+                            "resolved": false,
                             "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
                             "requires": {
                                 "safe-buffer": "~5.1.0"
@@ -11842,17 +12214,17 @@
                 },
                 "fs.realpath": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
                 },
                 "function-bind": {
                     "version": "1.1.1",
-                    "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
                 },
                 "gauge": {
                     "version": "2.7.4",
-                    "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+                    "resolved": false,
                     "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
                     "requires": {
                         "aproba": "^1.0.3",
@@ -11867,12 +12239,12 @@
                     "dependencies": {
                         "aproba": {
                             "version": "1.2.0",
-                            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+                            "resolved": false,
                             "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
                         },
                         "string-width": {
                             "version": "1.0.2",
-                            "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+                            "resolved": false,
                             "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
                             "requires": {
                                 "code-point-at": "^1.0.0",
@@ -11884,7 +12256,7 @@
                 },
                 "genfun": {
                     "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA=="
                 },
                 "gentle-fs": {
@@ -11907,12 +12279,12 @@
                     "dependencies": {
                         "aproba": {
                             "version": "1.2.0",
-                            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+                            "resolved": false,
                             "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
                         },
                         "iferr": {
                             "version": "0.1.5",
-                            "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
+                            "resolved": false,
                             "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE="
                         }
                     }
@@ -11924,7 +12296,7 @@
                 },
                 "get-stream": {
                     "version": "4.1.0",
-                    "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
                     "requires": {
                         "pump": "^3.0.0"
@@ -11932,7 +12304,7 @@
                 },
                 "getpass": {
                     "version": "0.1.7",
-                    "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+                    "resolved": false,
                     "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
                     "requires": {
                         "assert-plus": "^1.0.0"
@@ -11940,7 +12312,7 @@
                 },
                 "glob": {
                     "version": "7.1.6",
-                    "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+                    "resolved": false,
                     "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
                     "requires": {
                         "fs.realpath": "^1.0.0",
@@ -11953,7 +12325,7 @@
                 },
                 "global-dirs": {
                     "version": "0.1.1",
-                    "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
                     "requires": {
                         "ini": "^1.3.4"
@@ -11961,7 +12333,7 @@
                 },
                 "got": {
                     "version": "6.7.1",
-                    "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
                     "requires": {
                         "create-error-class": "^3.0.0",
@@ -11979,19 +12351,19 @@
                     "dependencies": {
                         "get-stream": {
                             "version": "3.0.0",
-                            "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+                            "resolved": false,
                             "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
                         }
                     }
                 },
                 "graceful-fs": {
                     "version": "4.2.4",
-                    "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+                    "resolved": false,
                     "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw=="
                 },
                 "har-schema": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
                 },
                 "har-validator": {
@@ -12028,7 +12400,7 @@
                 },
                 "has": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
                     "requires": {
                         "function-bind": "^1.1.1"
@@ -12036,17 +12408,17 @@
                 },
                 "has-flag": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
                 },
                 "has-symbols": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q="
                 },
                 "has-unicode": {
                     "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
                 },
                 "hosted-git-info": {
@@ -12056,12 +12428,12 @@
                 },
                 "http-cache-semantics": {
                     "version": "3.8.1",
-                    "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w=="
                 },
                 "http-proxy-agent": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==",
                     "requires": {
                         "agent-base": "4",
@@ -12070,7 +12442,7 @@
                 },
                 "http-signature": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
                     "requires": {
                         "assert-plus": "^1.0.0",
@@ -12080,7 +12452,7 @@
                 },
                 "https-proxy-agent": {
                     "version": "2.2.4",
-                    "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
+                    "resolved": false,
                     "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
                     "requires": {
                         "agent-base": "^4.3.0",
@@ -12089,7 +12461,7 @@
                 },
                 "humanize-ms": {
                     "version": "1.2.1",
-                    "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=",
                     "requires": {
                         "ms": "^2.0.0"
@@ -12097,7 +12469,7 @@
                 },
                 "iconv-lite": {
                     "version": "0.4.23",
-                    "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
+                    "resolved": false,
                     "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
                     "requires": {
                         "safer-buffer": ">= 2.1.2 < 3"
@@ -12105,12 +12477,12 @@
                 },
                 "iferr": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/iferr/-/iferr-1.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-9AfeLfji44r5TKInjhz3W9DyZI1zR1JAf2hVBMGhddAKPqBsupb89jGfbCTHIGZd6fGZl9WlHdn4AObygyMKwg=="
                 },
                 "ignore-walk": {
                     "version": "3.0.3",
-                    "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
                     "requires": {
                         "minimatch": "^3.0.4"
@@ -12118,22 +12490,22 @@
                 },
                 "import-lazy": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM="
                 },
                 "imurmurhash": {
                     "version": "0.1.4",
-                    "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+                    "resolved": false,
                     "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
                 },
                 "infer-owner": {
                     "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
+                    "resolved": false,
                     "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A=="
                 },
                 "inflight": {
                     "version": "1.0.6",
-                    "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+                    "resolved": false,
                     "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
                     "requires": {
                         "once": "^1.3.0",
@@ -12142,7 +12514,7 @@
                 },
                 "inherits": {
                     "version": "2.0.4",
-                    "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+                    "resolved": false,
                     "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
                 },
                 "ini": {
@@ -12152,7 +12524,7 @@
                 },
                 "init-package-json": {
                     "version": "1.10.3",
-                    "resolved": "https://registry.npmjs.org/init-package-json/-/init-package-json-1.10.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-zKSiXKhQveNteyhcj1CoOP8tqp1QuxPIPBl8Bid99DGLFqA1p87M6lNgfjJHSBoWJJlidGOv5rWjyYKEB3g2Jw==",
                     "requires": {
                         "glob": "^7.1.1",
@@ -12167,22 +12539,22 @@
                 },
                 "ip": {
                     "version": "1.1.5",
-                    "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
+                    "resolved": false,
                     "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
                 },
                 "ip-regex": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk="
                 },
                 "is-callable": {
                     "version": "1.1.4",
-                    "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
+                    "resolved": false,
                     "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA=="
                 },
                 "is-ci": {
                     "version": "1.2.1",
-                    "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==",
                     "requires": {
                         "ci-info": "^1.5.0"
@@ -12190,14 +12562,14 @@
                     "dependencies": {
                         "ci-info": {
                             "version": "1.6.0",
-                            "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz",
+                            "resolved": false,
                             "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A=="
                         }
                     }
                 },
                 "is-cidr": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/is-cidr/-/is-cidr-3.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-8Xnnbjsb0x462VoYiGlhEi+drY8SFwrHiSYuzc/CEwco55vkehTaxAyIjEdpi3EMvLPPJAJi9FlzP+h+03gp0Q==",
                     "requires": {
                         "cidr-regex": "^2.0.10"
@@ -12205,12 +12577,12 @@
                 },
                 "is-date-object": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY="
                 },
                 "is-fullwidth-code-point": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
                     "requires": {
                         "number-is-nan": "^1.0.0"
@@ -12218,7 +12590,7 @@
                 },
                 "is-installed-globally": {
                     "version": "0.1.0",
-                    "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=",
                     "requires": {
                         "global-dirs": "^0.1.0",
@@ -12227,17 +12599,17 @@
                 },
                 "is-npm": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ="
                 },
                 "is-obj": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8="
                 },
                 "is-path-inside": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
                     "requires": {
                         "path-is-inside": "^1.0.1"
@@ -12245,12 +12617,12 @@
                 },
                 "is-redirect": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ="
                 },
                 "is-regex": {
                     "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
+                    "resolved": false,
                     "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
                     "requires": {
                         "has": "^1.0.1"
@@ -12258,17 +12630,17 @@
                 },
                 "is-retry-allowed": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg=="
                 },
                 "is-stream": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
                 },
                 "is-symbol": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
                     "requires": {
                         "has-symbols": "^1.0.0"
@@ -12276,33 +12648,33 @@
                 },
                 "is-typedarray": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
                 },
                 "isarray": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
                 },
                 "isexe": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
                 },
                 "isstream": {
                     "version": "0.1.2",
-                    "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
                 },
                 "jsbn": {
                     "version": "0.1.1",
-                    "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
                     "optional": true
                 },
                 "json-parse-better-errors": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
                 },
                 "json-schema": {
@@ -12312,13 +12684,12 @@
                 },
                 "json-stringify-safe": {
                     "version": "5.0.1",
-                    "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
                 },
                 "jsonparse": {
                     "version": "1.3.1",
-                    "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
-                    "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA="
+                    "bundled": true
                 },
                 "jsprim": {
                     "version": "1.4.2",
@@ -12333,7 +12704,7 @@
                 },
                 "latest-version": {
                     "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=",
                     "requires": {
                         "package-json": "^4.0.0"
@@ -12341,7 +12712,7 @@
                 },
                 "lazy-property": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/lazy-property/-/lazy-property-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-hN3Es3Bnm6i9TNz6TAa0PVcREUc="
                 },
                 "libcipm": {
@@ -12368,7 +12739,7 @@
                 },
                 "libnpm": {
                     "version": "3.0.1",
-                    "resolved": "https://registry.npmjs.org/libnpm/-/libnpm-3.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-d7jU5ZcMiTfBqTUJVZ3xid44fE5ERBm9vBnmhp2ECD2Ls+FNXWxHSkO7gtvrnbLO78gwPdNPz1HpsF3W4rjkBQ==",
                     "requires": {
                         "bin-links": "^1.1.2",
@@ -12395,7 +12766,7 @@
                 },
                 "libnpmaccess": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-3.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-01512AK7MqByrI2mfC7h5j8N9V4I7MHJuk9buo8Gv+5QgThpOgpjB7sQBDDkeZqRteFb1QM/6YNdHfG7cDvfAQ==",
                     "requires": {
                         "aproba": "^2.0.0",
@@ -12406,7 +12777,7 @@
                 },
                 "libnpmconfig": {
                     "version": "1.2.1",
-                    "resolved": "https://registry.npmjs.org/libnpmconfig/-/libnpmconfig-1.2.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-9esX8rTQAHqarx6qeZqmGQKBNZR5OIbl/Ayr0qQDy3oXja2iFVQQI81R6GZ2a02bSNZ9p3YOGX1O6HHCb1X7kA==",
                     "requires": {
                         "figgy-pudding": "^3.5.1",
@@ -12416,7 +12787,7 @@
                     "dependencies": {
                         "find-up": {
                             "version": "3.0.0",
-                            "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+                            "resolved": false,
                             "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
                             "requires": {
                                 "locate-path": "^3.0.0"
@@ -12424,7 +12795,7 @@
                         },
                         "locate-path": {
                             "version": "3.0.0",
-                            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+                            "resolved": false,
                             "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
                             "requires": {
                                 "p-locate": "^3.0.0",
@@ -12433,7 +12804,7 @@
                         },
                         "p-limit": {
                             "version": "2.2.0",
-                            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
+                            "resolved": false,
                             "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
                             "requires": {
                                 "p-try": "^2.0.0"
@@ -12441,7 +12812,7 @@
                         },
                         "p-locate": {
                             "version": "3.0.0",
-                            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+                            "resolved": false,
                             "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
                             "requires": {
                                 "p-limit": "^2.0.0"
@@ -12449,14 +12820,14 @@
                         },
                         "p-try": {
                             "version": "2.2.0",
-                            "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+                            "resolved": false,
                             "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
                         }
                     }
                 },
                 "libnpmhook": {
                     "version": "5.0.3",
-                    "resolved": "https://registry.npmjs.org/libnpmhook/-/libnpmhook-5.0.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-UdNLMuefVZra/wbnBXECZPefHMGsVDTq5zaM/LgKNE9Keyl5YXQTnGAzEo+nFOpdRqTWI9LYi4ApqF9uVCCtuA==",
                     "requires": {
                         "aproba": "^2.0.0",
@@ -12467,7 +12838,7 @@
                 },
                 "libnpmorg": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/libnpmorg/-/libnpmorg-1.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-0sRUXLh+PLBgZmARvthhYXQAWn0fOsa6T5l3JSe2n9vKG/lCVK4nuG7pDsa7uMq+uTt2epdPK+a2g6btcY11Ww==",
                     "requires": {
                         "aproba": "^2.0.0",
@@ -12478,7 +12849,7 @@
                 },
                 "libnpmpublish": {
                     "version": "1.1.2",
-                    "resolved": "https://registry.npmjs.org/libnpmpublish/-/libnpmpublish-1.1.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-2yIwaXrhTTcF7bkJKIKmaCV9wZOALf/gsTDxVSu/Gu/6wiG3fA8ce8YKstiWKTxSFNC0R7isPUb6tXTVFZHt2g==",
                     "requires": {
                         "aproba": "^2.0.0",
@@ -12494,7 +12865,7 @@
                 },
                 "libnpmsearch": {
                     "version": "2.0.2",
-                    "resolved": "https://registry.npmjs.org/libnpmsearch/-/libnpmsearch-2.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-VTBbV55Q6fRzTdzziYCr64+f8AopQ1YZ+BdPOv16UegIEaE8C0Kch01wo4s3kRTFV64P121WZJwgmBwrq68zYg==",
                     "requires": {
                         "figgy-pudding": "^3.5.1",
@@ -12504,7 +12875,7 @@
                 },
                 "libnpmteam": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/libnpmteam/-/libnpmteam-1.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-p420vM28Us04NAcg1rzgGW63LMM6rwe+6rtZpfDxCcXxM0zUTLl7nPFEnRF3JfFBF5skF/yuZDUthTsHgde8QA==",
                     "requires": {
                         "aproba": "^2.0.0",
@@ -12530,7 +12901,7 @@
                 },
                 "lock-verify": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/lock-verify/-/lock-verify-2.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-vcLpxnGvrqisKvLQ2C2v0/u7LVly17ak2YSgoK4PrdsYBXQIax19vhKiLfvKNFx7FRrpTnitrpzF/uuCMuorIg==",
                     "requires": {
                         "npm-package-arg": "^6.1.0",
@@ -12539,7 +12910,7 @@
                 },
                 "lockfile": {
                     "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz",
+                    "resolved": false,
                     "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==",
                     "requires": {
                         "signal-exit": "^3.0.2"
@@ -12547,12 +12918,12 @@
                 },
                 "lodash._baseindexof": {
                     "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw="
                 },
                 "lodash._baseuniq": {
                     "version": "4.6.0",
-                    "resolved": "https://registry.npmjs.org/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-DrtE5FaBSveQXGIS+iybLVG4Qeg=",
                     "requires": {
                         "lodash._createset": "~4.0.0",
@@ -12561,17 +12932,17 @@
                 },
                 "lodash._bindcallback": {
                     "version": "3.0.1",
-                    "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4="
                 },
                 "lodash._cacheindexof": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI="
                 },
                 "lodash._createcache": {
                     "version": "3.1.2",
-                    "resolved": "https://registry.npmjs.org/lodash._createcache/-/lodash._createcache-3.1.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=",
                     "requires": {
                         "lodash._getnative": "^3.0.0"
@@ -12579,52 +12950,52 @@
                 },
                 "lodash._createset": {
                     "version": "4.0.3",
-                    "resolved": "https://registry.npmjs.org/lodash._createset/-/lodash._createset-4.0.3.tgz",
+                    "resolved": false,
                     "integrity": "sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY="
                 },
                 "lodash._getnative": {
                     "version": "3.9.1",
-                    "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U="
                 },
                 "lodash._root": {
                     "version": "3.0.1",
-                    "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI="
                 },
                 "lodash.clonedeep": {
                     "version": "4.5.0",
-                    "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
                 },
                 "lodash.restparam": {
                     "version": "3.6.1",
-                    "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU="
                 },
                 "lodash.union": {
                     "version": "4.6.0",
-                    "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg="
                 },
                 "lodash.uniq": {
                     "version": "4.5.0",
-                    "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
                 },
                 "lodash.without": {
                     "version": "4.4.0",
-                    "resolved": "https://registry.npmjs.org/lodash.without/-/lodash.without-4.4.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-PNRXSgC2e643OpS3SHcmQFB7eqw="
                 },
                 "lowercase-keys": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA=="
                 },
                 "lru-cache": {
                     "version": "5.1.1",
-                    "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
                     "requires": {
                         "yallist": "^3.0.2"
@@ -12632,7 +13003,7 @@
                 },
                 "make-dir": {
                     "version": "1.3.0",
-                    "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
                     "requires": {
                         "pify": "^3.0.0"
@@ -12640,7 +13011,7 @@
                 },
                 "make-fetch-happen": {
                     "version": "5.0.2",
-                    "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag==",
                     "requires": {
                         "agentkeepalive": "^3.4.1",
@@ -12663,12 +13034,12 @@
                 },
                 "mime-db": {
                     "version": "1.35.0",
-                    "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg=="
                 },
                 "mime-types": {
                     "version": "2.1.19",
-                    "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz",
+                    "resolved": false,
                     "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==",
                     "requires": {
                         "mime-db": "~1.35.0"
@@ -12676,20 +13047,20 @@
                 },
                 "minimatch": {
                     "version": "3.0.4",
-                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+                    "resolved": false,
                     "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
                     "requires": {
                         "brace-expansion": "^1.1.7"
                     }
                 },
                 "minimist": {
-                    "version": "1.2.6",
-                    "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
-                    "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
+                    "version": "1.2.5",
+                    "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+                    "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
                 },
                 "minizlib": {
                     "version": "1.3.3",
-                    "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
                     "requires": {
                         "minipass": "^2.9.0"
@@ -12697,7 +13068,7 @@
                     "dependencies": {
                         "minipass": {
                             "version": "2.9.0",
-                            "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
+                            "resolved": false,
                             "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
                             "requires": {
                                 "safe-buffer": "^5.1.2",
@@ -12708,7 +13079,7 @@
                 },
                 "mississippi": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
                     "requires": {
                         "concat-stream": "^1.5.0",
@@ -12725,22 +13096,22 @@
                 },
                 "mkdirp": {
                     "version": "0.5.5",
-                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+                    "resolved": false,
                     "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
                     "requires": {
                         "minimist": "^1.2.5"
                     },
                     "dependencies": {
                         "minimist": {
-                            "version": "1.2.6",
-                            "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
-                            "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
+                            "version": "1.2.5",
+                            "resolved": false,
+                            "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
                         }
                     }
                 },
                 "move-concurrently": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
                     "requires": {
                         "aproba": "^1.1.1",
@@ -12753,24 +13124,24 @@
                     "dependencies": {
                         "aproba": {
                             "version": "1.2.0",
-                            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+                            "resolved": false,
                             "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
                         }
                     }
                 },
                 "ms": {
                     "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
                 },
                 "mute-stream": {
                     "version": "0.0.7",
-                    "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
+                    "resolved": false,
                     "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
                 },
                 "node-fetch-npm": {
                     "version": "2.0.2",
-                    "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==",
                     "requires": {
                         "encoding": "^0.1.11",
@@ -12780,7 +13151,7 @@
                 },
                 "node-gyp": {
                     "version": "5.1.0",
-                    "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-OUTryc5bt/P8zVgNUmC6xdXiDJxLMAW8cF5tLQOT9E5sOQj+UeQxnnPy74K3CLCa/SOjjBlbuzDLR8ANwA+wmw==",
                     "requires": {
                         "env-paths": "^2.2.0",
@@ -12798,7 +13169,7 @@
                 },
                 "nopt": {
                     "version": "4.0.3",
-                    "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
                     "requires": {
                         "abbrev": "1",
@@ -12807,7 +13178,7 @@
                 },
                 "normalize-package-data": {
                     "version": "2.5.0",
-                    "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
                     "requires": {
                         "hosted-git-info": "^2.1.4",
@@ -12818,7 +13189,7 @@
                     "dependencies": {
                         "resolve": {
                             "version": "1.10.0",
-                            "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz",
+                            "resolved": false,
                             "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
                             "requires": {
                                 "path-parse": "^1.0.6"
@@ -12837,7 +13208,7 @@
                 },
                 "npm-bundled": {
                     "version": "1.1.1",
-                    "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
                     "requires": {
                         "npm-normalize-package-bin": "^1.0.1"
@@ -12845,12 +13216,12 @@
                 },
                 "npm-cache-filename": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/npm-cache-filename/-/npm-cache-filename-1.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-3tMGxbC/yHCp6fr4I7xfKD4FrhE="
                 },
                 "npm-install-checks": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-3.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-E4kzkyZDIWoin6uT5howP8VDvkM+E8IQDcHAycaAxMbwkqhIg5eEYALnXOl3Hq9MrkdQB/2/g1xwBINXdKSRkg==",
                     "requires": {
                         "semver": "^2.3.0 || 3.x || 4 || 5"
@@ -12873,17 +13244,17 @@
                 },
                 "npm-logical-tree": {
                     "version": "1.2.1",
-                    "resolved": "https://registry.npmjs.org/npm-logical-tree/-/npm-logical-tree-1.2.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-AJI/qxDB2PWI4LG1CYN579AY1vCiNyWfkiquCsJWqntRu/WwimVrC8yXeILBFHDwxfOejxewlmnvW9XXjMlYIg=="
                 },
                 "npm-normalize-package-bin": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA=="
                 },
                 "npm-package-arg": {
                     "version": "6.1.1",
-                    "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==",
                     "requires": {
                         "hosted-git-info": "^2.7.1",
@@ -12894,7 +13265,7 @@
                 },
                 "npm-packlist": {
                     "version": "1.4.8",
-                    "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
+                    "resolved": false,
                     "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
                     "requires": {
                         "ignore-walk": "^3.0.1",
@@ -12904,7 +13275,7 @@
                 },
                 "npm-pick-manifest": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==",
                     "requires": {
                         "figgy-pudding": "^3.5.1",
@@ -12914,7 +13285,7 @@
                 },
                 "npm-profile": {
                     "version": "4.0.4",
-                    "resolved": "https://registry.npmjs.org/npm-profile/-/npm-profile-4.0.4.tgz",
+                    "resolved": false,
                     "integrity": "sha512-Ta8xq8TLMpqssF0H60BXS1A90iMoM6GeKwsmravJ6wYjWwSzcYBTdyWa3DZCYqPutacBMEm7cxiOkiIeCUAHDQ==",
                     "requires": {
                         "aproba": "^1.1.2 || 2",
@@ -12945,7 +13316,7 @@
                 },
                 "npm-run-path": {
                     "version": "2.0.2",
-                    "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
                     "requires": {
                         "path-key": "^2.0.0"
@@ -12958,7 +13329,7 @@
                 },
                 "npmlog": {
                     "version": "4.1.2",
-                    "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
                     "requires": {
                         "are-we-there-yet": "~1.1.2",
@@ -12969,27 +13340,27 @@
                 },
                 "number-is-nan": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
                 },
                 "oauth-sign": {
                     "version": "0.9.0",
-                    "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
                 },
                 "object-assign": {
                     "version": "4.1.1",
-                    "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
                 },
                 "object-keys": {
                     "version": "1.0.12",
-                    "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
+                    "resolved": false,
                     "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag=="
                 },
                 "object.getownpropertydescriptors": {
                     "version": "2.0.3",
-                    "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz",
+                    "resolved": false,
                     "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=",
                     "requires": {
                         "define-properties": "^1.1.2",
@@ -12998,7 +13369,7 @@
                 },
                 "once": {
                     "version": "1.4.0",
-                    "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
                     "requires": {
                         "wrappy": "1"
@@ -13011,17 +13382,17 @@
                 },
                 "os-homedir": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
                 },
                 "os-tmpdir": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
                 },
                 "osenv": {
                     "version": "0.1.5",
-                    "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
+                    "resolved": false,
                     "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
                     "requires": {
                         "os-homedir": "^1.0.0",
@@ -13030,12 +13401,12 @@
                 },
                 "p-finally": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
                 },
                 "package-json": {
                     "version": "4.0.1",
-                    "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=",
                     "requires": {
                         "got": "^6.7.1",
@@ -13046,7 +13417,7 @@
                 },
                 "pacote": {
                     "version": "9.5.12",
-                    "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.5.12.tgz",
+                    "resolved": false,
                     "integrity": "sha512-BUIj/4kKbwWg4RtnBncXPJd15piFSVNpTzY0rysSr3VnMowTYgkGKcaHrbReepAkjTr8lH2CVWRi58Spg2CicQ==",
                     "requires": {
                         "bluebird": "^3.5.3",
@@ -13083,7 +13454,7 @@
                     "dependencies": {
                         "minipass": {
                             "version": "2.9.0",
-                            "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
+                            "resolved": false,
                             "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
                             "requires": {
                                 "safe-buffer": "^5.1.2",
@@ -13094,7 +13465,7 @@
                 },
                 "parallel-transform": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=",
                     "requires": {
                         "cyclist": "~0.2.2",
@@ -13104,7 +13475,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+                            "resolved": false,
                             "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
                             "requires": {
                                 "core-util-is": "~1.0.0",
@@ -13118,7 +13489,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+                            "resolved": false,
                             "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
                             "requires": {
                                 "safe-buffer": "~5.1.0"
@@ -13128,22 +13499,22 @@
                 },
                 "path-exists": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
                 },
                 "path-is-absolute": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
                 },
                 "path-is-inside": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM="
                 },
                 "path-key": {
                     "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
                 },
                 "path-parse": {
@@ -13153,32 +13524,32 @@
                 },
                 "performance-now": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
                 },
                 "pify": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
                 },
                 "prepend-http": {
                     "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
+                    "resolved": false,
                     "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw="
                 },
                 "process-nextick-args": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
                 },
                 "promise-inflight": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM="
                 },
                 "promise-retry": {
                     "version": "1.1.1",
-                    "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=",
                     "requires": {
                         "err-code": "^1.0.0",
@@ -13187,14 +13558,14 @@
                     "dependencies": {
                         "retry": {
                             "version": "0.10.1",
-                            "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz",
+                            "resolved": false,
                             "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q="
                         }
                     }
                 },
                 "promzard": {
                     "version": "0.3.0",
-                    "resolved": "https://registry.npmjs.org/promzard/-/promzard-0.3.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-JqXW7ox97kyxIggwWs+5O6OCqe4=",
                     "requires": {
                         "read": "1"
@@ -13202,12 +13573,12 @@
                 },
                 "proto-list": {
                     "version": "1.2.4",
-                    "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
+                    "resolved": false,
                     "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk="
                 },
                 "protoduck": {
                     "version": "5.0.1",
-                    "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==",
                     "requires": {
                         "genfun": "^5.0.0"
@@ -13215,22 +13586,22 @@
                 },
                 "prr": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY="
                 },
                 "pseudomap": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
                 },
                 "psl": {
                     "version": "1.1.29",
-                    "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz",
+                    "resolved": false,
                     "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ=="
                 },
                 "pump": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
                     "requires": {
                         "end-of-stream": "^1.1.0",
@@ -13239,7 +13610,7 @@
                 },
                 "pumpify": {
                     "version": "1.5.1",
-                    "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
                     "requires": {
                         "duplexify": "^3.6.0",
@@ -13249,7 +13620,7 @@
                     "dependencies": {
                         "pump": {
                             "version": "2.0.1",
-                            "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
+                            "resolved": false,
                             "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
                             "requires": {
                                 "end-of-stream": "^1.1.0",
@@ -13260,22 +13631,22 @@
                 },
                 "punycode": {
                     "version": "1.4.1",
-                    "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
                 },
                 "qrcode-terminal": {
                     "version": "0.12.0",
-                    "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ=="
                 },
                 "qs": {
                     "version": "6.5.2",
-                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
                 },
                 "query-string": {
                     "version": "6.8.2",
-                    "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.8.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-J3Qi8XZJXh93t2FiKyd/7Ec6GNifsjKXUsVFkSBj/kjLsDylWhnCz4NT1bkPcKotttPW+QbKGqqPH8OoI2pdqw==",
                     "requires": {
                         "decode-uri-component": "^0.2.0",
@@ -13285,12 +13656,12 @@
                 },
                 "qw": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/qw/-/qw-1.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-77/cdA+a0FQwRCassYNBLMi5ltQ="
                 },
                 "rc": {
                     "version": "1.2.8",
-                    "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+                    "resolved": false,
                     "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
                     "requires": {
                         "deep-extend": "^0.6.0",
@@ -13301,7 +13672,7 @@
                 },
                 "read": {
                     "version": "1.0.7",
-                    "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
+                    "resolved": false,
                     "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=",
                     "requires": {
                         "mute-stream": "~0.0.4"
@@ -13309,7 +13680,7 @@
                 },
                 "read-cmd-shim": {
                     "version": "1.0.5",
-                    "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-1.0.5.tgz",
+                    "resolved": false,
                     "integrity": "sha512-v5yCqQ/7okKoZZkBQUAfTsQ3sVJtXdNfbPnI5cceppoxEVLYA3k+VtV2omkeo8MS94JCy4fSiUwlRBAwCVRPUA==",
                     "requires": {
                         "graceful-fs": "^4.1.2"
@@ -13317,7 +13688,7 @@
                 },
                 "read-installed": {
                     "version": "4.0.3",
-                    "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz",
+                    "resolved": false,
                     "integrity": "sha1-/5uLZ/GH0eTCm5/rMfayI6zRkGc=",
                     "requires": {
                         "debuglog": "^1.0.1",
@@ -13331,7 +13702,7 @@
                 },
                 "read-package-json": {
                     "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A==",
                     "requires": {
                         "glob": "^7.1.1",
@@ -13343,7 +13714,7 @@
                 },
                 "read-package-tree": {
                     "version": "5.3.1",
-                    "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==",
                     "requires": {
                         "read-package-json": "^2.0.0",
@@ -13353,7 +13724,7 @@
                 },
                 "readable-stream": {
                     "version": "3.6.0",
-                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
                     "requires": {
                         "inherits": "^2.0.3",
@@ -13363,7 +13734,7 @@
                 },
                 "readdir-scoped-modules": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==",
                     "requires": {
                         "debuglog": "^1.0.1",
@@ -13374,7 +13745,7 @@
                 },
                 "registry-auth-token": {
                     "version": "3.4.0",
-                    "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==",
                     "requires": {
                         "rc": "^1.1.6",
@@ -13383,7 +13754,7 @@
                 },
                 "registry-url": {
                     "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
                     "requires": {
                         "rc": "^1.0.1"
@@ -13391,7 +13762,7 @@
                 },
                 "request": {
                     "version": "2.88.0",
-                    "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
                     "requires": {
                         "aws-sign2": "~0.7.0",
@@ -13418,7 +13789,7 @@
                 },
                 "require-directory": {
                     "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
                 },
                 "require-main-filename": {
@@ -13428,17 +13799,17 @@
                 },
                 "resolve-from": {
                     "version": "4.0.0",
-                    "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
                 },
                 "retry": {
                     "version": "0.12.0",
-                    "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs="
                 },
                 "rimraf": {
                     "version": "2.7.1",
-                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
                     "requires": {
                         "glob": "^7.1.3"
@@ -13446,7 +13817,7 @@
                 },
                 "run-queue": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
+                    "resolved": false,
                     "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
                     "requires": {
                         "aproba": "^1.1.1"
@@ -13454,29 +13825,29 @@
                     "dependencies": {
                         "aproba": {
                             "version": "1.2.0",
-                            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+                            "resolved": false,
                             "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
                         }
                     }
                 },
                 "safe-buffer": {
                     "version": "5.1.2",
-                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                 },
                 "safer-buffer": {
                     "version": "2.1.2",
-                    "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
                 },
                 "semver": {
                     "version": "5.7.1",
-                    "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
                 },
                 "semver-diff": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=",
                     "requires": {
                         "semver": "^5.0.3"
@@ -13484,12 +13855,12 @@
                 },
                 "set-blocking": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
                 },
                 "sha": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/sha/-/sha-3.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-DOYnM37cNsLNSGIG/zZWch5CKIRNoLdYUQTQlcgkRkoYIUwDYjqDyye16YcDZg/OPdcbUgTKMjc4SY6TB7ZAPw==",
                     "requires": {
                         "graceful-fs": "^4.1.2"
@@ -13497,7 +13868,7 @@
                 },
                 "shebang-command": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
                     "requires": {
                         "shebang-regex": "^1.0.0"
@@ -13505,27 +13876,27 @@
                 },
                 "shebang-regex": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
                 },
                 "signal-exit": {
                     "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
                 },
                 "slide": {
                     "version": "1.1.6",
-                    "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
+                    "resolved": false,
                     "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc="
                 },
                 "smart-buffer": {
                     "version": "4.1.0",
-                    "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw=="
                 },
                 "socks": {
                     "version": "2.3.3",
-                    "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==",
                     "requires": {
                         "ip": "1.1.5",
@@ -13534,7 +13905,7 @@
                 },
                 "socks-proxy-agent": {
                     "version": "4.0.2",
-                    "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==",
                     "requires": {
                         "agent-base": "~4.2.1",
@@ -13543,7 +13914,7 @@
                     "dependencies": {
                         "agent-base": {
                             "version": "4.2.1",
-                            "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
+                            "resolved": false,
                             "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
                             "requires": {
                                 "es6-promisify": "^5.0.0"
@@ -13553,12 +13924,12 @@
                 },
                 "sorted-object": {
                     "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/sorted-object/-/sorted-object-2.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-fWMfS9OnmKJK8d/8+/6DM3pd9fw="
                 },
                 "sorted-union-stream": {
                     "version": "2.1.3",
-                    "resolved": "https://registry.npmjs.org/sorted-union-stream/-/sorted-union-stream-2.1.3.tgz",
+                    "resolved": false,
                     "integrity": "sha1-x3lMfgd4gAUv9xqNSi27Sppjisc=",
                     "requires": {
                         "from2": "^1.3.0",
@@ -13567,7 +13938,7 @@
                     "dependencies": {
                         "from2": {
                             "version": "1.3.0",
-                            "resolved": "https://registry.npmjs.org/from2/-/from2-1.3.0.tgz",
+                            "resolved": false,
                             "integrity": "sha1-iEE7qqX5pZfP3pIh2GmGzTwGHf0=",
                             "requires": {
                                 "inherits": "~2.0.1",
@@ -13576,12 +13947,12 @@
                         },
                         "isarray": {
                             "version": "0.0.1",
-                            "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+                            "resolved": false,
                             "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
                         },
                         "readable-stream": {
                             "version": "1.1.14",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+                            "resolved": false,
                             "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
                             "requires": {
                                 "core-util-is": "~1.0.0",
@@ -13592,14 +13963,14 @@
                         },
                         "string_decoder": {
                             "version": "0.10.31",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+                            "resolved": false,
                             "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
                         }
                     }
                 },
                 "spdx-correct": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==",
                     "requires": {
                         "spdx-expression-parse": "^3.0.0",
@@ -13608,12 +13979,12 @@
                 },
                 "spdx-exceptions": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg=="
                 },
                 "spdx-expression-parse": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
                     "requires": {
                         "spdx-exceptions": "^2.1.0",
@@ -13627,12 +13998,12 @@
                 },
                 "split-on-first": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="
                 },
                 "sshpk": {
                     "version": "1.14.2",
-                    "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=",
                     "requires": {
                         "asn1": "~0.2.3",
@@ -13656,7 +14027,7 @@
                 },
                 "stream-each": {
                     "version": "1.2.2",
-                    "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==",
                     "requires": {
                         "end-of-stream": "^1.1.0",
@@ -13665,7 +14036,7 @@
                 },
                 "stream-iterate": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/stream-iterate/-/stream-iterate-1.2.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-K9fHcpbBcCpGSIuK1B95hl7s1OE=",
                     "requires": {
                         "readable-stream": "^2.1.5",
@@ -13674,7 +14045,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+                            "resolved": false,
                             "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
                             "requires": {
                                 "core-util-is": "~1.0.0",
@@ -13688,7 +14059,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+                            "resolved": false,
                             "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
                             "requires": {
                                 "safe-buffer": "~5.1.0"
@@ -13698,17 +14069,16 @@
                 },
                 "stream-shift": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI="
                 },
                 "strict-uri-encode": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
-                    "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
+                    "bundled": true
                 },
                 "string-width": {
                     "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
                     "requires": {
                         "is-fullwidth-code-point": "^2.0.0",
@@ -13717,17 +14087,17 @@
                     "dependencies": {
                         "ansi-regex": {
                             "version": "3.0.0",
-                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+                            "resolved": false,
                             "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
                         },
                         "is-fullwidth-code-point": {
                             "version": "2.0.0",
-                            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+                            "resolved": false,
                             "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
                         },
                         "strip-ansi": {
                             "version": "4.0.0",
-                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+                            "resolved": false,
                             "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
                             "requires": {
                                 "ansi-regex": "^3.0.0"
@@ -13737,27 +14107,25 @@
                 },
                 "string_decoder": {
                     "version": "1.3.0",
-                    "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
-                    "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+                    "bundled": true,
                     "requires": {
                         "safe-buffer": "~5.2.0"
                     },
                     "dependencies": {
                         "safe-buffer": {
                             "version": "5.2.0",
-                            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
-                            "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
+                            "bundled": true
                         }
                     }
                 },
                 "stringify-package": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg=="
                 },
                 "strip-ansi": {
                     "version": "3.0.1",
-                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
                     "requires": {
                         "ansi-regex": "^2.0.0"
@@ -13765,17 +14133,17 @@
                 },
                 "strip-eof": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
                 },
                 "strip-json-comments": {
                     "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
                 },
                 "supports-color": {
                     "version": "5.4.0",
-                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
                     "requires": {
                         "has-flag": "^3.0.0"
@@ -13797,7 +14165,7 @@
                     "dependencies": {
                         "minipass": {
                             "version": "2.9.0",
-                            "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
+                            "resolved": false,
                             "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
                             "requires": {
                                 "safe-buffer": "^5.1.2",
@@ -13818,7 +14186,7 @@
                 },
                 "term-size": {
                     "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=",
                     "requires": {
                         "execa": "^0.7.0"
@@ -13826,17 +14194,17 @@
                 },
                 "text-table": {
                     "version": "0.2.0",
-                    "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ="
                 },
                 "through": {
                     "version": "2.3.8",
-                    "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+                    "resolved": false,
                     "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
                 },
                 "through2": {
                     "version": "2.0.3",
-                    "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz",
+                    "resolved": false,
                     "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=",
                     "requires": {
                         "readable-stream": "^2.1.5",
@@ -13845,7 +14213,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+                            "resolved": false,
                             "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
                             "requires": {
                                 "core-util-is": "~1.0.0",
@@ -13859,7 +14227,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+                            "resolved": false,
                             "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
                             "requires": {
                                 "safe-buffer": "~5.1.0"
@@ -13869,17 +14237,17 @@
                 },
                 "timed-out": {
                     "version": "4.0.1",
-                    "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8="
                 },
                 "tiny-relative-date": {
                     "version": "1.3.0",
-                    "resolved": "https://registry.npmjs.org/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A=="
                 },
                 "tough-cookie": {
                     "version": "2.4.3",
-                    "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
                     "requires": {
                         "psl": "^1.1.24",
@@ -13888,7 +14256,7 @@
                 },
                 "tunnel-agent": {
                     "version": "0.6.0",
-                    "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
                     "requires": {
                         "safe-buffer": "^5.0.1"
@@ -13896,28 +14264,28 @@
                 },
                 "tweetnacl": {
                     "version": "0.14.5",
-                    "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+                    "resolved": false,
                     "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
                     "optional": true
                 },
                 "typedarray": {
                     "version": "0.0.6",
-                    "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+                    "resolved": false,
                     "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
                 },
                 "uid-number": {
                     "version": "0.0.6",
-                    "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz",
+                    "resolved": false,
                     "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE="
                 },
                 "umask": {
                     "version": "1.1.0",
-                    "resolved": "https://registry.npmjs.org/umask/-/umask-1.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0="
                 },
                 "unique-filename": {
                     "version": "1.1.1",
-                    "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
                     "requires": {
                         "unique-slug": "^2.0.0"
@@ -13925,7 +14293,7 @@
                 },
                 "unique-slug": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=",
                     "requires": {
                         "imurmurhash": "^0.1.4"
@@ -13933,7 +14301,7 @@
                 },
                 "unique-string": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=",
                     "requires": {
                         "crypto-random-string": "^1.0.0"
@@ -13941,17 +14309,17 @@
                 },
                 "unpipe": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
                 },
                 "unzip-response": {
                     "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c="
                 },
                 "update-notifier": {
                     "version": "2.5.0",
-                    "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==",
                     "requires": {
                         "boxen": "^1.2.1",
@@ -13983,7 +14351,7 @@
                 },
                 "url-parse-lax": {
                     "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=",
                     "requires": {
                         "prepend-http": "^1.0.1"
@@ -13991,17 +14359,17 @@
                 },
                 "util-deprecate": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
                 },
                 "util-extend": {
                     "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz",
+                    "resolved": false,
                     "integrity": "sha1-p8IW0mdUUWljeztu3GypEZ4v+T8="
                 },
                 "util-promisify": {
                     "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=",
                     "requires": {
                         "object.getownpropertydescriptors": "^2.0.3"
@@ -14009,12 +14377,12 @@
                 },
                 "uuid": {
                     "version": "3.3.3",
-                    "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ=="
                 },
                 "validate-npm-package-license": {
                     "version": "3.0.4",
-                    "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+                    "resolved": false,
                     "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
                     "requires": {
                         "spdx-correct": "^3.0.0",
@@ -14023,7 +14391,7 @@
                 },
                 "validate-npm-package-name": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=",
                     "requires": {
                         "builtins": "^1.0.3"
@@ -14031,7 +14399,7 @@
                 },
                 "verror": {
                     "version": "1.10.0",
-                    "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
                     "requires": {
                         "assert-plus": "^1.0.0",
@@ -14041,7 +14409,7 @@
                 },
                 "wcwidth": {
                     "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=",
                     "requires": {
                         "defaults": "^1.0.3"
@@ -14049,7 +14417,7 @@
                 },
                 "which": {
                     "version": "1.3.1",
-                    "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
                     "requires": {
                         "isexe": "^2.0.0"
@@ -14057,12 +14425,12 @@
                 },
                 "which-module": {
                     "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
                 },
                 "wide-align": {
                     "version": "1.1.2",
-                    "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
+                    "resolved": false,
                     "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
                     "requires": {
                         "string-width": "^1.0.2"
@@ -14070,7 +14438,7 @@
                     "dependencies": {
                         "string-width": {
                             "version": "1.0.2",
-                            "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+                            "resolved": false,
                             "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
                             "requires": {
                                 "code-point-at": "^1.0.0",
@@ -14082,7 +14450,7 @@
                 },
                 "widest-line": {
                     "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==",
                     "requires": {
                         "string-width": "^2.1.1"
@@ -14090,7 +14458,7 @@
                 },
                 "worker-farm": {
                     "version": "1.7.0",
-                    "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
+                    "resolved": false,
                     "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
                     "requires": {
                         "errno": "~0.1.7"
@@ -14107,9 +14475,9 @@
                     },
                     "dependencies": {
                         "ansi-regex": {
-                            "version": "4.1.1",
-                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
-                            "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
+                            "version": "4.1.0",
+                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+                            "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
                         },
                         "is-fullwidth-code-point": {
                             "version": "2.0.0",
@@ -14138,12 +14506,12 @@
                 },
                 "wrappy": {
                     "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+                    "resolved": false,
                     "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
                 },
                 "write-file-atomic": {
                     "version": "2.4.3",
-                    "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==",
                     "requires": {
                         "graceful-fs": "^4.1.11",
@@ -14153,12 +14521,12 @@
                 },
                 "xdg-basedir": {
                     "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz",
+                    "resolved": false,
                     "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ="
                 },
                 "xtend": {
                     "version": "4.0.1",
-                    "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+                    "resolved": false,
                     "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
                 },
                 "y18n": {
@@ -14168,7 +14536,7 @@
                 },
                 "yallist": {
                     "version": "3.0.3",
-                    "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
+                    "resolved": false,
                     "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A=="
                 },
                 "yargs": {
@@ -14278,26 +14646,26 @@
         "npm-run-path": {
             "version": "2.0.2",
             "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
-            "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==",
+            "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
             "requires": {
                 "path-key": "^2.0.0"
             }
         },
         "npmlog": {
-            "version": "5.0.1",
-            "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
-            "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
+            "version": "4.1.2",
+            "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
+            "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
             "requires": {
-                "are-we-there-yet": "^2.0.0",
-                "console-control-strings": "^1.1.0",
-                "gauge": "^3.0.0",
-                "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": {
-            "version": "2.1.1",
-            "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
-            "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
+            "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
             "requires": {
                 "boolbase": "^1.0.0"
             }
@@ -14305,7 +14673,7 @@
         "number-is-nan": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
-            "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ=="
+            "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
         },
         "nwsapi": {
             "version": "2.2.0",
@@ -14316,7 +14684,7 @@
         "oauth": {
             "version": "0.9.15",
             "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
-            "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
+            "integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE="
         },
         "oauth-sign": {
             "version": "0.9.0",
@@ -14326,12 +14694,12 @@
         "object-assign": {
             "version": "4.1.1",
             "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-            "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
+            "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
         },
         "object-copy": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
-            "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==",
+            "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
             "requires": {
                 "copy-descriptor": "^0.1.0",
                 "define-property": "^0.2.5",
@@ -14341,7 +14709,7 @@
                 "define-property": {
                     "version": "0.2.5",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-                    "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+                    "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
                     "requires": {
                         "is-descriptor": "^0.1.0"
                     }
@@ -14354,7 +14722,7 @@
                 "kind-of": {
                     "version": "3.2.2",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                    "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+                    "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -14362,9 +14730,9 @@
             }
         },
         "object-inspect": {
-            "version": "1.12.2",
-            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
-            "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ=="
+            "version": "1.7.0",
+            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
+            "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw=="
         },
         "object-is": {
             "version": "1.1.5",
@@ -14392,11 +14760,6 @@
                 "unpack-string": "0.0.2"
             },
             "dependencies": {
-                "commander": {
-                    "version": "2.20.3",
-                    "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
-                    "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
-                },
                 "inspect-parameters-declaration": {
                     "version": "0.0.10",
                     "resolved": "https://registry.npmjs.org/inspect-parameters-declaration/-/inspect-parameters-declaration-0.0.10.tgz",
@@ -14411,7 +14774,7 @@
                 "magicli": {
                     "version": "0.0.5",
                     "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz",
-                    "integrity": "sha512-wZbMtnl2v1b+Jp3xlqA9FU/O4I6YhGXR8xSY/eU2+gDAvut/F+W3gl4qs61iL4LELC7jqSAE6aAD5668EbmQHA==",
+                    "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=",
                     "requires": {
                         "commander": "^2.9.0",
                         "get-stdin": "^5.0.1",
@@ -14424,7 +14787,7 @@
         "object-visit": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
-            "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==",
+            "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
             "requires": {
                 "isobject": "^3.0.0"
             }
@@ -14441,20 +14804,18 @@
             }
         },
         "object.getownpropertydescriptors": {
-            "version": "2.1.4",
-            "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz",
-            "integrity": "sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==",
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz",
+            "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==",
             "requires": {
-                "array.prototype.reduce": "^1.0.4",
-                "call-bind": "^1.0.2",
-                "define-properties": "^1.1.4",
-                "es-abstract": "^1.20.1"
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.0-next.1"
             }
         },
         "object.pick": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
-            "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==",
+            "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
             "requires": {
                 "isobject": "^3.0.1"
             }
@@ -14466,9 +14827,9 @@
             "dev": true
         },
         "on-finished": {
-            "version": "2.4.1",
-            "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
-            "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+            "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
             "requires": {
                 "ee-first": "1.1.1"
             }
@@ -14481,7 +14842,7 @@
         "once": {
             "version": "1.4.0",
             "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-            "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+            "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
             "requires": {
                 "wrappy": "1"
             }
@@ -14535,19 +14896,28 @@
             }
         },
         "orderedmap": {
-            "version": "1.1.8",
-            "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-1.1.8.tgz",
-            "integrity": "sha512-eWEYOAggZZpZbJ9CTsqAKOTxlbBHdHZ8pzcfEvNTxGrjQ/m+Q25nSWUiMlT9MTbgpB6FOiBDKqsgJ2FlLDVNaw=="
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-1.1.5.tgz",
+            "integrity": "sha512-/fzlCGKRmfayGoI9UUXvJfc2nMZlJHW30QqEvwPvlg8tsX7jyiUSomYie6mYqx7Z9bOMGoag0H/q1PS/0PjYkg=="
+        },
+        "original": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
+            "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
+            "dev": true,
+            "requires": {
+                "url-parse": "^1.4.3"
+            }
         },
         "os-homedir": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
-            "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ=="
+            "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
         },
         "os-tmpdir": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
-            "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g=="
+            "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
         },
         "osenv": {
             "version": "0.1.5",
@@ -14566,12 +14936,12 @@
         "p-debounce": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/p-debounce/-/p-debounce-1.0.0.tgz",
-            "integrity": "sha512-ttOxn4Yt0hzIsLLqKi/Ry9QRxW+UQKdoWHz7g99Ci57zPkqUU3kbWKAeHuv+HfRLe109acYLUY6kuVCOOqnt4g=="
+            "integrity": "sha1-y38svu/YegnrqGHhErZ1J+Yh4v0="
         },
         "p-finally": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
-            "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow=="
+            "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
         },
         "p-limit": {
             "version": "2.3.0",
@@ -14612,7 +14982,7 @@
         "package-json": {
             "version": "4.0.1",
             "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz",
-            "integrity": "sha512-q/R5GrMek0vzgoomq6rm9OX+3PQve8sLwTirmK30YB3Cu0Bbt9OX9M/SIUnroN5BGJkzwGsFwDaRGD9EwBOlCA==",
+            "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=",
             "requires": {
                 "got": "^6.7.1",
                 "registry-auth-token": "^3.0.1",
@@ -14620,15 +14990,10 @@
                 "semver": "^5.1.0"
             },
             "dependencies": {
-                "get-stream": {
-                    "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
-                    "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ=="
-                },
                 "got": {
                     "version": "6.7.1",
                     "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
-                    "integrity": "sha512-Y/K3EDuiQN9rTZhBvPRWMLXIKdeD1Rj0nzunfoi0Yyn5WBEbzxXKU9Ub2X41oZBagVWOBU3MuDonFMgPWQFnwg==",
+                    "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
                     "requires": {
                         "create-error-class": "^3.0.0",
                         "duplexer3": "^0.1.4",
@@ -14690,6 +15055,13 @@
             "requires": {
                 "dot-case": "^3.0.4",
                 "tslib": "^2.0.3"
+            },
+            "dependencies": {
+                "tslib": {
+                    "version": "2.3.1",
+                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+                    "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+                }
             }
         },
         "parent-module": {
@@ -14711,12 +15083,6 @@
                 "lines-and-columns": "^1.1.6"
             }
         },
-        "parse5": {
-            "version": "5.1.0",
-            "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
-            "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==",
-            "dev": true
-        },
         "parseqs": {
             "version": "0.0.6",
             "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
@@ -14739,12 +15105,19 @@
             "requires": {
                 "no-case": "^3.0.4",
                 "tslib": "^2.0.3"
+            },
+            "dependencies": {
+                "tslib": {
+                    "version": "2.3.1",
+                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
+                    "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
+                }
             }
         },
         "pascalcase": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
-            "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw=="
+            "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ="
         },
         "passport": {
             "version": "0.4.1",
@@ -14766,15 +15139,15 @@
         "passport-local": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
-            "integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==",
+            "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=",
             "requires": {
                 "passport-strategy": "1.x.x"
             }
         },
         "passport-oauth2": {
-            "version": "1.6.1",
-            "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.6.1.tgz",
-            "integrity": "sha512-ZbV43Hq9d/SBSYQ22GOiglFsjsD1YY/qdiptA+8ej+9C1dL1TVB+mBE5kDH/D4AJo50+2i8f4bx0vg4/yDDZCQ==",
+            "version": "1.5.0",
+            "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.5.0.tgz",
+            "integrity": "sha512-kqBt6vR/5VlCK8iCx1/KpY42kQ+NEHZwsSyt4Y6STiNjU+wWICG1i8ucc1FapXDGO15C5O5VZz7+7vRzrDPXXQ==",
             "requires": {
                 "base64url": "3.x.x",
                 "oauth": "0.9.x",
@@ -14786,7 +15159,7 @@
         "passport-strategy": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
-            "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA=="
+            "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ="
         },
         "path-browserify": {
             "version": "1.0.1",
@@ -14796,37 +15169,37 @@
         "path-dirname": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
-            "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q=="
+            "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA="
         },
         "path-exists": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-            "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="
+            "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
         },
         "path-is-absolute": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-            "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="
+            "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
         },
         "path-is-inside": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
-            "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w=="
+            "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM="
         },
         "path-key": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
-            "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="
+            "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
         },
         "path-parse": {
-            "version": "1.0.7",
-            "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
-            "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+            "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
         },
         "path-to-regexp": {
             "version": "0.1.7",
             "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
-            "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
+            "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
         },
         "path-type": {
             "version": "4.0.0",
@@ -14841,7 +15214,7 @@
         "pause": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
-            "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
+            "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
         },
         "pdf-parse": {
             "version": "1.1.1",
@@ -14878,18 +15251,17 @@
             }
         },
         "pdfjs-dist": {
-            "version": "2.14.305",
-            "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.14.305.tgz",
-            "integrity": "sha512-5f7i25J1dKIBczhgfxEgNxfYNIxXEdxqo6Qb4ehY7Ja+p6AI4uUmk/OcVGXfRGm2ys5iaJJhJUwBFwv6Jl/Qww==",
+            "version": "2.13.216",
+            "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.13.216.tgz",
+            "integrity": "sha512-qn/9a/3IHIKZarTK6ajeeFXBkG15Lg1Fx99PxU09PAU2i874X8mTcHJYyDJxu7WDfNhV6hM7bRQBZU384anoqQ==",
             "requires": {
-                "dommatrix": "^1.0.1",
-                "web-streams-polyfill": "^3.2.1"
+                "web-streams-polyfill": "^3.2.0"
             }
         },
         "pend": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
-            "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="
+            "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
         },
         "perfect-scrollbar": {
             "version": "1.5.5",
@@ -14899,7 +15271,7 @@
         "performance-now": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
-            "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
+            "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
         },
         "picocolors": {
             "version": "1.0.0",
@@ -14909,22 +15281,24 @@
         "picomatch": {
             "version": "2.3.1",
             "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
-            "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
+            "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+            "dev": true
         },
         "pify": {
-            "version": "2.3.0",
-            "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-            "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+            "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+            "dev": true
         },
         "pinkie": {
             "version": "2.0.4",
             "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
-            "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg=="
+            "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA="
         },
         "pinkie-promise": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
-            "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==",
+            "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
             "requires": {
                 "pinkie": "^2.0.0"
             }
@@ -14935,42 +15309,56 @@
             "integrity": "sha512-6Rtbp7criZRwedlvWbUYxqlqJoAlMvYHo2UcRWq79xZ54vZcaNHpVBOcWkX3ErT2aUA69tv+uiv4zKJbhD/Wgg=="
         },
         "pkg-dir": {
-            "version": "4.2.0",
-            "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
-            "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
+            "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
+            "dev": true,
             "requires": {
-                "find-up": "^4.0.0"
+                "find-up": "^2.1.0"
             },
             "dependencies": {
-                "find-up": {
-                    "version": "4.1.0",
-                    "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
-                    "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+                "find-up": {
+                    "version": "2.1.0",
+                    "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+                    "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+                    "dev": true,
                     "requires": {
-                        "locate-path": "^5.0.0",
-                        "path-exists": "^4.0.0"
+                        "locate-path": "^2.0.0"
                     }
                 },
                 "locate-path": {
-                    "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
-                    "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+                    "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+                    "dev": true,
                     "requires": {
-                        "p-locate": "^4.1.0"
+                        "p-locate": "^2.0.0",
+                        "path-exists": "^3.0.0"
+                    }
+                },
+                "p-limit": {
+                    "version": "1.3.0",
+                    "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+                    "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+                    "dev": true,
+                    "requires": {
+                        "p-try": "^1.0.0"
                     }
                 },
                 "p-locate": {
-                    "version": "4.1.0",
-                    "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
-                    "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+                    "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+                    "dev": true,
                     "requires": {
-                        "p-limit": "^2.2.0"
+                        "p-limit": "^1.1.0"
                     }
                 },
-                "path-exists": {
-                    "version": "4.0.0",
-                    "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
-                    "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
+                "p-try": {
+                    "version": "1.0.0",
+                    "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+                    "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+                    "dev": true
                 }
             }
         },
@@ -14985,7 +15373,7 @@
         "plist": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/plist/-/plist-1.2.0.tgz",
-            "integrity": "sha512-dL9Xc2Aj3YyBnwvCNuHmFl2LWvQacm/HEAsoVwLiuu0POboMChETt5wexpU1P6F6MnibIucXlVsMFFgNUT2IyA==",
+            "integrity": "sha1-CEtQk93JJQbiWfh0uNmxr7jHlZM=",
             "requires": {
                 "base64-js": "0.0.8",
                 "util-deprecate": "1.0.2",
@@ -14996,7 +15384,7 @@
                 "base64-js": {
                     "version": "0.0.8",
                     "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
-                    "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw=="
+                    "integrity": "sha1-EQHpVE9KdrG8OybUUsqW16NeeXg="
                 }
             }
         },
@@ -15023,12 +15411,12 @@
             },
             "dependencies": {
                 "mkdirp": {
-                    "version": "0.5.6",
-                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
-                    "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+                    "version": "0.5.5",
+                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+                    "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
                     "dev": true,
                     "requires": {
-                        "minimist": "^1.2.6"
+                        "minimist": "^1.2.5"
                     }
                 }
             }
@@ -15036,29 +15424,33 @@
         "posix-character-classes": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
-            "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg=="
+            "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs="
         },
         "postcss": {
-            "version": "7.0.39",
-            "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
-            "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+            "version": "7.0.27",
+            "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz",
+            "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==",
             "dev": true,
             "requires": {
-                "picocolors": "^0.2.1",
-                "source-map": "^0.6.1"
+                "chalk": "^2.4.2",
+                "source-map": "^0.6.1",
+                "supports-color": "^6.1.0"
             },
             "dependencies": {
-                "picocolors": {
-                    "version": "0.2.1",
-                    "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
-                    "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
-                    "dev": true
-                },
                 "source-map": {
                     "version": "0.6.1",
                     "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
                     "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
                     "dev": true
+                },
+                "supports-color": {
+                    "version": "6.1.0",
+                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+                    "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+                    "dev": true,
+                    "requires": {
+                        "has-flag": "^3.0.0"
+                    }
                 }
             }
         },
@@ -15103,13 +15495,14 @@
             }
         },
         "postcss-selector-parser": {
-            "version": "6.0.10",
-            "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
-            "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
+            "version": "6.0.2",
+            "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
+            "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
             "dev": true,
             "requires": {
                 "cssesc": "^3.0.0",
-                "util-deprecate": "^1.0.2"
+                "indexes-of": "^1.0.1",
+                "uniq": "^1.0.1"
             }
         },
         "postcss-value-parser": {
@@ -15118,15 +15511,15 @@
             "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
         },
         "prebuild-install": {
-            "version": "5.3.6",
-            "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.6.tgz",
-            "integrity": "sha512-s8Aai8++QQGi4sSbs/M1Qku62PFK49Jm1CbgXklGz4nmHveDq0wzJkg7Na5QbnO1uNH8K7iqx2EQ/mV0MZEmOg==",
+            "version": "5.3.3",
+            "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.3.tgz",
+            "integrity": "sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g==",
             "requires": {
                 "detect-libc": "^1.0.3",
                 "expand-template": "^2.0.3",
                 "github-from-package": "0.0.0",
-                "minimist": "^1.2.3",
-                "mkdirp-classic": "^0.5.3",
+                "minimist": "^1.2.0",
+                "mkdirp": "^0.5.1",
                 "napi-build-utils": "^1.0.1",
                 "node-abi": "^2.7.0",
                 "noop-logger": "^0.1.1",
@@ -15137,110 +15530,18 @@
                 "tar-fs": "^2.0.0",
                 "tunnel-agent": "^0.6.0",
                 "which-pm-runs": "^1.0.0"
-            },
-            "dependencies": {
-                "ansi-regex": {
-                    "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-                    "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="
-                },
-                "aproba": {
-                    "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-                    "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
-                },
-                "are-we-there-yet": {
-                    "version": "1.1.7",
-                    "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
-                    "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
-                    "requires": {
-                        "delegates": "^1.0.0",
-                        "readable-stream": "^2.0.6"
-                    }
-                },
-                "detect-libc": {
-                    "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
-                    "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="
-                },
-                "gauge": {
-                    "version": "2.7.4",
-                    "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
-                    "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==",
-                    "requires": {
-                        "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"
-                    }
-                },
-                "is-fullwidth-code-point": {
-                    "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
-                    "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
-                    "requires": {
-                        "number-is-nan": "^1.0.0"
-                    }
-                },
-                "npmlog": {
-                    "version": "4.1.2",
-                    "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.2",
-                        "console-control-strings": "~1.1.0",
-                        "gauge": "~2.7.3",
-                        "set-blocking": "~2.0.0"
-                    }
-                },
-                "readable-stream": {
-                    "version": "2.3.7",
-                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
-                    "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
-                    "requires": {
-                        "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-width": {
-                    "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-                    "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
-                    "requires": {
-                        "code-point-at": "^1.0.0",
-                        "is-fullwidth-code-point": "^1.0.0",
-                        "strip-ansi": "^3.0.0"
-                    }
-                },
-                "strip-ansi": {
-                    "version": "3.0.1",
-                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-                    "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
-                    "requires": {
-                        "ansi-regex": "^2.0.0"
-                    }
-                }
             }
         },
         "prelude-ls": {
             "version": "1.1.2",
             "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
-            "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==",
+            "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
             "dev": true
         },
         "prepend-http": {
             "version": "1.0.4",
             "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
-            "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg=="
+            "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw="
         },
         "pretty-error": {
             "version": "4.0.0",
@@ -15274,7 +15575,7 @@
         "process": {
             "version": "0.11.10",
             "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
-            "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="
+            "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
         },
         "process-nextick-args": {
             "version": "2.0.1",
@@ -15297,23 +15598,23 @@
         "promise-inflight": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
-            "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==",
+            "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
             "dev": true
         },
         "prop-types": {
-            "version": "15.8.1",
-            "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
-            "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+            "version": "15.7.2",
+            "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.13.1"
+                "react-is": "^16.8.1"
             }
         },
         "prosemirror-commands": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.3.0.tgz",
-            "integrity": "sha512-BwBbZ5OAScPcm0x7H8SPbqjuEJnCU2RJT9LDyOiiIl/3NbL1nJZI4SFNHwU2e/tRr2Xe7JsptpzseqvZvToLBQ==",
+            "version": "1.2.2",
+            "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.2.2.tgz",
+            "integrity": "sha512-TX+KpWudMon06frryfpO/u7hsQv2hu8L4VSVbCpi3/7wXHBgl+35mV85qfa3RpT8xD2f3MdeoTqH0vy5JdbXPg==",
             "requires": {
                 "prosemirror-model": "^1.0.0",
                 "prosemirror-state": "^1.0.0",
@@ -15343,12 +15644,12 @@
         "prosemirror-find-replace": {
             "version": "0.9.0",
             "resolved": "https://registry.npmjs.org/prosemirror-find-replace/-/prosemirror-find-replace-0.9.0.tgz",
-            "integrity": "sha512-LfhQ/Zr0PkkJpCsr9vTJ5ZPYh49mSVVG+hHJ6djT+chlCW+t2ilSxBpBG+2IeE/I5nlbcvuLLAbxeI1g3pTCpA=="
+            "integrity": "sha1-QgsENNF5xdBJD44hSNhVGpVJY4I="
         },
         "prosemirror-history": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.3.0.tgz",
-            "integrity": "sha512-qo/9Wn4B/Bq89/YD+eNWFbAytu6dmIM85EhID+fz9Jcl9+DfGEo8TTSrRhP15+fFEoaPqpHSxlvSzSEbmlxlUA==",
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.2.0.tgz",
+            "integrity": "sha512-B9v9xtf4fYbKxQwIr+3wtTDNLDZcmMMmGiI3TAPShnUzvo+Rmv1GiUrsQChY1meetHl7rhML2cppF3FTs7f7UQ==",
             "requires": {
                 "prosemirror-state": "^1.2.2",
                 "prosemirror-transform": "^1.0.0",
@@ -15356,62 +15657,61 @@
             }
         },
         "prosemirror-inputrules": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.2.0.tgz",
-            "integrity": "sha512-eAW/M/NTSSzpCOxfR8Abw6OagdG0MiDAiWHQMQveIsZtoKVYzm0AflSPq/ymqJd56/Su1YPbwy9lM13wgHOFmQ==",
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.1.3.tgz",
+            "integrity": "sha512-ZaHCLyBtvbyIHv0f5p6boQTIJjlD6o2NPZiEaZWT2DA+j591zS29QQEMT4lBqwcLW3qRSf7ZvoKNbf05YrsStw==",
             "requires": {
                 "prosemirror-state": "^1.0.0",
                 "prosemirror-transform": "^1.0.0"
             }
         },
         "prosemirror-keymap": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.0.tgz",
-            "integrity": "sha512-TdSfu+YyLDd54ufN/ZeD1VtBRYpgZnTPnnbY+4R08DDgs84KrIPEPbJL8t1Lm2dkljFx6xeBE26YWH3aIzkPKg==",
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.1.5.tgz",
+            "integrity": "sha512-8SZgPH3K+GLsHL2wKuwBD9rxhsbnVBTwpHCO4VUO5GmqUQlxd/2GtBVWTsyLq4Dp3N9nGgPd3+lZFKUDuVp+Vw==",
             "requires": {
                 "prosemirror-state": "^1.0.0",
                 "w3c-keyname": "^2.2.0"
             }
         },
         "prosemirror-model": {
-            "version": "1.18.0",
-            "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.18.0.tgz",
-            "integrity": "sha512-EZLl0mvVIixEL1V538GsPP4Iw+D83gBRdLcjL7VouzoPPWlhhv/0Y0V9ttT5lUhF7jieIrNHOa3XNFs1FwgrOw==",
+            "version": "1.16.1",
+            "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.16.1.tgz",
+            "integrity": "sha512-r1/w0HDU40TtkXp0DyKBnFPYwd8FSlUSJmGCGFv4DeynfeSlyQF2FD0RQbVEMOe6P3PpUSXM6LZBV7W/YNZ4mA==",
             "requires": {
                 "orderedmap": "^1.1.0"
             }
         },
         "prosemirror-schema-list": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.2.0.tgz",
-            "integrity": "sha512-8PT/9xOx1HHdC7fDNNfhQ50Z8Mzu7nKyA1KCDltSpcZVZIbB0k7KtsHrnXyuIhbLlScoymBiLZ00c5MH6wdFsA==",
+            "version": "1.1.6",
+            "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.1.6.tgz",
+            "integrity": "sha512-aFGEdaCWmJzouZ8DwedmvSsL50JpRkqhQ6tcpThwJONVVmCgI36LJHtoQ4VGZbusMavaBhXXr33zyD2IVsTlkw==",
             "requires": {
                 "prosemirror-model": "^1.0.0",
-                "prosemirror-state": "^1.0.0",
                 "prosemirror-transform": "^1.0.0"
             }
         },
         "prosemirror-state": {
-            "version": "1.4.0",
-            "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.0.tgz",
-            "integrity": "sha512-mVDZdjNX/YT5FvypiwbphJe9psA5h+j9apsSszVRFc6oKFoIInvzdujh8QW9f9lwHtSYajLxNiM1hPhd0Sl1XA==",
+            "version": "1.3.4",
+            "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.3.4.tgz",
+            "integrity": "sha512-Xkkrpd1y/TQ6HKzN3agsQIGRcLckUMA9u3j207L04mt8ToRgpGeyhbVv0HI7omDORIBHjR29b7AwlATFFf2GLA==",
             "requires": {
                 "prosemirror-model": "^1.0.0",
                 "prosemirror-transform": "^1.0.0"
             }
         },
         "prosemirror-transform": {
-            "version": "1.6.0",
-            "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.6.0.tgz",
-            "integrity": "sha512-MAp7AjsjEGEqQY0sSMufNIUuEyB1ZR9Fqlm8dTwwWwpEJRv/plsKjWXBbx52q3Ml8MtaMcd7ic14zAHVB3WaMw==",
+            "version": "1.3.4",
+            "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.3.4.tgz",
+            "integrity": "sha512-gTsg3UIeaFuEY6+YmNPMgTpEkCKPedkFIUnsPpOMbclU701fEVI/e4VOXACXh3BO5rZJaBbEBwrnzB0mLp6eBA==",
             "requires": {
                 "prosemirror-model": "^1.0.0"
             }
         },
         "prosemirror-view": {
-            "version": "1.26.0",
-            "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.26.0.tgz",
-            "integrity": "sha512-tT8ICUAw6YfOTRIwVQJoCdF65BYxoWFjAqmv44Kn4phYjdLJ8Vc92dhL3LnmwAQuZ9yfPHzRR6Lfb3/5EVJHWg==",
+            "version": "1.23.9",
+            "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.23.9.tgz",
+            "integrity": "sha512-4ft8PuzYPLqQlYjkW7+jy3drquLrzB0cXu6bXyEgBEYnbv/uI/zBF1rGxmSZCDKwteKm1Wn24B+BSheSw/9sOA==",
             "requires": {
                 "prosemirror-model": "^1.16.0",
                 "prosemirror-state": "^1.0.0",
@@ -15435,13 +15735,13 @@
         "prr": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
-            "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
+            "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
             "dev": true
         },
         "pseudomap": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
-            "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ=="
+            "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
         },
         "psl": {
             "version": "1.8.0",
@@ -15449,9 +15749,9 @@
             "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
         },
         "pstree.remy": {
-            "version": "1.1.8",
-            "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
-            "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w=="
+            "version": "1.1.7",
+            "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.7.tgz",
+            "integrity": "sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A=="
         },
         "pug": {
             "version": "2.0.4",
@@ -15479,9 +15779,9 @@
             }
         },
         "pug-code-gen": {
-            "version": "2.0.3",
-            "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-2.0.3.tgz",
-            "integrity": "sha512-r9sezXdDuZJfW9J91TN/2LFbiqDhmltTFmGpHTsGdrNGp3p4SxAjjXEfnuK2e4ywYsRIVP0NeLbSAMHUcaX1EA==",
+            "version": "2.0.2",
+            "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",
@@ -15510,21 +15810,6 @@
                 "pug-walk": "^1.1.8",
                 "resolve": "^1.1.6",
                 "uglify-js": "^2.6.1"
-            },
-            "dependencies": {
-                "clean-css": {
-                    "version": "4.2.4",
-                    "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz",
-                    "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==",
-                    "requires": {
-                        "source-map": "~0.6.0"
-                    }
-                },
-                "source-map": {
-                    "version": "0.6.1",
-                    "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-                    "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
-                }
             }
         },
         "pug-lexer": {
@@ -15642,11 +15927,11 @@
                     "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g=="
                 },
                 "debug": {
-                    "version": "4.3.4",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
-                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+                    "version": "4.1.1",
+                    "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"
                     }
                 },
                 "https-proxy-agent": {
@@ -15659,31 +15944,26 @@
                     }
                 },
                 "mime": {
-                    "version": "2.6.0",
-                    "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
-                    "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="
-                },
-                "ms": {
-                    "version": "2.1.2",
-                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
-                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+                    "version": "2.4.6",
+                    "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
+                    "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA=="
                 }
             }
         },
         "pure-color": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz",
-            "integrity": "sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA=="
+            "integrity": "sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4="
         },
         "q": {
             "version": "1.5.1",
             "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
-            "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw=="
+            "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc="
         },
         "qs": {
-            "version": "6.5.3",
-            "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
-            "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA=="
+            "version": "6.5.2",
+            "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+            "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
         },
         "query-string": {
             "version": "6.14.1",
@@ -15699,7 +15979,7 @@
         "querystring": {
             "version": "0.2.0",
             "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
-            "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==",
+            "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
             "dev": true
         },
         "querystringify": {
@@ -15721,7 +16001,7 @@
         "random-bytes": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
-            "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ=="
+            "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs="
         },
         "randombytes": {
             "version": "2.1.0",
@@ -15738,24 +16018,14 @@
             "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
         },
         "raw-body": {
-            "version": "2.5.1",
-            "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
-            "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
+            "version": "2.4.3",
+            "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz",
+            "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==",
             "requires": {
                 "bytes": "3.1.2",
-                "http-errors": "2.0.0",
+                "http-errors": "1.8.1",
                 "iconv-lite": "0.4.24",
                 "unpipe": "1.0.0"
-            },
-            "dependencies": {
-                "iconv-lite": {
-                    "version": "0.4.24",
-                    "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
-                    "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
-                    "requires": {
-                        "safer-buffer": ">= 2.1.2 < 3"
-                    }
-                }
             }
         },
         "raw-loader": {
@@ -15765,6 +16035,18 @@
             "requires": {
                 "loader-utils": "^1.1.0",
                 "schema-utils": "^1.0.0"
+            },
+            "dependencies": {
+                "schema-utils": {
+                    "version": "1.0.0",
+                    "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+                    "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+                    "requires": {
+                        "ajv": "^6.1.0",
+                        "ajv-errors": "^1.0.0",
+                        "ajv-keywords": "^3.1.0"
+                    }
+                }
             }
         },
         "rc": {
@@ -15826,7 +16108,7 @@
         "react-base16-styling": {
             "version": "0.5.3",
             "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.5.3.tgz",
-            "integrity": "sha512-EPuchwVvYPSFFIjGpH0k6wM0HQsmJ0vCk7BSl5ryxMVFIWW4hX4Kksu4PNtxfgOxDebTLkJQ8iC7zwAql0eusg==",
+            "integrity": "sha1-OFjyTpxN2MvT9wLz901YHKKRcmk=",
             "requires": {
                 "base16": "^1.0.0",
                 "lodash.curry": "^4.0.1",
@@ -15871,13 +16153,6 @@
                 "d3-array": "^1.2.4",
                 "prop-types": "^15.7.2",
                 "warning": "^3.0.0"
-            },
-            "dependencies": {
-                "d3-array": {
-                    "version": "1.2.4",
-                    "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
-                    "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
-                }
             }
         },
         "react-datepicker": {
@@ -15895,7 +16170,7 @@
         "react-dock": {
             "version": "0.2.4",
             "resolved": "https://registry.npmjs.org/react-dock/-/react-dock-0.2.4.tgz",
-            "integrity": "sha512-ywUJPC/TIM9PO700skka0fH4aqbrH8RojUXejZFvjtqlc5KZ+xjHqFdo4A3j+dp+0NLFZ3Nai4xzcf3FUJ9BsQ==",
+            "integrity": "sha1-5yfcdVCztzEWY13LnA4E0Lev4Xw=",
             "requires": {
                 "lodash.debounce": "^3.1.1",
                 "prop-types": "^15.5.8"
@@ -15924,12 +16199,12 @@
             }
         },
         "react-draggable": {
-            "version": "4.4.5",
-            "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.5.tgz",
-            "integrity": "sha512-OMHzJdyJbYTZo4uQE393fHcqqPYsEtkjfMgvCHr6rejT+Ezn4OZbNyGH50vv+SunC1RMvwOTSWkEODQLzw1M9g==",
+            "version": "4.4.3",
+            "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.3.tgz",
+            "integrity": "sha512-jV4TE59MBuWm7gb6Ns3Q1mxX8Azffb7oTtDtBgFkxRvhDp38YAARmRplrj0+XGkhOJB5XziArX+4HUUABtyZ0w==",
             "requires": {
-                "clsx": "^1.1.1",
-                "prop-types": "^15.8.1"
+                "classnames": "^2.2.5",
+                "prop-types": "^15.6.0"
             }
         },
         "react-event-listener": {
@@ -15964,11 +16239,6 @@
                 "react-resizable": "^1.9.0"
             }
         },
-        "react-icons": {
-            "version": "4.4.0",
-            "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.4.0.tgz",
-            "integrity": "sha512-fSbvHeVYo/B5/L4VhB7sBA1i2tS8MkT0Hb9t2H1AVPkwGfVHLJCqyr2Py9dKMxsyM63Eng1GkdZfbWj+Fmv8Rg=="
-        },
         "react-image-lightbox-with-rotate": {
             "version": "5.1.1",
             "resolved": "https://registry.npmjs.org/react-image-lightbox-with-rotate/-/react-image-lightbox-with-rotate-5.1.1.tgz",
@@ -16017,9 +16287,9 @@
             },
             "dependencies": {
                 "@types/react": {
-                    "version": "17.0.45",
-                    "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.45.tgz",
-                    "integrity": "sha512-YfhQ22Lah2e3CHPsb93tRwIGNiSwkuz1/blk4e6QrWS0jQzCSNbGLtOEYhPg02W0yGTTmpajp7dCTbBAMN3qsg==",
+                    "version": "17.0.40",
+                    "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.40.tgz",
+                    "integrity": "sha512-UrXhD/JyLH+W70nNSufXqMZNuUD2cXHu6UjCllC6pmOQgBX4SGXOH8fjRka0O0Ee0HrFxapDD8Bwn81Kmiz6jQ==",
                     "optional": true,
                     "requires": {
                         "@types/prop-types": "*",
@@ -16028,23 +16298,28 @@
                     }
                 },
                 "@types/react-dom": {
-                    "version": "17.0.17",
-                    "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.17.tgz",
-                    "integrity": "sha512-VjnqEmqGnasQKV0CWLevqMTXBYG9GbwuE6x3VetERLh0cq2LTptFE73MrQi2S7GkKXCf2GgwItB/melLnxfnsg==",
+                    "version": "17.0.13",
+                    "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.13.tgz",
+                    "integrity": "sha512-wEP+B8hzvy6ORDv1QBhcQia4j6ea4SFIBttHYpXKPFZRviBvknq0FRh3VrIxeXUmsPkwuXVZrVGG7KUVONmXCQ==",
                     "optional": true,
                     "requires": {
-                        "@types/react": "^17"
+                        "@types/react": "*"
                     }
                 },
+                "acorn": {
+                    "version": "8.7.0",
+                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
+                    "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ=="
+                },
                 "core-js": {
-                    "version": "3.22.8",
-                    "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.22.8.tgz",
-                    "integrity": "sha512-UoGQ/cfzGYIuiq6Z7vWL1HfkE9U9IZ4Ub+0XSiJTCzvbZzgPA69oDF2f+lgJ6dFFLEdjW5O6svvoKzXX23xFkA=="
+                    "version": "3.21.1",
+                    "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.21.1.tgz",
+                    "integrity": "sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig=="
                 },
                 "csstype": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
+                    "version": "3.0.11",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
+                    "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==",
                     "optional": true
                 }
             }
@@ -16076,12 +16351,12 @@
             "integrity": "sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ=="
         },
         "react-modal": {
-            "version": "3.15.1",
-            "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.15.1.tgz",
-            "integrity": "sha512-duB9bxOaYg7Zt6TMFldIFxQRtSP+Dg3F1ZX3FXxSUn+3tZZ/9JCgeAQKDg7rhZSAqopq8TFRw3yIbnx77gyFTw==",
+            "version": "3.11.2",
+            "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.11.2.tgz",
+            "integrity": "sha512-o8gvvCOFaG1T7W6JUvsYjRjMVToLZgLIsi5kdhFIQCtHxDkA47LznX62j+l6YQkpXDbvQegsDyxe/+JJsFQN7w==",
             "requires": {
                 "exenv": "^1.2.0",
-                "prop-types": "^15.7.2",
+                "prop-types": "^15.5.10",
                 "react-lifecycles-compat": "^3.0.0",
                 "warning": "^4.0.3"
             },
@@ -16097,9 +16372,9 @@
             }
         },
         "react-onclickoutside": {
-            "version": "6.12.2",
-            "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.12.2.tgz",
-            "integrity": "sha512-NMXGa223OnsrGVp5dJHkuKxQ4czdLmXSp5jSV9OqiCky9LOpPATn3vLldc+q5fK3gKbEHvr7J1u0yhBh/xYkpA=="
+            "version": "6.12.1",
+            "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.12.1.tgz",
+            "integrity": "sha512-a5Q7CkWznBRUWPmocCvE8b6lEYw1s6+opp/60dCunhO+G6E4tDTO2Sd2jKE+leEnnrLAE2Wj5DlDHNqj5wPv1Q=="
         },
         "react-popper": {
             "version": "1.3.11",
@@ -16141,9 +16416,9 @@
             }
         },
         "react-redux": {
-            "version": "7.2.8",
-            "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.8.tgz",
-            "integrity": "sha512-6+uDjhs3PSIclqoCk0kd6iX74gzrGc3W5zcAjbrFgEdIjRSQObdIwfx80unTkVUYvbQ95Y8Av3OvFHq1w5EOUw==",
+            "version": "7.2.6",
+            "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz",
+            "integrity": "sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==",
             "requires": {
                 "@babel/runtime": "^7.15.4",
                 "@types/react-redux": "^7.1.20",
@@ -16153,6 +16428,14 @@
                 "react-is": "^17.0.2"
             },
             "dependencies": {
+                "@babel/runtime": {
+                    "version": "7.17.7",
+                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz",
+                    "integrity": "sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==",
+                    "requires": {
+                        "regenerator-runtime": "^0.13.4"
+                    }
+                },
                 "react-is": {
                     "version": "17.0.2",
                     "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
@@ -16161,9 +16444,9 @@
             }
         },
         "react-refresh-typescript": {
-            "version": "2.0.5",
-            "resolved": "https://registry.npmjs.org/react-refresh-typescript/-/react-refresh-typescript-2.0.5.tgz",
-            "integrity": "sha512-TlcxF9VMBBS8dHvMD+3dklgArXeIhjbtT7R09ycsXvhPPYmv70J67kiGT8mxe6/tYzkql9aCHaZAY6QYdutLdw=="
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/react-refresh-typescript/-/react-refresh-typescript-2.0.3.tgz",
+            "integrity": "sha512-CnR+UkpCFUIxW3u+KPxc6UhSO3M3aQxd9XCvgdry4a22LFwwITnjbANFUKtPGxA/Skw3PbZJrLiZ08UQnpXprQ=="
         },
         "react-resizable": {
             "version": "1.11.1",
@@ -16213,9 +16496,9 @@
                     }
                 },
                 "csstype": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
+                    "version": "3.0.11",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
+                    "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
                 },
                 "dom-helpers": {
                     "version": "5.2.1",
@@ -16252,7 +16535,7 @@
         "react-themeable": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/react-themeable/-/react-themeable-1.1.0.tgz",
-            "integrity": "sha512-kl5tQ8K+r9IdQXZd8WLa+xxYN04lLnJXRVhHfdgwsUJr/SlKJxIejoc9z9obEkx1mdqbTw1ry43fxEUwyD9u7w==",
+            "integrity": "sha1-fURm3ZsrX6dQWHJ4JenxUro3mg4=",
             "requires": {
                 "object-assign": "^3.0.0"
             },
@@ -16260,7 +16543,7 @@
                 "object-assign": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz",
-                    "integrity": "sha512-jHP15vXVGeVh1HuaA2wY6lxk+whK/x4KBG88VXeRma7CCun7iGD5qPc4eYykQ9sdQvg8jkwFKsSxHln2ybW3xQ=="
+                    "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I="
                 }
             }
         },
@@ -16299,7 +16582,7 @@
         "read-pkg": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
-            "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==",
+            "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
             "requires": {
                 "load-json-file": "^1.0.0",
                 "normalize-package-data": "^2.3.2",
@@ -16309,19 +16592,24 @@
                 "path-type": {
                     "version": "1.1.0",
                     "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
-                    "integrity": "sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==",
+                    "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
                     "requires": {
                         "graceful-fs": "^4.1.2",
                         "pify": "^2.0.0",
                         "pinkie-promise": "^2.0.0"
                     }
+                },
+                "pify": {
+                    "version": "2.3.0",
+                    "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+                    "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
                 }
             }
         },
         "read-pkg-up": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
-            "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==",
+            "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
             "requires": {
                 "find-up": "^1.0.0",
                 "read-pkg": "^1.0.0"
@@ -16330,7 +16618,7 @@
                 "find-up": {
                     "version": "1.1.2",
                     "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
-                    "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==",
+                    "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
                     "requires": {
                         "path-exists": "^2.0.0",
                         "pinkie-promise": "^2.0.0"
@@ -16339,7 +16627,7 @@
                 "path-exists": {
                     "version": "2.1.0",
                     "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
-                    "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==",
+                    "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
                     "requires": {
                         "pinkie-promise": "^2.0.0"
                     }
@@ -16385,12 +16673,12 @@
         "readline": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz",
-            "integrity": "sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg=="
+            "integrity": "sha1-xYDXfvLPyHUrEySYBg3JeTp6wBw="
         },
         "rechoir": {
             "version": "0.6.2",
             "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
-            "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==",
+            "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
             "requires": {
                 "resolve": "^1.1.6"
             }
@@ -16416,7 +16704,7 @@
         "redent": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
-            "integrity": "sha512-qtW5hKzGQZqKoh6JNSD+4lfitfPKGz42e6QwiRmPM5mmKtR0N41AbJRYu0xJi7nhOJ4WDgRkKvAk6tw4WIwR4g==",
+            "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
             "requires": {
                 "indent-string": "^2.1.0",
                 "strip-indent": "^1.0.1"
@@ -16425,20 +16713,20 @@
         "reduce-flatten": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz",
-            "integrity": "sha512-j5WfFJfc9CoXv/WbwVLHq74i/hdTUpy+iNC534LxczMRP67vJeK3V9JOdnL0N1cIRbn9mYhE2yVjvvKXDxvNXQ=="
+            "integrity": "sha1-JYx479FT3fk8tWEjf2EYTzaW4yc="
         },
         "redux": {
-            "version": "4.2.0",
-            "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz",
-            "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==",
+            "version": "4.1.2",
+            "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz",
+            "integrity": "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==",
             "requires": {
                 "@babel/runtime": "^7.9.2"
             }
         },
         "regenerator-runtime": {
-            "version": "0.13.9",
-            "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
-            "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
+            "version": "0.13.5",
+            "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
+            "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
         },
         "regex-not": {
             "version": "1.0.2",
@@ -16455,13 +16743,12 @@
             "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw=="
         },
         "regexp.prototype.flags": {
-            "version": "1.4.3",
-            "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
-            "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
+            "version": "1.4.1",
+            "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz",
+            "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==",
             "requires": {
                 "call-bind": "^1.0.2",
-                "define-properties": "^1.1.3",
-                "functions-have-names": "^1.2.2"
+                "define-properties": "^1.1.3"
             }
         },
         "registry-auth-token": {
@@ -16476,7 +16763,7 @@
         "registry-url": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
-            "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==",
+            "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
             "requires": {
                 "rc": "^1.0.1"
             }
@@ -16484,12 +16771,12 @@
         "relateurl": {
             "version": "0.2.7",
             "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
-            "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog=="
+            "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk="
         },
         "remove-trailing-separator": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
-            "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw=="
+            "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8="
         },
         "renderkid": {
             "version": "3.0.0",
@@ -16509,9 +16796,9 @@
                     "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
                 },
                 "dom-serializer": {
-                    "version": "1.4.1",
-                    "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
-                    "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
+                    "version": "1.3.2",
+                    "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
+                    "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
                     "requires": {
                         "domelementtype": "^2.0.1",
                         "domhandler": "^4.2.0",
@@ -16519,14 +16806,14 @@
                     }
                 },
                 "domelementtype": {
-                    "version": "2.3.0",
-                    "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
-                    "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
+                    "version": "2.2.0",
+                    "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
+                    "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
                 },
                 "domhandler": {
-                    "version": "4.3.1",
-                    "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
-                    "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+                    "version": "4.3.0",
+                    "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz",
+                    "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==",
                     "requires": {
                         "domelementtype": "^2.2.0"
                     }
@@ -16568,19 +16855,19 @@
             }
         },
         "repeat-element": {
-            "version": "1.1.4",
-            "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
-            "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ=="
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
+            "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g=="
         },
         "repeat-string": {
             "version": "1.6.1",
             "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
-            "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w=="
+            "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
         },
         "repeating": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
-            "integrity": "sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==",
+            "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
             "requires": {
                 "is-finite": "^1.0.0"
             }
@@ -16612,6 +16899,16 @@
                 "uuid": "^3.3.2"
             },
             "dependencies": {
+                "form-data": {
+                    "version": "2.3.3",
+                    "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.6",
+                        "mime-types": "^2.1.12"
+                    }
+                },
                 "tough-cookie": {
                     "version": "2.5.0",
                     "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
@@ -16634,6 +16931,14 @@
                 "tough-cookie": "^2.3.3"
             },
             "dependencies": {
+                "request-promise-core": {
+                    "version": "1.1.4",
+                    "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
+                    "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
+                    "requires": {
+                        "lodash": "^4.17.19"
+                    }
+                },
                 "tough-cookie": {
                     "version": "2.5.0",
                     "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
@@ -16646,20 +16951,21 @@
             }
         },
         "request-promise-core": {
-            "version": "1.1.4",
-            "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
-            "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz",
+            "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==",
+            "dev": true,
             "requires": {
-                "lodash": "^4.17.19"
+                "lodash": "^4.17.15"
             }
         },
         "request-promise-native": {
-            "version": "1.0.9",
-            "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz",
-            "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==",
+            "version": "1.0.8",
+            "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz",
+            "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==",
             "dev": true,
             "requires": {
-                "request-promise-core": "1.1.4",
+                "request-promise-core": "1.1.3",
                 "stealthy-require": "^1.1.1",
                 "tough-cookie": "^2.3.3"
             },
@@ -16684,7 +16990,7 @@
         "require-directory": {
             "version": "2.1.1",
             "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-            "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
+            "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
         },
         "require-from-string": {
             "version": "2.0.2",
@@ -16699,7 +17005,7 @@
         "require-package-name": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/require-package-name/-/require-package-name-2.0.1.tgz",
-            "integrity": "sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q=="
+            "integrity": "sha1-wR6XJ2tluOKSP3Xav1+y7ww4Qbk="
         },
         "require_optional": {
             "version": "1.0.1",
@@ -16713,14 +17019,14 @@
                 "resolve-from": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
-                    "integrity": "sha512-qpFcKaXsq8+oRoLilkwyc7zHGF5i9Q2/25NIgLQQ/+VVv9rU4qvr6nXVAw1DsnXJyQkZsR4Ytfbtg5ehfcUssQ=="
+                    "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c="
                 }
             }
         },
         "requires-port": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
-            "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+            "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
             "dev": true
         },
         "resize-observer-polyfill": {
@@ -16729,13 +17035,11 @@
             "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
         },
         "resolve": {
-            "version": "1.22.0",
-            "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
-            "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
+            "version": "1.17.0",
+            "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+            "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
             "requires": {
-                "is-core-module": "^2.8.1",
-                "path-parse": "^1.0.7",
-                "supports-preserve-symlinks-flag": "^1.0.0"
+                "path-parse": "^1.0.6"
             }
         },
         "resolve-alpn": {
@@ -16766,7 +17070,7 @@
         "resolve-url": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
-            "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg=="
+            "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="
         },
         "responselike": {
             "version": "2.0.0",
@@ -16791,18 +17095,18 @@
         "retry": {
             "version": "0.12.0",
             "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
-            "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
+            "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
             "dev": true
         },
         "reveal.js": {
-            "version": "4.3.1",
-            "resolved": "https://registry.npmjs.org/reveal.js/-/reveal.js-4.3.1.tgz",
-            "integrity": "sha512-1kyEnWeUkaCdBdX//XXq9dtBK95ppvIlSwlHelrP8/wrX6LcsYp4HT9WTFoFEOUBfVqkm8C2aHQ367o+UKfcxw=="
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/reveal.js/-/reveal.js-4.3.0.tgz",
+            "integrity": "sha512-KzZxJjj1gmxVNyplY6g9MiGwtDvZJiYkMvG1Qmaita7vWT/8eoTEK+RuIPLvxDeyxOtTz56u2wrOETVO79qL4A=="
         },
         "right-align": {
             "version": "0.1.3",
             "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
-            "integrity": "sha512-yqINtL/G7vs2v+dFIZmFUDbnVyFUJFKd6gK22Kgo6R4jfJGFtisKyncWDDULgjfqf4ASQuIQyjJ7XZ+3aWpsAg==",
+            "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
             "requires": {
                 "align-text": "^0.1.1"
             }
@@ -16816,9 +17120,9 @@
             }
         },
         "rope-sequence": {
-            "version": "1.3.3",
-            "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.3.tgz",
-            "integrity": "sha512-85aZYCxweiD5J8yTEbw+E6A27zSnLPNDL0WfPdw3YYodq7WjnTKo0q4dtyQ2gz23iPT8Q9CUyJtAaUNcTxRf5Q=="
+            "version": "1.3.2",
+            "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.2.tgz",
+            "integrity": "sha512-ku6MFrwEVSVmXLvy3dYph3LAMNS0890K7fabn+0YIRQ2T96T9F4gkFf0vf0WW0JUraNWwGRtInEpH7yO4tbQZg=="
         },
         "rtcpeerconnection-shim": {
             "version": "1.2.15",
@@ -16831,18 +17135,10 @@
         "run-queue": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
-            "integrity": "sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==",
+            "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
             "dev": true,
             "requires": {
                 "aproba": "^1.1.1"
-            },
-            "dependencies": {
-                "aproba": {
-                    "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-                    "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
-                    "dev": true
-                }
             }
         },
         "safe-buffer": {
@@ -16853,7 +17149,7 @@
         "safe-regex": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
-            "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==",
+            "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
             "requires": {
                 "ret": "~0.1.10"
             }
@@ -16896,12 +17192,6 @@
                 "semver": "^6.3.0"
             },
             "dependencies": {
-                "pify": {
-                    "version": "4.0.1",
-                    "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
-                    "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
-                    "dev": true
-                },
                 "semver": {
                     "version": "6.3.0",
                     "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
@@ -16928,26 +17218,16 @@
                 "object-assign": "^4.1.1"
             }
         },
-        "schema-utils": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
-            "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
-            "requires": {
-                "ajv": "^6.1.0",
-                "ajv-errors": "^1.0.0",
-                "ajv-keywords": "^3.1.0"
-            }
-        },
         "scss-loader": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/scss-loader/-/scss-loader-0.0.1.tgz",
-            "integrity": "sha512-SbT/smRJjkvvdHSEdAYAplosVkrtaSwwgUlnQCOuDS5sOKNjrS/eYCMvKeV6+YxK5cCOCsOJZd3vltrXatFp+g==",
+            "integrity": "sha1-6uAXueDzjBKlMtslwiC5Avs05nE=",
             "dev": true
         },
         "scss-tokenizer": {
             "version": "0.2.3",
             "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
-            "integrity": "sha512-dYE8LhncfBUar6POCxMTm0Ln+erjeczqEvCJib5/7XNkdw1FkUGgwMPY360FY0FgPWQxHWCx29Jl3oejyGLM9Q==",
+            "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=",
             "requires": {
                 "js-base64": "^2.1.8",
                 "source-map": "^0.4.2"
@@ -16956,7 +17236,7 @@
                 "source-map": {
                     "version": "0.4.4",
                     "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
-                    "integrity": "sha512-Y8nIfcb1s/7DcobUz1yOO1GSp7gyL+D9zLHDehT7iRESqGSxjJ448Sg7rvfgsRJCnKLdSl11uGf0s9X80cH0/A==",
+                    "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
                     "requires": {
                         "amdefine": ">=0.0.4"
                     }
@@ -16971,17 +17251,17 @@
         "section-iterator": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/section-iterator/-/section-iterator-2.0.0.tgz",
-            "integrity": "sha512-xvTNwcbeDayXotnV32zLb3duQsP+4XosHpb/F+tu6VzEZFmIjzPdNk6/O+QOOx5XTh08KL2ufdXeCO33p380pQ=="
+            "integrity": "sha1-v0RNev7rlK1Dw5rS+yYVFifMuio="
         },
         "select": {
             "version": "1.1.2",
             "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
-            "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
+            "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
         },
         "select-hose": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
-            "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==",
+            "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=",
             "dev": true
         },
         "selfsigned": {
@@ -16991,6 +17271,14 @@
             "dev": true,
             "requires": {
                 "node-forge": "^0.10.0"
+            },
+            "dependencies": {
+                "node-forge": {
+                    "version": "0.10.0",
+                    "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
+                    "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==",
+                    "dev": true
+                }
             }
         },
         "semver": {
@@ -17001,34 +17289,34 @@
         "semver-compare": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
-            "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="
+            "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w="
         },
         "semver-diff": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz",
-            "integrity": "sha512-gL8F8L4ORwsS0+iQ34yCYv///jsOq0ZL7WP55d1HnJ32o7tyFYEFQZQA22mrLIacZdU6xecaBBZ+uEiffGNyXw==",
+            "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=",
             "requires": {
                 "semver": "^5.0.3"
             }
         },
         "send": {
-            "version": "0.18.0",
-            "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
-            "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+            "version": "0.17.2",
+            "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz",
+            "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==",
             "requires": {
                 "debug": "2.6.9",
-                "depd": "2.0.0",
-                "destroy": "1.2.0",
+                "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": "2.0.0",
+                "http-errors": "1.8.1",
                 "mime": "1.6.0",
                 "ms": "2.1.3",
-                "on-finished": "2.4.1",
+                "on-finished": "~2.3.0",
                 "range-parser": "~1.2.1",
-                "statuses": "2.0.1"
+                "statuses": "~1.5.0"
             },
             "dependencies": {
                 "debug": {
@@ -17042,7 +17330,7 @@
                         "ms": {
                             "version": "2.0.0",
                             "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                            "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+                            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                         }
                     }
                 },
@@ -17054,13 +17342,10 @@
             }
         },
         "serialize-javascript": {
-            "version": "6.0.0",
-            "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
-            "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
-            "dev": true,
-            "requires": {
-                "randombytes": "^2.1.0"
-            }
+            "version": "1.9.1",
+            "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",
+            "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==",
+            "dev": true
         },
         "serializr": {
             "version": "1.5.4",
@@ -17070,7 +17355,7 @@
         "serve-index": {
             "version": "1.9.1",
             "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
-            "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==",
+            "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=",
             "dev": true,
             "requires": {
                 "accepts": "~1.3.4",
@@ -17091,16 +17376,10 @@
                         "ms": "2.0.0"
                     }
                 },
-                "depd": {
-                    "version": "1.1.2",
-                    "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
-                    "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
-                    "dev": true
-                },
                 "http-errors": {
                     "version": "1.6.3",
                     "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
-                    "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==",
+                    "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
                     "dev": true,
                     "requires": {
                         "depd": "~1.1.2",
@@ -17112,13 +17391,13 @@
                 "inherits": {
                     "version": "2.0.3",
                     "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-                    "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
+                    "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
                     "dev": true
                 },
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
                     "dev": true
                 },
                 "setprototypeof": {
@@ -17126,30 +17405,29 @@
                     "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
                     "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
                     "dev": true
-                },
-                "statuses": {
-                    "version": "1.5.0",
-                    "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
-                    "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
-                    "dev": true
                 }
             }
         },
         "serve-static": {
-            "version": "1.15.0",
-            "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
-            "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
+            "version": "1.14.2",
+            "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz",
+            "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==",
             "requires": {
                 "encodeurl": "~1.0.2",
                 "escape-html": "~1.0.3",
                 "parseurl": "~1.3.3",
-                "send": "0.18.0"
+                "send": "0.17.2"
             }
         },
         "set-blocking": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-            "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
+            "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
+        },
+        "set-immediate-shim": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
+            "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E="
         },
         "set-value": {
             "version": "2.0.1",
@@ -17165,17 +17443,22 @@
                 "extend-shallow": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-                    "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+                    "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
                     "requires": {
                         "is-extendable": "^0.1.0"
                     }
+                },
+                "is-extendable": {
+                    "version": "0.1.1",
+                    "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+                    "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
                 }
             }
         },
         "setimmediate": {
             "version": "1.0.5",
             "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
-            "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
+            "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
         },
         "setprototypeof": {
             "version": "1.2.0",
@@ -17211,89 +17494,29 @@
                 "tunnel-agent": "^0.6.0"
             },
             "dependencies": {
-                "ansi-regex": {
-                    "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-                    "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="
-                },
-                "aproba": {
-                    "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-                    "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
-                },
-                "are-we-there-yet": {
-                    "version": "1.1.7",
-                    "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
-                    "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
-                    "requires": {
-                        "delegates": "^1.0.0",
-                        "readable-stream": "^2.0.6"
-                    }
-                },
-                "chownr": {
-                    "version": "1.1.4",
-                    "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
-                    "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
-                },
-                "detect-libc": {
-                    "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
-                    "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="
-                },
-                "gauge": {
-                    "version": "2.7.4",
-                    "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
-                    "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==",
-                    "requires": {
-                        "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"
-                    }
-                },
-                "is-fullwidth-code-point": {
-                    "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
-                    "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
-                    "requires": {
-                        "number-is-nan": "^1.0.0"
-                    }
-                },
-                "mkdirp": {
-                    "version": "0.5.6",
-                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
-                    "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+                "fs-minipass": {
+                    "version": "2.1.0",
+                    "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+                    "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
                     "requires": {
-                        "minimist": "^1.2.6"
+                        "minipass": "^3.0.0"
                     }
                 },
-                "npmlog": {
-                    "version": "4.1.2",
-                    "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
-                    "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+                "minipass": {
+                    "version": "3.1.1",
+                    "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.1.tgz",
+                    "integrity": "sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w==",
                     "requires": {
-                        "are-we-there-yet": "~1.1.2",
-                        "console-control-strings": "~1.1.0",
-                        "gauge": "~2.7.3",
-                        "set-blocking": "~2.0.0"
+                        "yallist": "^4.0.0"
                     }
                 },
-                "readable-stream": {
-                    "version": "2.3.7",
-                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
-                    "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+                "minizlib": {
+                    "version": "2.1.0",
+                    "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.0.tgz",
+                    "integrity": "sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA==",
                     "requires": {
-                        "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"
+                        "minipass": "^3.0.0",
+                        "yallist": "^4.0.0"
                     }
                 },
                 "semver": {
@@ -17301,43 +17524,30 @@
                     "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
                     "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
                 },
-                "string-width": {
-                    "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-                    "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
-                    "requires": {
-                        "code-point-at": "^1.0.0",
-                        "is-fullwidth-code-point": "^1.0.0",
-                        "strip-ansi": "^3.0.0"
-                    }
-                },
-                "strip-ansi": {
-                    "version": "3.0.1",
-                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-                    "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
-                    "requires": {
-                        "ansi-regex": "^2.0.0"
-                    }
-                },
                 "tar": {
-                    "version": "5.0.11",
-                    "resolved": "https://registry.npmjs.org/tar/-/tar-5.0.11.tgz",
-                    "integrity": "sha512-E6q48d5y4XSCD+Xmwc0yc8lXuyDK38E0FB8N4S/drQRtXOMUhfhDxbB0xr2KKDhNfO51CFmoa6Oz00nAkWsjnA==",
-                    "requires": {
-                        "chownr": "^1.1.4",
-                        "fs-minipass": "^2.1.0",
-                        "minipass": "^3.1.3",
-                        "minizlib": "^2.1.2",
-                        "mkdirp": "^0.5.5",
+                    "version": "5.0.5",
+                    "resolved": "https://registry.npmjs.org/tar/-/tar-5.0.5.tgz",
+                    "integrity": "sha512-MNIgJddrV2TkuwChwcSNds/5E9VijOiw7kAc1y5hTNJoLDSuIyid2QtLYiCYNnICebpuvjhPQZsXwUL0O3l7OQ==",
+                    "requires": {
+                        "chownr": "^1.1.3",
+                        "fs-minipass": "^2.0.0",
+                        "minipass": "^3.0.0",
+                        "minizlib": "^2.1.0",
+                        "mkdirp": "^0.5.0",
                         "yallist": "^4.0.0"
                     }
+                },
+                "yallist": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+                    "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
                 }
             }
         },
         "shebang-command": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
-            "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
+            "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
             "requires": {
                 "shebang-regex": "^1.0.0"
             }
@@ -17345,7 +17555,7 @@
         "shebang-regex": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
-            "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ=="
+            "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
         },
         "shelljs": {
             "version": "0.8.5",
@@ -17365,6 +17575,13 @@
                 "call-bind": "^1.0.0",
                 "get-intrinsic": "^1.0.2",
                 "object-inspect": "^1.9.0"
+            },
+            "dependencies": {
+                "object-inspect": {
+                    "version": "1.12.0",
+                    "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
+                    "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g=="
+                }
             }
         },
         "sift": {
@@ -17373,24 +17590,24 @@
             "integrity": "sha512-+gxdEOMA2J+AI+fVsCqeNn7Tgx3M9ZN9jdi95939l1IJ8cZsqS8sqpJyOkic2SJk+1+98Uwryt/gL6XDaV+UZA=="
         },
         "signal-exit": {
-            "version": "3.0.7",
-            "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
-            "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
+            "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
         },
         "simple-assign": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/simple-assign/-/simple-assign-0.1.0.tgz",
-            "integrity": "sha512-otdSSQzuVsmDoe5MnSm4ZgHd5sl0ak6A1CTjW1R/DUHQ8xoZuU1NUzf9x6n9Dvp3nxpvW51WNMQ/7rQ9432xDg=="
+            "integrity": "sha1-F/0wZqXz13OPUDIbsPFMooHMS6o="
         },
         "simple-concat": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
-            "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
+            "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
         },
         "simple-get": {
-            "version": "3.1.1",
-            "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
-            "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
+            "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
             "requires": {
                 "decompress-response": "^4.2.0",
                 "once": "^1.3.1",
@@ -17400,7 +17617,7 @@
         "simple-swizzle": {
             "version": "0.2.2",
             "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
-            "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
+            "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
             "requires": {
                 "is-arrayish": "^0.3.1"
             },
@@ -17412,16 +17629,21 @@
                 }
             }
         },
+        "skmeans": {
+            "version": "0.9.7",
+            "resolved": "https://registry.npmjs.org/skmeans/-/skmeans-0.9.7.tgz",
+            "integrity": "sha512-hNj1/oZ7ygsfmPZ7ZfN5MUBRoGg1gtpnImuJBgLO0ljQ67DtJuiQaiYdS4lUA6s0KCwnPhGivtC/WRwIZLkHyg=="
+        },
         "slash": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
-            "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==",
+            "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
             "dev": true
         },
         "sliced": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz",
-            "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA=="
+            "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E="
         },
         "snapdragon": {
             "version": "0.8.2",
@@ -17449,7 +17671,7 @@
                 "define-property": {
                     "version": "0.2.5",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-                    "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+                    "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
                     "requires": {
                         "is-descriptor": "^0.1.0"
                     }
@@ -17457,15 +17679,20 @@
                 "extend-shallow": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-                    "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+                    "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
                     "requires": {
                         "is-extendable": "^0.1.0"
                     }
                 },
+                "is-extendable": {
+                    "version": "0.1.1",
+                    "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+                    "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
+                },
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                 }
             }
         },
@@ -17482,7 +17709,7 @@
                 "define-property": {
                     "version": "1.0.0",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-                    "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+                    "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
                     "requires": {
                         "is-descriptor": "^1.0.0"
                     }
@@ -17531,7 +17758,7 @@
                 "kind-of": {
                     "version": "3.2.2",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                    "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+                    "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -17595,12 +17822,12 @@
                 "isarray": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
-                    "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
+                    "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
                 },
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                 },
                 "socket.io-parser": {
                     "version": "3.3.2",
@@ -17627,7 +17854,7 @@
                 "component-emitter": {
                     "version": "1.2.1",
                     "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
-                    "integrity": "sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA=="
+                    "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
                 },
                 "debug": {
                     "version": "4.1.1",
@@ -17640,7 +17867,7 @@
                 "isarray": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
-                    "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
+                    "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
                 }
             }
         },
@@ -17664,13 +17891,13 @@
             }
         },
         "sockjs-client": {
-            "version": "1.6.1",
-            "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.1.tgz",
-            "integrity": "sha512-2g0tjOR+fRs0amxENLi/q5TiJTqY+WXFOzb5UwXndlK6TO3U/mirZznpx6w34HVMoc3g7cY24yC/ZMIYnDlfkw==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.0.tgz",
+            "integrity": "sha512-qVHJlyfdHFht3eBFZdKEXKTlb7I4IV41xnVNo8yUKA1UHcPJwgW2SvTq9LhnjjCywSkSK7c/e4nghU0GOoMCRQ==",
             "dev": true,
             "requires": {
                 "debug": "^3.2.7",
-                "eventsource": "^2.0.2",
+                "eventsource": "^1.1.0",
                 "faye-websocket": "^0.11.4",
                 "inherits": "^2.0.4",
                 "url-parse": "^1.5.10"
@@ -17698,19 +17925,16 @@
             },
             "dependencies": {
                 "node-fetch": {
-                    "version": "2.6.7",
-                    "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
-                    "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
-                    "requires": {
-                        "whatwg-url": "^5.0.0"
-                    }
+                    "version": "2.6.0",
+                    "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
+                    "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
                 }
             }
         },
         "source-map": {
             "version": "0.5.7",
             "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-            "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="
+            "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
         },
         "source-map-resolve": {
             "version": "0.5.3",
@@ -17725,9 +17949,10 @@
             }
         },
         "source-map-support": {
-            "version": "0.5.21",
-            "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
-            "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+            "version": "0.5.19",
+            "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+            "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+            "dev": true,
             "requires": {
                 "buffer-from": "^1.0.0",
                 "source-map": "^0.6.0"
@@ -17736,28 +17961,29 @@
                 "source-map": {
                     "version": "0.6.1",
                     "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-                    "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+                    "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+                    "dev": true
                 }
             }
         },
         "source-map-url": {
-            "version": "0.4.1",
-            "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
-            "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw=="
+            "version": "0.4.0",
+            "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
+            "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM="
         },
         "sparse-bitfield": {
             "version": "3.0.3",
             "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
-            "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
+            "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
             "optional": true,
             "requires": {
                 "memory-pager": "^1.0.2"
             }
         },
         "spdx-correct": {
-            "version": "3.1.1",
-            "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
-            "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+            "version": "3.1.0",
+            "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.0"
@@ -17769,18 +17995,18 @@
             "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A=="
         },
         "spdx-expression-parse": {
-            "version": "3.0.1",
-            "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
-            "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+            "version": "3.0.0",
+            "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.1.0",
                 "spdx-license-ids": "^3.0.0"
             }
         },
         "spdx-license-ids": {
-            "version": "3.0.11",
-            "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz",
-            "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g=="
+            "version": "3.0.5",
+            "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
+            "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q=="
         },
         "spdy": {
             "version": "4.0.2",
@@ -17851,7 +18077,7 @@
         "split-skip": {
             "version": "0.0.2",
             "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.2.tgz",
-            "integrity": "sha512-weHOi8BolsDnGIwhhWHbA+wKSuSpvWwjRrdj8SdbIIis2vSwOE37CQP8x3EleuzxanUr3AK8BdUy4MkiOULPZg=="
+            "integrity": "sha1-2J2Iu9L3Pka1FYqjcKVhIk6A1GE="
         },
         "split-string": {
             "version": "3.1.0",
@@ -17864,12 +18090,12 @@
         "sprintf-js": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-            "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
+            "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
         },
         "sshpk": {
-            "version": "1.17.0",
-            "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
-            "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==",
+            "version": "1.16.1",
+            "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
+            "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
             "requires": {
                 "asn1": "~0.2.3",
                 "assert-plus": "^1.0.0",
@@ -17916,7 +18142,7 @@
                 "define-property": {
                     "version": "0.2.5",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-                    "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+                    "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
                     "requires": {
                         "is-descriptor": "^0.1.0"
                     }
@@ -17924,9 +18150,9 @@
             }
         },
         "statuses": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
-            "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
+            "version": "1.5.0",
+            "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+            "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
         },
         "stdout-stream": {
             "version": "1.4.1",
@@ -17995,7 +18221,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                 }
             }
         },
@@ -18025,23 +18251,41 @@
             "integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg=="
         },
         "string.prototype.trimend": {
-            "version": "1.0.5",
-            "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz",
-            "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==",
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
+            "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
             "requires": {
-                "call-bind": "^1.0.2",
-                "define-properties": "^1.1.4",
-                "es-abstract": "^1.19.5"
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.5"
+            }
+        },
+        "string.prototype.trimleft": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz",
+            "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==",
+            "requires": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.5",
+                "string.prototype.trimstart": "^1.0.0"
+            }
+        },
+        "string.prototype.trimright": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz",
+            "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==",
+            "requires": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.5",
+                "string.prototype.trimend": "^1.0.0"
             }
         },
         "string.prototype.trimstart": {
-            "version": "1.0.5",
-            "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz",
-            "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==",
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
+            "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
             "requires": {
-                "call-bind": "^1.0.2",
-                "define-properties": "^1.1.4",
-                "es-abstract": "^1.19.5"
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.5"
             }
         },
         "string_decoder": {
@@ -18061,15 +18305,10 @@
                 "unpack-string": "0.0.2"
             },
             "dependencies": {
-                "commander": {
-                    "version": "2.20.3",
-                    "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
-                    "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
-                },
                 "magicli": {
                     "version": "0.0.5",
                     "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz",
-                    "integrity": "sha512-wZbMtnl2v1b+Jp3xlqA9FU/O4I6YhGXR8xSY/eU2+gDAvut/F+W3gl4qs61iL4LELC7jqSAE6aAD5668EbmQHA==",
+                    "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=",
                     "requires": {
                         "commander": "^2.9.0",
                         "get-stdin": "^5.0.1",
@@ -18116,7 +18355,7 @@
                 "get-stdin": {
                     "version": "4.0.1",
                     "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
-                    "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw=="
+                    "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4="
                 }
             }
         },
@@ -18133,6 +18372,19 @@
             "requires": {
                 "loader-utils": "^1.1.0",
                 "schema-utils": "^1.0.0"
+            },
+            "dependencies": {
+                "schema-utils": {
+                    "version": "1.0.0",
+                    "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+                    "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+                    "dev": true,
+                    "requires": {
+                        "ajv": "^6.1.0",
+                        "ajv-errors": "^1.0.0",
+                        "ajv-keywords": "^3.1.0"
+                    }
+                }
             }
         },
         "styled-components": {
@@ -18153,27 +18405,12 @@
                 "stylis": "^3.5.0",
                 "stylis-rule-sheet": "^0.0.10",
                 "supports-color": "^5.5.0"
-            },
-            "dependencies": {
-                "@emotion/is-prop-valid": {
-                    "version": "0.8.8",
-                    "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
-                    "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
-                    "requires": {
-                        "@emotion/memoize": "0.7.4"
-                    }
-                },
-                "stylis": {
-                    "version": "3.5.4",
-                    "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz",
-                    "integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q=="
-                }
             }
         },
         "stylis": {
-            "version": "4.0.13",
-            "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
-            "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
+            "version": "3.5.4",
+            "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz",
+            "integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q=="
         },
         "stylis-rule-sheet": {
             "version": "0.0.10",
@@ -18181,9 +18418,9 @@
             "integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw=="
         },
         "supercluster": {
-            "version": "7.1.5",
-            "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.5.tgz",
-            "integrity": "sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==",
+            "version": "7.1.4",
+            "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.4.tgz",
+            "integrity": "sha512-GhKkRM1jMR6WUwGPw05fs66pOFWhf59lXq+Q3J3SxPvhNcmgOtLRV6aVQPMRsmXdpaeFJGivt+t7QXUPL3ff4g==",
             "requires": {
                 "kdbush": "^3.0.0"
             }
@@ -18196,11 +18433,6 @@
                 "has-flag": "^3.0.0"
             }
         },
-        "supports-preserve-symlinks-flag": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
-            "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
-        },
         "symbol-observable": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
@@ -18225,9 +18457,10 @@
             }
         },
         "tapable": {
-            "version": "2.2.1",
-            "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
-            "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+            "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+            "dev": true
         },
         "tar": {
             "version": "6.1.11",
@@ -18242,37 +18475,40 @@
                 "yallist": "^4.0.0"
             },
             "dependencies": {
+                "chownr": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+                    "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
+                },
                 "mkdirp": {
                     "version": "1.0.4",
                     "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
                     "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
+                },
+                "yallist": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+                    "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
                 }
             }
         },
         "tar-fs": {
-            "version": "2.1.1",
-            "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
-            "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz",
+            "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==",
             "requires": {
                 "chownr": "^1.1.1",
                 "mkdirp-classic": "^0.5.2",
                 "pump": "^3.0.0",
-                "tar-stream": "^2.1.4"
-            },
-            "dependencies": {
-                "chownr": {
-                    "version": "1.1.4",
-                    "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
-                    "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
-                }
+                "tar-stream": "^2.0.0"
             }
         },
         "tar-stream": {
-            "version": "2.2.0",
-            "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
-            "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz",
+            "integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==",
             "requires": {
-                "bl": "^4.0.3",
+                "bl": "^4.0.1",
                 "end-of-stream": "^1.4.1",
                 "fs-constants": "^1.0.0",
                 "inherits": "^2.0.3",
@@ -18302,36 +18538,80 @@
             }
         },
         "terser": {
-            "version": "5.14.0",
-            "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.0.tgz",
-            "integrity": "sha512-JC6qfIEkPBd9j1SMO3Pfn+A6w2kQV54tv+ABQLgZr7dA3k/DL/OBoYSWxzVpZev3J+bUHXfr55L8Mox7AaNo6g==",
+            "version": "5.12.1",
+            "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz",
+            "integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==",
+            "dev": true,
             "requires": {
-                "@jridgewell/source-map": "^0.3.2",
                 "acorn": "^8.5.0",
                 "commander": "^2.20.0",
+                "source-map": "~0.7.2",
                 "source-map-support": "~0.5.20"
             },
             "dependencies": {
-                "commander": {
-                    "version": "2.20.3",
-                    "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
-                    "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+                "acorn": {
+                    "version": "8.7.0",
+                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
+                    "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
+                    "dev": true
+                },
+                "source-map": {
+                    "version": "0.7.3",
+                    "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+                    "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+                    "dev": true
+                },
+                "source-map-support": {
+                    "version": "0.5.21",
+                    "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+                    "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+                    "dev": true,
+                    "requires": {
+                        "buffer-from": "^1.0.0",
+                        "source-map": "^0.6.0"
+                    },
+                    "dependencies": {
+                        "source-map": {
+                            "version": "0.6.1",
+                            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+                            "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+                            "dev": true
+                        }
+                    }
                 }
             }
         },
         "terser-webpack-plugin": {
-            "version": "5.3.3",
-            "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz",
-            "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==",
+            "version": "5.3.1",
+            "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz",
+            "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==",
             "dev": true,
             "requires": {
-                "@jridgewell/trace-mapping": "^0.3.7",
                 "jest-worker": "^27.4.5",
                 "schema-utils": "^3.1.1",
                 "serialize-javascript": "^6.0.0",
+                "source-map": "^0.6.1",
                 "terser": "^5.7.2"
             },
             "dependencies": {
+                "ajv": {
+                    "version": "6.12.6",
+                    "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+                    "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+                    "dev": true,
+                    "requires": {
+                        "fast-deep-equal": "^3.1.1",
+                        "fast-json-stable-stringify": "^2.0.0",
+                        "json-schema-traverse": "^0.4.1",
+                        "uri-js": "^4.2.2"
+                    }
+                },
+                "ajv-keywords": {
+                    "version": "3.5.2",
+                    "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+                    "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+                    "dev": true
+                },
                 "schema-utils": {
                     "version": "3.1.1",
                     "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
@@ -18342,6 +18622,21 @@
                         "ajv": "^6.12.5",
                         "ajv-keywords": "^3.5.2"
                     }
+                },
+                "serialize-javascript": {
+                    "version": "6.0.0",
+                    "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+                    "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+                    "dev": true,
+                    "requires": {
+                        "randombytes": "^2.1.0"
+                    }
+                },
+                "source-map": {
+                    "version": "0.6.1",
+                    "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+                    "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+                    "dev": true
                 }
             }
         },
@@ -18449,7 +18744,7 @@
                 "kind-of": {
                     "version": "3.2.2",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                    "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+                    "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -18497,7 +18792,7 @@
                 "nopt": {
                     "version": "1.0.10",
                     "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
-                    "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
+                    "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
                     "requires": {
                         "abbrev": "1"
                     }
@@ -18515,9 +18810,13 @@
             }
         },
         "tr46": {
-            "version": "0.0.3",
-            "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
-            "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
+            "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+            "dev": true,
+            "requires": {
+                "punycode": "^2.1.0"
+            }
         },
         "translate-google-api": {
             "version": "1.0.4",
@@ -18534,11 +18833,6 @@
                     "requires": {
                         "follow-redirects": "^1.10.0"
                     }
-                },
-                "follow-redirects": {
-                    "version": "1.15.1",
-                    "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
-                    "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA=="
                 }
             }
         },
@@ -18582,25 +18876,6 @@
                 "loader-utils": "^1.0.2",
                 "micromatch": "^3.1.4",
                 "semver": "^5.0.1"
-            },
-            "dependencies": {
-                "enhanced-resolve": {
-                    "version": "4.5.0",
-                    "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz",
-                    "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==",
-                    "dev": true,
-                    "requires": {
-                        "graceful-fs": "^4.1.2",
-                        "memory-fs": "^0.5.0",
-                        "tapable": "^1.0.0"
-                    }
-                },
-                "tapable": {
-                    "version": "1.1.3",
-                    "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
-                    "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
-                    "dev": true
-                }
             }
         },
         "ts-node": {
@@ -18622,7 +18897,7 @@
                 "arrify": {
                     "version": "1.0.1",
                     "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
-                    "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==",
+                    "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
                     "dev": true
                 }
             }
@@ -18808,9 +19083,10 @@
             }
         },
         "tslib": {
-            "version": "2.4.0",
-            "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
-            "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
+            "version": "1.11.1",
+            "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
+            "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==",
+            "dev": true
         },
         "tslint": {
             "version": "5.20.1",
@@ -18833,29 +19109,11 @@
                 "tsutils": "^2.29.0"
             },
             "dependencies": {
-                "builtin-modules": {
-                    "version": "1.1.1",
-                    "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
-                    "integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==",
-                    "dev": true
-                },
-                "commander": {
-                    "version": "2.20.3",
-                    "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
-                    "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
-                    "dev": true
-                },
                 "diff": {
                     "version": "4.0.2",
                     "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
                     "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
                     "dev": true
-                },
-                "tslib": {
-                    "version": "1.14.1",
-                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
-                    "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
-                    "dev": true
                 }
             }
         },
@@ -18895,14 +19153,6 @@
             "dev": true,
             "requires": {
                 "tslib": "^1.8.1"
-            },
-            "dependencies": {
-                "tslib": {
-                    "version": "1.14.1",
-                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
-                    "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
-                    "dev": true
-                }
             }
         },
         "tunnel-agent": {
@@ -18918,12 +19168,6 @@
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
             "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
         },
-        "type": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
-            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
-            "dev": true
-        },
         "type-check": {
             "version": "0.3.2",
             "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
@@ -18958,9 +19202,9 @@
             "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
         },
         "typescript": {
-            "version": "4.7.3",
-            "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz",
-            "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==",
+            "version": "4.6.2",
+            "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz",
+            "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==",
             "dev": true
         },
         "typescript-collections": {
@@ -18982,11 +19226,6 @@
                 "vscode-uri": "^1.0.5"
             },
             "dependencies": {
-                "commander": {
-                    "version": "2.20.3",
-                    "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
-                    "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
-                },
                 "fs-extra": {
                     "version": "7.0.1",
                     "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
@@ -19000,7 +19239,7 @@
                 "jsonfile": {
                     "version": "4.0.0",
                     "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
-                    "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+                    "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
                     "requires": {
                         "graceful-fs": "^4.1.6"
                     }
@@ -19013,9 +19252,9 @@
             "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0="
         },
         "ua-parser-js": {
-            "version": "0.7.31",
-            "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz",
-            "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ=="
+            "version": "0.7.21",
+            "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz",
+            "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ=="
         },
         "uglify-js": {
             "version": "2.8.29",
@@ -19030,12 +19269,12 @@
                 "camelcase": {
                     "version": "1.2.1",
                     "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
-                    "integrity": "sha512-wzLkDa4K/mzI1OSITC+DUyjgIl/ETNHE9QvYgy6J6Jvqyyz4C0Xfd+lQhb19sX2jMpZV4IssUn0VDVmglV+s4g=="
+                    "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk="
                 },
                 "cliui": {
                     "version": "2.1.0",
                     "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
-                    "integrity": "sha512-GIOYRizG+TGoc7Wgc1LiOTLare95R3mzKgoln+Q/lE4ceiYH19gUpl0l0Ffq4lJDEf3FxujMe6IBfOCs7pfqNA==",
+                    "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
                     "requires": {
                         "center-align": "^0.1.1",
                         "right-align": "^0.1.1",
@@ -19070,19 +19309,26 @@
             }
         },
         "uid2": {
-            "version": "0.0.4",
-            "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz",
-            "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA=="
+            "version": "0.0.3",
+            "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz",
+            "integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I="
         },
         "unbox-primitive": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
-            "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
+            "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==",
             "requires": {
-                "call-bind": "^1.0.2",
-                "has-bigints": "^1.0.2",
-                "has-symbols": "^1.0.3",
+                "function-bind": "^1.1.1",
+                "has-bigints": "^1.0.1",
+                "has-symbols": "^1.0.2",
                 "which-boxed-primitive": "^1.0.2"
+            },
+            "dependencies": {
+                "has-symbols": {
+                    "version": "1.0.3",
+                    "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+                    "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
+                }
             }
         },
         "unbzip2-stream": {
@@ -19095,14 +19341,32 @@
             }
         },
         "undefsafe": {
-            "version": "2.0.5",
-            "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
-            "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz",
+            "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==",
+            "requires": {
+                "debug": "^2.2.0"
+            },
+            "dependencies": {
+                "debug": {
+                    "version": "2.6.9",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+                    "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+                    "requires": {
+                        "ms": "2.0.0"
+                    }
+                },
+                "ms": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                }
+            }
         },
         "underscore": {
-            "version": "1.13.4",
-            "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz",
-            "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ=="
+            "version": "1.10.2",
+            "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz",
+            "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg=="
         },
         "unicode-trie": {
             "version": "0.3.1",
@@ -19116,7 +19380,7 @@
                 "pako": {
                     "version": "0.2.9",
                     "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
-                    "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA=="
+                    "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU="
                 }
             }
         },
@@ -19129,8 +19393,21 @@
                 "get-value": "^2.0.6",
                 "is-extendable": "^0.1.1",
                 "set-value": "^2.0.1"
+            },
+            "dependencies": {
+                "is-extendable": {
+                    "version": "0.1.1",
+                    "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+                    "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
+                }
             }
         },
+        "uniq": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
+            "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
+            "dev": true
+        },
         "unique-filename": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
@@ -19189,7 +19466,7 @@
                 "has-value": {
                     "version": "0.3.1",
                     "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
-                    "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==",
+                    "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
                     "requires": {
                         "get-value": "^2.0.3",
                         "has-values": "^0.1.4",
@@ -19199,7 +19476,7 @@
                         "isobject": {
                             "version": "2.1.0",
                             "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
-                            "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
+                            "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
                             "requires": {
                                 "isarray": "1.0.0"
                             }
@@ -19209,7 +19486,7 @@
                 "has-values": {
                     "version": "0.1.4",
                     "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
-                    "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ=="
+                    "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E="
                 }
             }
         },
@@ -19256,9 +19533,9 @@
             }
         },
         "uri-js": {
-            "version": "4.4.1",
-            "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
-            "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+            "version": "4.2.2",
+            "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+            "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
             "requires": {
                 "punycode": "^2.1.0"
             }
@@ -19281,7 +19558,7 @@
                 "punycode": {
                     "version": "1.3.2",
                     "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
-                    "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==",
+                    "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
                     "dev": true
                 }
             }
@@ -19297,9 +19574,19 @@
             },
             "dependencies": {
                 "mime": {
-                    "version": "2.6.0",
-                    "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
-                    "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="
+                    "version": "2.4.4",
+                    "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
+                    "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA=="
+                },
+                "schema-utils": {
+                    "version": "1.0.0",
+                    "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+                    "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+                    "requires": {
+                        "ajv": "^6.1.0",
+                        "ajv-errors": "^1.0.0",
+                        "ajv-keywords": "^3.1.0"
+                    }
                 }
             }
         },
@@ -19337,6 +19624,13 @@
             "integrity": "sha512-7/hqDrWa0iMnCoET9W1T07EmD4Eg/Wmoj/X8TGBc++ECRK4m5yTsjP4O6s0yagbxfqIOuUkIxe2/sA+VR2GxZA==",
             "requires": {
                 "fast-deep-equal": "^3.1.3"
+            },
+            "dependencies": {
+                "fast-deep-equal": {
+                    "version": "3.1.3",
+                    "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+                    "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+                }
             }
         },
         "use-memo-one": {
@@ -19419,13 +19713,6 @@
                 "assert-plus": "^1.0.0",
                 "core-util-is": "1.0.2",
                 "extsprintf": "^1.2.0"
-            },
-            "dependencies": {
-                "core-util-is": {
-                    "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-                    "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="
-                }
             }
         },
         "void-elements": {
@@ -19434,9 +19721,9 @@
             "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w="
         },
         "vscode-jsonrpc": {
-            "version": "8.0.1",
-            "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.1.tgz",
-            "integrity": "sha512-N/WKvghIajmEvXpatSzvTvOIz61ZSmOSa4BRA4pTLi+1+jozquQKP/MkaylP9iB68k73Oua1feLQvH3xQuigiQ=="
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz",
+            "integrity": "sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A=="
         },
         "vscode-languageserver": {
             "version": "5.3.0-next.10",
@@ -19448,18 +19735,18 @@
             }
         },
         "vscode-languageserver-protocol": {
-            "version": "3.17.1",
-            "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.1.tgz",
-            "integrity": "sha512-BNlAYgQoYwlSgDLJhSG+DeA8G1JyECqRzM2YO6tMmMji3Ad9Mw6AW7vnZMti90qlAKb0LqAlJfSVGEdqMMNzKg==",
+            "version": "3.15.3",
+            "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz",
+            "integrity": "sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==",
             "requires": {
-                "vscode-jsonrpc": "8.0.1",
-                "vscode-languageserver-types": "3.17.1"
+                "vscode-jsonrpc": "^5.0.1",
+                "vscode-languageserver-types": "3.15.1"
             }
         },
         "vscode-languageserver-types": {
-            "version": "3.17.1",
-            "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz",
-            "integrity": "sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ=="
+            "version": "3.15.1",
+            "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz",
+            "integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ=="
         },
         "vscode-textbuffer": {
             "version": "1.0.0",
@@ -19472,9 +19759,9 @@
             "integrity": "sha512-obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ=="
         },
         "vue-template-compiler": {
-            "version": "2.6.14",
-            "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz",
-            "integrity": "sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g==",
+            "version": "2.6.11",
+            "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz",
+            "integrity": "sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA==",
             "requires": {
                 "de-indent": "^1.0.2",
                 "he": "^1.1.0"
@@ -19503,14 +19790,6 @@
                 "domexception": "^1.0.1",
                 "webidl-conversions": "^4.0.2",
                 "xml-name-validator": "^3.0.0"
-            },
-            "dependencies": {
-                "webidl-conversions": {
-                    "version": "4.0.2",
-                    "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
-                    "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
-                    "dev": true
-                }
             }
         },
         "walkdir": {
@@ -19527,9 +19806,9 @@
             }
         },
         "watchpack": {
-            "version": "2.4.0",
-            "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
-            "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
+            "version": "2.3.1",
+            "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz",
+            "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==",
             "dev": true,
             "requires": {
                 "glob-to-regexp": "^0.4.1",
@@ -19554,19 +19833,20 @@
             }
         },
         "web-streams-polyfill": {
-            "version": "3.2.1",
-            "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
-            "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q=="
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz",
+            "integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA=="
         },
         "webidl-conversions": {
-            "version": "3.0.1",
-            "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
-            "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
+            "version": "4.0.2",
+            "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+            "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
+            "dev": true
         },
         "webpack": {
-            "version": "5.73.0",
-            "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz",
-            "integrity": "sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==",
+            "version": "5.69.1",
+            "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.69.1.tgz",
+            "integrity": "sha512-+VyvOSJXZMT2V5vLzOnDuMz5GxEqLk7hKWQ56YxPW/PQRUuKimPqmEIJOx8jHYeyo65pKbapbW464mvsKbaj4A==",
             "dev": true,
             "requires": {
                 "@types/eslint-scope": "^3.7.3",
@@ -19578,13 +19858,13 @@
                 "acorn-import-assertions": "^1.7.6",
                 "browserslist": "^4.14.5",
                 "chrome-trace-event": "^1.0.2",
-                "enhanced-resolve": "^5.9.3",
+                "enhanced-resolve": "^5.8.3",
                 "es-module-lexer": "^0.9.0",
                 "eslint-scope": "5.1.1",
                 "events": "^3.2.0",
                 "glob-to-regexp": "^0.4.1",
                 "graceful-fs": "^4.2.9",
-                "json-parse-even-better-errors": "^2.3.1",
+                "json-parse-better-errors": "^1.0.2",
                 "loader-runner": "^4.2.0",
                 "mime-types": "^2.1.27",
                 "neo-async": "^2.6.2",
@@ -19595,6 +19875,52 @@
                 "webpack-sources": "^3.2.3"
             },
             "dependencies": {
+                "acorn": {
+                    "version": "8.7.0",
+                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
+                    "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
+                    "dev": true
+                },
+                "ajv": {
+                    "version": "6.12.6",
+                    "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+                    "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+                    "dev": true,
+                    "requires": {
+                        "fast-deep-equal": "^3.1.1",
+                        "fast-json-stable-stringify": "^2.0.0",
+                        "json-schema-traverse": "^0.4.1",
+                        "uri-js": "^4.2.2"
+                    }
+                },
+                "ajv-keywords": {
+                    "version": "3.5.2",
+                    "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+                    "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+                    "dev": true
+                },
+                "enhanced-resolve": {
+                    "version": "5.9.2",
+                    "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz",
+                    "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==",
+                    "dev": true,
+                    "requires": {
+                        "graceful-fs": "^4.2.4",
+                        "tapable": "^2.2.0"
+                    }
+                },
+                "graceful-fs": {
+                    "version": "4.2.9",
+                    "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
+                    "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==",
+                    "dev": true
+                },
+                "neo-async": {
+                    "version": "2.6.2",
+                    "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+                    "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+                    "dev": true
+                },
                 "schema-utils": {
                     "version": "3.1.1",
                     "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
@@ -19605,6 +19931,12 @@
                         "ajv": "^6.12.5",
                         "ajv-keywords": "^3.5.2"
                     }
+                },
+                "tapable": {
+                    "version": "2.2.1",
+                    "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+                    "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+                    "dev": true
                 }
             }
         },
@@ -19658,6 +19990,11 @@
                         "strip-final-newline": "^2.0.0"
                     }
                 },
+                "get-stream": {
+                    "version": "6.0.1",
+                    "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+                    "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="
+                },
                 "interpret": {
                     "version": "2.2.0",
                     "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
@@ -19713,21 +20050,21 @@
             }
         },
         "webpack-dev-middleware": {
-            "version": "5.3.3",
-            "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz",
-            "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==",
+            "version": "5.3.1",
+            "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz",
+            "integrity": "sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==",
             "requires": {
                 "colorette": "^2.0.10",
-                "memfs": "^3.4.3",
+                "memfs": "^3.4.1",
                 "mime-types": "^2.1.31",
                 "range-parser": "^1.2.1",
                 "schema-utils": "^4.0.0"
             },
             "dependencies": {
                 "ajv": {
-                    "version": "8.11.0",
-                    "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
-                    "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
+                    "version": "8.10.0",
+                    "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz",
+                    "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==",
                     "requires": {
                         "fast-deep-equal": "^3.1.1",
                         "json-schema-traverse": "^1.0.0",
@@ -19741,6 +20078,13 @@
                     "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
                     "requires": {
                         "fast-deep-equal": "^3.1.3"
+                    },
+                    "dependencies": {
+                        "fast-deep-equal": {
+                            "version": "3.1.3",
+                            "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+                            "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+                        }
                     }
                 },
                 "json-schema-traverse": {
@@ -19748,6 +20092,19 @@
                     "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
                     "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
                 },
+                "mime-db": {
+                    "version": "1.52.0",
+                    "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+                    "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+                },
+                "mime-types": {
+                    "version": "2.1.35",
+                    "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+                    "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+                    "requires": {
+                        "mime-db": "1.52.0"
+                    }
+                },
                 "schema-utils": {
                     "version": "4.0.0",
                     "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
@@ -19805,7 +20162,7 @@
                 "ansi-regex": {
                     "version": "2.1.1",
                     "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-                    "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+                    "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
                     "dev": true
                 },
                 "debug": {
@@ -19830,7 +20187,7 @@
                 "memory-fs": {
                     "version": "0.4.1",
                     "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
-                    "integrity": "sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==",
+                    "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
                     "dev": true,
                     "requires": {
                         "errno": "^0.1.3",
@@ -19876,7 +20233,7 @@
                 "resolve-cwd": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
-                    "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==",
+                    "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
                     "dev": true,
                     "requires": {
                         "resolve-from": "^3.0.0"
@@ -19885,9 +20242,20 @@
                 "resolve-from": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
-                    "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==",
+                    "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
                     "dev": true
                 },
+                "schema-utils": {
+                    "version": "1.0.0",
+                    "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+                    "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+                    "dev": true,
+                    "requires": {
+                        "ajv": "^6.1.0",
+                        "ajv-errors": "^1.0.0",
+                        "ajv-keywords": "^3.1.0"
+                    }
+                },
                 "semver": {
                     "version": "6.3.0",
                     "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
@@ -19965,9 +20333,9 @@
                     "dev": true
                 },
                 "html-entities": {
-                    "version": "2.3.3",
-                    "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz",
-                    "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==",
+                    "version": "2.3.2",
+                    "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz",
+                    "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==",
                     "dev": true
                 },
                 "strip-ansi": {
@@ -20041,23 +20409,12 @@
             "dev": true,
             "requires": {
                 "iconv-lite": "0.4.24"
-            },
-            "dependencies": {
-                "iconv-lite": {
-                    "version": "0.4.24",
-                    "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
-                    "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
-                    "dev": true,
-                    "requires": {
-                        "safer-buffer": ">= 2.1.2 < 3"
-                    }
-                }
             }
         },
         "whatwg-fetch": {
-            "version": "3.6.2",
-            "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz",
-            "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA=="
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
+            "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q=="
         },
         "whatwg-mimetype": {
             "version": "2.3.0",
@@ -20066,19 +20423,16 @@
             "dev": true
         },
         "whatwg-url": {
-            "version": "5.0.0",
-            "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
-            "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
+            "version": "7.1.0",
+            "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
+            "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
+            "dev": true,
             "requires": {
-                "tr46": "~0.0.3",
-                "webidl-conversions": "^3.0.0"
+                "lodash.sortby": "^4.7.0",
+                "tr46": "^1.0.1",
+                "webidl-conversions": "^4.0.2"
             }
         },
-        "when": {
-            "version": "3.7.8",
-            "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz",
-            "integrity": "sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I="
-        },
         "which": {
             "version": "1.3.1",
             "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
@@ -20105,21 +20459,103 @@
             "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
         },
         "which-pm-runs": {
-            "version": "1.1.0",
-            "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz",
-            "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
+            "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
         },
         "which-typed-array": {
-            "version": "1.1.8",
-            "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz",
-            "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==",
+            "version": "1.1.7",
+            "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz",
+            "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==",
             "requires": {
                 "available-typed-arrays": "^1.0.5",
                 "call-bind": "^1.0.2",
-                "es-abstract": "^1.20.0",
-                "for-each": "^0.3.3",
+                "es-abstract": "^1.18.5",
+                "foreach": "^2.0.5",
                 "has-tostringtag": "^1.0.0",
-                "is-typed-array": "^1.1.9"
+                "is-typed-array": "^1.1.7"
+            },
+            "dependencies": {
+                "es-abstract": {
+                    "version": "1.19.1",
+                    "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz",
+                    "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==",
+                    "requires": {
+                        "call-bind": "^1.0.2",
+                        "es-to-primitive": "^1.2.1",
+                        "function-bind": "^1.1.1",
+                        "get-intrinsic": "^1.1.1",
+                        "get-symbol-description": "^1.0.0",
+                        "has": "^1.0.3",
+                        "has-symbols": "^1.0.2",
+                        "internal-slot": "^1.0.3",
+                        "is-callable": "^1.2.4",
+                        "is-negative-zero": "^2.0.1",
+                        "is-regex": "^1.1.4",
+                        "is-shared-array-buffer": "^1.0.1",
+                        "is-string": "^1.0.7",
+                        "is-weakref": "^1.0.1",
+                        "object-inspect": "^1.11.0",
+                        "object-keys": "^1.1.1",
+                        "object.assign": "^4.1.2",
+                        "string.prototype.trimend": "^1.0.4",
+                        "string.prototype.trimstart": "^1.0.4",
+                        "unbox-primitive": "^1.0.1"
+                    }
+                },
+                "has-symbols": {
+                    "version": "1.0.3",
+                    "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+                    "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
+                },
+                "is-callable": {
+                    "version": "1.2.4",
+                    "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz",
+                    "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w=="
+                },
+                "is-regex": {
+                    "version": "1.1.4",
+                    "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+                    "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+                    "requires": {
+                        "call-bind": "^1.0.2",
+                        "has-tostringtag": "^1.0.0"
+                    }
+                },
+                "object-inspect": {
+                    "version": "1.12.0",
+                    "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
+                    "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g=="
+                },
+                "object.assign": {
+                    "version": "4.1.2",
+                    "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+                    "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+                    "requires": {
+                        "call-bind": "^1.0.0",
+                        "define-properties": "^1.1.3",
+                        "has-symbols": "^1.0.1",
+                        "object-keys": "^1.1.1"
+                    }
+                },
+                "string.prototype.trimend": {
+                    "version": "1.0.4",
+                    "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
+                    "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
+                    "requires": {
+                        "call-bind": "^1.0.2",
+                        "define-properties": "^1.1.3"
+                    }
+                },
+                "string.prototype.trimstart": {
+                    "version": "1.0.4",
+                    "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
+                    "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
+                    "requires": {
+                        "call-bind": "^1.0.2",
+                        "define-properties": "^1.1.3"
+                    }
+                }
             }
         },
         "wide-align": {
@@ -20165,13 +20601,6 @@
             "requires": {
                 "acorn": "^3.1.0",
                 "acorn-globals": "^3.0.0"
-            },
-            "dependencies": {
-                "acorn": {
-                    "version": "3.3.0",
-                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
-                    "integrity": "sha512-OLUyIIZ7mF5oaAUT1w0TFqQS81q3saT46x8t7ukpPjMNk+nbs4ZHhs7ToV8EWnLYLepjETXd4XaCE4uxkMeqUw=="
-                }
             }
         },
         "word-wrap": {
@@ -20224,9 +20653,9 @@
             },
             "dependencies": {
                 "ansi-regex": {
-                    "version": "4.1.1",
-                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
-                    "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
+                    "version": "4.1.0",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+                    "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
                 },
                 "string-width": {
                     "version": "3.1.0",
@@ -20264,9 +20693,9 @@
             }
         },
         "ws": {
-            "version": "7.5.8",
-            "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.8.tgz",
-            "integrity": "sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw=="
+            "version": "7.2.5",
+            "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz",
+            "integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA=="
         },
         "xdg-basedir": {
             "version": "3.0.0",
@@ -20290,7 +20719,7 @@
                 "lodash": {
                     "version": "3.10.1",
                     "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
-                    "integrity": "sha512-9mDDwqVIma6OZX79ZlDACZl8sBm0TEnkf99zV3iMA4GzkIT/9hiqP5mY0HoT1iNLCrKc/R1HByV+yJfRWVJryQ=="
+                    "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y="
                 }
             }
         },
@@ -20330,14 +20759,14 @@
             "dev": true
         },
         "y18n": {
-            "version": "4.0.3",
-            "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
-            "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+            "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
         },
         "yallist": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-            "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+            "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
         },
         "yaml": {
             "version": "1.10.2",
@@ -20362,9 +20791,9 @@
             },
             "dependencies": {
                 "ansi-regex": {
-                    "version": "4.1.1",
-                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
-                    "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
+                    "version": "4.1.0",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+                    "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
                 },
                 "string-width": {
                     "version": "3.1.0",
@@ -20436,9 +20865,9 @@
             }
         },
         "zustand": {
-            "version": "3.7.2",
-            "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
-            "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA=="
+            "version": "3.7.1",
+            "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.1.tgz",
+            "integrity": "sha512-wHBCZlKj+bg03/hP+Tzv24YhnqqP8MCeN9ECPDXoF01062SIbnfl3j9O0znkDw1lNTY0a8WN3F///a0UhhaEqg=="
         }
     }
 }
diff --git a/package.json b/package.json
index d5b442bea..c8455c8a3 100644
--- a/package.json
+++ b/package.json
@@ -132,7 +132,6 @@
         "@types/dom-speech-recognition": "0.0.1",
         "@types/formidable": "1.0.31",
         "@types/google-maps": "^3.2.3",
-        "@types/react-icons": "^3.0.0",
         "@types/react-reconciler": "^0.26.4",
         "@types/reveal": "^3.3.33",
         "@types/supercluster": "^7.1.0",
@@ -175,7 +174,6 @@
         "express-session": "^1.17.2",
         "express-validator": "^5.3.1",
         "expressjs": "^1.0.1",
-        "ffmpeg": "0.0.4",
         "file-saver": "^2.0.5",
         "find-in-files": "^0.5.0",
         "fit-curve": "^0.1.7",
@@ -256,7 +254,6 @@
         "react-datepicker": "^3.8.0",
         "react-dom": "^16.14.0",
         "react-grid-layout": "^0.18.3",
-        "react-icons": "^4.3.1",
         "react-image-lightbox-with-rotate": "^5.1.1",
         "react-jsx-parser": "^1.29.0",
         "react-loading": "^2.0.3",
diff --git a/src/Utils.ts b/src/Utils.ts
index b87980397..4dd9979ea 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -677,8 +677,6 @@ export function StopEvent(e: React.PointerEvent | React.MouseEvent) {
     e.preventDefault();
 }
 
-
-
 export function setupMoveUpEvents(
     target: object,
     e: React.PointerEvent,
@@ -694,7 +692,6 @@ export function setupMoveUpEvents(
     (target as any)._lastTap = Date.now();
     (target as any)._downX = (target as any)._lastX = e.clientX;
     (target as any)._downY = (target as any)._lastY = e.clientY;
-    (target as any)._noClick = false;
 
     const _moveEvent = (e: PointerEvent): void => {
         if (Math.abs(e.clientX - (target as any)._downX) > Utils.DRAG_THRESHOLD || Math.abs(e.clientY - (target as any)._downY) > Utils.DRAG_THRESHOLD) {
@@ -726,20 +723,17 @@ export function setupMoveUpEvents(
                 clearTimeout((target as any)._doubleTime);
                 (target as any)._doubleTime = undefined;
             }
-            (target as any)._noClick = clickEvent(e, (target as any)._doubleTap);
+            clickEvent(e, (target as any)._doubleTap);
         }
         document.removeEventListener("pointermove", _moveEvent);
         document.removeEventListener("pointerup", _upEvent);
     };
-    const _clickEvent = (e: MouseEvent): void => {
-        if ((target as any)._noClick) e.stopPropagation();
-        document.removeEventListener("click", _clickEvent, true);
-    }
     if (stopPropagation) {
         e.stopPropagation();
         e.preventDefault();
     }
+    document.removeEventListener("pointermove", _moveEvent);
+    document.removeEventListener("pointerup", _upEvent);
     document.addEventListener("pointermove", _moveEvent);
     document.addEventListener("pointerup", _upEvent);
-    document.addEventListener("click", _clickEvent, true);
 }
\ No newline at end of file
diff --git a/src/client/Network.ts b/src/client/Network.ts
index 3597e7b2b..bf2918734 100644
--- a/src/client/Network.ts
+++ b/src/client/Network.ts
@@ -4,46 +4,46 @@ import { Upload } from "../server/SharedMediaTypes";
 
 export namespace Networking {
 
-      export async function FetchFromServer(relativeRoute: string) {
-            return (await fetch(relativeRoute)).text();
-      }
+    export async function FetchFromServer(relativeRoute: string) {
+        return (await fetch(relativeRoute)).text();
+    }
 
-      export async function PostToServer(relativeRoute: string, body?: any) {
-            const options = {
-                  uri: Utils.prepend(relativeRoute),
-                  method: "POST",
-                  body,
-                  json: true
-            };
-            return requestPromise.post(options);
-      }
+    export async function PostToServer(relativeRoute: string, body?: any) {
+        const options = {
+            uri: Utils.prepend(relativeRoute),
+            method: "POST",
+            body,
+            json: true
+        };
+        return requestPromise.post(options);
+    }
 
-      export async function UploadFilesToServer<T extends Upload.FileInformation = Upload.FileInformation>(files: File | File[]): Promise<Upload.FileResponse<T>[]> {
-            const formData = new FormData();
-            if (Array.isArray(files)) {
-                  if (!files.length) {
-                        return [];
-                  }
-                  files.forEach(file => formData.append(Utils.GenerateGuid(), file));
-            } else {
-                  formData.append(Utils.GenerateGuid(), files);
+    export async function UploadFilesToServer<T extends Upload.FileInformation = Upload.FileInformation>(files: File | File[]): Promise<Upload.FileResponse<T>[]> {
+        const formData = new FormData();
+        if (Array.isArray(files)) {
+            if (!files.length) {
+                return [];
             }
-            const parameters = {
-                  method: 'POST',
-                  body: formData
-            };
-            const response = await fetch("/uploadFormData", parameters);
-            return response.json();
-      }
+            files.forEach(file => formData.append(Utils.GenerateGuid(), file));
+        } else {
+            formData.append(Utils.GenerateGuid(), files);
+        }
+        const parameters = {
+            method: 'POST',
+            body: formData
+        };
+        const response = await fetch("/uploadFormData", parameters);
+        return response.json();
+    }
 
-      export async function UploadYoutubeToServer<T extends Upload.FileInformation = Upload.FileInformation>(videoId: string): Promise<Upload.FileResponse<T>[]> {
-            const parameters = {
-                  method: 'POST',
-                  body: JSON.stringify({ videoId }),
-                  json: true
-            };
-            const response = await fetch("/uploadYoutubeVideo", parameters);
-            return response.json();
-      }
+    export async function UploadYoutubeToServer<T extends Upload.FileInformation = Upload.FileInformation>(videoId: string): Promise<Upload.FileResponse<T>[]> {
+        const parameters = {
+            method: 'POST',
+            body: JSON.stringify({ videoId }),
+            json: true
+        };
+        const response = await fetch("/uploadYoutubeVideo", parameters);
+        return response.json();
+    }
 
 }
\ No newline at end of file
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index ca942a38a..161dff6e0 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -9,7 +9,6 @@ export enum DocumentType {
     KVP = "kvp",
     VID = "video",
     AUDIO = "audio",
-    REC = "recording",
     PDF = "pdf",
     INK = "inks",
     SCREENSHOT = "screenshot",
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 5f009573e..2ca9cdb71 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -11,7 +11,7 @@ import { RichTextField } from "../../fields/RichTextField";
 import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
 import { ComputedField, ScriptField } from "../../fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../fields/Types";
-import { AudioField, ImageField, MapField, PdfField, RecordingField, VideoField, WebField, YoutubeField } from "../../fields/URLField";
+import { AudioField, ImageField, MapField, PdfField, VideoField, WebField, YoutubeField } from "../../fields/URLField";
 import { SharingPermissions } from "../../fields/util";
 import { Upload } from "../../server/SharedMediaTypes";
 import { OmitKeys, Utils, aggregateBounds } from "../../Utils";
@@ -61,7 +61,6 @@ import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo";
 import { DocumentType } from "./DocumentTypes";
 import { IconProp } from "@fortawesome/fontawesome-svg-core";
 import { MapBox } from "../views/nodes/MapBox/MapBox";
-import { RecordingBox } from "../views/nodes/RecordingBox/RecordingBox";
 
 const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", ""));
 class EmptyBox {
@@ -98,7 +97,6 @@ export class DocumentOptions {
     allowOverlayDrop?: BOOLt = new BoolInfo("can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?");
     childDropAction?: DROPt = new DAInfo("what should happen to the source document when it's dropped onto a child of a collection ");
     targetDropAction?: DROPt = new DAInfo("what should happen to the source document when ??? ");
-    userColor?: string; // color associated with a Dash user (seen in header fields of shared documents)
     color?: string; // foreground color data doc
     backgroundColor?: STRt = new StrInfo("background color for data doc");
     _backgroundColor?: STRt = new StrInfo("background color for each template layout doc (overrides backgroundColor)", true);
@@ -115,8 +113,7 @@ export class DocumentOptions {
     _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height", true);
     _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units", true);
     _fitWidth?: BOOLt = new BoolInfo("whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)", true);
-    _fitContentsToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents
-    _contentBounds?: List<number>; // the (forced) bounds of the document to display. format is: [left, top, right, bottom]
+    _fitToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents
     _lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged
     _lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed
     _isPushpin?: boolean; // whether document, when clicked, toggles display of its link target
@@ -191,18 +188,11 @@ export class DocumentOptions {
     childLayoutString?: string; // template string for collection to use to render its children
     childDontRegisterViews?: boolean;
     childHideLinkButton?: boolean; // hide link buttons on all children
-    childContextMenuFilters?: List<ScriptField>;
-    childContextMenuScripts?: List<ScriptField>;
-    childContextMenuLabels?: List<string>;
-    childContextMenuIcons?: List<string>;
     hideLinkButton?: boolean; // whether the blue link counter button should be hidden
-    hideDecorationTitle?: boolean;
-    hideOpenButton?: boolean;
-    hideResizeHandles?: boolean;
-    hideDocumentButtonBar?: boolean;
     hideAllLinks?: boolean; // whether all individual blue anchor dots should be hidden
     isTemplateForField?: string; // the field key for which the containing document is a rendering template
     isTemplateDoc?: boolean;
+    watchedDocuments?: Doc; // list of documents an icon doc monitors in order to display a badge count
     targetScriptKey?: string; // where to write a template script (used by collections with click templates which need to target onClick, onDoubleClick, etc)
     templates?: List<string>;
     hero?: ImageField; // primary image that best represents a compound document (e.g., for a buxton device document that has multiple images)
@@ -254,8 +244,7 @@ export class DocumentOptions {
     numBtnType?: string;
     numBtnMax?: number;
     numBtnMin?: number;
-    switchToggle?: boolean;    
-    badgeValue?: ScriptField;
+    switchToggle?: boolean;
 
     //LINEAR VIEW
     linearViewIsExpanded?: boolean; // is linear view expanded
@@ -299,7 +288,6 @@ export class DocumentOptions {
     treeViewHideHeader?: boolean; // whether to hide the header for a document in a tree view
     treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items.
     treeViewGrowsHorizontally?: boolean; // whether an embedded tree view of the document can grow horizontally without growing vertically
-    treeViewChildDoubleClick?: ScriptField; // 
 
     // Action Button
     buttonMenu?: boolean; // whether a action button should be displayed 
@@ -421,10 +409,6 @@ export namespace Docs {
                 layout: { view: AudioBox, dataField: defaultDataKey },
                 options: { _height: 100, backgroundColor: "lightGray", links: "@links(self)" }
             }],
-            [DocumentType.REC, {
-                layout: { view: VideoBox, dataField: defaultDataKey },
-                options: { _height: 100, backgroundColor: "pink", links: "@links(self)" }
-            }],
             [DocumentType.PDF, {
                 layout: { view: PDFBox, dataField: defaultDataKey },
                 options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: "@links(self)" }
@@ -492,7 +476,7 @@ export namespace Docs {
                 options: { hideLinkButton: true, _width: 40, _height: 40, borderRounding: "100%", links: "@links(self)" },
             }],
             [DocumentType.WEBCAM, {
-                layout: { view: RecordingBox, dataField: defaultDataKey },
+                layout: { view: DashWebRTCVideo, dataField: defaultDataKey },
                 options: { links: "@links(self)" }
             }],
             [DocumentType.PRESELEMENT, {
@@ -669,7 +653,6 @@ export namespace Docs {
             // so that the list of annotations is already initialised, prevents issues in addonly.
             // without this, if a doc has no annotations but the user has AddOnly privileges, they won't be able to add an annotation because they would have needed to create the field's list which they don't have permissions to do.
             dataProps[fieldKey + "-annotations"] = new List<Doc>();
-            dataProps[fieldKey + "-sidebar"] = new List<Doc>();
             const dataDoc = Doc.assign(Doc.MakeDelegate(proto, protoId), dataProps, undefined, true);
 
             const viewFirstProps: { [id: string]: any } = {};
@@ -688,9 +671,9 @@ export namespace Docs {
             return viewDoc;
         }
 
-        export function ImageDocument(url: string|ImageField, options: DocumentOptions = {}) {
-            const imgField =  url instanceof ImageField ? url : new ImageField(url);
-            return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField.url.href), ...options });
+        export function ImageDocument(url: string, options: DocumentOptions = {}) {
+            const imgField = new ImageField(url);
+            return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(url), ...options });
         }
 
         export function PresDocument(options: DocumentOptions = {}) {
@@ -727,10 +710,6 @@ export namespace Docs {
                 { ...options, backgroundColor: ComputedField.MakeFunction("this._mediaState === 'playing' ? 'green':'gray'") as any });
         }
 
-        export function RecordingDocument(url: string, options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.REC), "", options);
-        }
-
         export function SearchDocument(options: DocumentOptions = {}) {
             return InstanceFromProto(Prototypes.get(DocumentType.SEARCH), new List<Doc>([]), options);
         }
@@ -1118,11 +1097,8 @@ export namespace DocUtils {
 
     export function MakeLinkToActiveAudio(getSourceDoc: () => Doc, broadcastEvent = true) {
         broadcastEvent && runInAction(() => DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1);
-        return DocUtils.ActiveRecordings.map(audio => {
-            const link = DocUtils.MakeLink({ doc: getSourceDoc() }, { doc: audio.getAnchor() || audio.props.Document }, "recording annotation:linked recording", "recording timeline");
-            link && (link.followLinkLocation = "add:right");
-            return link;
-        });
+        return DocUtils.ActiveRecordings.map(audio =>
+            DocUtils.MakeLink({ doc: getSourceDoc() }, { doc: audio.getAnchor() || audio.props.Document }, "recording annotation:linked recording", "recording timeline"));
     }
 
     export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", description: string = "", id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) {
@@ -1194,9 +1170,6 @@ export namespace DocUtils {
         } else if (field instanceof AudioField) {
             created = Docs.Create.AudioDocument((field).url.href, resolved);
             layout = AudioBox.LayoutString;
-        } else if (field instanceof RecordingField) {
-            created = Docs.Create.RecordingDocument((field).url.href, resolved);
-            layout = RecordingBox.LayoutString;
         } else if (field instanceof InkField) {
             created = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), (field).inkData, resolved);
             layout = InkingStroke.LayoutString;
diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js
index 012ee163c..82b10608d 100644
--- a/src/client/goldenLayout.js
+++ b/src/client/goldenLayout.js
@@ -458,9 +458,8 @@
                 if (this._bDragging === true) {
                     this._bDragging = false;
                     this.emit('dragStop', oEvent, this._nOriginalX + this._nX);
-                } else  { // make title receive pointer events to allow setting insertion position or selecting texst range
-                    const classname = typeof oEvent.target?.className === "string" ? oEvent.target.className : "";
-                    if (classname.includes("lm_title_wrap")) {
+                } else if (oEvent.target) { // make title receive pointer events to allow setting insertion position or selecting texst range
+                    if (oEvent.target.className.includes("lm_title_wrap")) {
                         oEvent.target.children[0].style.pointerEvents = "all";
                         oEvent.target.children[0].focus();
                     }
@@ -1432,7 +1431,7 @@
             this.root.callDownwards('_$init');
 
             if (config.maximisedItemId === '__glMaximised') {
-                this.root.getItemsById(config.maximisedItemId)[0]?.toggleMaximise();
+                this.root.getItemsById(config.maximisedItemId)[0].toggleMaximise();
             }
         },
 
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b4e18a8bb..51bfdbbd2 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -7,8 +7,8 @@ import { List } from "../../fields/List";
 import { PrefetchProxy } from "../../fields/Proxy";
 import { RichTextField } from "../../fields/RichTextField";
 import { ComputedField, ScriptField } from "../../fields/ScriptField";
-import { BoolCast, Cast, DateCast, DocCast, NumCast, PromiseValue, StrCast } from "../../fields/Types";
-import { ImageField, nullAudio } from "../../fields/URLField";
+import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types";
+import { nullAudio } from "../../fields/URLField";
 import { SharingPermissions } from "../../fields/util";
 import { Utils } from "../../Utils";
 import { DocServer } from "../DocServer";
@@ -22,10 +22,11 @@ import { TreeView } from "../views/collections/TreeView";
 import { Colors } from "../views/global/globalEnums";
 import { MainView } from "../views/MainView";
 import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox";
+import { LabelBox } from "../views/nodes/LabelBox";
 import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView";
 import { OverlayView } from "../views/OverlayView";
 import { DocumentManager } from "./DocumentManager";
-import { DragManager } from "./DragManager";
+import { DragManager, dropActionType } from "./DragManager";
 import { makeTemplate, MakeTemplate } from "./DropConverter";
 import { HistoryUtil } from "./History";
 import { LinkManager } from "./LinkManager";
@@ -271,8 +272,8 @@ export class CurrentUserUtils {
             doc.emptyScreenshot = Docs.Create.ScreenshotDocument({ ...standardOps(), title: "empty screenshot", _width: 400, _height: 200 });
         }
         if (doc.emptyWall === undefined) {
-            doc.emptyWall = Docs.Create.WebCamDocument("", { _width: 400, _height: 200, title: "recording", system: true, cloneFieldFilter: new List<string>(["system"]) });
-            (doc.emptyWall as Doc).recording = true;
+            doc.emptyWall = Docs.Create.ScreenshotDocument({ ...standardOps(), title: "screen snapshot", _width: 400, _height: 200, });
+            (doc.emptyWall as Doc).videoWall = true;
         }
         if (doc.emptyAudio === undefined) {
             doc.emptyAudio = Docs.Create.AudioDocument(nullAudio, { ...standardOps(), title: "audio recording", x: 200, y: 200, _width: 200, _height: 100, });
@@ -354,7 +355,6 @@ export class CurrentUserUtils {
     }
 
     static menuBtnDescriptions(doc: Doc) {
-        const badgeValue = ScriptField.MakeFunction("((len) => len ? len: undefined)(docList(self.target.data).filter(doc => !docList(self.target.viewed).includes(doc)).length)")
         return [
             { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
             { title: "Search", target: Cast(doc.mySearchPanel, Doc, null), icon: "search", click: 'selectMainMenu(self)' },
@@ -362,7 +362,7 @@ export class CurrentUserUtils {
             { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" },
             { title: "Imports", target: Cast(doc.myImportDocs, Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
             { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
-            { title: "Shared  with me", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', badgeValue},
+            { title: "Shared  with me", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc },
             { title: "Trails", target: Cast(doc.myTrails, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
             { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" },
         ];
@@ -370,9 +370,8 @@ export class CurrentUserUtils {
 
     static async setupMenuPanel(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
         if (doc.menuStack === undefined) {
-            await this.setupLinkDocs(doc, linkDatabaseId);
-            await this.setupSharedDocs(doc, sharingDocumentId);  // sets up the right sidebar collection for mobile upload documents and sharing
-            const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, badgeValue, hidden }) =>
+            await this.setupSharingSidebar(doc, sharingDocumentId, linkDatabaseId);  // sets up the right sidebar collection for mobile upload documents and sharing
+            const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, watchedDocuments, hidden }) =>
                 Docs.Create.FontIconDocument({
                     icon,
                     btnType: ButtonType.MenuButton,
@@ -383,13 +382,12 @@ export class CurrentUserUtils {
                     dontUndo: true,
                     title,
                     target,
-                    dontRegisterView: true,
                     hidden: hidden ? ComputedField.MakeFunction("IsNoviceMode()") as any : undefined,
                     _dropAction: "alias",
                     _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
                     _width: 60,
                     _height: 60,
-                    badgeValue,
+                    watchedDocuments,
                     onClick: ScriptField.MakeScript(click, { scriptContext: "any" })
                 })
             );
@@ -402,7 +400,6 @@ export class CurrentUserUtils {
                 _chromeHidden: true,
                 backgroundColor: Colors.DARK_GRAY,
                 boxShadow: "rgba(0,0,0,0)",
-                dontRegisterView: true,
                 dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
                 ignoreClick: true,
                 _gridGap: 0,
@@ -723,7 +720,7 @@ export class CurrentUserUtils {
             ];
             doc.dockedBtns = CurrentUserUtils.linearButtonList({
                 title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true,
-                childDontRegisterViews: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true
+                linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true
             }, btnDescs.map(desc => doc[`dockedBtn-${desc.title}`] as Doc ?? (doc[`dockedBtn-${desc.title}`] = dockBtn({ title: desc.title, ...desc.opts() }))));
         }
     }
@@ -837,13 +834,12 @@ export class CurrentUserUtils {
         });
         if (doc.contextMenuBtns === undefined) {
             doc.contextMenuBtns = CurrentUserUtils.linearButtonList(
-                { title: "menu buttons", flexGap: 0, childDontRegisterViews: true,  linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 },
+                { title: "menu buttons", flexGap: 0, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 },
                 CurrentUserUtils.contextMenuTools(doc).map(params =>
                     !params.subMenu ?
                         btnFunc(params) :
                         CurrentUserUtils.linearButtonList({
                             title: params.title,
-                            childDontRegisterViews: true,
                             linearViewSubMenu: true, flexGap: 0, ignoreClick: true,
                             linearViewExpandable: true, icon: params.title, _height: 30,
                             linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined,
@@ -859,7 +855,6 @@ export class CurrentUserUtils {
                         btnFunc(params) :
                         CurrentUserUtils.linearButtonList({
                             title: params.title,
-                            childDontRegisterViews: true,
                             linearViewSubMenu: true, flexGap: 0, ignoreClick: true,
                             linearViewExpandable: true, icon: params.title, _height: 30,
                             linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined,
@@ -902,7 +897,10 @@ export class CurrentUserUtils {
     }
 
     // Sharing sidebar is where shared documents are contained
-    static async setupLinkDocs(doc: Doc, linkDatabaseId: string) {
+    static async setupSharingSidebar(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
+        if (doc.myPublishedDocs === undefined) {
+            doc.myPublishedDocs = new List<Doc>();
+        }
         if (doc.myLinkDatabase === undefined) {
             let linkDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(linkDatabaseId);
             if (!linkDocs) {
@@ -914,41 +912,29 @@ export class CurrentUserUtils {
             }
             doc.myLinkDatabase = new PrefetchProxy(linkDocs);
         }
-    }
-    // A user's sharing document is where all documents that are shared to that user are placed.  
-    // When the user views one of these documents, it will be added to the sharing documents 'viewed' list field
-    // The sharing document also stores the user's color value which helps distinguish shared documents from personal documents
-    static async setupSharedDocs(doc: Doc, sharingDocumentId: string) {
-        const addToDashboards = ScriptField.MakeScript(`addToDashboards(self)`);
-        const dashboardFilter = ScriptField.MakeFunction(`doc._viewType === '${CollectionViewType.Docking}'`, { doc: Doc.name });
-        const dblClkScript = ScriptField.MakeScript("{scriptContext.openLevel(documentView); addDocToList(scriptContext.props.treeView.props.Document, 'viewed', documentView.rootDoc);}", {scriptContext:"any", documentView:Doc.name})
-           
-        const sharedDocOpts:DocumentOptions = {
-            title: "My Shared Docs",
-            userColor: "rgb(202, 202, 202)",
-            childContextMenuFilters: new List<ScriptField>([dashboardFilter!,]),
-            childContextMenuScripts: new List<ScriptField>([addToDashboards!,]),
-            childContextMenuLabels: new List<string>(["Add to Dashboards",]),
-            childContextMenuIcons: new List<string>(["user-plus",]),
-            treeViewChildDoubleClick: dblClkScript, 
-         };
-        const sharedRequiredDocOpts:DocumentOptions = { 
-            "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment,
-            childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15,
-            // NOTE: treeViewHideTitle & _showTitle is for a TreeView's editable title, _showTitle is for DocumentViews title bar
-            _showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, 
-            explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
-        };
-        
-        const sharedDocs = Docs.newAccount ? undefined : DocCast(doc.mySharedDocs) ?? DocCast(await DocServer.GetRefField(sharingDocumentId + "outer"));
-        if (!(sharedDocs instanceof Doc)) {
-            doc.mySharedDocs = new PrefetchProxy(
-                Docs.Create.TreeDocument([], {...sharedDocOpts, ...sharedRequiredDocOpts}, sharingDocumentId + "outer", sharingDocumentId));
-        } else {
-            Object.entries(sharedRequiredDocOpts).forEach(pair => {
-                const targetDoc = pair[0].startsWith("_") ? sharedDocs as Doc : Doc.GetProto(sharedDocs as Doc);
-                targetDoc[pair[0]] = pair[1];
-            });
+        // TODO:glr NOTE: treeViewHideTitle & _showTitle may be confusing, treeViewHideTitle is for the editable title (just for tree view), _showTitle is to show the Document title for any document
+        if (doc.mySharedDocs === undefined) {
+            let sharedDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(sharingDocumentId + "outer");
+            if (!sharedDocs) {
+                sharedDocs = Docs.Create.TreeDocument([], {
+                    title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15,
+                    _showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment,
+                    _chromeHidden: true, boxShadow: "0 0",
+                    explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
+                }, sharingDocumentId + "outer", sharingDocumentId);
+                (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Augment;
+            }
+            if (sharedDocs instanceof Doc) {
+                Doc.GetProto(sharedDocs).userColor = sharedDocs.userColor || "rgb(202, 202, 202)";
+                const addToDashboards = ScriptField.MakeScript(`addToDashboards(self)`);
+                const dashboardFilter = ScriptField.MakeFunction(`doc._viewType === '${CollectionViewType.Docking}'`, { doc: Doc.name });
+                sharedDocs.childContextMenuFilters = new List<ScriptField>([dashboardFilter!,]);
+                sharedDocs.childContextMenuScripts = new List<ScriptField>([addToDashboards!,]);
+                sharedDocs.childContextMenuLabels = new List<string>(["Add to Dashboards",]);
+                sharedDocs.childContextMenuIcons = new List<string>(["user-plus",]);
+
+            }
+            doc.mySharedDocs = new PrefetchProxy(sharedDocs as Doc);
         }
     }
 
@@ -959,7 +945,7 @@ export class CurrentUserUtils {
             doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([], {
                 title: "My Imports", _forceActive: true, buttonMenu: true, buttonMenuDoc: newImportButton, ignoreClick: true, _showTitle: "title", _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0,
                 childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true,
-                dontRegisterView: true, explainer: "This is where documents that are Imported into Dash will go."
+                explainer: "This is where documents that are Imported into Dash will go."
             }));
         }
     }
@@ -968,7 +954,7 @@ export class CurrentUserUtils {
     static setupSearchSidebar(doc: Doc) {
         if (doc.mySearchPanel === undefined) {
             doc.mySearchPanel = new PrefetchProxy(Docs.Create.SearchDocument({
-                dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, _searchDoc: true,
+                backgroundColor: "dimgray", ignoreClick: true, _searchDoc: true,
                 childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "Search Panel", system: true
             })) as any as Doc;
         }
@@ -1056,7 +1042,6 @@ export class CurrentUserUtils {
         doc.savedFilters = new List<Doc>();
         doc.filterDocCount = 0;
         doc.freezeChildren = "remove|add";
-        doc.myPublishedDocs = doc.myPublishedDocs ?? new List<Doc>();
         doc.myHeaderBarDoc = doc.myHeaderBarDoc ?? Docs.Create.MulticolumnDocument([], { title: "header bar", system: true });
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
         this.setupDocTemplates(doc); // sets up the template menu of templates
@@ -1109,7 +1094,6 @@ export class CurrentUserUtils {
                     Docs.newAccount = !(field instanceof Doc);
                     await Docs.Prototypes.initialize();
                     const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc;
-                    Docs.newAccount &&(userDoc.activePage = "home");
                     const updated = this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId);
                     (await DocListCastAsync(Cast(Doc.UserDoc().myLinkDatabase, Doc, null)?.data))?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager
                         const a1 = await Cast(link?.anchor1, Doc, null);
@@ -1127,9 +1111,6 @@ export class CurrentUserUtils {
 
     public static openDashboard = (userDoc: Doc, doc: Doc, fromHistory = false) => {
         CurrentUserUtils.MainDocId = doc[Id];
-        if (!DocListCast(CurrentUserUtils.MyDashboards.data).includes(doc)) {
-            Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", doc);
-        }
 
         if (doc) {  // this has the side-effect of setting the main container since we're assigning the active/guest dashboard
             !("presentationView" in doc) && (doc.presentationView = new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })]));
@@ -1203,42 +1184,18 @@ export class CurrentUserUtils {
         input.click();
     }
 
-    public static CaptureDashboardThumbnail() {
-        const docView = CollectionDockingView.Instance.props.DocumentView?.();
-        const content = docView?.ContentDiv;
-        if (docView && content) {
-            const _width = Number(getComputedStyle(content).width.replace("px",""));
-            const _height = Number(getComputedStyle(content).height.replace("px",""));
-            return CollectionFreeFormView.UpdateIcon(
-                docView.layoutDoc[Id] + "-icon" + (new Date()).getTime(),
-                content,
-                _width, _height,
-                _width, _height, 0, 1, true, docView.layoutDoc[Id] + "-icon", 
-                (iconFile, _nativeWidth, _nativeHeight) => {
-                    const img = Docs.Create.ImageDocument(new ImageField(iconFile), { title: docView.rootDoc.title+"-icon", _width, _height, _nativeWidth, _nativeHeight});
-                    const proto = Cast(img.proto, Doc, null)!;
-                    proto["data-nativeWidth"] = _width;
-                    proto["data-nativeHeight"] = _height;
-                    Doc.GetProto(CurrentUserUtils.ActiveDashboard).thumb = img;
-                });
-        }
-
-    }
-
     public static async snapshotDashboard(userDoc: Doc) {
         const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard);
         Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy);
         CurrentUserUtils.openDashboard(userDoc, copy);
     }
 
-    public static closeActiveDashboard = () => {
-          Doc.UserDoc().activeDashboard = undefined;
-    }
-
     public static createNewDashboard = async (userDoc: Doc, id?: string) => {
         const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true);
         const dashboards = await Cast(userDoc.myDashboards, Doc) as Doc;
         const dashboardCount = DocListCast(dashboards.data).length + 1;
+        const emptyPane = Cast(userDoc.emptyPane, Doc, null);
+        emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
         const freeformOptions: DocumentOptions = {
             x: 0,
             y: 400,
@@ -1246,7 +1203,7 @@ export class CurrentUserUtils {
             _height: 1000,
             _fitWidth: true,
             _backgroundGridShow: true,
-            title: `Untitled Tab 1`,
+            title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`,
         };
         const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, "row");
@@ -1255,12 +1212,11 @@ export class CurrentUserUtils {
         // switching the tabs from the datadoc to the regular doc
         const dashboardTabs = DocListCast(dashboardDoc[DataSym].data);
         dashboardDoc.data = new List<Doc>(dashboardTabs);
-        dashboardDoc["pane-count"] = 1;
 
         userDoc.activePresentation = presentation;
 
         Doc.AddDocToList(dashboards, "data", dashboardDoc);
-      //   CurrentUserUtils.openDashboard(userDoc, dashboardDoc);
+        CurrentUserUtils.openDashboard(userDoc, dashboardDoc);
     }
 
     public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index dbfba8992..9f8c49081 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -455,7 +455,7 @@ export namespace DragManager {
             if (dragData instanceof DocumentDragData) {
                 dragData.userDropAction = e.ctrlKey && e.altKey ? "copy" : e.ctrlKey ? "alias" : dragData.defaultDropAction;
             }
-            if (((e.target as any)?.className === "lm_tabs" || (e.target as any)?.className === "lm_header" || e?.shiftKey) && dragData.draggedDocuments.length === 1) {
+            if (((e.target as any)?.className === "lm_tabs" || e?.shiftKey) && dragData.draggedDocuments.length === 1) {
                 if (!startWindowDragTimer) {
                     startWindowDragTimer = setTimeout(async () => {
                         startWindowDragTimer = undefined;
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 632348306..e6f75a7db 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -58,10 +58,8 @@ export namespace HistoryUtil {
 
     export function getState(): ParsedUrl {
         const state = copyState(history.state);
-        if (state) {
-            state.initializers = state.initializers || {};
-        }
-        return state ?? {initializers:{}};
+        state.initializers = state.initializers || {};
+        return state;
     }
 
     // export function addHandler(handler: (state: ParsedUrl | null) => void) {
diff --git a/src/client/util/RecordingApi.ts b/src/client/util/RecordingApi.ts
deleted file mode 100644
index 021feee9a..000000000
--- a/src/client/util/RecordingApi.ts
+++ /dev/null
@@ -1,269 +0,0 @@
-import { CollectionFreeFormView } from "../views/collections/collectionFreeForm";
-import { IReactionDisposer, observable, reaction } from "mobx";
-import { NumCast } from "../../fields/Types";
-import { Doc } from "../../fields/Doc";
-import { VideoBox } from "../views/nodes/VideoBox";
-import { scaleDiverging } from "d3-scale";
-import { Transform } from "./Transform";
-
-type Movement = {
-    time: number,
-    panX: number,
-    panY: number,
-    scale: number,
-}
-
-type Presentation = {
-    movements: Array<Movement> | null
-    meta: Object,
-}
-
-export class RecordingApi {
-
-    private static NULL_PRESENTATION: Presentation = {
-        movements: null,
-        meta: {},
-    }
-
-    // instance variables
-    private currentPresentation: Presentation;
-    private isRecording: boolean;
-    private absoluteStart: number;
-
-
-    // create static instance and getter for global use
-    @observable static _instance: RecordingApi;
-    public static get Instance(): RecordingApi { return RecordingApi._instance }
-    public constructor() {
-        // init the global instance
-        RecordingApi._instance = this;
-
-        // init the instance variables
-        this.currentPresentation = RecordingApi.NULL_PRESENTATION
-        this.isRecording = false;
-        this.absoluteStart = -1;
-
-        // used for tracking movements in the view frame
-        this.disposeFunc = null;
-        this.recordingFFView = null;
-
-        // for now, set playFFView
-        this.playFFView = null;
-        this.timers = null;
-    }
-
-    // little helper :)
-    private get isInitPresenation(): boolean {
-        return this.currentPresentation.movements === null
-    }
-
-    public start = (meta?: Object): Error | undefined => {
-        // check if already init a presentation
-        if (!this.isInitPresenation) {
-            console.error('[recordingApi.ts] start() failed: current presentation data exists. please call clear() first.')
-            return new Error('[recordingApi.ts] start()')
-        }
-
-        // update the presentation mode
-        Doc.UserDoc().presentationMode = 'recording'
-
-        // (1a) get start date for presenation
-        const startDate = new Date()
-        // (1b) set start timestamp to absolute timestamp
-        this.absoluteStart = startDate.getTime()
-
-        // (2) assign meta content if it exists
-        this.currentPresentation.meta = meta || {}
-        // (3) assign start date to currentPresenation
-        this.currentPresentation.movements = []
-        // (4) set isRecording true to allow trackMovements
-        this.isRecording = true
-    }
-
-    public clear = (): Error | Presentation => {
-        // TODO: maybe archive the data?
-        if (this.isRecording) {
-            console.error('[recordingApi.ts] clear() failed: currently recording presentation. call pause() first')
-            return new Error('[recordingApi.ts] clear()')
-        }
-
-        // update the presentation mode
-        Doc.UserDoc().presentationMode = 'none'
-        // set the previus recording view to the play view
-        this.playFFView = this.recordingFFView
-
-        const presCopy = { ...this.currentPresentation }
-
-        // clear presenation data
-        this.currentPresentation = RecordingApi.NULL_PRESENTATION
-        // clear isRecording
-        this.isRecording = false
-        // clear absoluteStart
-        this.absoluteStart = -1
-        // clear the disposeFunc
-        this.removeRecordingFFView()
-
-        return presCopy;
-    }
-
-    public pause = (): Error | undefined => {
-        if (this.isInitPresenation) {
-            console.error('[recordingApi.ts] pause() failed: no presentation started. try calling init() first')
-            return new Error('[recordingApi.ts] pause(): no presentation')
-        }
-        // don't allow track movments
-        this.isRecording = false
-
-        // set adjust absoluteStart to add the time difference
-        const timestamp = new Date().getTime()
-        this.absoluteStart = timestamp - this.absoluteStart
-    }
-
-    public resume = () => {
-        this.isRecording = true
-        // set absoluteStart to the difference in time
-        this.absoluteStart = new Date().getTime() - this.absoluteStart
-    }
-
-    private trackMovements = (panX: number, panY: number, scale: number = 0): Error | undefined => {
-        // ensure we are recording
-        if (!this.isRecording) {
-            return new Error('[recordingApi.ts] trackMovements()')
-        }
-        // check to see if the presetation is init
-        if (this.isInitPresenation) {
-            return new Error('[recordingApi.ts] trackMovements(): no presentation')
-        }
-
-        // get the time
-        const time = new Date().getTime() - this.absoluteStart
-        // make new movement object
-        const movement: Movement = { time, panX, panY, scale }
-        
-        // add that movement to the current presentation data's movement array
-        this.currentPresentation.movements && this.currentPresentation.movements.push(movement)
-    }
-
-    // instance variable for the FFView
-    private disposeFunc: IReactionDisposer | null;
-    private recordingFFView: CollectionFreeFormView | null;
-    
-    // set the FFView that will be used in a reaction to track the movements
-    public setRecordingFFView = (view: CollectionFreeFormView): void => {
-        // set the view to the current view
-        if (view === this.recordingFFView || view == null) return;
-        
-        // this.recordingFFView = view;
-        // set the reaction to track the movements
-        this.disposeFunc = reaction(
-            () => ({ x: NumCast(view.Document.panX, -1), y: NumCast(view.Document.panY, -1), scale: NumCast(view.Document.viewScale, -1) }),
-            (res) => (res.x !== -1 && res.y !== -1 && this.isRecording) && this.trackMovements(res.x, res.y, res.scale)
-        )
-
-        // for now, set the most recent recordingFFView to the playFFView
-        this.recordingFFView = view;
-    }
-
-    // call on dispose function to stop tracking movements
-    public removeRecordingFFView = (): void => {
-        this.disposeFunc?.();
-        this.disposeFunc = null;
-    }
-
-    // TODO: extract this into different class with pause and resume recording
-    // TODO: store the FFview with the movements
-    private playFFView: CollectionFreeFormView | null;
-    private timers: NodeJS.Timeout[] | null;
-
-    public setPlayFFView = (view: CollectionFreeFormView): void => {
-        this.playFFView = view
-    }
-
-    // pausing movements will dispose all timers that are planned to replay the movements
-    // play movemvents will recreate them when the user resumes the presentation
-    public pauseMovements = (): undefined | Error => {
-        if (this.playFFView === null) {
-            return new Error('[recordingApi.ts] pauseMovements() failed: no view')
-        }
-
-        if (!this._isPlaying) {
-            //return new Error('[recordingApi.ts] pauseMovements() failed: not playing')
-            return
-        }
-        this._isPlaying = false
-        // TODO: set userdoc presentMode to browsing
-        this.timers?.map(timer => clearTimeout(timer))
-
-        // this.videoBox = null;
-    }
-    
-    private videoBox: VideoBox | null = null;
-
-    // by calling pause on the VideoBox, the pauseMovements will be called
-    public pauseVideoAndMovements = (): boolean => {
-        this.videoBox?.Pause()
-
-        this.pauseMovements()
-        return this.videoBox == null
-    }
-
-    public _isPlaying = false;
-
-    public playMovements = (presentation: Presentation, timeViewed: number = 0, videoBox?: VideoBox): undefined | Error => {
-        if (presentation.movements === null || this.playFFView === null) {
-            return new Error('[recordingApi.ts] followMovements() failed: no presentation data or no view')
-        }
-        if(this._isPlaying) return;
-
-        this._isPlaying = true;
-        Doc.UserDoc().presentationMode = 'watching';
-
-        // TODO: consider this bug at the end of the clip on seek
-        this.videoBox = videoBox || null;
-
-        // only get the movements that are remaining in the video time left
-        const filteredMovements = presentation.movements.filter(movement => movement.time > timeViewed * 1000)
-
-        // helper to replay a movement
-        const document = this.playFFView
-        let preScale = -1;
-        const zoomAndPan = (movement: Movement) => {
-            const { panX, panY, scale } = movement;
-            (scale !== -1 && preScale !== scale) && document.zoomSmoothlyAboutPt([panX, panY], scale, 0);
-            document.Document._panX = panX;
-            document.Document._panY = panY;
-
-            preScale = scale;
-        }
-
-        // set the first frame to be at the start of the pres
-        zoomAndPan(filteredMovements[0]);
-
-        // make timers that will execute each movement at the correct replay time
-        this.timers = filteredMovements.map(movement => {
-            const timeDiff = movement.time - timeViewed*1000
-            return setTimeout(() => {
-                // replay the movement
-                zoomAndPan(movement)
-                // if last movement, presentation is done -> set the instance var
-                if (movement === filteredMovements[filteredMovements.length - 1]) RecordingApi.Instance._isPlaying = false;
-            }, timeDiff)
-        })
-    }
-
-    // Unfinished code for tracing multiple free form views
-    // export let pres: Map<CollectionFreeFormView, IReactionDisposer> = new Map()
-
-    // export function AddRecordingFFView(ffView: CollectionFreeFormView): void {
-    //     pres.set(ffView,
-    //          reaction(() => ({ x: ffView.panX, y: ffView.panY }),
-    //     (pt) => RecordingApi.trackMovements(ffView, pt.x, pt.y)))
-    //     )
-    // }
-
-    // export function RemoveRecordingFFView(ffView: CollectionFreeFormView): void {
-    //     const disposer = pres.get(ffView);
-    //     disposer?.();
-    //     pres.delete(ffView)
-    // }
-}
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 223b0268c..3b0a47b54 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -223,7 +223,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
         paramList.push(`${key}: ${typeof val === "object" ? Object.getPrototypeOf(val).constructor.name : typeof val}`);
     }
     const paramString = paramList.join(", ");
-    const body = addReturn ? `return ${script};` : script;
+    const body = addReturn ? `return ${script};` : `return ${script};`;
     const reqTypes = requiredType ? `: ${requiredType}` : '';
     const funcScript = `(function(${paramString})${reqTypes} { ${body} })`;
     host.writeFile("file.ts", funcScript);
diff --git a/src/client/views/DashboardView.scss b/src/client/views/DashboardView.scss
deleted file mode 100644
index 67587dd2b..000000000
--- a/src/client/views/DashboardView.scss
+++ /dev/null
@@ -1,48 +0,0 @@
-.dashboard-view {
-      padding: 50px;
-      display: flex;
-      flex-direction: row;
-
-      .left-menu {
-            display: flex;
-            flex-direction: column;
-            width: 300px;
-      }
-
-      .all-dashboards {
-            display: flex;
-            flex-direction: row;
-            flex-wrap: wrap;
-            overflow-y: scroll;
-      }
-}
-
-.text-button {
-      padding: 10px 0;
-      &:hover {
-            font-weight: 500;
-      }
-
-      &.selected {
-            font-weight: 700;
-      }
-}
-
-.dashboard-container {
-      border-radius: 10px;
-      width: 250px;
-      border: solid .5px grey;
-      display: flex;
-      flex-direction: column;
-      margin: 0 30px 30px 30px;
-      overflow: hidden;
-
-      &:hover {
-            border: solid 1.5px grey;  
-      }
-
-      .title {
-            margin: 10px;
-            font-weight: 500;
-      }
-}
\ No newline at end of file
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
deleted file mode 100644
index efc1644fe..000000000
--- a/src/client/views/DashboardView.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import { action, observable } from "mobx";
-import { extname } from 'path';
-import { observer } from "mobx-react";
-import * as React from 'react';
-import { Doc, DocListCast } from "../../fields/Doc";
-import { Id } from "../../fields/FieldSymbols";
-import { Cast, ImageCast, StrCast } from "../../fields/Types";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { UndoManager } from "../util/UndoManager";
-import "./DashboardView.scss"
-
-enum DashboardGroup {
-      MyDashboards, SharedDashboards
-}
-
-@observer
-export class DashboardView extends React.Component {
-
-      //TODO: delete dashboard, share dashboard, etc.
-
-      @observable
-      private selectedDashboardGroup = DashboardGroup.MyDashboards;
-
-      @action
-      selectDashboardGroup = (group: DashboardGroup) => {
-            this.selectedDashboardGroup = group
-      }
-
-      newDashboard = async () => {
-            const batch = UndoManager.StartBatch("new dash");
-            await CurrentUserUtils.createNewDashboard(Doc.UserDoc());
-            batch.end();
-      }
-
-      clickDashboard = async (e: React.MouseEvent, dashboard: Doc) => {
-            if (e.detail === 2) {
-                  Doc.UserDoc().activeDashboard = dashboard;
-                  Doc.UserDoc().activePage = "dashboard";
-            }
-      }
-
-      getDashboards = () => {
-            const allDashbaords = DocListCast(CurrentUserUtils.MyDashboards.data);
-            // TODO: filter the dashboards
-            // return allDashbaords.filter(...)
-            return allDashbaords
-      }
-
-      render() {
-            return <div className="dashboard-view">
-                  <div className="left-menu">
-                        <div className="text-button" onClick={this.newDashboard}>New</div>
-                        <div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.MyDashboards && 'selected'}`}onClick={() => this.selectDashboardGroup(DashboardGroup.MyDashboards) }>My Dashboards</div>
-                        <div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.SharedDashboards && 'selected'}`} onClick={() => this.selectDashboardGroup(DashboardGroup.SharedDashboards) }>Shared Dashboards</div>
-                  </div>
-                  <div className="all-dashboards">
-                        {this.getDashboards().map((dashboard) => {
-                              const href = ImageCast((dashboard.thumb as Doc)?.data)?.url.href;
-                              return <div className="dashboard-container" key={dashboard[Id]} onClick={e => this.clickDashboard(e, dashboard)}>
-                                    <img src={href ?? "https://media.istockphoto.com/photos/hot-air-balloons-flying-over-the-botan-canyon-in-turkey-picture-id1297349747?b=1&k=20&m=1297349747&s=170667a&w=0&h=oH31fJty_4xWl_JQ4OIQWZKP8C6ji9Mz7L4XmEnbqRU="}></img>
-                                    <div className="title"> {StrCast(dashboard.title)} </div>
-                              </div>
-
-                        })}
-
-                  </div>
-            </div>
-      }
-}
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 8486b3d62..9c176a4fd 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -60,9 +60,12 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() {
         @computed get layoutDoc() { return Doc.Layout(this.props.Document); }
         // This is the data part of a document -- ie, the data that is constant across all views of the document
         @computed get dataDoc() { return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym]; }
+
         // key where data is stored
         @computed get fieldKey() { return this.props.fieldKey; }
 
+        lookupField = (field: string) => ScriptCast(this.layoutDoc.lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field, container: this.props.DocumentView?.().props.treeViewDoc ?? this.props.ContainingCollectionDoc }).result;
+
         isContentActive = (outsideReaction?: boolean) => (
             this.props.isContentActive?.() === false ? false :
                 (CurrentUserUtils.SelectedTool !== InkTool.None ||
@@ -92,6 +95,7 @@ export interface ViewBoxAnnotatableProps {
 export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>() {
     class Component extends Touchable<P> {
         @observable _annotationKeySuffix = () => "annotations";
+
         @observable _isAnyChildContentActive = false;
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
         @computed get Document() { return this.props.Document; }
@@ -183,7 +187,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             const targetDataDoc = this.props.Document[DataSym];
             const docList = DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]);
             const added = docs.filter(d => !docList.includes(d));
-            const effectiveAcl = GetEffectiveAcl(targetDataDoc);
+            const effectiveAcl = GetEffectiveAcl(this.dataDoc);
 
             if (added.length) {
                 if (effectiveAcl === AclPrivate || effectiveAcl === AclReadonly) {
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 0bbe473d7..25297e167 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -204,13 +204,58 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <div className="dash-tooltip">{SelectionManager.Views().length > 1 ? "Pin multiple documents to presentation" : "Pin to presentation"}</div>}>
             <div className="documentButtonBar-icon"
                 style={{ color: "white" }}
-                onClick={(e => TabDocView.PinDoc(this.props.views().filter(v => v).map(dv => dv!.rootDoc), {pinDocView: true}))}
-             >
+                onClick={undoBatch(e => this.props.views().map(view => view && this.pinWithView(view.props.Document)))}>
                 <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
             </div>
         </Tooltip>;
     }
 
+    @undoBatch
+    @action
+    pinWithView = (targetDoc: Doc) => {
+        if (targetDoc) {
+            TabDocView.PinDoc(targetDoc);
+            setTimeout(() => {
+                const activeDoc = PresBox.Instance.childDocs[PresBox.Instance.childDocs.length - 1];
+                const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(targetDoc.type as any) || targetDoc._viewType === CollectionViewType.Stacking;
+                const pannable: boolean = ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG);
+                if (scrollable) {
+                    const scroll = targetDoc._scrollTop;
+                    activeDoc.presPinView = true;
+                    activeDoc.presPinViewScroll = scroll;
+                } else if (targetDoc.type === DocumentType.VID) {
+                    activeDoc.presPinView = true;
+                    activeDoc.presStartTime = targetDoc._currentTimecode;
+                    activeDoc.presEndTime = NumCast(targetDoc._currentTimecode) + 0.1;
+                    activeDoc.presPinTimecode = targetDoc._currentTimecode;
+                } else if (pannable) {
+                    const x = targetDoc._panX;
+                    const y = targetDoc._panY;
+                    const scale = targetDoc._viewScale;
+                    activeDoc.presPinView = true;
+                    activeDoc.presPinViewX = x;
+                    activeDoc.presPinViewY = y;
+                    activeDoc.presPinViewScale = scale;
+                } else if (targetDoc.type === DocumentType.COMPARISON) {
+                    const width = targetDoc._clipWidth;
+                    activeDoc.presPinClipWidth = width;
+                    activeDoc.presPinView = true;
+                }
+            });
+        }
+    }
+
+    @computed
+    get pinWithViewButton() {
+        const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: "auto", width: 17, transform: 'translate(0, 1px)' }} />;
+        const targetDoc = this.view0?.props.Document;
+        return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Pin with current view"}</div></>}>
+            <div className="documentButtonBar-icon" onClick={() => this.pinWithView(targetDoc)}>
+                {presPinWithViewIcon}
+            </div>
+        </Tooltip>;
+    }
+
     @computed
     get shareButton() {
         const targetDoc = this.view0?.props.Document;
@@ -335,6 +380,9 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <div className="documentButtonBar-button">
                 {this.pinButton}
             </div>
+            {/* <div className="documentButtonBar-button">
+                {this.pinWithViewButton}
+            </div> */}
             {!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button">
                 {this.shareButton}
             </div>}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index b666b5977..29e088143 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -33,549 +33,548 @@ import React = require("react");
 
 @observer
 export class DocumentDecorations extends React.Component<{ PanelWidth: number, PanelHeight: number, boundsLeft: number, boundsTop: number }, { value: string }> {
-      static Instance: DocumentDecorations;
-      private _resizeHdlId = "";
-      private _keyinput = React.createRef<HTMLInputElement>();
-      private _resizeBorderWidth = 16;
-      private _linkBoxHeight = 20 + 3; // link button height + margin
-      private _titleHeight = 20;
-      private _resizeUndo?: UndoManager.Batch;
-      private _offX = 0; _offY = 0;  // offset from click pt to inner edge of resize border
-      private _snapX = 0; _snapY = 0; // last snapped location of resize border
-      private _dragHeights = new Map<Doc, { start: number, lowest: number }>();
-      private _inkDragDocs: { doc: Doc, x: number, y: number, width: number, height: number }[] = [];
-
-      @observable private _accumulatedTitle = "";
-      @observable private _titleControlString: string = "#title";
-      @observable private _edtingTitle = false;
-      @observable private _hidden = false;
-      @observable public AddToSelection = false; // if Shift is pressed, then this should be set so that clicking on the selection background is ignored so overlapped documents can be added to the selection set.
-      @observable public Interacting = false;
-      @observable public pushIcon: IconProp = "arrow-alt-circle-up";
-      @observable public pullIcon: IconProp = "arrow-alt-circle-down";
-      @observable public pullColor: string = "white";
-
-      constructor(props: any) {
-            super(props);
-            DocumentDecorations.Instance = this;
-            reaction(() => SelectionManager.Views().slice(), action(docs => this._edtingTitle = false));
-      }
-
-      @computed
-      get Bounds() {
-            const views = SelectionManager.Views();
-            return views.filter(dv => dv.props.renderDepth > 0).map(dv => dv.getBounds()).reduce((bounds, rect) =>
-                  !rect ? bounds :
-                        {
-                              x: Math.min(rect.left, bounds.x),
-                              y: Math.min(rect.top, bounds.y),
-                              r: Math.max(rect.right, bounds.r),
-                              b: Math.max(rect.bottom, bounds.b),
-                              c: views.length === 1 ? rect.center : undefined
-                        },
-                  { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE, c: undefined as ({ X: number, Y: number } | undefined) });
-      }
-
-      @action
-      titleBlur = () => {
-            this._edtingTitle = false;
-            if (this._accumulatedTitle.startsWith("#") || this._accumulatedTitle.startsWith("=")) {
-                  this._titleControlString = this._accumulatedTitle;
-            } else if (this._titleControlString.startsWith("#")) {
-                  const titleFieldKey = this._titleControlString.substring(1);
-                  UndoManager.RunInBatch(() => titleFieldKey && SelectionManager.Views().forEach(d => {
-                        if (titleFieldKey === "title") {
-                              d.dataDoc["title-custom"] = !this._accumulatedTitle.startsWith("-");
-                              if (StrCast(d.rootDoc.title).startsWith("@") && !this._accumulatedTitle.startsWith("@")) {
-                                    Doc.RemoveDocFromList(Doc.UserDoc(), "myPublishedDocs", d.rootDoc);
-                              }
-                              if (!StrCast(d.rootDoc.title).startsWith("@") && this._accumulatedTitle.startsWith("@")) {
-                                    Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", d.rootDoc);
-                              }
-                        }
-                        //@ts-ignore
-                        const titleField = (+this._accumulatedTitle === this._accumulatedTitle ? +this._accumulatedTitle : this._accumulatedTitle);
-                        Doc.SetInPlace(d.rootDoc, titleFieldKey, titleField, true);
-
-                        if (d.rootDoc.syncLayoutFieldWithTitle) {
-                              const title = titleField.toString();
-                              const curKey = Doc.LayoutFieldKey(d.rootDoc);
-                              if (curKey !== title && d.dataDoc[title] === undefined) {
-                                    d.rootDoc.layout = FormattedTextBox.LayoutString(title);
-                                    setTimeout(() => {
-                                          const val = d.dataDoc[curKey];
-                                          d.dataDoc[curKey] = undefined;
-                                          d.dataDoc[title] = val;
-                                    });
-                              }
-                        }
-                  }), "title blur");
-            }
-      }
-
-      titleEntered = (e: React.KeyboardEvent) => {
-            if (e.key === "Enter") {
-                  e.stopPropagation();
-                  (e.target as any).blur();
-            }
-      }
-
-      @action onTitleDown = (e: React.PointerEvent): void => {
-            setupMoveUpEvents(this, e, e => this.onBackgroundMove(true, e), (e) => { }, action((e) => {
-                  !this._edtingTitle && (this._accumulatedTitle = this._titleControlString.startsWith("#") ? this.selectionTitle : this._titleControlString);
-                  this._edtingTitle = true;
-                  this._keyinput.current && setTimeout(this._keyinput.current.focus);
-            }));
-      }
-
-      onBackgroundDown = (e: React.PointerEvent) => setupMoveUpEvents(this, e, e => this.onBackgroundMove(false, e), emptyFunction, emptyFunction);
-
-      @action
-      onBackgroundMove = (dragTitle: boolean, e: PointerEvent): boolean => {
-            const dragDocView = SelectionManager.Views()[0];
-            const { left, top } = dragDocView.getBounds() || { left: 0, top: 0 };
-            const dragData = new DragManager.DocumentDragData(SelectionManager.Views().map(dv => dv.props.Document), dragDocView.props.dropAction);
-            dragData.offset = dragDocView.props.ScreenToLocalTransform().transformDirection(e.x - left, e.y - top);
-            dragData.moveDocument = dragDocView.props.moveDocument;
-            dragData.isDocDecorationMove = true;
-            dragData.canEmbed = dragTitle;
-            this._hidden = this.Interacting = true;
-            DragManager.StartDocumentDrag(SelectionManager.Views().map(dv => dv.ContentDiv!), dragData, e.x, e.y, {
-                  dragComplete: action(e => {
-                        dragData.canEmbed && SelectionManager.DeselectAll();
-                        this._hidden = this.Interacting = false;
-                  }),
-                  hideSource: true
-            });
-            return true;
-      }
-
-      _deleteAfterIconify = false;
-      _iconifyBatch: UndoManager.Batch | undefined;
-      onCloseClick = (forceDeleteOrIconify: boolean | undefined) => {
-
+    static Instance: DocumentDecorations;
+    private _resizeHdlId = "";
+    private _keyinput = React.createRef<HTMLInputElement>();
+    private _resizeBorderWidth = 16;
+    private _linkBoxHeight = 20 + 3; // link button height + margin
+    private _titleHeight = 20;
+    private _resizeUndo?: UndoManager.Batch;
+    private _offX = 0; _offY = 0;  // offset from click pt to inner edge of resize border
+    private _snapX = 0; _snapY = 0; // last snapped location of resize border
+    private _dragHeights = new Map<Doc, { start: number, lowest: number }>();
+    private _inkDragDocs: { doc: Doc, x: number, y: number, width: number, height: number }[] = [];
+
+    @observable private _accumulatedTitle = "";
+    @observable private _titleControlString: string = "#title";
+    @observable private _edtingTitle = false;
+    @observable private _hidden = false;
+    @observable public AddToSelection = false; // if Shift is pressed, then this should be set so that clicking on the selection background is ignored so overlapped documents can be added to the selection set.
+    @observable public Interacting = false;
+    @observable public pushIcon: IconProp = "arrow-alt-circle-up";
+    @observable public pullIcon: IconProp = "arrow-alt-circle-down";
+    @observable public pullColor: string = "white";
+
+    constructor(props: any) {
+        super(props);
+        DocumentDecorations.Instance = this;
+        reaction(() => SelectionManager.Views().slice(), action(docs => this._edtingTitle = false));
+    }
+
+    @computed
+    get Bounds() {
+        const views = SelectionManager.Views();
+        return views.filter(dv => dv.props.renderDepth > 0).map(dv => dv.getBounds()).reduce((bounds, rect) =>
+            !rect ? bounds :
+                {
+                    x: Math.min(rect.left, bounds.x),
+                    y: Math.min(rect.top, bounds.y),
+                    r: Math.max(rect.right, bounds.r),
+                    b: Math.max(rect.bottom, bounds.b),
+                    c: views.length === 1 ? rect.center : undefined
+                },
+            { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE, c: undefined as ({ X: number, Y: number } | undefined) });
+    }
+
+    @action
+    titleBlur = () => {
+        this._edtingTitle = false;
+        if (this._accumulatedTitle.startsWith("#") || this._accumulatedTitle.startsWith("=")) {
+            this._titleControlString = this._accumulatedTitle;
+        } else if (this._titleControlString.startsWith("#")) {
+            const titleFieldKey = this._titleControlString.substring(1);
+            UndoManager.RunInBatch(() => titleFieldKey && SelectionManager.Views().forEach(d => {
+                if (titleFieldKey === "title") {
+                    d.dataDoc["title-custom"] = !this._accumulatedTitle.startsWith("-");
+                    if (StrCast(d.rootDoc.title).startsWith("@") && !this._accumulatedTitle.startsWith("@")) {
+                        Doc.RemoveDocFromList(Doc.UserDoc(), "myPublishedDocs", d.rootDoc);
+                    }
+                    if (!StrCast(d.rootDoc.title).startsWith("@") && this._accumulatedTitle.startsWith("@")) {
+                        Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", d.rootDoc);
+                    }
+                }
+                //@ts-ignore
+                const titleField = (+this._accumulatedTitle === this._accumulatedTitle ? +this._accumulatedTitle : this._accumulatedTitle);
+                Doc.SetInPlace(d.rootDoc, titleFieldKey, titleField, true);
+
+                if (d.rootDoc.syncLayoutFieldWithTitle) {
+                    const title = titleField.toString();
+                    const curKey = Doc.LayoutFieldKey(d.rootDoc);
+                    if (curKey !== title && d.dataDoc[title] === undefined) {
+                        d.rootDoc.layout = FormattedTextBox.LayoutString(title);
+                        setTimeout(() => {
+                            const val = d.dataDoc[curKey];
+                            d.dataDoc[curKey] = undefined;
+                            d.dataDoc[title] = val;
+                        });
+                    }
+                }
+            }), "title blur");
+        }
+    }
+
+    titleEntered = (e: React.KeyboardEvent) => {
+        if (e.key === "Enter") {
+            e.stopPropagation();
+            (e.target as any).blur();
+        }
+    }
+
+    @action onTitleDown = (e: React.PointerEvent): void => {
+        setupMoveUpEvents(this, e, e => this.onBackgroundMove(true, e), (e) => { }, action((e) => {
+            !this._edtingTitle && (this._accumulatedTitle = this._titleControlString.startsWith("#") ? this.selectionTitle : this._titleControlString);
+            this._edtingTitle = true;
+            this._keyinput.current && setTimeout(this._keyinput.current.focus);
+        }));
+    }
+
+    onBackgroundDown = (e: React.PointerEvent) => setupMoveUpEvents(this, e, e => this.onBackgroundMove(false, e), emptyFunction, emptyFunction);
+
+    @action
+    onBackgroundMove = (dragTitle: boolean, e: PointerEvent): boolean => {
+        const dragDocView = SelectionManager.Views()[0];
+        const { left, top } = dragDocView.getBounds() || { left: 0, top: 0 };
+        const dragData = new DragManager.DocumentDragData(SelectionManager.Views().map(dv => dv.props.Document), dragDocView.props.dropAction);
+        dragData.offset = dragDocView.props.ScreenToLocalTransform().transformDirection(e.x - left, e.y - top);
+        dragData.moveDocument = dragDocView.props.moveDocument;
+        dragData.isDocDecorationMove = true;
+        dragData.canEmbed = dragTitle;
+        this._hidden = this.Interacting = true;
+        DragManager.StartDocumentDrag(SelectionManager.Views().map(dv => dv.ContentDiv!), dragData, e.x, e.y, {
+            dragComplete: action(e => {
+                dragData.canEmbed && SelectionManager.DeselectAll();
+                this._hidden = this.Interacting = false;
+            }),
+            hideSource: true
+        });
+        return true;
+    }
+
+    _deleteAfterIconify = false;
+    _iconifyBatch: UndoManager.Batch | undefined;
+    onCloseClick = (forceDeleteOrIconify: boolean | undefined) => {
+        if (this.canDelete) {
             const views = SelectionManager.Views().slice().filter(v => v);
             if (forceDeleteOrIconify === false && this._iconifyBatch) return;
             this._deleteAfterIconify = forceDeleteOrIconify || this._iconifyBatch ? true : false;
             if (!this._iconifyBatch) {
-                  this._iconifyBatch = UndoManager.StartBatch("iconifying");
+                this._iconifyBatch = UndoManager.StartBatch("iconifying");
             } else {
-                  forceDeleteOrIconify = false; // can't force immediate close in the middle of iconifying -- have to wait until iconifying completes
+                forceDeleteOrIconify = false; // can't force immediate close in the middle of iconifying -- have to wait until iconifying completes
             }
             var iconifyingCount = views.length;
             const finished = action((force?: boolean) => {
-                  if ((force || --iconifyingCount === 0) && this._iconifyBatch) {
-                        if (this._deleteAfterIconify) {
-                              views.forEach(iconView => iconView.props.removeDocument?.(iconView.props.Document));
-                              SelectionManager.DeselectAll();
-                        }
-                        this._iconifyBatch?.end();
-                        this._iconifyBatch = undefined;
-                  }
+                if ((force || --iconifyingCount === 0) && this._iconifyBatch) {
+                    if (this._deleteAfterIconify) {
+                        views.forEach(iconView => iconView.props.removeDocument?.(iconView.props.Document));
+                        SelectionManager.DeselectAll();
+                    }
+                    this._iconifyBatch?.end();
+                    this._iconifyBatch = undefined;
+                }
             });
             if (forceDeleteOrIconify) finished(forceDeleteOrIconify);
             else if (!this._deleteAfterIconify) views.forEach(dv => dv.iconify(finished));
+        }
+    }
+    onMaximizeDown = (e: React.PointerEvent) => {
+        setupMoveUpEvents(this, e, () => {
+            DragManager.StartWindowDrag?.(e, [SelectionManager.Views().slice(-1)[0].rootDoc]);
+            return true;
+        }, emptyFunction, this.onMaximizeClick, false, false);
+    }
+
+    onMaximizeClick = (e: any): void => {
+        const selectedDocs = SelectionManager.Views();
+        if (selectedDocs.length) {
+            if (e.ctrlKey) {    // open an alias in a new tab with Ctrl Key
+                const bestAlias = DocListCast(selectedDocs[0].props.Document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
+                CollectionDockingView.AddSplit(bestAlias ?? Doc.MakeAlias(selectedDocs[0].props.Document), "right");
+            } else if (e.shiftKey) {   // open centered in a new workspace with Shift Key
+                const alias = Doc.MakeAlias(selectedDocs[0].props.Document);
+                alias.context = undefined;
+                alias.x = -alias[WidthSym]() / 2;
+                alias.y = -alias[HeightSym]() / 2;
+                CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([alias], { title: "Tab for " + alias.title }), "right");
+            } else if (e.altKey) {    // open same document in new tab
+                CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, "right");
+            } else {
+                var openDoc = selectedDocs[0].props.Document;
+                if (openDoc.layoutKey === "layout_icon") {
+                    openDoc = DocListCast(openDoc.aliases).find(alias => !alias.context) ?? Doc.MakeAlias(openDoc);
+                    Doc.deiconifyView(openDoc);
+                }
+                LightboxView.SetLightboxDoc(openDoc, undefined, selectedDocs.slice(1).map(view => view.props.Document));
+            }
+        }
+        SelectionManager.DeselectAll();
+    }
+
+    onIconifyClick = (): void => {
+        SelectionManager.Views().forEach(dv => dv?.iconify());
+        SelectionManager.DeselectAll();
+    }
+
+    onSelectorClick = () => SelectionManager.Views()?.[0]?.props.ContainingCollectionView?.props.select(false);
+
+    onRadiusDown = (e: React.PointerEvent): void => {
+        this._resizeUndo = UndoManager.StartBatch("DocDecs set radius");
+        setupMoveUpEvents(this, e, (e, down) => {
+            const dist = Math.sqrt((e.clientX - down[0]) * (e.clientX - down[0]) + (e.clientY - down[1]) * (e.clientY - down[1]));
+            SelectionManager.Views().map(dv => dv.props.Document).map(doc => doc.layout instanceof Doc ? doc.layout : doc.isTemplateForField ? doc : Doc.GetProto(doc)).
+                map(d => d.borderRounding = `${Math.max(0, dist < 3 ? 0 : dist)}px`);
+            return false;
+        }, (e) => this._resizeUndo?.end(), (e) => { });
+    }
+
+    @action
+    onRotateDown = (e: React.PointerEvent): void => {
+        const rotateUndo = UndoManager.StartBatch("rotatedown");
+        const selectedInk = SelectionManager.Views().filter(i => i.ComponentView instanceof InkingStroke);
+        const centerPoint = !selectedInk.length ? { X: this.Bounds.x, Y: this.Bounds.y } : { X: this.Bounds.c?.X ?? (this.Bounds.x + this.Bounds.r) / 2, Y: this.Bounds.c?.Y ?? (this.Bounds.y + this.Bounds.b) / 2 };
+        setupMoveUpEvents(this, e,
+            (e: PointerEvent, down: number[], delta: number[]) => {
+                const previousPoint = { X: e.clientX, Y: e.clientY };
+                const movedPoint = { X: e.clientX - delta[0], Y: e.clientY - delta[1] };
+                const angle = InkStrokeProperties.angleChange(previousPoint, movedPoint, centerPoint);
+                if (selectedInk.length) {
+                    angle && InkStrokeProperties.Instance.rotateInk(selectedInk, -angle, centerPoint);
+                } else {
+                    SelectionManager.Views().forEach(dv => dv.rootDoc._jitterRotation = NumCast(dv.rootDoc._jitterRotation) - angle * 180 / Math.PI);
+                }
+                return false;
+            },
+            () => {
+                rotateUndo?.end();
+                UndoManager.FilterBatches(["data", "x", "y", "width", "height"]);
+            },
+            emptyFunction);
+    }
+
+    @action
+    onPointerDown = (e: React.PointerEvent): void => {
+        DragManager.docsBeingDragged = SelectionManager.Views().map(dv => dv.rootDoc);
+        this._inkDragDocs = DragManager.docsBeingDragged
+            .filter(doc => doc.type === DocumentType.INK)
+            .map(doc => {
+                if (InkStrokeProperties.Instance._lock) {
+                    Doc.SetNativeHeight(doc, NumCast(doc._height));
+                    Doc.SetNativeWidth(doc, NumCast(doc._width));
+                }
+                return ({ doc, x: NumCast(doc.x), y: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) });
+            });
 
-      }
-      onMaximizeDown = (e: React.PointerEvent) => {
-            setupMoveUpEvents(this, e, () => {
-                  DragManager.StartWindowDrag?.(e, [SelectionManager.Views().slice(-1)[0].rootDoc]);
-                  return true;
-            }, emptyFunction, this.onMaximizeClick, false, false);
-      }
-
-      onMaximizeClick = (e: any): void => {
-            const selectedDocs = SelectionManager.Views();
-            if (selectedDocs.length) {
-                  if (e.ctrlKey) {    // open an alias in a new tab with Ctrl Key
-                        const bestAlias = DocListCast(selectedDocs[0].props.Document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
-                        CollectionDockingView.AddSplit(bestAlias ?? Doc.MakeAlias(selectedDocs[0].props.Document), "right");
-                  } else if (e.shiftKey) {   // open centered in a new workspace with Shift Key
-                        const alias = Doc.MakeAlias(selectedDocs[0].props.Document);
-                        alias.context = undefined;
-                        alias.x = -alias[WidthSym]() / 2;
-                        alias.y = -alias[HeightSym]() / 2;
-                        CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([alias], { title: "Tab for " + alias.title }), "right");
-                  } else if (e.altKey) {    // open same document in new tab
-                        CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, "right");
-                  } else {
-                        var openDoc = selectedDocs[0].props.Document;
-                        if (openDoc.layoutKey === "layout_icon") {
-                              openDoc = DocListCast(openDoc.aliases).find(alias => !alias.context) ?? Doc.MakeAlias(openDoc);
-                              Doc.deiconifyView(openDoc);
+        setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
+        this.Interacting = true; // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them
+        this._resizeHdlId = e.currentTarget.className;
+        const bounds = e.currentTarget.getBoundingClientRect();
+        this._offX = this._resizeHdlId.toLowerCase().includes("left") ? bounds.right - e.clientX : bounds.left - e.clientX;
+        this._offY = this._resizeHdlId.toLowerCase().includes("top") ? bounds.bottom - e.clientY : bounds.top - e.clientY;
+        this._resizeUndo = UndoManager.StartBatch("DocDecs resize");
+        this._snapX = e.pageX;
+        this._snapY = e.pageY;
+        const ffviewSet = new Set<CollectionFreeFormView>();
+        SelectionManager.Views().forEach(docView => {
+            const ffview = docView.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+            ffview && ffviewSet.add(ffview);
+            this._dragHeights.set(docView.layoutDoc, { start: NumCast(docView.rootDoc._height), lowest: NumCast(docView.rootDoc._height) });
+        });
+        Array.from(ffviewSet).map(ffview => ffview.setupDragLines(false));
+    }
+
+    onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
+        const first = SelectionManager.Views()[0];
+        let thisPt = { x: e.clientX - this._offX, y: e.clientY - this._offY };
+        var fixedAspect = Doc.NativeAspect(first.layoutDoc);
+        InkStrokeProperties.Instance._lock && SelectionManager.Views().filter(dv => dv.rootDoc.type === DocumentType.INK)
+            .forEach(dv => fixedAspect = Doc.NativeAspect(dv.rootDoc));
+
+        const resizeHdl = this._resizeHdlId.split(" ")[0];
+        if (fixedAspect && (resizeHdl === "documentDecorations-bottomRightResizer" || resizeHdl === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles
+            const project = (p: number[], a: number[], b: number[]) => {
+                const atob = [b[0] - a[0], b[1] - a[1]];
+                const atop = [p[0] - a[0], p[1] - a[1]];
+                const len = atob[0] * atob[0] + atob[1] * atob[1];
+                let dot = atop[0] * atob[0] + atop[1] * atob[1];
+                const t = dot / len;
+                dot = (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]);
+                return [a[0] + atob[0] * t, a[1] + atob[1] * t];
+            };
+            const tl = first.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+            const drag = project([e.clientX + this._offX, e.clientY + this._offY], tl, [tl[0] + fixedAspect, tl[1] + 1]);
+            thisPt = DragManager.snapDragAspect(drag, fixedAspect);
+        } else {
+            thisPt = DragManager.snapDrag(e, -this._offX, -this._offY, this._offX, this._offY);
+        }
+
+        move[0] = thisPt.x - this._snapX;
+        move[1] = thisPt.y - this._snapY;
+        this._snapX = thisPt.x;
+        this._snapY = thisPt.y;
+        let dragBottom = false, dragRight = false, dragBotRight = false;
+        let dX = 0, dY = 0, dW = 0, dH = 0;
+        switch (this._resizeHdlId.split(" ")[0]) {
+            case "": break;
+            case "documentDecorations-topLeftResizer":
+                dX = -1;
+                dY = -1;
+                dW = -move[0];
+                dH = -move[1];
+                break;
+            case "documentDecorations-topRightResizer":
+                dW = move[0];
+                dY = -1;
+                dH = -move[1];
+                break;
+            case "documentDecorations-topResizer":
+                dY = -1;
+                dH = -move[1];
+                dragBottom = true;
+                break;
+            case "documentDecorations-bottomLeftResizer":
+                dX = -1;
+                dW = -move[0];
+                dH = move[1];
+                break;
+            case "documentDecorations-bottomRightResizer":
+                dW = move[0];
+                dH = move[1];
+                dragBotRight = true;
+                break;
+            case "documentDecorations-bottomResizer":
+                dH = move[1];
+                dragBottom = true;
+                break;
+            case "documentDecorations-leftResizer":
+                dX = -1;
+                dW = -move[0];
+                break;
+            case "documentDecorations-rightResizer":
+                dW = move[0];
+                dragRight = true;
+                break;
+        }
+
+        SelectionManager.Views().forEach(action((docView: DocumentView) => {
+            if (e.ctrlKey && !Doc.NativeHeight(docView.props.Document)) docView.toggleNativeDimensions();
+            if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
+                const doc = Document(docView.rootDoc);
+                const nwidth = docView.nativeWidth;
+                const nheight = docView.nativeHeight;
+                const docheight = doc._height || 0;
+                const docwidth = doc._width || 0;
+                const width = docwidth;
+                let height = (docheight || (nheight / nwidth * width));
+                height = !height || isNaN(height) ? 20 : height;
+                const scale = docView.props.ScreenToLocalTransform().Scale;
+                const modifyNativeDim = (e.ctrlKey || doc.forceReflow) && doc.nativeDimModifiable;
+                if (nwidth && nheight) {
+                    if (nwidth / nheight !== width / height && !dragBottom) {
+                        height = nheight / nwidth * width;
+                    }
+                    if (modifyNativeDim && !dragBottom) { // ctrl key enables modification of the nativeWidth or nativeHeight durin the interaction
+                        if (Math.abs(dW) > Math.abs(dH)) dH = dW * nheight / nwidth;
+                        else dW = dH * nwidth / nheight;
+                    }
+                }
+                let actualdW = Math.max(width + (dW * scale), 20);
+                let actualdH = Math.max(height + (dH * scale), 20);
+                const fixedAspect = (nwidth && nheight && !doc._fitWidth);
+                if (fixedAspect) {
+                    if ((Math.abs(dW) > Math.abs(dH) && (!dragBottom || !modifyNativeDim)) || dragRight) {
+                        if (dragRight && modifyNativeDim) {
+                            doc._nativeWidth = actualdW / (doc._width || 1) * Doc.NativeWidth(doc);
+                        } else {
+                            if (!doc._fitWidth) {
+                                actualdH = nheight / nwidth * actualdW;
+                                doc._height = actualdH;
+                            }
+                            else if (!modifyNativeDim || dragBotRight) doc._height = actualdH;
                         }
-                        LightboxView.SetLightboxDoc(openDoc, undefined, selectedDocs.slice(1).map(view => view.props.Document));
-                  }
-            }
-            SelectionManager.DeselectAll();
-      }
-
-      onIconifyClick = (): void => {
-            SelectionManager.Views().forEach(dv => dv?.iconify());
-            SelectionManager.DeselectAll();
-      }
-
-      onSelectorClick = () => SelectionManager.Views()?.[0]?.props.ContainingCollectionView?.props.select(false);
-
-      onRadiusDown = (e: React.PointerEvent): void => {
-            this._resizeUndo = UndoManager.StartBatch("DocDecs set radius");
-            setupMoveUpEvents(this, e, (e, down) => {
-                  const dist = Math.sqrt((e.clientX - down[0]) * (e.clientX - down[0]) + (e.clientY - down[1]) * (e.clientY - down[1]));
-                  SelectionManager.Views().map(dv => dv.props.Document).map(doc => doc.layout instanceof Doc ? doc.layout : doc.isTemplateForField ? doc : Doc.GetProto(doc)).
-                        map(d => d.borderRounding = `${Math.max(0, dist < 3 ? 0 : dist)}px`);
-                  return false;
-            }, (e) => this._resizeUndo?.end(), (e) => { });
-      }
-
-      @action
-      onRotateDown = (e: React.PointerEvent): void => {
-            const rotateUndo = UndoManager.StartBatch("rotatedown");
-            const selectedInk = SelectionManager.Views().filter(i => i.ComponentView instanceof InkingStroke);
-            const centerPoint = !selectedInk.length ? { X: this.Bounds.x, Y: this.Bounds.y } : { X: this.Bounds.c?.X ?? (this.Bounds.x + this.Bounds.r) / 2, Y: this.Bounds.c?.Y ?? (this.Bounds.y + this.Bounds.b) / 2 };
-            setupMoveUpEvents(this, e,
-                  (e: PointerEvent, down: number[], delta: number[]) => {
-                        const previousPoint = { X: e.clientX, Y: e.clientY };
-                        const movedPoint = { X: e.clientX - delta[0], Y: e.clientY - delta[1] };
-                        const angle = InkStrokeProperties.angleChange(previousPoint, movedPoint, centerPoint);
-                        if (selectedInk.length) {
-                              angle && InkStrokeProperties.Instance.rotateInk(selectedInk, -angle, centerPoint);
+                        doc._width = actualdW;
+                    }
+                    else {
+                        if (dragBottom && (modifyNativeDim ||
+                            (docView.layoutDoc.nativeHeightUnfrozen && docView.layoutDoc._fitWidth))) { // frozen web pages, PDFs, and some RTFS have frozen nativewidth/height.  But they are marked to allow their nativeHeight to be explicitly modified with fitWidth and vertical resizing. (ie, with fitWidth they can't grow horizontally to match a vertical resize so it makes more sense to change their nativeheight even if the ctrl key isn't used)
+                            doc._nativeHeight = actualdH / (doc._height || 1) * Doc.NativeHeight(doc);
+                            doc._autoHeight = false;
                         } else {
-                              SelectionManager.Views().forEach(dv => dv.rootDoc._jitterRotation = NumCast(dv.rootDoc._jitterRotation) - angle * 180 / Math.PI);
+                            if (!doc._fitWidth) {
+                                actualdW = nwidth / nheight * actualdH;
+                                doc._width = actualdW;
+                            }
+                            else if (!modifyNativeDim || dragBotRight) doc._width = actualdW;
                         }
-                        return false;
-                  },
-                  () => {
-                        rotateUndo?.end();
-                        UndoManager.FilterBatches(["data", "x", "y", "width", "height"]);
-                  },
-                  emptyFunction);
-      }
-
-      @action
-      onPointerDown = (e: React.PointerEvent): void => {
-            DragManager.docsBeingDragged = SelectionManager.Views().map(dv => dv.rootDoc);
-            this._inkDragDocs = DragManager.docsBeingDragged
-                  .filter(doc => doc.type === DocumentType.INK)
-                  .map(doc => {
-                        if (InkStrokeProperties.Instance._lock) {
-                              Doc.SetNativeHeight(doc, NumCast(doc._height));
-                              Doc.SetNativeWidth(doc, NumCast(doc._width));
+                        if (!modifyNativeDim) {
+                            actualdH = Math.min(nheight / nwidth * NumCast(doc._width), actualdH);
+                            doc._height = actualdH;
                         }
-                        return ({ doc, x: NumCast(doc.x), y: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) });
-                  });
-
-            setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
-            this.Interacting = true; // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them
-            this._resizeHdlId = e.currentTarget.className;
-            const bounds = e.currentTarget.getBoundingClientRect();
-            this._offX = this._resizeHdlId.toLowerCase().includes("left") ? bounds.right - e.clientX : bounds.left - e.clientX;
-            this._offY = this._resizeHdlId.toLowerCase().includes("top") ? bounds.bottom - e.clientY : bounds.top - e.clientY;
-            this._resizeUndo = UndoManager.StartBatch("DocDecs resize");
-            this._snapX = e.pageX;
-            this._snapY = e.pageY;
-            const ffviewSet = new Set<CollectionFreeFormView>();
-            SelectionManager.Views().forEach(docView => {
-                  const ffview = docView.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
-                  ffview && ffviewSet.add(ffview);
-                  this._dragHeights.set(docView.layoutDoc, { start: NumCast(docView.rootDoc._height), lowest: NumCast(docView.rootDoc._height) });
-            });
-            Array.from(ffviewSet).map(ffview => ffview.setupDragLines(false));
-      }
-
-      onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
-            const first = SelectionManager.Views()[0];
-            let thisPt = { x: e.clientX - this._offX, y: e.clientY - this._offY };
-            var fixedAspect = Doc.NativeAspect(first.layoutDoc);
-            InkStrokeProperties.Instance._lock && SelectionManager.Views().filter(dv => dv.rootDoc.type === DocumentType.INK)
-                  .forEach(dv => fixedAspect = Doc.NativeAspect(dv.rootDoc));
-
-            const resizeHdl = this._resizeHdlId.split(" ")[0];
-            if (fixedAspect && (resizeHdl === "documentDecorations-bottomRightResizer" || resizeHdl === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles
-                  const project = (p: number[], a: number[], b: number[]) => {
-                        const atob = [b[0] - a[0], b[1] - a[1]];
-                        const atop = [p[0] - a[0], p[1] - a[1]];
-                        const len = atob[0] * atob[0] + atob[1] * atob[1];
-                        let dot = atop[0] * atob[0] + atop[1] * atob[1];
-                        const t = dot / len;
-                        dot = (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]);
-                        return [a[0] + atob[0] * t, a[1] + atob[1] * t];
-                  };
-                  const tl = first.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
-                  const drag = project([e.clientX + this._offX, e.clientY + this._offY], tl, [tl[0] + fixedAspect, tl[1] + 1]);
-                  thisPt = DragManager.snapDragAspect(drag, fixedAspect);
-            } else {
-                  thisPt = DragManager.snapDrag(e, -this._offX, -this._offY, this._offX, this._offY);
+                        else doc._height = actualdH;
+                    }
+                } else {
+                    dH && (doc._height = actualdH);
+                    dW && (doc._width = actualdW);
+                    dH && (doc._autoHeight = false);
+                }
+                doc.x = (doc.x || 0) + dX * (actualdW - docwidth);
+                doc.y = (doc.y || 0) + dY * (actualdH - docheight);
+                doc._lastModified = new DateField();
             }
-
-            move[0] = thisPt.x - this._snapX;
-            move[1] = thisPt.y - this._snapY;
-            this._snapX = thisPt.x;
-            this._snapY = thisPt.y;
-            let dragBottom = false, dragRight = false, dragBotRight = false;
-            let dX = 0, dY = 0, dW = 0, dH = 0;
-            switch (this._resizeHdlId.split(" ")[0]) {
-                  case "": break;
-                  case "documentDecorations-topLeftResizer":
-                        dX = -1;
-                        dY = -1;
-                        dW = -move[0];
-                        dH = -move[1];
-                        break;
-                  case "documentDecorations-topRightResizer":
-                        dW = move[0];
-                        dY = -1;
-                        dH = -move[1];
-                        break;
-                  case "documentDecorations-topResizer":
-                        dY = -1;
-                        dH = -move[1];
-                        dragBottom = true;
-                        break;
-                  case "documentDecorations-bottomLeftResizer":
-                        dX = -1;
-                        dW = -move[0];
-                        dH = move[1];
-                        break;
-                  case "documentDecorations-bottomRightResizer":
-                        dW = move[0];
-                        dH = move[1];
-                        dragBotRight = true;
-                        break;
-                  case "documentDecorations-bottomResizer":
-                        dH = move[1];
-                        dragBottom = true;
-                        break;
-                  case "documentDecorations-leftResizer":
-                        dX = -1;
-                        dW = -move[0];
-                        break;
-                  case "documentDecorations-rightResizer":
-                        dW = move[0];
-                        dragRight = true;
-                        break;
+            const val = this._dragHeights.get(docView.layoutDoc);
+            if (val) this._dragHeights.set(docView.layoutDoc, { start: val.start, lowest: Math.min(val.lowest, NumCast(docView.layoutDoc._height)) });
+        }));
+        return false;
+    }
+
+    @action
+    onPointerUp = (e: PointerEvent): void => {
+        this._resizeHdlId = "";
+        this.Interacting = false;
+        this._resizeUndo?.end();
+        SnappingManager.clearSnapLines();
+
+        // detect autoHeight gesture and apply
+        SelectionManager.Views().map(docView => ({ doc: docView.layoutDoc, hgts: this._dragHeights.get(docView.layoutDoc) }))
+            .filter(pair => pair.hgts && pair.hgts.lowest < pair.hgts.start && pair.hgts.lowest <= 20)
+            .forEach(pair => pair.doc._autoHeight = true);
+        //need to change points for resize, or else rotation/control points will fail.
+        this._inkDragDocs.map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] }))
+            .forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => {
+                Doc.GetProto(doc).data = new InkField(inkPts.map(ipt =>  // (new x — oldx) + newWidth * (oldxpoint /oldWidth)
+                ({
+                    X: (NumCast(doc.x) - x) + NumCast(doc.width) * ipt.X / width,
+                    Y: (NumCast(doc.y) - y) + NumCast(doc.height) * ipt.Y / height
+                })));
+                Doc.SetNativeWidth(doc, undefined);
+                Doc.SetNativeHeight(doc, undefined);
+            });
+    }
+
+    @computed
+    get selectionTitle(): string {
+        if (SelectionManager.Views().length === 1) {
+            const selected = SelectionManager.Views()[0];
+            if (selected.ComponentView?.getTitle?.()) {
+                return selected.ComponentView.getTitle();
             }
-
-            SelectionManager.Views().forEach(action((docView: DocumentView) => {
-                  if (e.ctrlKey && !Doc.NativeHeight(docView.props.Document)) docView.toggleNativeDimensions();
-                  if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
-                        const doc = Document(docView.rootDoc);
-                        const nwidth = docView.nativeWidth;
-                        const nheight = docView.nativeHeight;
-                        const docheight = doc._height || 0;
-                        const docwidth = doc._width || 0;
-                        const width = docwidth;
-                        let height = (docheight || (nheight / nwidth * width));
-                        height = !height || isNaN(height) ? 20 : height;
-                        const scale = docView.props.ScreenToLocalTransform().Scale;
-                        const modifyNativeDim = (e.ctrlKey || doc.forceReflow) && doc.nativeDimModifiable;
-                        if (nwidth && nheight) {
-                              if (nwidth / nheight !== width / height && !dragBottom) {
-                                    height = nheight / nwidth * width;
-                              }
-                              if (modifyNativeDim && !dragBottom) { // ctrl key enables modification of the nativeWidth or nativeHeight durin the interaction
-                                    if (Math.abs(dW) > Math.abs(dH)) dH = dW * nheight / nwidth;
-                                    else dW = dH * nwidth / nheight;
-                              }
-                        }
-                        let actualdW = Math.max(width + (dW * scale), 20);
-                        let actualdH = Math.max(height + (dH * scale), 20);
-                        const fixedAspect = (nwidth && nheight && !doc._fitWidth);
-                        if (fixedAspect) {
-                              if ((Math.abs(dW) > Math.abs(dH) && (!dragBottom || !modifyNativeDim)) || dragRight) {
-                                    if (dragRight && modifyNativeDim) {
-                                          doc._nativeWidth = actualdW / (doc._width || 1) * Doc.NativeWidth(doc);
-                                    } else {
-                                          if (!doc._fitWidth) {
-                                                actualdH = nheight / nwidth * actualdW;
-                                                doc._height = actualdH;
-                                          }
-                                          else if (!modifyNativeDim || dragBotRight) doc._height = actualdH;
-                                    }
-                                    doc._width = actualdW;
-                              }
-                              else {
-                                    if (dragBottom && (modifyNativeDim ||
-                                          (docView.layoutDoc.nativeHeightUnfrozen && docView.layoutDoc._fitWidth))) { // frozen web pages, PDFs, and some RTFS have frozen nativewidth/height.  But they are marked to allow their nativeHeight to be explicitly modified with fitWidth and vertical resizing. (ie, with fitWidth they can't grow horizontally to match a vertical resize so it makes more sense to change their nativeheight even if the ctrl key isn't used)
-                                          doc._nativeHeight = actualdH / (doc._height || 1) * Doc.NativeHeight(doc);
-                                          doc._autoHeight = false;
-                                    } else {
-                                          if (!doc._fitWidth) {
-                                                actualdW = nwidth / nheight * actualdH;
-                                                doc._width = actualdW;
-                                          }
-                                          else if (!modifyNativeDim || dragBotRight) doc._width = actualdW;
-                                    }
-                                    if (!modifyNativeDim) {
-                                          actualdH = Math.min(nheight / nwidth * NumCast(doc._width), actualdH);
-                                          doc._height = actualdH;
-                                    }
-                                    else doc._height = actualdH;
-                              }
-                        } else {
-                              dH && (doc._height = actualdH);
-                              dW && (doc._width = actualdW);
-                              dH && (doc._autoHeight = false);
-                        }
-                        doc.x = (doc.x || 0) + dX * (actualdW - docwidth);
-                        doc.y = (doc.y || 0) + dY * (actualdH - docheight);
-                        doc._lastModified = new DateField();
-                  }
-                  const val = this._dragHeights.get(docView.layoutDoc);
-                  if (val) this._dragHeights.set(docView.layoutDoc, { start: val.start, lowest: Math.min(val.lowest, NumCast(docView.layoutDoc._height)) });
-            }));
-            return false;
-      }
-
-      @action
-      onPointerUp = (e: PointerEvent): void => {
-            this._resizeHdlId = "";
-            this.Interacting = false;
-            this._resizeUndo?.end();
-            SnappingManager.clearSnapLines();
-
-            // detect autoHeight gesture and apply
-            SelectionManager.Views().map(docView => ({ doc: docView.layoutDoc, hgts: this._dragHeights.get(docView.layoutDoc) }))
-                  .filter(pair => pair.hgts && pair.hgts.lowest < pair.hgts.start && pair.hgts.lowest <= 20)
-                  .forEach(pair => pair.doc._autoHeight = true);
-            //need to change points for resize, or else rotation/control points will fail.
-            this._inkDragDocs.map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] }))
-                  .forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => {
-                        Doc.GetProto(doc).data = new InkField(inkPts.map(ipt =>  // (new x — oldx) + newWidth * (oldxpoint /oldWidth)
-                        ({
-                              X: (NumCast(doc.x) - x) + NumCast(doc.width) * ipt.X / width,
-                              Y: (NumCast(doc.y) - y) + NumCast(doc.height) * ipt.Y / height
-                        })));
-                        Doc.SetNativeWidth(doc, undefined);
-                        Doc.SetNativeHeight(doc, undefined);
-                  });
-      }
-
-      @computed
-      get selectionTitle(): string {
-            if (SelectionManager.Views().length === 1) {
-                  const selected = SelectionManager.Views()[0];
-                  if (selected.ComponentView?.getTitle?.()) {
-                        return selected.ComponentView.getTitle();
-                  }
-                  if (this._titleControlString.startsWith("=")) {
-                        return ScriptField.MakeFunction(this._titleControlString.substring(1), { doc: Doc.name })!.script.run({ self: selected.rootDoc, this: selected.layoutDoc }, console.log).result?.toString() || "";
-                  }
-                  if (this._titleControlString.startsWith("#")) {
-                        return Field.toString(selected.props.Document[this._titleControlString.substring(1)] as Field) || "-unset-";
-                  }
-                  return this._accumulatedTitle;
+            if (this._titleControlString.startsWith("=")) {
+                return ScriptField.MakeFunction(this._titleControlString.substring(1), { doc: Doc.name })!.script.run({ self: selected.rootDoc, this: selected.layoutDoc }, console.log).result?.toString() || "";
             }
-            return SelectionManager.Views().length > 1 ? "-multiple-" : "-unset-";
-      }
-
-      @computed get hasIcons() {
-            return SelectionManager.Views().some(docView => docView.rootDoc.layoutKey === "layout_icon");
-      }
-
-      render() {
-            const bounds = this.Bounds;
-            const seldoc = SelectionManager.Views().slice(-1)[0];
-            if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
-                  return (null);
+            if (this._titleControlString.startsWith("#")) {
+                return Field.toString(selected.props.Document[this._titleControlString.substring(1)] as Field) || "-unset-";
             }
-            // hide the decorations if the parent chooses to hide it or if the document itself hides it
-            const hideResizers = seldoc.props.hideResizeHandles || seldoc.rootDoc.hideResizeHandles || seldoc.rootDoc._isGroup;
-            const hideTitle = seldoc.props.hideDecorationTitle || seldoc.rootDoc.hideDecorationTitle;
-            const hideDocumentButtonBar = seldoc.props.hideDocumentButtonBar || seldoc.rootDoc.hideDocumentButtonBar;
-            // if multiple documents have been opened at the same time, then don't show open button
-            const hideOpenButton = seldoc.props.hideOpenButton || seldoc.rootDoc.hideOpenButton ||
-                  SelectionManager.Views().some(docView => docView.props.Document._stayInCollection || docView.props.Document.isGroup || docView.props.Document.hideOpenButton);
-            const hideDeleteButton = seldoc.props.hideDeleteButton || seldoc.rootDoc.hideDeleteButton ||
-                  SelectionManager.Views().some(docView => {
-                        const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit;
-                        return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
-                  });
-            const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
-                  <Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top">
-                        <div className={`documentDecorations-${key}Button`} onContextMenu={e => e.preventDefault()}
-                              onPointerDown={pointerDown ?? (e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(e => click!(e))))} >
-                              <FontAwesomeIcon icon={icon as any} />
-                        </div>
-                  </Tooltip>);
-
-            const colorScheme = StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme);
-            const titleArea = hideTitle ? (null) :
-                  this._edtingTitle ?
-                        <input ref={this._keyinput} className={`documentDecorations-title${colorScheme}`}
-                              type="text" name="dynbox" autoComplete="on"
-                              value={this._accumulatedTitle}
-                              onBlur={e => this.titleBlur()}
-                              onChange={action(e => this._accumulatedTitle = e.target.value)}
-                              onKeyDown={this.titleEntered} /> :
-                        <div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown} >
-                              <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${this.selectionTitle}`}</span>
-                        </div>;
-
-
-            const leftBounds = this.props.boundsLeft;
-            const topBounds = LightboxView.LightboxDoc ? 0 : this.props.boundsTop;
-            bounds.x = Math.max(leftBounds, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2;
-            bounds.y = Math.max(topBounds, bounds.y - this._resizeBorderWidth / 2 - this._titleHeight) + this._resizeBorderWidth / 2 + this._titleHeight;
-            const borderRadiusDraggerWidth = 15;
-            bounds.r = Math.max(bounds.x, Math.max(leftBounds, Math.min(window.innerWidth, bounds.r + borderRadiusDraggerWidth + this._resizeBorderWidth / 2) - this._resizeBorderWidth / 2 - borderRadiusDraggerWidth));
-            bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight));
-
-            const useRotation = seldoc.ComponentView instanceof InkingStroke || seldoc.ComponentView instanceof ImageBox;
-            const resizerScheme = colorScheme ? "documentDecorations-resizer" + colorScheme : "";
-
-            const rotation = NumCast(seldoc.rootDoc._jitterRotation);
-
-            return (<div className={`documentDecorations${colorScheme}`}>
-                  <div className="documentDecorations-background" style={{
-                        transform: `rotate(${rotation}deg)`,
-                        transformOrigin: "top  left",
-                        width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
-                        height: (bounds.b - bounds.y + this._resizeBorderWidth) + "px",
-                        left: bounds.x - this._resizeBorderWidth / 2,
-                        top: bounds.y - this._resizeBorderWidth / 2,
-                        pointerEvents: DocumentDecorations.Instance.AddToSelection || this.Interacting ? "none" : "all",
-                        display: SelectionManager.Views().length <= 1 ? "none" : undefined
-                  }} onPointerDown={this.onBackgroundDown} onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} />
-                  {bounds.r - bounds.x < 15 && bounds.b - bounds.y < 15 ? (null) : <>
-                        <div className="documentDecorations-container" key="container" style={{
-                              transform: ` translate(${bounds.x - this._resizeBorderWidth / 2}px, ${bounds.y - this._resizeBorderWidth / 2 - this._titleHeight}px) rotate(${rotation}deg)`,
-                              transformOrigin: `8px 26px`,
-                              width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
-                              height: (bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight) + "px",
-                        }}>
-                              {hideDeleteButton ? <div /> : topBtn("close", this.hasIcons ? "times" : "window-maximize", undefined, e => this.onCloseClick(this.hasIcons ? true : undefined), "Close")}
-                              {titleArea}
-                              {hideOpenButton ? (null) : topBtn("open", "external-link-alt", this.onMaximizeDown, undefined, "Open in Tab (ctrl: as alias, shift: in new collection)")}
-                              {hideResizers ? (null) :
-                                    <>
-                                          <div key="tl" className={`documentDecorations-topLeftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                                          <div key="t" className={`documentDecorations-topResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                                          <div key="tr" className={`documentDecorations-topRightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                                          <div key="l" className={`documentDecorations-leftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                                          <div key="c" className={`documentDecorations-centerCont ${resizerScheme}`}></div>
-                                          <div key="r" className={`documentDecorations-rightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                                          <div key="bl" className={`documentDecorations-bottomLeftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                                          <div key="b" className={`documentDecorations-bottomResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                                          <div key="br" className={`documentDecorations-bottomRightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-
-                                          {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) :
-                                                topBtn("selector", "arrow-alt-circle-up", undefined, this.onSelectorClick, "tap to select containing document")}
-                                          <div key="rot" className={`documentDecorations-${useRotation ? "rotation" : "borderRadius"}`}
-                                                onPointerDown={useRotation ? this.onRotateDown : this.onRadiusDown}
-                                                onContextMenu={e => e.preventDefault()}>{useRotation && "⟲"}</div>
-                                    </>
-                              }
-
-                              {hideDocumentButtonBar ? (null) :
-                                    <div className="link-button-container" key="links"
-                                          style={{
-                                                transform: ` translate(${- this._resizeBorderWidth / 2 + 10}px, ${this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `,
-                                          }}>
-                                          <DocumentButtonBar views={SelectionManager.Views} />
-                                    </div>}
-                        </div >
-                  </>}
-            </div >
-            );
-      }
+            return this._accumulatedTitle;
+        }
+        return SelectionManager.Views().length > 1 ? "-multiple-" : "-unset-";
+    }
+
+    @computed get canDelete() {
+        return SelectionManager.Views().some(docView => {
+            if (docView.rootDoc.stayInCollection) return false;
+            const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit;
+            //return (!docView.rootDoc._stayInCollection || docView.rootDoc.isInkMask) &&
+            return (collectionAcl === AclAdmin || collectionAcl === AclEdit || GetEffectiveAcl(docView.rootDoc) === AclAdmin);
+        });
+    }
+    @computed get hasIcons() {
+        return SelectionManager.Views().some(docView => docView.rootDoc.layoutKey === "layout_icon");
+    }
+
+    render() {
+        const bounds = this.Bounds;
+        const seldoc = SelectionManager.Views().slice(-1)[0];
+        if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
+            return (null);
+        }
+        const hideResizers = seldoc.props.hideResizeHandles || seldoc.rootDoc.hideResizeHandles || seldoc.rootDoc._isGroup;
+        const hideTitle = seldoc.props.hideDecorationTitle || seldoc.rootDoc.hideDecorationTitle;
+        const canOpen = SelectionManager.Views().some(docView => !docView.props.Document._stayInCollection && !docView.props.Document.isGroup && !docView.props.Document.hideOpenButton);
+        const canDelete = this.canDelete;
+        const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
+            <Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top">
+                <div className={`documentDecorations-${key}Button`} onContextMenu={e => e.preventDefault()}
+                    onPointerDown={pointerDown ?? (e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(e => click!(e))))} >
+                    <FontAwesomeIcon icon={icon as any} />
+                </div>
+            </Tooltip>);
+
+        const colorScheme = StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme);
+        const titleArea = hideTitle ? <div className="documentDecorations-title" onPointerDown={this.onTitleDown} key="title" /> :
+            this._edtingTitle ?
+                <input ref={this._keyinput} className={`documentDecorations-title${colorScheme}`}
+                    type="text" name="dynbox" autoComplete="on"
+                    value={this._accumulatedTitle}
+                    onBlur={e => this.titleBlur()}
+                    onChange={action(e => this._accumulatedTitle = e.target.value)}
+                    onKeyDown={this.titleEntered} /> :
+                <div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown} >
+                    <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${this.selectionTitle}`}</span>
+                </div>;
+
+
+        const leftBounds = this.props.boundsLeft;
+        const topBounds = LightboxView.LightboxDoc ? 0 : this.props.boundsTop;
+        bounds.x = Math.max(leftBounds, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2;
+        bounds.y = Math.max(topBounds, bounds.y - this._resizeBorderWidth / 2 - this._titleHeight) + this._resizeBorderWidth / 2 + this._titleHeight;
+        const borderRadiusDraggerWidth = 15;
+        bounds.r = Math.max(bounds.x, Math.max(leftBounds, Math.min(window.innerWidth, bounds.r + borderRadiusDraggerWidth + this._resizeBorderWidth / 2) - this._resizeBorderWidth / 2 - borderRadiusDraggerWidth));
+        bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight));
+
+        const useRotation = seldoc.ComponentView instanceof InkingStroke || seldoc.ComponentView instanceof ImageBox;
+        const resizerScheme = colorScheme ? "documentDecorations-resizer" + colorScheme : "";
+
+        const rotation = NumCast(seldoc.rootDoc._jitterRotation);
+
+        return (<div className={`documentDecorations${colorScheme}`}>
+            <div className="documentDecorations-background" style={{
+                transform: `rotate(${rotation}deg)`,
+                transformOrigin: "top  left",
+                width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
+                height: (bounds.b - bounds.y + this._resizeBorderWidth) + "px",
+                left: bounds.x - this._resizeBorderWidth / 2,
+                top: bounds.y - this._resizeBorderWidth / 2,
+                pointerEvents: DocumentDecorations.Instance.AddToSelection || this.Interacting ? "none" : "all",
+                display: SelectionManager.Views().length <= 1 ? "none" : undefined
+            }} onPointerDown={this.onBackgroundDown} onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} />
+            {bounds.r - bounds.x < 15 && bounds.b - bounds.y < 15 ? (null) : <>
+                <div className="documentDecorations-container" key="container" style={{
+                    transform: ` translate(${bounds.x - this._resizeBorderWidth / 2}px, ${bounds.y - this._resizeBorderWidth / 2 - this._titleHeight}px) rotate(${rotation}deg)`,
+                    transformOrigin: `8px 26px`,
+                    width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
+                    height: (bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight) + "px",
+                }}>
+                    {!canDelete ? <div /> : topBtn("close", this.hasIcons ? "times" : "window-maximize", undefined, e => this.onCloseClick(this.hasIcons ? true : undefined), "Close")}
+                    {titleArea}
+                    {!canOpen ? (null) : topBtn("open", "external-link-alt", this.onMaximizeDown, undefined, "Open in Tab (ctrl: as alias, shift: in new collection)")}
+                    {hideResizers ? (null) :
+                        <>
+                            <div key="tl" className={`documentDecorations-topLeftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="t" className={`documentDecorations-topResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="tr" className={`documentDecorations-topRightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="l" className={`documentDecorations-leftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="c" className={`documentDecorations-centerCont ${resizerScheme}`}></div>
+                            <div key="r" className={`documentDecorations-rightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="bl" className={`documentDecorations-bottomLeftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="b" className={`documentDecorations-bottomResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                            <div key="br" className={`documentDecorations-bottomRightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+
+                            {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) :
+                                topBtn("selector", "arrow-alt-circle-up", undefined, this.onSelectorClick, "tap to select containing document")}
+                            <div key="rot" className={`documentDecorations-${useRotation ? "rotation" : "borderRadius"}`}
+                                onPointerDown={useRotation ? this.onRotateDown : this.onRadiusDown}
+                                onContextMenu={e => e.preventDefault()}>{useRotation && "⟲"}</div>
+                        </>
+                    }
+                    {seldoc?.Document.type === DocumentType.FONTICON ? (null) :
+                        <div className="link-button-container" key="links"
+                            style={{
+                                transform: ` translate(${- this._resizeBorderWidth / 2 + 10}px, ${this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `,
+                            }}>
+                            <DocumentButtonBar views={SelectionManager.Views} />
+                        </div>}
+                </div >
+            </>}
+        </div >
+        );
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 6afe64868..a118cc144 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -25,7 +25,6 @@ import { RadialMenu } from "./nodes/RadialMenu";
 import HorizontalPalette from "./Palette";
 import { Touchable } from "./Touchable";
 import TouchScrollableMenu, { TouchScrollableMenuItem } from "./TouchScrollableMenu";
-import { InkTranscription } from "./InkTranscription";
 
 @observer
 export class GestureOverlay extends Touchable {
@@ -128,7 +127,7 @@ export class GestureOverlay extends Touchable {
             // pen is also a touch, but with a radius of 0.5 (at least with the surface pens)
             // and this seems to be the only way of differentiating pen and touch on touch events
             if (pt.radiusX > 1 && pt.radiusY > 1) {
-                InkTranscription.Instance.createInkGroup();
+                createInkGroup();
                 Doc.UserDoc().activeInkTool = InkTool.None;
                 this.prevPoints.set(pt.identifier, pt);
             }
@@ -497,7 +496,7 @@ export class GestureOverlay extends Touchable {
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
             setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => {
                 if (doubleTap) {
-                    InkTranscription.Instance.createInkGroup();
+                    createInkGroup();
                     CurrentUserUtils.SelectedTool = InkTool.None;
                     return;
                 }
@@ -645,7 +644,7 @@ export class GestureOverlay extends Touchable {
                         (controlPoints[0].Y - controlPoints.lastElement().Y) * (controlPoints[0].Y - controlPoints.lastElement().Y));
                     if (controlPoints.length > 4 && dist < 10) controlPoints[controlPoints.length - 1] = controlPoints[0];
                     this._points = controlPoints;
-                    this.dispatchGesture(GestureUtils.Gestures.Stroke);                    
+                    this.dispatchGesture(GestureUtils.Gestures.Stroke);
                     // TODO: nda - check inks to group here
                     checkInksToGroup();
                 }
@@ -847,7 +846,7 @@ export class GestureOverlay extends Touchable {
 
     @computed get elements() {
         const selView = SelectionManager.Views().lastElement();
-        const width = Number(ActiveInkWidth()) * NumCast(selView?.rootDoc._viewScale, 1) / (selView?.props.ScreenToLocalTransform().Scale || 1);
+        const width = Number(ActiveInkWidth()) * NumCast(selView?.rootDoc.viewScale, 1) / (selView?.props.ScreenToLocalTransform().Scale || 1);
         const rect = this._overlayRef.current?.getBoundingClientRect();
         const B = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 }; //this.getBounds(this._points, true);
         B.left = B.left - width / 2;
diff --git a/src/client/views/InkTangentHandles.tsx b/src/client/views/InkTangentHandles.tsx
index ae35bc980..b15e4260d 100644
--- a/src/client/views/InkTangentHandles.tsx
+++ b/src/client/views/InkTangentHandles.tsx
@@ -13,6 +13,7 @@ import { Colors } from "./global/globalEnums";
 import { InkingStroke } from "./InkingStroke";
 import { InkStrokeProperties } from "./InkStrokeProperties";
 import { DocumentView } from "./nodes/DocumentView";
+
 export interface InkHandlesProps {
     inkDoc: Doc;
     inkView: DocumentView;
@@ -102,7 +103,7 @@ export class InkTangentHandles extends React.Component<InkHandlesProps> {
                             r={screenSpaceLineWidth * 2}
                             fill={Colors.MEDIUM_BLUE}
                             strokeWidth={1}
-                            stroke={Colors.BLACK}
+                            stroke={Colors.MEDIUM_BLUE}
                             onPointerDown={e => this.onHandleDown(e, pts.I)}
                             pointerEvents="all"
                             cursor="default"
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index 8ab54918c..0ac1770cc 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -2,20 +2,16 @@ import * as iink from 'iink-js';
 import { action, observable } from 'mobx';
 import * as React from 'react';
 import { Doc, DocListCast, HeightSym, WidthSym } from '../../fields/Doc';
-import { InkData, InkField, InkTool } from "../../fields/InkField";
+import { InkData, InkField } from "../../fields/InkField";
 import { Cast, DateCast, NumCast } from '../../fields/Types';
 import { aggregateBounds } from '../../Utils';
 import { DocumentType } from "../documents/DocumentTypes";
 import { DocumentManager } from "../util/DocumentManager";
 import { CollectionFreeFormView } from './collections/collectionFreeForm';
 import { InkingStroke } from './InkingStroke';
-import { CurrentUserUtils } from '../util/CurrentUserUtils';
-import "./InkTranscription.scss";
+import './InkTranscription.scss';
 
 
-/**
- * Class component that handles inking in writing mode
- */
 export class InkTranscription extends React.Component {
     static Instance: InkTranscription;
 
@@ -46,8 +42,8 @@ export class InkTranscription extends React.Component {
                     protocol: 'WEBSOCKET',
                     server: {
                         host: 'cloud.myscript.com',
-                        applicationKey: process.env.IINKJS_APP,
-                        hmacKey: process.env.IINKJS_HMAC,
+                        applicationKey: '7277ec34-0c2e-4ee1-9757-ccb657e3f89f',
+                        hmacKey: 'f5cb18f2-1f95-4ddb-96ac-3f7c888dffc1',
                         websocket: {
                             pingEnabled: false,
                             autoReconnect: true
@@ -107,15 +103,7 @@ export class InkTranscription extends React.Component {
         return this._textRef = r;
     }
 
-    /**
-     * Handles processing Dash Doc data for ink transcription.
-     * 
-     * @param groupDoc the group which contains the ink strokes we want to transcribe 
-     * @param containingLayout the layout which contains the group
-     * @param inkDocs the ink docs contained within the selected group
-     * @param math boolean whether to do math transcription or not
-     */
-    transcribeInk = (groupDoc: Doc | undefined, containingLayout: Doc, inkDocs: Doc[], math: boolean) => {
+    transcribeInk = (groupDoc: Doc | undefined, inkDocs: Doc[], math: boolean, ffView?: CollectionFreeFormView) => {
         if (!groupDoc) return;
         const validInks = inkDocs.filter(s => s.type === DocumentType.INK);
 
@@ -141,13 +129,6 @@ export class InkTranscription extends React.Component {
         }
     }
 
-    /**
-     * Converts the Dash Ink Data to JSON.
-     * 
-     * @param stroke The dash ink data
-     * @param time the time of the stroke
-     * @returns json object representation of ink data
-     */
     inkJSON = (stroke: InkData, time: number) => {
         return {
             "pointerType": "PEN",
@@ -159,17 +140,28 @@ export class InkTranscription extends React.Component {
         };
     }
 
-    /**
-     * Creates subgroups for each word for the whole text transcription
-     * @param wordInkDocMap the mapping of words to ink strokes (Ink Docs)
-     */
+    mmToPixel = (mm: number) => {
+        return ((96 * mm) / 25.4);
+    }
+
+    calcBounds = (coords: any) => {
+        // find max and min x values and subtract
+        const max = Math.max(...coords);
+        const min = Math.min(...coords);
+        return max - min;
+    }
+
     subgroupsTranscriptions = (wordInkDocMap: Map<string, Doc[]>) => {
+        // loop through the words in wordInkDocMap
+        // for each word, get the inkDocs
+
         // iterate through the keys of wordInkDocMap
         wordInkDocMap.forEach(async (inkDocs: Doc[], word: string) => {
             const selected = inkDocs.slice();
             if (!selected) {
                 return;
             }
+            // TODO: nda - probably have to cast this to an actual Doc
             const ctx = await Cast(selected[0].context, Doc);
             if (!ctx) {
                 return;
@@ -179,16 +171,56 @@ export class InkTranscription extends React.Component {
             if (!docView) return;
             const marqViewRef = docView._marqueeViewRef.current;
             if (!marqViewRef) return;
-            this.groupInkDocs(selected, docView, word);
+            //  loop through selected an get the bound
+            const bounds: { x: number, y: number, width?: number, height?: number }[] = []
+
+            selected.map(action(d => {
+                const x = NumCast(d.x);
+                const y = NumCast(d.y);
+                const width = d[WidthSym]();
+                const height = d[HeightSym]();
+                bounds.push({ x, y, width, height });
+            }))
+
+            const aggregBounds = aggregateBounds(bounds, 0, 0);
+
+            if (marqViewRef) {
+                marqViewRef._downX = aggregBounds.x;
+                marqViewRef._downY = aggregBounds.y;
+                marqViewRef._lastX = aggregBounds.r;
+                marqViewRef._lastY = aggregBounds.b;
+            }
+
+            // set the vals for bounds in marqueeView
+
+            selected.map(action(d => {
+                const dx = NumCast(d.x);
+                const dy = NumCast(d.y);
+                delete d.x;
+                delete d.y;
+                delete d.activeFrame;
+                delete d._timecodeToShow;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+                delete d._timecodeToHide;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+                // calculate pos based on bounds
+                if (marqViewRef?.Bounds) {
+                    d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2;
+                    d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
+                }
+                return d;
+            }));
+
+            docView.props.removeDocument?.(selected);
+            const newCollection = marqViewRef?.getCollection(selected, undefined, true);
+            if (newCollection) {
+                newCollection.height = newCollection[HeightSym]();
+                newCollection.width = newCollection[WidthSym]();
+                newCollection.title = word;
+            }
+            // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
+            newCollection && docView.props.addDocument?.(newCollection);
         });
     }
 
-    /**
-     * Event listener function for when the 'exported' event is heard.
-     * 
-     * @param e the event objects
-     * @param ref the ref to the editor
-     */
     exportInk = (e: any, ref: any) => {
         const exports = e.detail.exports;
         if (exports) {
@@ -248,88 +280,6 @@ export class InkTranscription extends React.Component {
             }
         }
     }
-    
-/**
- * Creates the ink grouping once the user leaves the writing mode.
- */
-createInkGroup() {
-    // TODO nda - if document being added to is a inkGrouping then we can just add to that group
-    if (CurrentUserUtils.SelectedTool === InkTool.Write) {
-        CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
-            // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
-            const selected = ffView.unprocessedDocs;
-            const newCollection = this.groupInkDocs(selected, ffView);
-            ffView.unprocessedDocs = [];
-
-            InkTranscription.Instance.transcribeInk(newCollection, ffView.layoutDoc, selected, false);
-        });
-    }
-    CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
-}
-
-/**
- * Creates the groupings for a given list of ink docs on a specific doc view
- * @param selected: the list of ink docs to create a grouping of
- * @param docView: the view in which we want the grouping to be created
- * @param word: optional param if the group we are creating is a word (subgrouping individual words)
- * @returns a new collection Doc or undefined if the grouping fails
- */
-  groupInkDocs(selected: Doc[], docView: CollectionFreeFormView, word?: string): Doc | undefined {
-    const bounds: { x: number, y: number, width?: number, height?: number }[] = []
-
-    // calculate the necessary bounds from the selected ink docs
-    selected.map(action(d => {
-        const x = NumCast(d.x);
-        const y = NumCast(d.y);
-        const width = d[WidthSym]();
-        const height = d[HeightSym]();
-        bounds.push({ x, y, width, height });
-    }))
-
-    // calculate the aggregated bounds
-    const aggregBounds = aggregateBounds(bounds, 0, 0);
-    const marqViewRef = docView._marqueeViewRef.current;
-
-    // set the vals for bounds in marqueeView
-    if (marqViewRef) {
-        marqViewRef._downX = aggregBounds.x;
-        marqViewRef._downY = aggregBounds.y;
-        marqViewRef._lastX = aggregBounds.r;
-        marqViewRef._lastY = aggregBounds.b;
-    }
-
-    // map through all the selected ink strokes and create the groupings
-    selected.map(action(d => {
-        const dx = NumCast(d.x);
-        const dy = NumCast(d.y);
-        delete d.x;
-        delete d.y;
-        delete d.activeFrame;
-        delete d._timecodeToShow;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-        delete d._timecodeToHide;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-        // calculate pos based on bounds
-        if (marqViewRef?.Bounds) {
-            d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2;
-            d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
-        }
-        return d;
-    }));
-    docView.props.removeDocument?.(selected);
-    // Gets a collection based on the selected nodes using a marquee view ref
-    const newCollection = marqViewRef?.getCollection(selected, undefined, true);
-    if (newCollection) {
-        newCollection.height = newCollection[HeightSym]();
-        newCollection.width = newCollection[WidthSym]();
-        // if the grouping we are creating is an individual word
-        if (word) {
-            newCollection.title = word;
-        }
-    }
-
-    // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
-    newCollection && docView.props.addDocument?.(newCollection);
-    return newCollection;
-}
 
     render() {
         return (
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index fcc4aea13..f67f37bfb 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -207,6 +207,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
 
     future = () => LightboxView._future;
     tourMap = () => LightboxView._tourMap;
+    fitToBox = () => LightboxView._docTarget === LightboxView.LightboxDoc;
     render() {
         let downx = 0, downy = 0;
         return !LightboxView.LightboxDoc ? (null) :
@@ -240,6 +241,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                         DataDoc={undefined}
                         LayoutTemplate={LightboxView.LightboxDocTemplate}
                         addDocument={undefined}
+                        // fitContentsToDoc={this.fitToBox} // bcz: why do we want this?  when we initially open a colletion, we shrinkwrap it which allows for user navigation.  if we later encounter a collection, it's not clear to me that we want to make it either shrinkwrap or fitContents...
                         isDocumentActive={returnFalse}
                         isContentActive={returnTrue}
                         addDocTab={this.addDocTab}
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 49c2dcf34..8560ccb29 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -8,9 +8,7 @@ import { AssignAllExtensions } from "../../extensions/General/Extensions";
 import { Docs } from "../documents/Documents";
 import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { LinkManager } from "../util/LinkManager";
-import { RecordingApi } from "../util/RecordingApi";
 import { CollectionView } from "./collections/CollectionView";
-import { DashboardView } from './DashboardView';
 import { MainView } from "./MainView";
 
 AssignAllExtensions();
@@ -38,6 +36,5 @@ AssignAllExtensions();
     const expires = "expires=" + d.toUTCString();
     document.cookie = `loadtime=${loading};${expires};path=/`;
     new LinkManager();
-    new RecordingApi;
     ReactDOM.render(<MainView />, document.getElementById('root'));
 })();
\ No newline at end of file
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index a695577d0..15cd2c144 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -20,8 +20,6 @@
     position: relative;
     width: 100%;
     height: 100%;
-    display: flex;
-    flex-direction: column;
 }
 
 // add nodes menu. Note that the + button is actually an input label, not an actual button.
@@ -31,11 +29,6 @@
     left: calc(100% + 5px);
     z-index: 1;
     pointer-events: none;
-    display: flex;
-    flex-direction: row;
-    align-items: center;
-    justify-content: space-between;
-    gap: 10px;
 }
 
 .mainView-snapLines {
@@ -155,8 +148,7 @@
     }
 }
 
-.mainView-innerContent,
-.mainView-innerContent-Dark {
+.mainView-innerContent, .mainView-innerContent-Dark {
     display: contents;
     flex-direction: row;
     position: relative;
@@ -189,8 +181,8 @@
 .mainView-libraryHandle {
     background-color: $light-gray;
 }
-
-.mainView-innerContent-Dark {
+.mainView-innerContent-Dark 
+{
     .propertiesView {
         background-color: #252525;
 
@@ -216,7 +208,6 @@
         background: #353535;
     }
 }
-
 .mainView-container-Dark {
     .contextMenu-cont {
         background: $medium-gray;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 0f7e3188a..f0011d65f 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -37,7 +37,6 @@ import { CollectionView, CollectionViewType } from './collections/CollectionView
 import "./collections/TreeView.scss";
 import { ComponentDecorations } from './ComponentDecorations';
 import { ContextMenu } from './ContextMenu';
-import { DashboardView } from './DashboardView';
 import { DictationOverlay } from './DictationOverlay';
 import { DocumentDecorations } from './DocumentDecorations';
 import { GestureOverlay } from './GestureOverlay';
@@ -84,7 +83,7 @@ export class MainView extends React.Component {
 
     @computed private get dashboardTabHeight() { return 27; } //  27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js
     @computed private get topOfDashUI() { return Number(DASHBOARD_SELECTOR_HEIGHT.replace("px", "")); }
-    @computed private get topOfHeaderBarDoc() { return this.topOfDashUI; }
+    @computed private get topOfHeaderBarDoc() { return this.topOfDashUI + this.topMenuHeight(); }
     @computed private get topOfSidebarDoc() { return this.topOfDashUI + this.topMenuHeight(); }
     @computed private get topOfMainDoc() { return this.topOfDashUI + this.topMenuHeight() + this.headerBarDocHeight(); }
     @computed private get topOfMainDocContent() { return this.topOfMainDoc + this.dashboardTabHeight; }
@@ -180,7 +179,7 @@ export class MainView extends React.Component {
         fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt,
         fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer,
         fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV,
-        fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideoSlash, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes, fa.faFlag,
+        fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes, fa.faFlag,
         fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
         fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
         fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any,
@@ -227,7 +226,17 @@ export class MainView extends React.Component {
         if (received && !this.userDoc) {
             reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()), { fireImmediately: true });
         } else {
-            PromiseValue(this.userDoc.activeDashboard).then(dash => {
+            if (received && CurrentUserUtils._urlState.sharing) {
+                reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized,
+                    initialized => initialized && received && DocServer.GetRefField(received).then(docField => {
+                        if (docField instanceof Doc && docField._viewType !== CollectionViewType.Docking) {
+                            CollectionDockingView.AddSplit(docField, "right");
+                        }
+                    }),
+                );
+            }
+            const activeDash = PromiseValue(this.userDoc.activeDashboard);
+            activeDash.then(dash => {
                 if (dash instanceof Doc) CurrentUserUtils.openDashboard(this.userDoc, dash);
                 else CurrentUserUtils.createNewDashboard(this.userDoc);
             });
@@ -277,7 +286,7 @@ export class MainView extends React.Component {
     headerBarScreenXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.headerBarDocHeight(), 1);
 
     @computed get headerBarDocView() {
-        return <div className="mainView-headerBar" style={{ height: this.headerBarDocHeight() }}>
+        return <div style={{ height: this.headerBarDocHeight() }}>
             <DocumentView key="headerBarDoc"
                 Document={this.headerBarDoc}
                 DataDoc={undefined}
@@ -288,12 +297,11 @@ export class MainView extends React.Component {
                 styleProvider={DefaultStyleProvider}
                 rootSelected={returnTrue}
                 removeDocument={returnFalse}
-                fitContentsToBox={returnTrue}
+                fitContentsToDoc={returnTrue}
                 isDocumentActive={returnTrue} // headerBar is always documentActive (ie, the docView gets pointer events)
                 isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive
                 ScreenToLocalTransform={this.headerBarScreenXf}
                 childHideResizeHandles={returnTrue}
-                dontRegisterView={true}
                 hideResizeHandles={true}
                 PanelWidth={this.headerBarDocWidth}
                 PanelHeight={this.headerBarDocHeight}
@@ -494,6 +502,15 @@ export class MainView extends React.Component {
         </>;
     }
 
+    @computed get headerBar() {
+        return !this.userDoc ? (null) :
+            <div className="mainView-dashboardArea" style={{
+                height: this.headerBarDocHeight(),
+                width: "100%",
+            }} >
+                {this.headerBarDocView}
+            </div>;
+    }
 
     @computed get mainDashboardArea() {
         return !this.userDoc ? (null) :
@@ -575,7 +592,6 @@ export class MainView extends React.Component {
                     searchFilterDocs={returnEmptyDoclist}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined} />
-                {['watching', 'recording'].includes(String(this.userDoc?.presentationMode) ?? '') ? <div style={{ border: '.5rem solid green', padding: '5px' }}>{this.userDoc?.presentationMode}</div> : <></>}
             </div>;
     }
     @computed get snapLines() {
@@ -661,20 +677,12 @@ export class MainView extends React.Component {
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
             {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => DocumentLinksButton.LinkEditorDocView = undefined)} docView={DocumentLinksButton.LinkEditorDocView} /> : (null)}
             {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)}
-            
-            {((page:string) => {
-                switch (page) {
-                    case "dashboard":
-                    default:return <>
-                            <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 1999 }} >
-                                    <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
-                            </div> 
-                            <GestureOverlay> {this.mainDashboardArea}   </GestureOverlay>
-                        </>;
-                    case "home":  return <DashboardView/>;
-                } })(StrCast(Doc.UserDoc().activePage))
-            }
-            
+            <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 2001 }} >
+                <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
+            </div>
+            <GestureOverlay >
+                {this.mainDashboardArea}
+            </GestureOverlay>
             <PreviewCursor />
             <TaskCompletionBox />
             <ContextMenu />
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 20b99788c..e15624e23 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -132,14 +132,14 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         if (savedAnnoMap.size === 0) return undefined;
         const savedAnnos = Array.from(savedAnnoMap.values())[0];
         if (savedAnnos.length && (savedAnnos[0] as any).marqueeing) {
-            const scale = (this.props.scaling?.() || 1);
+            const scale = this.props.scaling?.() || 1;
             const anno = savedAnnos[0];
             const containerOffset = this.props.containerOffset?.() || [0, 0];
             const marqueeAnno = Docs.Create.FreeformDocument([], { _isLinkButton: isLinkButton, backgroundColor: color, annotationOn: this.props.rootDoc, title: "Annotation on " + this.props.rootDoc.title });
-            marqueeAnno.x = NumCast(this.props.docView.props.Document.panXMin) + (parseInt(anno.style.left || "0") - containerOffset[0]) / scale/ NumCast(this.props.docView.props.Document._viewScale,1);
-            marqueeAnno.y = NumCast(this.props.docView.props.Document.panYMin) + (parseInt(anno.style.top || "0") - containerOffset[1]) / scale/ NumCast(this.props.docView.props.Document._viewScale,1) + NumCast(this.props.scrollTop);
-            marqueeAnno._height = parseInt(anno.style.height || "0") / scale/ NumCast(this.props.docView.props.Document._viewScale,1);
-            marqueeAnno._width = parseInt(anno.style.width || "0") / scale/ NumCast(this.props.docView.props.Document._viewScale,1);
+            marqueeAnno.x = (parseInt(anno.style.left || "0") - containerOffset[0]) / scale;
+            marqueeAnno.y = (parseInt(anno.style.top || "0") - containerOffset[1]) / scale + NumCast(this.props.scrollTop);
+            marqueeAnno._height = parseInt(anno.style.height || "0") / scale;
+            marqueeAnno._width = parseInt(anno.style.width || "0") / scale;
             anno.remove();
             savedAnnoMap.clear();
             return marqueeAnno;
diff --git a/src/client/views/OverlayView.scss b/src/client/views/OverlayView.scss
index 302e7a5e3..555f4298d 100644
--- a/src/client/views/OverlayView.scss
+++ b/src/client/views/OverlayView.scss
@@ -1,13 +1,3 @@
-.overlayView {
-    position: absolute;
-    pointer-events: none;
-    top: 0;
-    width: 100vw;
-    height: 100vh;
-    /* background-color: pink; */
-    z-index: 100000;
-}
-
 .overlayWindow-outerDiv {
     border-radius: 5px;
     overflow: hidden;
@@ -56,6 +46,4 @@
 .overlayView-doc {
     z-index: 9002; //so that it appears above chroma 
     position: absolute;
-    top: 0;
-    left: 0;
 }
\ No newline at end of file
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index ab5dc74c9..95e146df4 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -1,10 +1,8 @@
-import { docs } from "googleapis/build/src/apis/docs";
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
-import { computedFn } from "mobx-utils";
 import * as React from "react";
 import ReactLoading from 'react-loading';
-import { Doc, WidthSym, HeightSym } from "../../fields/Doc";
+import { Doc, HeightSym, WidthSym } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { Cast, NumCast } from "../../fields/Types";
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils } from "../../Utils";
@@ -152,10 +150,6 @@ export class OverlayView extends React.Component {
         (doc instanceof Doc ? [doc] : doc).forEach(doc => Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc));
         return true;
     }
-    
-    docScreenToLocalXf = computedFn(function docScreenToLocalXf(this: any, doc: Doc) {
-        return () => new Transform(-NumCast(doc.x), -NumCast(doc.y), 1);
-    }.bind(this));
 
     @computed get overlayDocs() {
         return CurrentUserUtils.OverlayDocs?.map(d => {
@@ -189,7 +183,7 @@ export class OverlayView extends React.Component {
                 offsetx = NumCast(d.x) - e.clientX;
                 offsety = NumCast(d.y) - e.clientY;
             };
-            return <div className="overlayView-doc" ref={dref} key={d[Id]} onPointerDown={onPointerDown} style={{ top: d.type === 'presentation' ? 0 : undefined, width: NumCast(d._width), height: NumCast(d._height), transform: `translate(${d.x}px, ${d.y}px)` }}>
+            return <div className="overlayView-doc" ref={dref} key={d[Id]} onPointerDown={onPointerDown} style={{ top: 0, width: NumCast(d._width), height: NumCast(d._height), transform: `translate(${d.x}px, ${d.y}px)` }}>
                 <DocumentView
                     Document={d}
                     rootSelected={returnTrue}
@@ -198,9 +192,9 @@ export class OverlayView extends React.Component {
                     removeDocument={this.removeOverlayDoc}
                     PanelWidth={d[WidthSym]}
                     PanelHeight={d[HeightSym]}
-                    ScreenToLocalTransform={this.docScreenToLocalXf(d)}
+                    ScreenToLocalTransform={() => new Transform(-NumCast(d.x), -NumCast(d.y), 1)}
                     renderDepth={1}
-                    isDocumentActive={returnTrue}
+                    isDocumentActive={returnFalse}
                     isContentActive={returnTrue}
                     whenChildContentsActiveChanged={emptyFunction}
                     focus={DocUtils.DefaultFocus}
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 74055f057..24857d8ee 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -3,23 +3,25 @@ import { Tooltip } from '@material-ui/core';
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, Opt } from "../../fields/Doc";
-import { Id } from "../../fields/FieldSymbols";
 import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
 import { BoolCast, StrCast } from "../../fields/Types";
-import { ImageField } from "../../fields/URLField";
 import { DocUtils } from '../documents/Documents';
 import { DocumentType } from '../documents/DocumentTypes';
 import { SelectionManager } from '../util/SelectionManager';
 import { undoBatch } from '../util/UndoManager';
 import { CollectionViewType } from './collections/CollectionView';
-import { Colors } from "./global/globalEnums";
 import { InkingStroke } from './InkingStroke';
 import { DocumentView } from './nodes/DocumentView';
-import { VideoBox } from "./nodes/VideoBox";
-import { pasteImageBitmap } from "./nodes/WebBoxRenderer";
 import './PropertiesButtons.scss';
 import React = require("react");
+import { Colors } from "./global/globalEnums";
+import { CollectionFreeFormView } from "./collections/collectionFreeForm";
+import { DocumentManager } from "../util/DocumentManager";
+import { pasteImageBitmap } from "./nodes/WebBoxRenderer";
+import { VideoBox } from "./nodes/VideoBox";
+import { Id } from "../../fields/FieldSymbols";
+import { ImageField } from "../../fields/URLField";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -71,7 +73,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         return this.propertyToggleBtn("Lock\xA0View", "_lockedTransform", on => `${on ? "Unlock" : "Lock"} panning of view`, on => "lock");
     }
     @computed get fitContentButton() {
-        return this.propertyToggleBtn("View All", "_fitContentsToBox", on => `${on ? "Don't" : "Do"} fit content to container visible area`, on => "eye");
+        return this.propertyToggleBtn("View All", "_fitToBox", on => `${on ? "Don't" : "Do"} fit content to container visible area`, on => "eye");
     }
     @computed get fitWidthButton() {
         return this.propertyToggleBtn("Fit\xA0Width", "_fitWidth", on => `${on ? "Don't" : "Do"} fit content to width of container`, on => "arrows-alt-h");
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index 16eb95cf4..bcfd2dd56 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -300,7 +300,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     Document={layoutDoc}
                     DataDoc={this.dataDoc}
                     renderDepth={1}
-                    fitContentsToBox={returnTrue}
+                    fitContentsToDoc={returnTrue}
                     rootSelected={returnFalse}
                     styleProvider={DefaultStyleProvider}
                     docViewPath={returnEmptyDoclist}
diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx
index 4fecfa4d9..2b045aa6c 100644
--- a/src/client/views/ScriptingRepl.tsx
+++ b/src/client/views/ScriptingRepl.tsx
@@ -123,9 +123,8 @@ export class ScriptingRepl extends React.Component {
         let stopProp = true;
         switch (e.key) {
             case "Enter": {
-                e.stopPropagation();
                 const docGlobals: { [name: string]: any } = {};
-                Array.from(DocumentManager.Instance.DocumentViews).forEach((dv, i) => docGlobals[`d${i}`] = dv.props.Document);
+                DocumentManager.Instance.DocumentViews.forEach((dv, i) => docGlobals[`d${i}`] = dv.props.Document);
                 const globals = ScriptingGlobals.makeMutableGlobalsCopy(docGlobals);
                 const script = CompileScript(this.commandString, { typecheck: false, addReturn: true, editable: true, params: { args: "any" }, transformer: this.getTransformer(), globals });
                 if (!script.compiled) {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index d2687df17..de2106e4a 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -26,7 +26,6 @@ import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView";
 import { CollectionViewType } from './CollectionView';
 import { TabDocView } from './TabDocView';
 import React = require("react");
-import { SelectionManager } from '../../util/SelectionManager';
 const _global = (window /* browser */ || global /* node */) as any;
 
 @observer
@@ -51,8 +50,9 @@ export class CollectionDockingView extends CollectionSubView() {
     public _flush: UndoManager.Batch | undefined;
     private _ignoreStateChange = "";
     public tabMap: Set<any> = new Set();
+    public get initialized() { return this._goldenLayout !== null; }
     public get HasFullScreen() { return this._goldenLayout._maximisedItem !== null; }
-    private _goldenLayout: any = null;
+    @observable private _goldenLayout: any = null;
 
     constructor(props: SubCollectionViewProps) {
         super(props);
@@ -118,7 +118,6 @@ export class CollectionDockingView extends CollectionSubView() {
 
     @undoBatch
     public static OpenFullScreen(doc: Doc) {
-        SelectionManager.DeselectAll();
         const instance = CollectionDockingView.Instance;
         if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") {
             return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
@@ -172,6 +171,12 @@ export class CollectionDockingView extends CollectionSubView() {
     @undoBatch
     @action
     public static AddSplit(document: Doc, pullSide: string, stack?: any, panelName?: string) {
+        if (document.type === DocumentType.PRES) {
+            const docs = Cast(Cast(Doc.UserDoc().myOverlayDocs, Doc, null).data, listSpec(Doc), []);
+            if (docs.includes(document)) {
+                docs.splice(docs.indexOf(document), 1);
+            }
+        }
         if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), document);
 
         const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document);
@@ -180,7 +185,6 @@ export class CollectionDockingView extends CollectionSubView() {
             return true;
         }
         const instance = CollectionDockingView.Instance;
-        const glayRoot = instance._goldenLayout.root;
         if (!instance) return false;
         const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName);
 
@@ -188,75 +192,71 @@ export class CollectionDockingView extends CollectionSubView() {
             stack.addChild(docContentConfig, undefined);
             stack.setActiveContentItem(stack.contentItems[stack.contentItems.length - 1]);
         } else {
-            const newContentItem = () => {
-                const newItem = glayRoot.layoutManager.createContentItem({ type: 'stack', content: [docContentConfig] }, instance._goldenLayout);
-                newItem.callDownwards('_$init');
-                return newItem;
-            }
-            if (glayRoot.contentItems.length === 0) { // if no rows / columns
-                glayRoot.addChild(newContentItem());
-            } else if (glayRoot.contentItems[0].isStack) {
-                glayRoot.contentItems[0].addChild(docContentConfig);
+            const newItemStackConfig = { type: 'stack', content: [docContentConfig] };
+            const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
+            if (instance._goldenLayout.root.contentItems.length === 0) { // if no rows / columns
+                instance._goldenLayout.root.addChild(newContentItem);
+            } else if (instance._goldenLayout.root.contentItems[0].isStack) {
+                instance._goldenLayout.root.contentItems[0].addChild(docContentConfig);
             } else if (
-                glayRoot.contentItems.length === 1 &&
-                glayRoot.contentItems[0].contentItems.length === 1 &&
-                glayRoot.contentItems[0].contentItems[0].contentItems.length === 0) {
-                glayRoot.contentItems[0].contentItems[0].addChild(docContentConfig);
+                instance._goldenLayout.root.contentItems.length === 1 &&
+                instance._goldenLayout.root.contentItems[0].contentItems.length === 1 &&
+                instance._goldenLayout.root.contentItems[0].contentItems[0].contentItems.length === 0) {
+                instance._goldenLayout.root.contentItems[0].contentItems[0].addChild(docContentConfig);
             }
             else if (instance._goldenLayout.root.contentItems[0].isRow) { // if row
                 switch (pullSide) {
                     default:
-                    case "right": glayRoot.contentItems[0].addChild(newContentItem()); break;
-                    case "left": glayRoot.contentItems[0].addChild(newContentItem(), 0); break;
+                    case "right": instance._goldenLayout.root.contentItems[0].addChild(newContentItem); break;
+                    case "left": instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0); break;
                     case "top":
                     case "bottom":
                         // if not going in a row layout, must add already existing content into column
-                        const rowlayout = glayRoot.contentItems[0];
+                        const rowlayout = instance._goldenLayout.root.contentItems[0];
                         const newColumn = rowlayout.layoutManager.createContentItem({ type: "column" }, instance._goldenLayout);
 
-                        const newItem = newContentItem();
-                        instance._goldenLayout.saveScrollTops(rowlayout.element);
+                        CollectionDockingView.Instance._goldenLayout.saveScrollTops(rowlayout.element);
                         rowlayout.parent.replaceChild(rowlayout, newColumn);
                         if (pullSide === "top") {
                             newColumn.addChild(rowlayout, undefined, true);
-                            newColumn.addChild(newItem, 0, true);
+                            newColumn.addChild(newContentItem, 0, true);
                         } else if (pullSide === "bottom") {
-                            newColumn.addChild(newItem, undefined, true);
+                            newColumn.addChild(newContentItem, undefined, true);
                             newColumn.addChild(rowlayout, 0, true);
                         }
-                        instance._goldenLayout.restoreScrollTops(rowlayout.element);
+                        CollectionDockingView.Instance._goldenLayout.restoreScrollTops(rowlayout.element);
 
                         rowlayout.config.height = 50;
-                        newItem.config.height = 50;
+                        newContentItem.config.height = 50;
                 }
             } else {// if (instance._goldenLayout.root.contentItems[0].isColumn) { // if column
                 switch (pullSide) {
-                    case "top": glayRoot.contentItems[0].addChild(newContentItem(), 0); break;
-                    case "bottom": glayRoot.contentItems[0].addChild(newContentItem()); break;
+                    case "top": instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0); break;
+                    case "bottom": instance._goldenLayout.root.contentItems[0].addChild(newContentItem); break;
                     case "left":
                     case "right":
                     default:
                         // if not going in a row layout, must add already existing content into column
-                        const collayout = glayRoot.contentItems[0];
+                        const collayout = instance._goldenLayout.root.contentItems[0];
                         const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout);
 
-                        const newItem = newContentItem();
-                        instance._goldenLayout.saveScrollTops(collayout.element);
+                        CollectionDockingView.Instance._goldenLayout.saveScrollTops(collayout.element);
                         collayout.parent.replaceChild(collayout, newRow);
                         if (pullSide === "left") {
                             newRow.addChild(collayout, undefined, true);
-                            newRow.addChild(newItem, 0, true);
+                            newRow.addChild(newContentItem, 0, true);
                         } else {
-                            newRow.addChild(newItem, undefined, true);
+                            newRow.addChild(newContentItem, undefined, true);
                             newRow.addChild(collayout, 0, true);
                         }
-                        instance._goldenLayout.restoreScrollTops(collayout.element);
+                        CollectionDockingView.Instance._goldenLayout.restoreScrollTops(collayout.element);
 
                         collayout.config.width = 50;
-                        newItem.config.width = 50;
+                        newContentItem.config.width = 50;
                 }
             }
             instance._ignoreStateChange = JSON.stringify(instance._goldenLayout.toConfig());
+            newContentItem.callDownwards('_$init');
         }
 
         return instance.layoutChanged();
@@ -271,7 +271,7 @@ export class CollectionDockingView extends CollectionSubView() {
         return true;
     }
 
-    setupGoldenLayout = async () => {
+    async setupGoldenLayout() {
         const config = StrCast(this.props.Document.dockingConfig);
         if (config) {
             const matches = config.match(/\"documentId\":\"[a-z0-9-]+\"/g);
@@ -288,19 +288,26 @@ export class CollectionDockingView extends CollectionSubView() {
                         this._goldenLayout.unbind('stackCreated', this.stackCreated);
                     } catch (e) { }
                 }
-                this.tabMap.clear();
-                this._goldenLayout.destroy();
             }
-            const glay = this._goldenLayout = new GoldenLayout(JSON.parse(config));
-            glay.on('tabCreated', this.tabCreated);
-            glay.on('tabDestroyed', this.tabDestroyed);
-            glay.on('stackCreated', this.stackCreated);
-            glay.registerComponent('DocumentFrameRenderer', TabDocView);
-            glay.container = this._containerRef.current;
-            glay.init();
-            glay.root.layoutManager.on('itemDropped', this.tabItemDropped);
-            glay.root.layoutManager.on('dragStart', this.tabDragStart);
-            glay.root.layoutManager.on('activeContentItemChanged', this.stateChanged);
+            this.tabMap.clear();
+            this._goldenLayout?.destroy();
+            runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config)));
+            this._goldenLayout.on('tabCreated', this.tabCreated);
+            this._goldenLayout.on('tabDestroyed', this.tabDestroyed);
+            this._goldenLayout.on('stackCreated', this.stackCreated);
+            this._goldenLayout.registerComponent('DocumentFrameRenderer', TabDocView);
+            this._goldenLayout.container = this._containerRef.current;
+            if (this._goldenLayout.config.maximisedItemId === '__glMaximised') {
+                try {
+                    this._goldenLayout.config.root.getItemsById(this._goldenLayout.config.maximisedItemId)[0].toggleMaximise();
+                } catch (e) {
+                    this._goldenLayout.config.maximisedItemId = null;
+                }
+            }
+            this._goldenLayout.init();
+            this._goldenLayout.root.layoutManager.on('itemDropped', this.tabItemDropped);
+            this._goldenLayout.root.layoutManager.on('dragStart', this.tabDragStart);
+            this._goldenLayout.root.layoutManager.on('activeContentItemChanged', this.stateChanged);
         }
     }
 
@@ -315,7 +322,7 @@ export class CollectionDockingView extends CollectionSubView() {
                     }
                     this._ignoreStateChange = "";
                 });
-            setTimeout(this.setupGoldenLayout);
+            setTimeout(() => this.setupGoldenLayout(), 0);
             //window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window
         }
     }
@@ -361,8 +368,7 @@ export class CollectionDockingView extends CollectionSubView() {
             const htmlTarget = e.target as HTMLElement;
             window.addEventListener("mouseup", this.onPointerUp);
             if (!htmlTarget.closest("*.lm_content") && (htmlTarget.closest("*.lm_tab") || htmlTarget.closest("*.lm_stack"))) {
-                const className = typeof htmlTarget.className === "string" ? htmlTarget.className : "";
-                if (!className.includes("lm_close") && !className.includes("lm_maximise")) {
+                if (htmlTarget.className !== "lm_close_tab") {
                     this._flush = UndoManager.StartBatch("golden layout edit");
                 }
             }
@@ -407,37 +413,29 @@ export class CollectionDockingView extends CollectionSubView() {
         const docs = !docids ? [] : docids.map(id => DocServer.GetCachedRefField(id)).filter(f => f).map(f => f as Doc);
         const changesMade = this.props.Document.dockcingConfig !== json;
         if (changesMade && !this._flush) {
-            UndoManager.RunInBatch(() => {
-                this.props.Document.dockingConfig = json;
-                this.props.Document.data = new List<Doc>(docs);
-            }, "state changed");
+            this.props.Document.dockingConfig = json;
+            this.props.Document.data = new List<Doc>(docs);
         }
         return changesMade;
     }
 
     tabDestroyed = (tab: any) => {
-        Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", tab.DashDoc);
-        Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true);
-        const dview = CollectionDockingView.Instance.props.Document;
-        const fieldKey = CollectionDockingView.Instance.props.fieldKey;
-        Doc.RemoveDocFromList(dview, fieldKey, tab.DashDoc);
         this.tabMap.delete(tab);
         tab._disposers && Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
         tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele));
         this.stateChanged();
     }
     tabCreated = (tab: any) => {
-        this.tabMap.add(tab);
         tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab?.(tab);  // have to explicitly initialize tabs that reuse contents from previous tabs (ie, when dragging a tab around a new tab is created for the old content)
     }
 
     stackCreated = (stack: any) => {
         stack.header?.element.on('mousedown', (e: any) => {
             if (e.target === stack.header?.element[0] && e.button === 2) {
-                const dashboard= CurrentUserUtils.ActiveDashboard;
-                dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1;
+                const emptyPane = CurrentUserUtils.EmptyPane;
+                emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
                 const docToAdd = Docs.Create.FreeformDocument([], {
-                    _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _backgroundGridShow: true, _fitWidth: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}`,
+                    _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _backgroundGridShow: true, _fitWidth: true, title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`,
                 });
                 this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
                 CollectionDockingView.AddSplit(docToAdd, "", stack);
@@ -454,17 +452,14 @@ export class CollectionDockingView extends CollectionSubView() {
                     alert('cant delete the last stack');
                 }
             }));
-
-        stack.header?.controlsContainer.find('.lm_maximise') //get the close icon
-            .click(() => setTimeout(this.stateChanged));
         stack.header?.controlsContainer.find('.lm_popout') //get the popout icon
             .off('click') //unbind the current click handler
             .click(action(() => {
                 // stack.config.fixed = !stack.config.fixed;  // force the stack to have a fixed size
-                const dashboard = CurrentUserUtils.ActiveDashboard;
-                dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1;
+                const emptyPane = CurrentUserUtils.EmptyPane;
+                emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
                 const docToAdd = Docs.Create.FreeformDocument([], {
-                    _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, _backgroundGridShow: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}`
+                    _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, _backgroundGridShow: true, title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`
                 });
                 this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
                 CollectionDockingView.AddSplit(docToAdd, "", stack);
diff --git a/src/client/views/collections/CollectionDockingViewMenu.scss b/src/client/views/collections/CollectionDockingViewMenu.scss
new file mode 100644
index 000000000..4157f0f7e
--- /dev/null
+++ b/src/client/views/collections/CollectionDockingViewMenu.scss
@@ -0,0 +1,34 @@
+
+.dockingViewButtonSelector {
+    div {
+        overflow: visible !important;
+    }
+
+    display: inline-block;
+    width:100%;
+    height:100%;
+}
+.dockingViewButtonSelector-flyout {
+    position: relative;
+    z-index: 9999;
+    background-color: #eeeeee;
+    box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
+    color: black;
+
+    padding: 10px;
+    border-radius: 3px;
+    display: inline-block;
+    height: 100%;
+    width: 100%;
+    border-radius: 3px;
+
+    hr {
+        height: 1px;
+        margin: 0px;
+        background-color: gray;
+        border-top: 0px;
+        border-bottom: 0px;
+        border-right: 0px;
+        border-left: 0px;
+    }
+}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionDockingViewMenu.tsx b/src/client/views/collections/CollectionDockingViewMenu.tsx
new file mode 100644
index 000000000..1cab293a8
--- /dev/null
+++ b/src/client/views/collections/CollectionDockingViewMenu.tsx
@@ -0,0 +1,48 @@
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Tooltip } from "@material-ui/core";
+import { action, computed, observable } from "mobx";
+import { observer } from "mobx-react";
+import * as React from "react";
+import { DocumentButtonBar } from "../DocumentButtonBar";
+import { DocumentView } from "../nodes/DocumentView";
+const higflyout = require("@hig/flyout");
+export const { anchorPoints } = higflyout;
+export const Flyout = higflyout.default;
+
+@observer
+export class CollectionDockingViewMenu extends React.Component<{ views: () => DocumentView[], Stack: any }> {
+    customStylesheet(styles: any) {
+        return {
+            ...styles,
+            panel: {
+                ...styles.panel,
+                minWidth: "100px"
+            },
+        };
+    }
+    _ref = React.createRef<HTMLDivElement>();
+
+    @computed get flyout() {
+        return (
+            <div className="dockingViewButtonSelector-flyout" title=" " ref={this._ref}>
+                <DocumentButtonBar views={this.props.views} stack={this.props.Stack} />
+            </div>
+        );
+    }
+
+    @observable _tooltipOpen: boolean = false;
+    render() {
+        return <Tooltip open={this._tooltipOpen} onClose={action(() => this._tooltipOpen = false)} title={<><div className="dash-tooltip">Tap for toolbar, drag to create alias in another pane</div></>} placement="bottom">
+            <span className="dockingViewButtonSelector"
+                onPointerEnter={action(() => !this._ref.current?.getBoundingClientRect().width && (this._tooltipOpen = true))}
+                onPointerDown={action(e => {
+                    this.props.views()[0]?.select(false);
+                    this._tooltipOpen = false;
+                })} >
+                <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.flyout} stylesheet={this.customStylesheet}>
+                    <FontAwesomeIcon icon={"arrows-alt"} size={"sm"} />
+                </Flyout>
+            </span>
+        </Tooltip >;
+    }
+}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 39f6466d6..23fd4206c 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -252,8 +252,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     };
     _clusterCommand = {
         params: ["target"], title: "fit content",
-        script: "self.target._fitContentsToBox = !self.target._fitContentsToBox;",
-        immediate: undoBatch((source: Doc[]) => this.target._fitContentsToBox = !this.target._fitContentsToBox),
+        script: "self.target._fitToBox = !self.target._fitToBox;",
+        immediate: undoBatch((source: Doc[]) => this.target._fitToBox = !this.target._fitToBox),
         initialize: emptyFunction
     };
     _fitContentCommand = {
@@ -457,7 +457,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
         const isPinned = targetDoc && Doc.isDocPinned(targetDoc);
         return !targetDoc ? (null) : <Tooltip key="pin" title={<div className="dash-tooltip">{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}</div>} placement="top">
             <button className="antimodeMenu-button" style={{ backgroundColor: isPinned ? "121212" : undefined, borderLeft: "1px solid gray" }}
-                onClick={e => TabDocView.PinDoc(targetDoc, { /* unpin: isPinned*/ })}>
+                onClick={e => TabDocView.PinDoc(targetDoc, { unpin: isPinned })}>
                 <FontAwesomeIcon className="colMenu-icon" size="lg" icon="map-pin" />
             </button>
         </Tooltip>;
@@ -507,6 +507,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
                 activeDoc.presPinViewY = y;
                 activeDoc.presPinViewScale = scale;
             } else if (targetDoc.type === DocumentType.VID) {
+                activeDoc.presPinTimecode = targetDoc._currentTimecode;
                 activeDoc.presPinView = true;
             } else if (targetDoc.type === DocumentType.COMPARISON) {
                 const width = targetDoc._clipWidth;
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 372629ddf..8bdcfeea9 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -465,7 +465,6 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             _stayInCollection: true,
             useLinkSmallAnchor: true,
             hideLinkButton: true,
-            _isLinkButton: true,
             annotationOn: rootDoc,
             _timelineLabel: true,
             borderRounding: anchorEndTime === undefined ? "100%" : undefined
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 73572299a..2f002736d 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -30,10 +30,11 @@
         width: 90%;
         margin: 5px;
         font-size: 11px;
+        background-color: $light-blue;
         color: $medium-blue;
         padding: 10px;
-        border-radius: 5px;
-        border: solid .5px $medium-blue;
+        border-radius: 10px;
+        border: solid 2px $medium-blue;
     }
 }
 
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 7573b938a..4f6f45d2f 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -135,7 +135,7 @@ export class CollectionTimeView extends CollectionSubView() {
             onClick={this.contentsDown}>
             <CollectionFreeFormView {...this.props}
                 engineProps={{ pivotField: this.pivotField, docFilters: this.childDocFilters, docRangeFilters: this.childDocRangeFilters }}
-                fitContentsToBox={returnTrue}
+                fitContentsToDoc={returnTrue}
                 childClickScript={this._childClickedScript}
                 viewDefDivClick={this._viewDefDivClick}
                 childFreezeDimensions={true}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 022b9fa24..dab3d490d 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -4,15 +4,15 @@ import { Tooltip } from '@material-ui/core';
 import 'golden-layout/src/css/goldenlayout-base.css';
 import 'golden-layout/src/css/goldenlayout-dark-theme.css';
 import { clamp } from 'lodash';
-import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
 import { observer } from "mobx-react";
 import * as ReactDOM from 'react-dom';
-import { DataSym, Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+import { DataSym, Doc, DocListCast, DocListCastAsync, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { FieldId } from "../../../fields/RefField";
 import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from "../../../Utils";
+import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { DocUtils } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -27,16 +27,14 @@ import { Colors, Shadows } from '../global/globalEnums';
 import { LightboxView } from '../LightboxView';
 import { MainView } from '../MainView';
 import { DocFocusOptions, DocumentView, DocumentViewProps } from "../nodes/DocumentView";
-import { DashFieldView } from '../nodes/formattedText/DashFieldView';
 import { PinProps, PresBox, PresMovement } from '../nodes/trails';
 import { DefaultStyleProvider, StyleProp } from '../StyleProvider';
 import { CollectionDockingView } from './CollectionDockingView';
+import { CollectionDockingViewMenu } from './CollectionDockingViewMenu';
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
 import { CollectionView, CollectionViewType } from './CollectionView';
 import "./TabDocView.scss";
 import React = require("react");
-import { listSpec } from '../../../fields/Schema';
-import { ScriptField } from '../../../fields/ScriptField';
 const _global = (window /* browser */ || global /* node */) as any;
 
 interface TabDocViewProps {
@@ -84,6 +82,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             const iconWrap = document.createElement("div");
             const closeWrap = document.createElement("div");
 
+
             titleEle.size = StrCast(doc.title).length + 3;
             titleEle.value = doc.title;
             titleEle.onkeydown = (e: KeyboardEvent) => {
@@ -94,33 +93,26 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 Doc.GetProto(doc).title = e.currentTarget.value;
             }));
 
+            const dragBtnDown = (e: React.PointerEvent) => {
+                setupMoveUpEvents(this, e, e => !e.defaultPrevented && DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY), returnFalse, emptyFunction);
+            };
+
+
             if (tab.element[0].children[1].children.length === 1) {
-                iconWrap.className = "lm_iconWrap lm_moreInfo";
-                const dragBtnDown = (e: React.PointerEvent) => {
-                    setupMoveUpEvents(this, e, e => !e.defaultPrevented && DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY), returnFalse, action(e => {
-                        if (this.view) {
-                            SelectionManager.SelectView(this.view, false);
-                            let child = this.view.ContentDiv!.children[0];
-                            while (child.children.length) {
-                                const next = Array.from(child.children).find(c => c.className?.toString().includes("SVGAnimatedString") || typeof (c.className) === "string");
-                                if (next?.className?.toString().includes(DocumentView.ROOT_DIV)) break;
-                                if (next?.className?.toString().includes(DashFieldView.name)) break;
-                                if (next) child = next;
-                                else break;
-                            }
-                            simulateMouseClick(child, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
-                        }
-                        else { this._activated = true;
-                              setTimeout(() =>this.view && SelectionManager.SelectView(this.view, false));
-                        }
-                    }));
+                iconWrap.className = "lm_iconWrap";
+                iconWrap.id = "lm_iconWrap";
+                closeWrap.className = "lm_iconWrap";
+                closeWrap.id = "lm_closeWrap";
+                closeWrap.onclick = (e: MouseEvent) => {
+                    tab.header.parent.contentItem.remove();
+                    Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", tab.DashDoc);
+                    Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true);
                 };
-                
                 const docIcon = <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} />;
-                const closeIcon = <FontAwesomeIcon icon={"eye"} />;
+                const closeIcon = <FontAwesomeIcon icon={"times"} />;
                 ReactDOM.render(docIcon, iconWrap);
                 ReactDOM.render(closeIcon, closeWrap);
-                tab.reactComponents = [iconWrap, closeWrap];
+                // tab.element[0].append(closeWrap);
                 tab.element[0].prepend(iconWrap);
                 tab._disposers.layerDisposer = reaction(() => ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }),
                     ({ layer, color }) => {
@@ -131,6 +123,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                         titleEle.style.backgroundColor = "transparent";
                         iconWrap.style.color = textColor;
                         closeWrap.style.color = textColor;
+                        moreInfoDrag.style.backgroundColor = textColor;
                         tab.element[0].style.background = !layer ? color : "dimgrey";
                     }, { fireImmediately: true });
             }
@@ -163,6 +156,19 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     // toggle.style.textTransform = selected ? "uppercase" : "";
                 }));
 
+            //attach the selection doc buttons menu to the drag handle
+            const stack: HTMLDivElement = tab.contentItem.parent;
+            const header: HTMLDivElement = tab;
+            stack.onscroll = action((e: any) => {
+                console.log('scrolling...');
+            });
+            const moreInfoDrag = document.createElement("div");
+            moreInfoDrag.className = "lm_iconWrap";
+            tab._disposers.buttonDisposer = reaction(() => this.view, view =>
+                view && [ReactDOM.render(<span><CollectionDockingViewMenu views={() => [view]} Stack={stack} /></span>, moreInfoDrag), tab._disposers.buttonDisposer?.()],
+                { fireImmediately: true });
+            // tab.reactComponents = [moreInfoDrag];
+            // tab.element[0].children[3].before(moreInfoDrag);
 
             // highlight the tab when the tab document is brushed in any part of the UI
             tab._disposers.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => {
@@ -179,21 +185,20 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     UndoManager.RunInBatch(() => tab.contentItem.remove(), "delete tab");
                 });
         }
+        CollectionDockingView.Instance.tabMap.add(tab);
     }
 
     /**
      * Adds a document to the presentation view
      **/
     @action
-    public static PinDoc(docs: Doc|Doc[], pinProps?: PinProps) {
-        const docList = ((docs instanceof Doc) ? [docs]: docs);
-        const batch = UndoManager.StartBatch("pinning doc");
-
-        // all docs will be added to the ActivePresentation as stored on CurrentUserUtils
+    public static async PinDoc(doc: Doc, pinProps?: PinProps) {
+        if (pinProps?.unpin) console.log('TODO: Remove UNPIN from this location');
+        //add this new doc to props.Document
         const curPres = CurrentUserUtils.ActivePresentation;
-        curPres && docList.forEach(doc => {
-            // Edge Case 1: Cannot pin document to itself
+        if (curPres) {
             if (doc === curPres) { alert("Cannot pin presentation document to itself"); return; }
+            const batch = UndoManager.StartBatch("pinning doc");
             const pinDoc = Doc.MakeAlias(doc);
             pinDoc.presentationTargetDoc = doc;
             pinDoc.title = doc.title + " - Slide";
@@ -213,48 +218,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             const size: number = PresBox.Instance?._selectedArray.size;
             const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined;
             const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null);
-			// If pinWithView option set then update scale and x / y props of slide
-			if (pinProps?.pinWithView) {
-				const viewProps = pinProps.pinWithView;
-				pinDoc.presPinView = true;
-            	pinDoc.presPinViewX = viewProps.bounds.left + viewProps.bounds.width / 2;
-            	pinDoc.presPinViewY = viewProps.bounds.top + viewProps.bounds.height / 2;
-            	pinDoc.presPinViewScale = viewProps.scale;
-                pinDoc.contentBounds = new List<number>([viewProps.bounds.left, viewProps.bounds.top, viewProps.bounds.left+viewProps.bounds.width, viewProps.bounds.top+viewProps.bounds.height]);
-            }
-            if (pinProps?.pinDocView) {
-                const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(pinDoc.type as any) || pinDoc._viewType === CollectionViewType.Stacking;
-                const pannable: boolean = ((pinDoc.type === DocumentType.COL && doc._viewType === CollectionViewType.Freeform) || doc.type === DocumentType.IMG);
-                if (scrollable) {
-                    const scroll = doc._scrollTop;
-                    pinDoc.presPinView = true;
-                    pinDoc.presPinViewScroll = scroll;
-                } else if ([DocumentType.AUDIO, DocumentType.VID].includes(doc.type as any)) {
-                    pinDoc.presPinView = true;
-                    pinDoc.presStartTime = doc._currentTimecode;
-                    pinDoc.presEndTime = NumCast(doc._currentTimecode) + 0.1;
-                } else if (pannable) {
-                    pinDoc.presPinView = true;
-                    pinDoc.presPinViewX = pinDoc._panX;
-                    pinDoc.presPinViewY = pinDoc._panY;
-                    pinDoc.presPinViewScale = pinDoc._viewScale;
-                    const pw = NumCast(pinProps.panelWidth);
-                    const ph = NumCast(pinProps.panelHeight);
-                    const ps = NumCast(pinDoc._viewScale);
-                    if (pw && ph && ps) {
-                        pinDoc.contentBounds = new List<number>([
-                            NumCast(pinDoc.panX)-pw/2/ps,
-                            NumCast(pinDoc.panY)-ph/2/ps,
-                            NumCast(pinDoc.panX)+pw/2/ps,
-                            NumCast(pinDoc.panY)+ph/2/ps]);
-                    }
-                } else if (doc.type === DocumentType.COMPARISON) {
-                    const width = doc._clipWidth;
-                    pinDoc.presPinClipWidth = width;
-                    pinDoc.presPinView = true;
-                }
-			}
-            pinDoc.onClick = ScriptField.MakeFunction("navigateToDoc(self.presentationTargetDoc, self)");
             Doc.AddDocToList(curPres, "data", pinDoc, presSelected);
             if (!pinProps?.audioRange && duration !== undefined) {
                 pinDoc.mediaStart = "manual";
@@ -273,16 +236,20 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 pinDoc.presMovement = PresMovement.None;
             }
             if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
-            PresBox.Instance?._selectedArray.clear();
-            pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
-        });
-        if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) {
-            const docs = Cast(Cast(Doc.UserDoc().myOverlayDocs, Doc, null).data, listSpec(Doc), []);
-            if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1);
-            CollectionDockingView.AddSplit(curPres, "right");
-            setTimeout(() => DocumentManager.Instance.jumpToDocument(docList.lastElement(), false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things
+            const dview = CollectionDockingView.Instance.props.Document;
+            const fieldKey = CollectionDockingView.Instance.props.fieldKey;
+            const tabdocs = await DocListCastAsync(dview[fieldKey]);
+            runInAction(() => {
+                if (!pinProps?.hidePresBox && !tabdocs?.includes(curPres)) {
+                    tabdocs?.push(curPres);  // bcz: Argh! this is annoying.  if multiple documents are pinned, this will get called multiple times before the presentation view is drawn.  Thus it won't be in the tabdocs list and it will get created multple times.  so need to explicilty add the presbox to the list of open tabs
+                    CollectionDockingView.AddSplit(curPres, "right");
+                }
+                PresBox.Instance?._selectedArray.clear();
+                pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
+                DocumentManager.Instance.jumpToDocument(doc, false, undefined, []);
+                batch.end();
+            });
         }
-        setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs
     }
 
     componentDidMount() {
@@ -305,6 +272,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     componentWillUnmount() {
         this._tabReaction?.();
         this._view && DocumentManager.Instance.RemoveView(this._view);
+        this.tab && CollectionDockingView.Instance.tabMap.delete(this.tab);
 
         this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged);
     }
@@ -452,7 +420,9 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             }} ref={ref => {
                 if (this._mainCont = ref) {
                     if (this._lastTab) {
+                        console.log("DUP tab")
                         this._view && DocumentManager.Instance.RemoveView(this._view);
+                        CollectionDockingView.Instance.tabMap.delete(this._lastTab);
                     }
                     this._lastTab = this.tab;
                     (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document);
@@ -565,11 +535,11 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                     docFilters={CollectionDockingView.Instance.childDocFilters}
                     docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
                     searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
-                    fitContentsToBox={returnTrue}
+                    fitContentsToDoc={returnTrue}
                 />
                 <div className="miniOverlay" onPointerDown={this.miniDown} >
                     <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% `, }} />
                 </div>
             </div>;
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 59dc5671b..23b6a7f72 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -216,11 +216,8 @@ export class TreeView extends React.Component<TreeViewProps> {
         this.props.hierarchyIndex !== undefined && this.props.RemFromMap?.(this.doc, this.props.hierarchyIndex);
     }
 
-    componentDidUpdate() {
-        this.props.hierarchyIndex !== undefined && this.props.AddToMap?.(this.doc, this.props.hierarchyIndex);
-    }
-
     componentDidMount() {
+        // TODO: [AL] add these
         this.props.hierarchyIndex !== undefined && this.props.AddToMap?.(this.doc, this.props.hierarchyIndex);
     }
 
@@ -471,7 +468,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             const docs = expandKey === "aliases" ? this.childAliases : expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs;
             let downX = 0, downY = 0;
             return <>
-                {!docs?.length || this.props.AddToMap /* hack to identify pres box trees */ ? (null) : <div className={'treeView-sorting'} style={{ background: sortings[sorting]?.color }} >
+                {!docs?.length ? (null) : <div className={'treeView-sorting'} style={{ background: sortings[sorting]?.color }} >
                     {sortings[sorting]?.label}
                 </div>}
                 <ul key={expandKey + "more"} title="click to change sort order" className={this.doc.treeViewHideTitle ? "no-indent" : ""}
@@ -609,11 +606,13 @@ export class TreeView extends React.Component<TreeViewProps> {
         return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
     }
 
+    // TODO: currently doc focus works, but can't seem to edit title
+    // onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
     onChildClick = () => {
-        return this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!);
+        return this.props.onChildClick?.() ?? (ScriptField.MakeFunction(`DocFocusOrOpen(self)`)! || this._editTitleScript?.());
     }
 
-    onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeViewChildDoubleClick,(!this.props.treeView.outlineMode ? this._openScript?.():null));
+    onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
 
     refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document);
     ignoreEvent = (e: any) => {
@@ -796,7 +795,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             isDocumentActive={isActive}
             styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
             hideTitle={asText}
-            fitContentsToBox={returnTrue}
+            fitContentsToDoc={returnTrue}
             hideDecorationTitle={this.props.treeView.outlineMode}
             hideResizeHandles={this.props.treeView.outlineMode}
             onClick={this.onChildClick}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 52e99f26b..12ad6b02b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -9,7 +9,7 @@ import { InkData, InkField, InkTool, PointData, Segment } from "../../../../fiel
 import { List } from "../../../../fields/List";
 import { ObjectField } from "../../../../fields/ObjectField";
 import { RichTextField } from "../../../../fields/RichTextField";
-import { listSpec } from "../../../../fields/Schema";
+import { createSchema, listSpec } from "../../../../fields/Schema";
 import { ScriptField } from "../../../../fields/ScriptField";
 import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
 import { ImageField } from "../../../../fields/URLField";
@@ -26,7 +26,6 @@ import { DragManager, dropActionType } from "../../../util/DragManager";
 import { HistoryUtil } from "../../../util/History";
 import { InteractionUtils } from "../../../util/InteractionUtils";
 import { LinkManager } from "../../../util/LinkManager";
-import { RecordingApi } from "../../../util/RecordingApi";
 import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
 import { SearchUtil } from "../../../util/SearchUtil";
 import { SelectionManager } from "../../../util/SelectionManager";
@@ -42,7 +41,6 @@ import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveIn
 import { LightboxView } from "../../LightboxView";
 import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
 import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from "../../nodes/DocumentView";
-import { FieldViewProps } from "../../nodes/FieldView";
 import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
 import { PresBox } from "../../nodes/trails/PresBox";
 import { VideoBox } from "../../nodes/VideoBox";
@@ -52,14 +50,26 @@ import { CollectionDockingView } from "../CollectionDockingView";
 import { CollectionSubView } from "../CollectionSubView";
 import { TreeViewType } from "../CollectionTreeView";
 import { CollectionViewType } from "../CollectionView";
-import { TabDocView } from "../TabDocView";
 import { computePivotLayout, computerPassLayout, computerStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from "./CollectionFreeFormLayoutEngines";
 import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
 import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
-import e = require("connect-flash");
-
+import { FieldView, FieldViewProps } from "../../nodes/FieldView";
+
+export const panZoomSchema = createSchema({
+    _panX: "number",
+    _panY: "number",
+    _currentTimecode: "number",
+    _timecodeToShow: "number",
+    _currentFrame: "number",
+    _useClusters: "boolean",
+    _viewTransition: "string",
+    _xPadding: "number",         // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
+    _yPadding: "number",         // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
+    _fitToBox: "boolean",
+    scrollHeight: "number"    // this will be set when the collection is an annotation overlay for a PDF/Webpage
+});
 
 export type collectionFreeformViewProps = {
     annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox)
@@ -127,20 +137,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x))
         };
     }
-    @computed get fitContentsToBox() { return (this.props.fitContentsToBox?.() || this.Document._fitContentsToBox) && !this.isAnnotationOverlay; }
-    @computed get contentBounds() { 
-        const cb = Cast(this.rootDoc.contentBounds, listSpec("number"));
-        return cb ? {x:cb[0], y:cb[1], r:cb[2], b: cb[3]} : 
-            this.props.contentBounds?.() ?? 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.fitContentsToBox ? 0 : Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)); }
-    @computed get nativeHeight() { return this.fitContentsToBox ? 0 : Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)); }
+    @computed get fitToContent() { return (this.props.fitContentsToDoc?.() || this.Document._fitToBox) && !this.isAnnotationOverlay; }
+    @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.fitToContent ? 0 : Doc.NativeWidth(this.Document); }
+    @computed get nativeHeight() { return this.fitToContent ? 0 : Doc.NativeHeight(this.Document); }
     @computed get cachedCenteringShiftX(): number {
-        const scaling = this.fitContentsToBox || !this.contentScaling ? 1 : this.contentScaling;
+        const scaling = this.fitToContent || !this.contentScaling ? 1 : this.contentScaling;
         return this.props.isAnnotationOverlay ? 0 : this.props.PanelWidth() / 2 / scaling;  // shift so pan position is at center of window for non-overlay collections
     }
     @computed get cachedCenteringShiftY(): number {
-        const scaling = this.fitContentsToBox || !this.contentScaling ? 1 : this.contentScaling;
+        const scaling = this.fitToContent || !this.contentScaling ? 1 : this.contentScaling;
         return this.props.isAnnotationOverlay ? 0 : this.props.PanelHeight() / 2 / scaling;// shift so pan position is at center of window for non-overlay collections
     }
     @computed get cachedGetLocalTransform(): Transform {
@@ -165,13 +171,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         this.layoutDoc._panY = vals.bounds.cy;
         this.layoutDoc._viewScale = vals.scale;
     }
-    freeformData = (force?: boolean) => !this._firstRender && (this.fitContentsToBox || force) ? this.fitToContentVals : undefined;
-    reverseNativeScaling = () => this.fitContentsToBox ? true : false;
-    // panx, pany, zoomscale all attempt to get values first from the layout controller, then from the layout/dataDoc (or template layout doc), and finally from the resolved template data document.
-    // this search order, for example, allows icons of cropped images to find the panx/pany/zoom on the cropped image's data doc instead of the usual layout doc because the zoom/panX/panY define the cropped image 
-    panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document._panX, NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panX, 1));
-    panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY, NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panY, 1));
-    zoomScaling = () => this.freeformData()?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.[this.scaleFieldKey], 1));
+    freeformData = (force?: boolean) => !this._firstRender && (this.fitToContent || force) ? this.fitToContentVals : undefined;
+    reverseNativeScaling = () => this.fitToContent ? true : false;
+    panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document._panX);
+    panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY);
+    zoomScaling = () => (this.freeformData()?.scale ?? NumCast(this.Document[this.scaleFieldKey], 1));
     contentTransform = () => !this.cachedCenteringShiftX && !this.cachedCenteringShiftY && this.zoomScaling() === 1 ? "" : `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`;
     getTransform = () => this.cachedGetTransform.copy();
     getLocalTransform = () => this.cachedGetLocalTransform.copy();
@@ -998,16 +1002,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     setPan(panX: number, panY: number, panTime: number = 0, clamp: boolean = false) {
-        // set the current respective FFview to the tab being panned.
-        (Doc.UserDoc()?.presentationMode === 'recording') && RecordingApi.Instance.setRecordingFFView(this);
-        // TODO: make this based off the specific recording FFView
-        (Doc.UserDoc()?.presentationMode === 'none') && RecordingApi.Instance.setPlayFFView(this);
-        if (Doc.UserDoc()?.presentationMode === 'watching') {
-            RecordingApi.Instance.pauseVideoAndMovements();
-            Doc.UserDoc().presentationMode = 'none';
-            // RecordingApi.Instance.pauseMovements()
-        }
-
         if (!this.isAnnotationOverlay && clamp) {
             // this section wraps the pan position, horizontally and/or vertically whenever the content is panned out of the viewing bounds
             const docs = this.childLayoutPairs.map(pair => pair.layout).filter(doc => doc instanceof Doc);
@@ -1234,6 +1228,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             Document={childLayout}
             renderDepth={this.props.renderDepth + 1}
             replica={entry.replica}
+            dataTransition={entry.transition}
             suppressSetHeight={this.layoutEngine ? true : false}
             renderCutoffProvider={this.renderCutoffProvider}
             ContainingCollectionView={this.props.CollectionView}
@@ -1273,7 +1268,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
             pointerEvents={this.pointerEvents}
             jitterRotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
-        //fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
+        //fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
         />;
     }
     addDocTab = action((doc: Doc, where: string) => {
@@ -1416,9 +1411,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica)
             }));
 
-        if (this.props.isAnnotationOverlay && this.props.Document[this.scaleFieldKey]) {   // don't zoom out farther than 1-1 if it's a bounded item (image, video, pdf), otherwise don't allow zooming in closer than 1-1 if it's a text sidebar
-            if (this.props.scaleField) this.props.Document[this.scaleFieldKey] = Math.min(1, this.zoomScaling());
-            else this.props.Document[this.scaleFieldKey] = Math.max(1,this.zoomScaling()); // NumCast(this.props.Document[this.scaleFieldKey]));
+        if (this.props.isAnnotationOverlay) {   // don't zoom out farther than 1-1 if it's a bounded item (image, video, pdf), otherwise don't allow zooming in closer than 1-1 if it's a text sidebar
+            if (this.props.scaleField) this.props.Document[this.scaleFieldKey] = Math.min(1, NumCast(this.props.Document[this.scaleFieldKey], 1));
+            else this.props.Document[this.scaleFieldKey] = Math.max(1, NumCast(this.props.Document[this.scaleFieldKey]));
         }
 
         this.Document._useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
@@ -1495,71 +1490,51 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         }));
     }
 
-    static replaceCanvases(oldDiv: HTMLElement, newDiv: HTMLElement) {
+    replaceCanvases = (oldDiv: HTMLElement, newDiv: HTMLElement) => {
         if (oldDiv.childNodes && newDiv) {
             for (let i = 0; i < oldDiv.childNodes.length; i++) {
                 this.replaceCanvases(oldDiv.childNodes[i] as HTMLElement, newDiv.childNodes[i] as HTMLElement);
             }
         }
         if (oldDiv instanceof HTMLCanvasElement) {
-            if (oldDiv.className === "collectionFreeFormView-grid") {
-                const newCan = newDiv as HTMLCanvasElement;
-                const parEle = newCan.parentElement as HTMLElement;
-                parEle.removeChild(newCan);
-                parEle.appendChild(document.createElement('div'))
-            } else {
-                const canvas = oldDiv;
-                const img = document.createElement('img'); // create a Image Element
-                img.src = canvas.toDataURL();   //image source
-                img.style.width = canvas.style.width;
-                img.style.height = canvas.style.height;
-                const newCan = newDiv as HTMLCanvasElement;
-                const parEle = newCan.parentElement as HTMLElement;
-                parEle.removeChild(newCan);
-                parEle.appendChild(img);
-            }
+            const canvas = oldDiv;
+            const img = document.createElement('img'); // create a Image Element
+            img.src = canvas.toDataURL();   //image source
+            img.style.width = canvas.style.width;
+            img.style.height = canvas.style.height;
+            const newCan = newDiv as HTMLCanvasElement;
+            const parEle = newCan.parentElement as HTMLElement;
+            parEle.removeChild(newCan);
+            parEle.appendChild(img);
         }
     }
 
-    updateIcon = () => CollectionFreeFormView.UpdateIcon(
-        this.layoutDoc[Id] + "-icon" + (new Date()).getTime(),
-        this.props.docViewPath().lastElement().ContentDiv!,
-        this.layoutDoc[WidthSym](), this.layoutDoc[HeightSym](), 
-        this.props.PanelWidth(), this.props.PanelHeight(), 0, 1, false, "",
-        (iconFile, nativeWidth, nativeHeight) => {
-            this.dataDoc.icon = new ImageField(iconFile);
-            this.dataDoc["icon-nativeWidth"] = nativeWidth;
-            this.dataDoc["icon-nativeHeight"] = nativeHeight;
-        });
-
-    public static UpdateIcon(
-        filename:string, docViewContent:HTMLElement, 
-        width: number, height: number, 
-        panelWidth:number, panelHeight: number, 
-        scrollTop:number, 
-        realNativeHeight: number,
-        noSuffix: boolean,
-        replaceRootFilename: string| undefined,
-        cb:(iconFile:string, nativeWidth:number, nativeHeight:number) => any) 
-    {
+    updateIcon = () => {
+        const docViewContent = this.props.docViewPath().lastElement().ContentDiv!;
         const newDiv = docViewContent.cloneNode(true) as HTMLDivElement;
-        newDiv.style.width = width.toString();
-        newDiv.style.height = height.toString();
+        newDiv.style.width = (this.layoutDoc[WidthSym]()).toString();
+        newDiv.style.height = (this.layoutDoc[HeightSym]()).toString();
         this.replaceCanvases(docViewContent, newDiv);
-        const htmlString = new XMLSerializer().serializeToString(newDiv);
-        const nativeWidth = width;
-        const nativeHeight = height;
-        return CreateImage(
-            Utils.prepend(""),
+        const htmlString = this._mainCont && new XMLSerializer().serializeToString(newDiv);
+        const nativeWidth = this.layoutDoc[WidthSym]();
+        const nativeHeight = this.layoutDoc[HeightSym]();
+
+        CreateImage(
+            "",
             document.styleSheets,
             htmlString,
             nativeWidth,
-            nativeWidth * panelHeight / panelWidth,
-            scrollTop * panelHeight  / realNativeHeight
+            nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(),
+            NumCast(this.layoutDoc._scrollTop)
         ).then
-            (async (data_url: any) => {
-                const returnedFilename = await VideoBox.convertDataUri(data_url, filename, noSuffix, replaceRootFilename);
-                cb(returnedFilename as string, nativeWidth, nativeHeight);
+            ((data_url: any) => {
+                VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true, this.layoutDoc[Id] + "-icon").then(
+                    returnedfilename => setTimeout(action(() => {
+
+                        this.dataDoc.icon = new ImageField(returnedfilename);
+                        this.dataDoc["icon-nativeWidth"] = nativeWidth;
+                        this.dataDoc["icon-nativeHeight"] = nativeHeight;
+                    }), 500));
             })
             .catch(function (error: any) {
                 console.error('oops, something went wrong!', error);
@@ -1630,9 +1605,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
         appearanceItems.push({ description: "Reset View", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" });
         !Doc.UserDoc().noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
-        appearanceItems.push({ description: `${this.fitContentsToBox ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitContentsToBox = !this.fitContentsToBox, icon: !this.fitContentsToBox ? "expand-arrows-alt" : "compress-arrows-alt" });
-        appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, {pinDocView:true, panelWidth: this.props.PanelWidth(), panelHeight:this.props.PanelHeight()}), icon: "map-pin" });
-        //appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" });
+        appearanceItems.push({ description: `${this.fitToContent ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
+        appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" });
         this.props.ContainingCollectionView &&
             appearanceItems.push({ description: "Ungroup collection", event: this.promoteCollection, icon: "table" });
 
@@ -1788,7 +1762,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             isAnnotationOverlay={this.isAnnotationOverlay}>
             <div className="marqueeView-div" ref={this._marqueeRef} style={{ opacity: this.props.dontRenderDocuments ? 0 : undefined }}>
                 {this.layoutDoc._backgroundGridShow ?
-                    <div><CollectionFreeFormBackgroundGrid  // bcz : UGHH  don't know why, but if we don't wrap in a div, then PDF's don't render whenn taking snapshot of a dashboard and the background grid is on!!?
+                    <CollectionFreeFormBackgroundGrid
                         PanelWidth={this.props.PanelWidth}
                         PanelHeight={this.props.PanelHeight}
                         panX={this.panX}
@@ -1798,7 +1772,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                         isAnnotationOverlay={this.isAnnotationOverlay}
                         cachedCenteringShiftX={this.cachedCenteringShiftX}
                         cachedCenteringShiftY={this.cachedCenteringShiftY}
-                    /></div> : (null)}
+                    /> : (null)}
                 <CollectionFreeFormViewPannableContents
                     isAnnotationOverlay={this.isAnnotationOverlay}
                     isAnnotationOverlayScrollable={this.props.isAnnotationOverlayScrollable}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 051da795f..7865f2381 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -19,7 +19,7 @@ import { Transform } from "../../../util/Transform";
 import { undoBatch, UndoManager } from "../../../util/UndoManager";
 import { ContextMenu } from "../../ContextMenu";
 import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
-import { PinViewProps, PresBox } from "../../nodes/trails/PresBox";
+import { PresBox } from "../../nodes/trails/PresBox";
 import { PresMovement } from "../../nodes/trails/PresEnums";
 import { VideoBox } from "../../nodes/VideoBox";
 import { pasteImageBitmap } from "../../nodes/WebBoxRenderer";
@@ -30,7 +30,6 @@ import { TreeView } from "../TreeView";
 import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
 import "./MarqueeView.scss";
 import React = require("react");
-import { TabDocView } from "../TabDocView";
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -44,14 +43,6 @@ interface MarqueeViewProps {
     ungroup?: () => void;
     setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void;
 }
-
-export interface MarqueeViewBounds {
-	left: number;
-	top: number;
-	width: number;
-	height: number;
-}
-
 @observer
 export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps>
 {
@@ -70,8 +61,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         const topLeft = this.Transform.transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
         // nda - args to transformDirection is just x and y diff btw downX/Y and lastX/Y
         const size = this.Transform.transformDirection(this._lastX - this._downX, this._lastY - this._downY);
-		const bounds:MarqueeViewBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) }
-        return bounds;
+        return { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
     }
     get inkDoc() { return this.props.Document; }
     get ink() { return Cast(this.props.Document.ink, InkField); }
@@ -274,7 +264,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             document.removeEventListener("wheel", hideMarquee);
         };
         if (PresBox.startMarquee) {
-            this.pinWithView();
+            this.pinMarqueeView();
             PresBox.startMarquee = false;
         }
         if (!this._commandExecuted && (Math.abs(this.Bounds.height * this.Bounds.width) > 100) && !PresBox.startMarquee) {
@@ -395,23 +385,98 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         this.hideMarquee();
     }
 
-	/**
-	 * This triggers the TabDocView.PinDoc method which is the universal method
-	 * used to pin documents to the currently active presentation trail. 
-	 * 
-	 * This one is unique in that it includes the bounds associated with marquee view. 
-	 */
     @undoBatch
     @action
-    pinWithView = async () => {
-        const scale = Math.min(this.props.PanelWidth() / this.Bounds.width, this.props.PanelHeight() / this.Bounds.height);
-		const doc = this.props.Document;
-		const viewOptions:PinViewProps = {
-			bounds: this.Bounds,
-			scale: scale
-		};
-		TabDocView.PinDoc(doc, {pinWithView: viewOptions});
-		MarqueeOptionsMenu.Instance.fadeOut(true);
+    pinMarqueeView = async () => {
+        const doc = this.props.Document;
+        const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
+        if (curPres) {
+            console.log(curPres);
+            if (doc === curPres) { alert("Cannot pin presentation document to itself"); return; }
+            const pinDoc = Doc.MakeAlias(doc);
+            pinDoc.presentationTargetDoc = doc;
+            pinDoc.presMovement = PresMovement.Zoom;
+            pinDoc.groupWithUp = false;
+            pinDoc.context = curPres;
+            pinDoc.title = doc.title + " - Slide";// these should potentially all be props passed down by the CollectionTreeView to the TreeView elements.  That way the PresBox could configure all of its children at render time
+            pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
+            pinDoc.treeViewHeaderWidth = "100%"; // forces the header to grow to be the same size as its largest sibling.
+            pinDoc.treeViewChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc.
+            pinDoc.treeViewFieldKey = "data"; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
+            pinDoc.treeViewExpandedView = "data";// in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
+            pinDoc.treeViewGrowsHorizontally = true;// the document expands horizontally when displayed as a tree view header  
+            pinDoc.treeViewHideHeaderIfTemplate = true;  // this will force the document to render itself as the tree view header
+
+            const presArray = PresBox.Instance?.sortArray();
+            const size = PresBox.Instance?._selectedArray.size;
+            const presSelected = presArray && size ? presArray[size - 1] : undefined;
+            Doc.AddDocToList(curPres, "data", pinDoc, presSelected);
+            if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
+            const dview = CollectionDockingView.Instance.props.Document;
+            const fieldKey = CollectionDockingView.Instance.props.fieldKey;
+            const tabdocs = await DocListCastAsync(dview[fieldKey]);
+            if (!tabdocs?.includes(curPres)) {
+                tabdocs?.push(curPres);  // bcz: Argh! this is annoying.  if multiple documents are pinned, this will get called multiple times before the presentation view is drawn.  Thus it won't be in the tabdocs list and it will get created multple times.  so need to explicilty add the presbox to the list of open tabs
+                CollectionDockingView.AddSplit(curPres, "right");
+            }
+            PresBox.Instance?._selectedArray.clear();
+            pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Updates selected array
+            const index = PresBox.Instance?.childDocs.indexOf(pinDoc);
+            index && (curPres._itemIndex = index);
+            const scale = Math.min(this.props.PanelWidth() / this.Bounds.width, this.props.PanelHeight() / this.Bounds.height);
+            pinDoc.presPinView = true;
+            pinDoc.presPinViewX = this.Bounds.left + this.Bounds.width / 2;
+            pinDoc.presPinViewY = this.Bounds.top + this.Bounds.height / 2;
+            pinDoc.presPinViewScale = scale;
+        }
+        this.hideMarquee();
+    }
+
+    @undoBatch
+    @action
+    pinWithView = async (e: KeyboardEvent | React.PointerEvent | undefined) => {
+        const doc = this.props.Document;
+        const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
+        if (curPres) {
+            if (doc === curPres) { alert("Cannot pin presentation document to itself"); return; }
+            const pinDoc = Doc.MakeAlias(doc);
+            pinDoc.presentationTargetDoc = doc;
+            pinDoc.presMovement = PresMovement.Zoom;
+            pinDoc.groupWithUp = false;
+            pinDoc.context = curPres;
+            pinDoc.title = doc.title + " - Slide";
+            pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
+            pinDoc.treeViewHeaderWidth = "100%"; // forces the header to grow to be the same size as its largest sibling.
+            pinDoc.treeViewChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc.
+            pinDoc.treeViewFieldKey = "data"; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
+            pinDoc.treeViewExpandedView = "data";// in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
+            pinDoc.treeViewGrowsHorizontally = true;// the document expands horizontally when displayed as a tree view header  
+            pinDoc.treeViewHideHeaderIfTemplate = true;  // this will force the document to render itself as the tree view header
+
+            const presArray = PresBox.Instance?.sortArray();
+            const size = PresBox.Instance?._selectedArray.size;
+            const presSelected = presArray && size ? presArray[size - 1] : undefined;
+            Doc.AddDocToList(curPres, "data", pinDoc, presSelected);
+            if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true; const dview = CollectionDockingView.Instance.props.Document;
+            const fieldKey = CollectionDockingView.Instance.props.fieldKey;
+            const tabdocs = await DocListCastAsync(dview[fieldKey]);
+            if (!tabdocs?.includes(curPres)) {
+                tabdocs?.push(curPres);  // bcz: Argh! this is annoying.  if multiple documents are pinned, this will get called multiple times before the presentation view is drawn.  Thus it won't be in the tabdocs list and it will get created multple times.  so need to explicilty add the presbox to the list of open tabs
+                CollectionDockingView.AddSplit(curPres, "right");
+            }
+            PresBox.Instance?._selectedArray.clear();
+            pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Updates selected array
+            const index = PresBox.Instance?.childDocs.indexOf(pinDoc);
+            index && (curPres._itemIndex = index);
+            if (e instanceof KeyboardEvent ? e.key === "c" : true) {
+                const scale = Math.min(this.props.PanelWidth() / this.Bounds.width, this.props.PanelHeight() / this.Bounds.height);
+                pinDoc.presPinView = true;
+                pinDoc.presPinViewX = this.Bounds.left + this.Bounds.width / 2;
+                pinDoc.presPinViewY = this.Bounds.top + this.Bounds.height / 2;
+                pinDoc.presPinViewScale = scale;
+            }
+        }
+        MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
     }
 
@@ -434,7 +499,6 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             }));
             this.props.removeDocument?.(selected);
         }
-        // TODO: nda - this is the code to actually get a new grouped collection
         const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined, group);
         this.props.addDocument?.(newCollection);
         this.props.selectDocuments([newCollection]);
@@ -517,7 +581,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             d.y = NumCast(d.y) - this.Bounds.top;
             return d;
         });
-        const summary = Docs.Create.TextDocument("", { _backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitContentsToBox: true, _showSidebar: true, title: "overview" });
+        const summary = Docs.Create.TextDocument("", { _backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" });
         const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: "transparent" });
         DocUtils.MakeLink({ doc: summary }, { doc: portal }, "summary of:summarized by", "");
 
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 9b1cb1601..c0a33a5e0 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -153,7 +153,6 @@ export class CollectionLinearView extends CollectionSubView() {
                 PanelWidth={nested ? doc[WidthSym] : this.dimension}
                 PanelHeight={nested || doc._height ? doc[HeightSym] : this.dimension}
                 renderDepth={this.props.renderDepth + 1}
-                dontRegisterView={BoolCast(this.rootDoc.childDontRegisterViews)}
                 focus={emptyFunction}
                 styleProvider={this.props.styleProvider}
                 docViewPath={returnEmptyDoclist}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index b7ba94940..e2dfb25e2 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -257,7 +257,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
             hideResizeHandles={this.props.childHideResizeHandles?.()}
             hideDecorationTitle={this.props.childHideDecorationTitle?.()}
-            fitContentsToBox={this.props.fitContentsToBox}
+            fitContentsToDoc={this.props.fitContentsToDoc}
             PanelWidth={width}
             PanelHeight={height}
             rootSelected={this.rootSelected}
@@ -272,7 +272,6 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
-            dontRegisterView={this.props.dontRegisterView}
             addDocument={this.props.addDocument}
             moveDocument={this.props.moveDocument}
             removeDocument={this.props.removeDocument}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 338639a83..3010e36aa 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -264,14 +264,13 @@ export class CollectionMultirowView extends CollectionSubView() {
             isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
             hideResizeHandles={this.props.childHideResizeHandles?.()}
             hideDecorationTitle={this.props.childHideDecorationTitle?.()}
-            fitContentsToBox={this.props.fitContentsToBox}
+            fitContentsToDoc={this.props.fitContentsToDoc}
             focus={this.props.focus}
             docFilters={this.childDocFilters}
             docRangeFilters={this.childDocRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
-            dontRegisterView={this.props.dontRegisterView}
             addDocument={this.props.addDocument}
             moveDocument={this.props.moveDocument}
             removeDocument={this.props.removeDocument}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index f45068b6a..b731479a5 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -402,7 +402,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                 <DocumentView
                     Document={this.previewDocument}
                     DataDoc={undefined}
-                    fitContentsToBox={returnTrue}
+                    fitContentsToDoc={returnTrue}
                     freezeDimensions={true}
                     dontCenter={"y"}
                     focus={DocUtils.DefaultFocus}
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index a75e7a0c4..3ddcf803d 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -102,10 +102,6 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
 
                     this.props.itemHandler?.(this.props.linkDoc);
                 } else {
-                    const focusDoc = Cast(this.props.linkDoc.anchor1, Doc, null)?.annotationOn === this.props.sourceDoc ? Cast(this.props.linkDoc.anchor1, Doc, null) :
-                        Cast(this.props.linkDoc.anchor2, Doc, null)?.annotationOn === this.props.sourceDoc ? Cast(this.props.linkDoc.anchor12, Doc, null) : undefined;
-
-                    if (focusDoc) this.props.docView.ComponentView?.scrollFocus?.(focusDoc, true);
                     LinkManager.FollowLink(this.props.linkDoc, this.props.sourceDoc, this.props.docView.props, false);
                 }
             });
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 145b6d5a6..1d06f368f 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -4,6 +4,8 @@ import { action, computed, IReactionDisposer, observable, runInAction } from "mo
 import { observer } from "mobx-react";
 import { DateField } from "../../../fields/DateField";
 import { Doc, DocListCast } from "../../../fields/Doc";
+import { documentSchema } from "../../../fields/documentSchemas";
+import { makeInterface } from "../../../fields/Schema";
 import { ComputedField } from "../../../fields/ScriptField";
 import { Cast, DateCast, NumCast } from "../../../fields/Types";
 import { AudioField, nullAudio } from "../../../fields/URLField";
@@ -294,10 +296,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
 
     // button for starting and stopping the recording
-    Record = (e: React.PointerEvent) => {
-        e.button === 0 && !e.ctrlKey && setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => {
+    Record = (e: React.MouseEvent) => {
+        if (e.button === 0 && !e.ctrlKey) {
             this._recorder ? this.stopRecording() : this.recordAudioAnnotation();
-        }), false);
+            e.stopPropagation();
+        }
     }
 
     // for play button
@@ -335,28 +338,27 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     // for dictation button, creates a text document for dictation
     onFile = (e: any) => {
-        setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => {
-            const newDoc = CurrentUserUtils.GetNewTextDoc(
-                "",
-                NumCast(this.rootDoc.x),
-                NumCast(this.rootDoc.y) +
-                NumCast(this.layoutDoc._height) +
-                10,
-                NumCast(this.layoutDoc._width),
-                2 * NumCast(this.layoutDoc._height)
-            );
-            Doc.GetProto(newDoc).recordingSource = this.dataDoc;
-            Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.fieldKey}-recordingStart"]`);
-            Doc.GetProto(newDoc).mediaState = ComputedField.MakeFunction("self.recordingSource.mediaState");
-            const overlayDoc = Doc.UserDoc().myOverlayDocs as Doc;
-            if (DocListCast(overlayDoc[Doc.LayoutFieldKey(overlayDoc)]).includes(this.rootDoc)) {
-                newDoc.x = this.rootDoc.x;
-                newDoc.y = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
-                Doc.AddDocToList(overlayDoc, undefined, newDoc);
-            } else {
-                this.props.addDocument?.(newDoc);
-            }
-        }), false);
+        const newDoc = CurrentUserUtils.GetNewTextDoc(
+            "",
+            NumCast(this.rootDoc.x),
+            NumCast(this.rootDoc.y) +
+            NumCast(this.layoutDoc._height) +
+            10,
+            NumCast(this.layoutDoc._width),
+            2 * NumCast(this.layoutDoc._height)
+        );
+        Doc.GetProto(newDoc).recordingSource = this.dataDoc;
+        Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.fieldKey}-recordingStart"]`);
+        Doc.GetProto(newDoc).mediaState = ComputedField.MakeFunction("self.recordingSource.mediaState");
+        const overlayDoc = Doc.UserDoc().myOverlayDocs as Doc;
+        if (DocListCast(overlayDoc[Doc.LayoutFieldKey(overlayDoc)]).includes(this.rootDoc)) {
+            newDoc.x = this.rootDoc.x;
+            newDoc.y = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
+            Doc.AddDocToList(overlayDoc, undefined, newDoc);
+        } else {
+            this.props.addDocument?.(newDoc);
+        }
+        e.stopPropagation();
     }
 
 
@@ -369,21 +371,21 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
 
     // pause the time during recording phase
-    recordPause = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => {
-            this._pauseStart = new Date().getTime();
-            this._paused = true;
-            this._recorder.pause();
-        }), false);
+    @action
+    recordPause = (e: React.MouseEvent) => {
+        this._pauseStart = new Date().getTime();
+        this._paused = true;
+        this._recorder.pause();
+        e.stopPropagation();
     }
 
     // continue the recording
-    recordPlay = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => {
-            this._pauseEnd = new Date().getTime();
-            this._paused = false;
-            this._recorder.resume();
-        }), false);
+    @action
+    recordPlay = (e: React.MouseEvent) => {
+        this._pauseEnd = new Date().getTime();
+        this._paused = false;
+        this._recorder.resume();
+        e.stopPropagation();
     }
 
 
@@ -502,19 +504,19 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     // UI for recording, initially displayed when new audio created in Dash
     @computed get recordingControls() {
         return <div className="audiobox-recorder">
-            <div className="audiobox-dictation" onPointerDown={this.onFile}>
+            <div className="audiobox-dictation" onClick={this.onFile}>
                 <FontAwesomeIcon
                     size="2x"
                     icon="file-alt" />
             </div>
             {[media_state.Recording, media_state.Playing].includes(this.mediaState) ?
                 <div className="recording-controls" onClick={e => e.stopPropagation()}>
-                    <div className="record-button" onPointerDown={this.Record}>
+                    <div className="record-button" onClick={this.Record}>
                         <FontAwesomeIcon
                             size="2x"
                             icon="stop" />
                     </div>
-                    <div className="record-button" onPointerDown={this._paused ? this.recordPlay : this.recordPause}>
+                    <div className="record-button" onClick={this._paused ? this.recordPlay : this.recordPause}>
                         <FontAwesomeIcon
                             size="2x"
                             icon={this._paused ? "play" : "pause"} />
@@ -524,7 +526,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     </div>
                 </div>
                 :
-                <div className="audiobox-start-record" onPointerDown={this.Record}>
+                <div className="audiobox-start-record">
                     <FontAwesomeIcon icon="microphone" />
                     RECORD
                 </div>}
@@ -653,6 +655,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             ref={this.setupTimelineDrop}
             className="audiobox-container"
             onContextMenu={this.specificContextMenu}
+            onClick={!this.path && !this._recorder ? this.recordAudioAnnotation : undefined}
             style={{ pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined }}
         >
             {!this.path ? this.recordingControls : this.playbackControls}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index bedc97575..5a0ab9110 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -6,7 +6,7 @@ import { listSpec } from "../../../fields/Schema";
 import { ComputedField } from "../../../fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { TraceMobx } from "../../../fields/util";
-import { DashColor, numberRange, OmitKeys } from "../../../Utils";
+import { DashColor, numberRange } from "../../../Utils";
 import { DocumentManager } from "../../util/DocumentManager";
 import { SelectionManager } from "../../util/SelectionManager";
 import { Transform } from "../../util/Transform";
@@ -170,7 +170,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 width: this.panelWidth(),
                 height: this.panelHeight(),
                 transform: this.transform,
-                transition: this.dataProvider?.transition ?? (this.props.dataTransition ? this.props.dataTransition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.dataTransition)),
+                transition: this.props.dataTransition ? this.props.dataTransition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.dataTransition),
                 zIndex: this.ZInd,
                 mixBlendMode: mixBlendMode,
                 display: this.ZInd === -99 ? "none" : undefined
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 70732e74c..d4e8ffc7f 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -41,7 +41,6 @@ import { WebBox } from "./WebBox";
 import React = require("react");
 import XRegExp = require("xregexp");
 import { MapBox } from "./MapBox/MapBox";
-import { RecordingBox } from "./RecordingBox";
 
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
@@ -226,7 +225,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
                 components={{
                     FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, LabelBox, EquationBox, SliderBox, FieldView,
                     CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
-                    PDFBox, VideoBox, AudioBox, RecordingBox, PresBox, YoutubeBox, PresElementBox, SearchBox, FilterBox, FunctionPlotBox,
+                    PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, SearchBox, FilterBox, FunctionPlotBox,
                     ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, LinkBox, ScriptingBox, MapBox,
                     ScreenshotBox,
                     HTMLtag, ComparisonBox
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 60d16f044..073f734c0 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -85,7 +85,7 @@ export interface DocComponentView {
     getAnchor?: () => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
     scrollFocus?: (doc: Doc, smooth: boolean) => Opt<number>; // returns the duration of the focus
     setViewSpec?: (anchor: Doc, preview: boolean) => void;  // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
-    reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height.  However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
+    reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height.  However, some content views (e.g., FreeFormView w/ fitToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
     shrinkWrap?: () => void;  // requests a document to display all of its contents with no white space.  currently only implemented (needed?) for freeform views
     menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected.
     isAnyChildContentActive?: () => boolean; // is any child content of the document active
@@ -112,8 +112,7 @@ export interface DocumentViewSharedProps {
     renderDepth: number;
     Document: Doc;
     DataDoc?: Doc;
-    contentBounds?: () => (undefined|{x:number, y:number, r:number, b:number});
-    fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitContentsToBox property on a Document
+    fitContentsToDoc?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitToBox property on a Document
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
     suppressSetHeight?: boolean;
@@ -164,9 +163,6 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
     hideResizeHandles?: boolean; // whether to suppress DocumentDecorations when this document is selected
     hideTitle?: boolean;  // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
     hideDecorationTitle?: boolean;  // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
-    hideDocumentButtonBar?: boolean;
-    hideOpenButton?: boolean;
-    hideDeleteButton?: boolean;
     treeViewDoc?: Doc;
     isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events
     isContentActive: () => boolean | undefined; // whether document contents should handle pointer events
@@ -764,7 +760,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             DocListCast(this.Document.links).length && appearanceItems.splice(0, 0, { description: `${this.layoutDoc.hideLinkButton ? "Show" : "Hide"} Link Button`, event: action(() => this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton), icon: "eye" });
             !appearance && cm.addItem({ description: "UI Controls...", subitems: appearanceItems, icon: "compass" });
 
-            if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
+            if (!Doc.IsSystem(this.rootDoc) && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
                 !Doc.UserDoc().noviceMode && appearanceItems.splice(0, 0, { description: `${!this.layoutDoc._showAudio ? "Show" : "Hide"} Audio Button`, event: action(() => this.layoutDoc._showAudio = !this.layoutDoc._showAudio), icon: "microphone" });
                 const existingOnClick = cm.findByDescription("OnClick...");
                 const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
@@ -834,10 +830,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
             const help = cm.findByDescription("Help...");
             const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
-            helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
-            !Doc.UserDoc().noviceMode && helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
-            !Doc.UserDoc().noviceMode && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
-            !Doc.UserDoc().noviceMode && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" });
+            !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
+            helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
+            !Doc.UserDoc().novice && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
+            !Doc.UserDoc().novice && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" });
             cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
         }
 
@@ -960,7 +956,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @computed get allLinkEndpoints() {  // the small blue dots that mark the endpoints of links
         TraceMobx();
         if (this.layoutDoc.unrendered || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return null;
-        if (this.rootDoc.type=== DocumentType.PRES || this.rootDoc.type === DocumentType.LINK || this.props.dontRegisterView) return (null);
+        if (this.layoutDoc.presBox || this.rootDoc.type === DocumentType.LINK || this.props.dontRegisterView) return (null);
         const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => !d.hidden);
         return filtered.map((link, i) =>
             <div className="documentView-anchorCont" key={link[Id]}>
@@ -1204,7 +1200,6 @@ export class DocumentView extends React.Component<DocumentViewProps> {
 
     @observable public docView: DocumentViewInternal | undefined | null;
 
-    showContextMenu(pageX:number, pageY:number) { return this.docView?.onContextMenu(undefined, pageX, pageY); }
     get Document() { return this.props.Document; }
     get topMost() { return this.props.renderDepth === 0; }
     get rootDoc() { return this.docView?.rootDoc || this.Document; }
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 67cf18d8b..79c1f1c40 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -7,7 +7,6 @@ import { List } from "../../../fields/List";
 import { WebField } from "../../../fields/URLField";
 import { DocumentView, DocumentViewSharedProps } from "./DocumentView";
 import { ScriptField } from "../../../fields/ScriptField";
-import { RecordingBox } from "./RecordingBox";
 
 //
 // these properties get assigned through the render() method of the DocumentView when it creates this node.
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index e3708d7b9..28834a202 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -206,7 +206,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
             facetValues.strings.map(val => {
                 const num = val ? Number(val) : Number.NaN;
                 if (Number.isNaN(num)) {
-                    val && nonNumbers++;
+                    num && nonNumbers++;
                 } else {
                     minVal = Math.min(num, minVal);
                     maxVal = Math.max(num, maxVal);
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 8c27b3508..ab4ed6b33 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -357,7 +357,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return <div className="imageBox-annotationLayer" style={{ height: this.props.PanelHeight() }} ref={this._annotationLayer} />;
     }
     marqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._viewScale,1) <= NumCast(this.rootDoc.viewScaleMin,1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
             setupMoveUpEvents(this, e, action(e => {
                 MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                 this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 6c7e174f7..152288d8b 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -121,7 +121,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
             LinkDocPreview.Clear();
             LinkManager.FollowLink(this._linkDoc, this._linkSrc, this.props.docProps, false);
         } else if (this.props.hrefs?.length) {
-            this.props.docProps?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true }), "add:right");
+            this.props.docProps?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _width: 200, _height: 400, useCors: true }), "add:right");
         }
     }
     width = () => {
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 5f4c17ee6..9f1c019fe 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -259,7 +259,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
             if (this._loadPending && this._map.getBounds()) {
                 this._loadPending = false;
-                this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
+                this.layoutDoc.fitToBox && this.fitBounds(this._map);
             }
         }, 250);
         // listener to addmarker event
@@ -274,7 +274,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     centered = () => {
         if (this._loadPending && this._map.getBounds()) {
             this._loadPending = false;
-            this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
+            this.layoutDoc.fitToBox && this.fitBounds(this._map);
         }
         this.dataDoc.mapLat = this._map.getCenter()?.lat();
         this.dataDoc.mapLng = this._map.getCenter()?.lng();
@@ -284,7 +284,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     zoomChanged = () => {
         if (this._loadPending && this._map.getBounds()) {
             this._loadPending = false;
-            this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
+            this.layoutDoc.fitToBox && this.fitBounds(this._map);
         }
         this.dataDoc.mapZoom = this._map.getZoom();
     }
diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
index bdf0f9d64..db7dcb09d 100644
--- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
+++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
@@ -45,9 +45,11 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
 
 
     _stack: CollectionStackingView | null | undefined;
-    childFitWidth = (doc:Doc) => doc.type === DocumentType.RTF;
-    addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.AddDocToList(this.props.place, "data", d), true as boolean);
-    removeDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, "data", d), true as boolean);
+
+    // Collection stacking view for documents in the infowindow of a map marker
+    @computed get renderChildDocs() {
+        return;
+    }
     render() {
         return <InfoWindow anchor={this.props.markerMap[this.props.place[Id]]} onCloseClick={this.handleInfoWindowClose} >
             <div className="mapbox-infowindow">
@@ -71,10 +73,10 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
                         rootSelected={returnFalse}
                         childHideResizeHandles={returnTrue}
                         childHideDecorationTitle={returnTrue}
-                        childFitWidth={this.childFitWidth}
+                        childFitWidth={doc => doc.type === DocumentType.RTF}
                         // childDocumentsActive={returnFalse}
-                        removeDocument={this.removeDoc}
-                        addDocument={this.addDoc}
+                        removeDocument={(doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, "data", d), true as boolean)}
+                        addDocument={(doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.AddDocToList(this.props.place, "data", d), true as boolean)}
                         renderDepth={this.props.renderDepth + 1}
                         viewType={CollectionViewType.Stacking}
                         pointerEvents={returnAll}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 4eb07fd8d..f2ca6c96e 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -26,7 +26,6 @@ import { ImageBox } from './ImageBox';
 import "./PDFBox.scss";
 import { VideoBox } from './VideoBox';
 import React = require("react");
-import { CollectionFreeFormView } from '../collections/collectionFreeForm';
 
 @observer
 export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
@@ -148,23 +147,34 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
     updateIcon = () => {
-        // currently we render pdf icons as text labels
+        return; // currently we render pdf icons as text labels
         const docViewContent = this.props.docViewPath().lastElement().ContentDiv!;
-        const filename = this.layoutDoc[Id] + "-icon" + (new Date()).getTime();
-        this._pdfViewer?._mainCont.current && CollectionFreeFormView.UpdateIcon(
-            filename, docViewContent, 
-            this.layoutDoc[WidthSym](), this.layoutDoc[HeightSym](), 
-            this.props.PanelWidth(), this.props.PanelHeight(),  
-            NumCast(this.layoutDoc._scrollTop),
-            NumCast(this.rootDoc[this.fieldKey + "-nativeHeight"], 1), 
-            true,  
-            this.layoutDoc[Id] + "-icon",
-            (iconFile:string, nativeWidth:number, nativeHeight:number) => {
-                setTimeout(() => {
-                    this.dataDoc.icon = new ImageField(iconFile);
-                    this.dataDoc["icon-nativeWidth"] = nativeWidth;
-                    this.dataDoc["icon-nativeHeight"] = nativeHeight;
-                }, 500);
+        const newDiv = docViewContent.cloneNode(true) as HTMLDivElement;
+        newDiv.style.width = (this.layoutDoc[WidthSym]()).toString();
+        newDiv.style.height = (this.layoutDoc[HeightSym]()).toString();
+        this.replaceCanvases(docViewContent, newDiv);
+        const htmlString = this._pdfViewer?._mainCont.current && new XMLSerializer().serializeToString(newDiv);
+        const nativeWidth = this.layoutDoc[WidthSym]();
+        const nativeHeight = this.layoutDoc[HeightSym]();
+
+        CreateImage(
+            "",
+            document.styleSheets,
+            htmlString,
+            nativeWidth,
+            nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(),
+            NumCast(this.layoutDoc._scrollTop) * this.props.PanelHeight() / NumCast(this.rootDoc[this.fieldKey + "-nativeHeight"])
+        ).then
+            ((data_url: any) => {
+                VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true, this.layoutDoc[Id] + "-icon").then(
+                    returnedfilename => setTimeout(action(() => {
+                        this.dataDoc.icon = new ImageField(returnedfilename);
+                        this.dataDoc["icon-nativeWidth"] = nativeWidth;
+                        this.dataDoc["icon-nativeHeight"] = nativeHeight;
+                    }), 500));
+            })
+            .catch(function (error: any) {
+                console.error('oops, something went wrong!', error);
             });
     }
 
diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.scss b/src/client/views/nodes/RecordingBox/ProgressBar.scss
deleted file mode 100644
index a493b0b89..000000000
--- a/src/client/views/nodes/RecordingBox/ProgressBar.scss
+++ /dev/null
@@ -1,26 +0,0 @@
-
-.progressbar {
-      position: absolute;
-      display: flex;
-      justify-content: flex-start;
-      bottom: 10px;
-      width: 80%;
-      height: 5px;
-      background-color: gray;
-
-      &.done {
-            top: 0;
-            width: 0px;
-            height: 5px;
-            background-color: red;
-            z-index: 2;
-      }
-
-      &.mark {
-            top: 0;
-            background-color: transparent;
-            border-right: 2px solid white;
-            z-index: 3;
-            pointer-events: none; 
-      }
-}
\ No newline at end of file
diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
deleted file mode 100644
index 82d5e1f04..000000000
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import * as React from 'react';
-import { useEffect } from "react"
-import "./ProgressBar.scss"
-
-interface ProgressBarProps {
-      progress: number,
-      marks: number[],
-}
-
-export function ProgressBar(props: ProgressBarProps) {
-
-      // const handleClick = (e: React.MouseEvent) => {
-      //       let progressbar = document.getElementById('progressbar')!
-      //       let bounds = progressbar!.getBoundingClientRect();
-      //       let x = e.clientX - bounds.left;
-      //       let percent = x / progressbar.clientWidth * 100
-
-      //       for (let i = 0; i < props.marks.length; i++) {
-      //             let start = i == 0 ? 0 : props.marks[i-1];
-      //             if (percent > start && percent < props.marks[i]) {
-      //                   props.playSegment(i)
-      //                   // console.log(i)
-      //                   // console.log(percent)
-      //                   // console.log(props.marks[i])
-      //                   break
-      //             }
-      //       }
-      // }
-
-      return (
-            <div className="progressbar" id="progressbar">
-                  <div
-                        className="progressbar done"
-                        style={{ width: `${props.progress}%` }}
-                  // onClick={handleClick}
-                  ></div>
-                  {props.marks.map((mark) => {
-                        return <div
-                              className="progressbar mark"
-                              style={{ width: `${mark}%` }}
-                        ></div>
-                  })}
-            </div>
-      )
-}
\ No newline at end of file
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
deleted file mode 100644
index 10393624b..000000000
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import { action, observable } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import { VideoField } from "../../../../fields/URLField";
-import { Upload } from "../../../../server/SharedMediaTypes";
-import { ViewBoxBaseComponent } from "../../DocComponent";
-import { FieldView } from "../FieldView";
-import { VideoBox } from "../VideoBox";
-import { RecordingView } from './RecordingView';
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { RecordingApi } from "../../../util/RecordingApi";
-import { Doc, FieldsSym } from "../../../../fields/Doc";
-import { Id } from "../../../../fields/FieldSymbols";
-
-
-@observer
-export class RecordingBox extends ViewBoxBaseComponent() {
-
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(RecordingBox, fieldKey); }
-
-    private _ref: React.RefObject<HTMLDivElement> = React.createRef();
-
-    constructor(props: any) {
-        super(props);
-    }
-
-    componentDidMount() {
-        console.log("set native width and height")
-        Doc.SetNativeWidth(this.dataDoc, 1280);
-        Doc.SetNativeHeight(this.dataDoc, 720);
-    }
-
-    @observable result: Upload.FileInformation | undefined = undefined
-    @observable videoDuration: number | undefined = undefined
-
-    @action
-    setVideoDuration = (duration: number) => {
-        this.videoDuration = duration
-    }
-
-    @action
-    setResult = (info: Upload.FileInformation, trackScreen: boolean) => {
-        this.result = info
-        this.dataDoc.type = DocumentType.VID;
-        this.dataDoc[this.fieldKey + "-duration"] = this.videoDuration;
-
-        this.dataDoc.layout = VideoBox.LayoutString(this.fieldKey);
-        this.dataDoc[this.props.fieldKey] = new VideoField(this.result.accessPaths.agnostic.client);
-        this.dataDoc[this.fieldKey + "-recorded"] = true;
-        // stringify the presenation and store it
-        if (trackScreen) {
-            this.dataDoc[this.fieldKey + "-presentation"] = JSON.stringify(RecordingApi.Instance.clear());
-        }
-    }
-
-    render() {
-        return <div className="recordingBox" ref={this._ref}>
-            {!this.result && <RecordingView setResult={this.setResult} setDuration={this.setVideoDuration} id={Doc.GetProto(this.rootDoc)[Id]} />}
-        </div>;
-    }
-}
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.scss b/src/client/views/nodes/RecordingBox/RecordingView.scss
deleted file mode 100644
index 9b2f6d070..000000000
--- a/src/client/views/nodes/RecordingBox/RecordingView.scss
+++ /dev/null
@@ -1,207 +0,0 @@
-video {
-      // flex: 100%;
-      width: 100%;
-      // min-height: 400px;
-      //height: auto;
-      height: 100%;
-      //display: block;
-      object-fit: cover;
-      background-color: black;
-}
-
-button {
-      margin: 0 .5rem
-}
-
-.recording-container {
-      height: 100%;
-      width: 100%;
-      // display: flex;
-      pointer-events: all;
-      background-color: grey;
-}
-
-.video-wrapper {
-      //   max-width: 600px;
-      //   max-width: 700px;
-      position: relative;
-      display: flex;
-      justify-content: center;
-      //   overflow: hidden;
-      border-radius: 10px;
-      margin: 0;
-}
-
-.video-wrapper:hover .controls {
-      bottom: 30px;
-      transform: translateY(0%);
-      opacity: 100%;
-}
-
-.controls {
-      display: flex;
-      align-items: center;
-      justify-content: space-evenly;
-      position: absolute;
-      padding: 14px;
-      width: 100%;
-      max-width: 500px;
-      // max-height: 20%;
-      flex-wrap: wrap;
-      background: rgba(255, 255, 255, 0.25);
-      box-shadow: 0 8px 32px 0 rgba(255, 255, 255, 0.1);
-      backdrop-filter: blur(4px);
-      border-radius: 10px;
-      border: 1px solid rgba(255, 255, 255, 0.18);
-      // transform: translateY(150%);
-      transition: all 0.3s ease-in-out;
-      // opacity: 0%;
-      bottom: 30px;
-      // bottom: -150px;
-}
-
-.actions button {
-      background: none;
-      border: none;
-      outline: none;
-      cursor: pointer;
-}
-
-.actions button i {
-      background-color: none;
-      color: white;
-      font-size: 30px;
-}
-
-
-.velocity {
-      appearance: none;
-      background: none;
-      color: white;
-      outline: none;
-      border: none;
-      text-align: center;
-      font-size: 16px;
-}
-
-.mute-btn {
-      background: none;
-      border: none;
-      outline: none;
-      cursor: pointer;
-}
-
-.mute-btn i {
-      background-color: none;
-      color: white;
-      font-size: 20px;
-}
-
-.recording-sign {
-      height: 20px;
-      width: auto;
-      display: flex;
-      flex-direction: row;
-      position: absolute;
-      top: 10px;
-      right: 15px;
-      align-items: center;
-      justify-content: center;
-
-      .timer {
-            font-size: 15px;
-            color: white;
-            margin: 0;
-      }
-
-      .dot {
-            height: 15px;
-            width: 15px;
-            margin: 5px;
-            background-color: red;
-            border-radius: 50%;
-            display: inline-block;
-      }
-}
-
-.controls-inner-container {
-      display: flex;
-      flex-direction: row;
-      justify-content: center;
-      width: 100%;
-
-}
-
-.record-button-wrapper {
-      width: 35px;
-      height: 35px;
-      font-size: 0;
-      background-color: grey;
-      border: 0px;
-      border-radius: 35px;
-      margin: 10px;
-      display: flex;
-      justify-content: center;
-
-      .record-button {
-            background-color: red;
-            border: 0px;
-            border-radius: 50%;
-            height: 80%;
-            width: 80%;
-            align-self: center;
-            margin: 0;
-
-            &:hover {
-                  height: 85%;
-                  width: 85%;
-            }
-      }
-
-      .stop-button {
-            background-color: red;
-            border: 0px;
-            border-radius: 10%;
-            height: 70%;
-            width: 70%;
-            align-self: center;
-            margin: 0;
-
-
-            // &:hover {
-            //   width: 40px;
-            //   height: 40px
-            // }
-      }
-
-}
-
-.options-wrapper {
-      height: 100%;
-      display: flex;
-      flex-direction: row;
-      align-items: center;
-      position: absolute;
-      top: 0;
-      bottom: 0;
-
-      &.video-edit-wrapper {
-
-            right: 50% - 15;
-
-            .track-screen {
-                  font-weight: 200;
-            }
-
-      }
-
-      &.track-screen-wrapper {
-
-            right: 50% - 30;
-
-            .track-screen {
-                  font-weight: 200;
-            }
-
-      }
-}
\ No newline at end of file
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
deleted file mode 100644
index b95335792..000000000
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ /dev/null
@@ -1,282 +0,0 @@
-import * as React from 'react';
-import "./RecordingView.scss";
-import { ReactElement, useCallback, useEffect, useRef, useState } from "react";
-import { ProgressBar } from "./ProgressBar"
-import { MdBackspace } from 'react-icons/md';
-import { FaCheckCircle } from 'react-icons/fa';
-import { IconContext } from "react-icons";
-import { Networking } from '../../../Network';
-import { Upload } from '../../../../server/SharedMediaTypes';
-
-import { RecordingApi } from '../../../util/RecordingApi';
-import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
-
-interface MediaSegment {
-    videoChunks: any[],
-    endTime: number
-}
-
-interface IRecordingViewProps {
-    setResult: (info: Upload.FileInformation, trackScreen: boolean) => void
-    setDuration: (seconds: number) => void
-    id: string
-}
-
-const MAXTIME = 100000;
-
-export function RecordingView(props: IRecordingViewProps) {
-
-    const [recording, setRecording] = useState(false);
-    const recordingTimerRef = useRef<number>(0);
-    const [recordingTimer, setRecordingTimer] = useState(0); // unit is 0.01 second
-    const [playing, setPlaying] = useState(false);
-    const [progress, setProgress] = useState(0);
-
-    const [videos, setVideos] = useState<MediaSegment[]>([]);
-    const videoRecorder = useRef<MediaRecorder | null>(null);
-    const videoElementRef = useRef<HTMLVideoElement | null>(null);
-
-    const [finished, setFinished] = useState<boolean>(false)
-    const [trackScreen, setTrackScreen] = useState<boolean>(true)
-
-
-
-    const DEFAULT_MEDIA_CONSTRAINTS = {
-        video: {
-            width: 1280,
-            height: 720,
-        },
-        audio: {
-            echoCancellation: true,
-            noiseSuppression: true,
-            sampleRate: 44100
-        }
-    }
-
-    useEffect(() => {
-
-        if (finished) {
-            props.setDuration(recordingTimer * 100)
-            let allVideoChunks: any = []
-            videos.forEach((vid) => {
-                console.log(vid.videoChunks)
-                allVideoChunks = allVideoChunks.concat(vid.videoChunks)
-            })
-
-            const videoFile = new File(allVideoChunks, "video.mkv", { type: allVideoChunks[0].type, lastModified: Date.now() });
-
-            Networking.UploadFilesToServer(videoFile)
-                .then((data) => {
-                    const result = data[0].result
-                    if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox
-                        props.setResult(result, trackScreen)
-                    } else {
-                        alert("video conversion failed");
-                    }
-                })
-
-        }
-
-
-    }, [finished])
-
-    useEffect(() => {
-        // check if the browser supports media devices on first load
-        if (!navigator.mediaDevices) {
-            console.log('This browser does not support getUserMedia.')
-        }
-        console.log('This device has the correct media devices.')
-    }, [])
-
-    useEffect(() => {
-        // get access to the video element on every render
-        videoElementRef.current = document.getElementById(`video-${props.id}`) as HTMLVideoElement;
-    })
-
-    useEffect(() => {
-        let interval: any = null;
-        if (recording) {
-            interval = setInterval(() => {
-                setRecordingTimer(unit => unit + 1);
-            }, 10);
-        } else if (!recording && recordingTimer !== 0) {
-            clearInterval(interval);
-        }
-        return () => clearInterval(interval);
-    }, [recording])
-
-    useEffect(() => {
-        setVideoProgressHelper(recordingTimer)
-        recordingTimerRef.current = recordingTimer;
-    }, [recordingTimer])
-
-    const setVideoProgressHelper = (progress: number) => {
-        const newProgress = (progress / MAXTIME) * 100;
-        setProgress(newProgress)
-    }
-    const startShowingStream = async (mediaConstraints = DEFAULT_MEDIA_CONSTRAINTS) => {
-        const stream = await navigator.mediaDevices.getUserMedia(mediaConstraints)
-
-        videoElementRef.current!.src = ""
-        videoElementRef.current!.srcObject = stream
-        videoElementRef.current!.muted = true
-
-        return stream
-    }
-
-    const record = async () => {
-        const stream = await startShowingStream();
-        videoRecorder.current = new MediaRecorder(stream)
-
-        // temporary chunks of video
-        let videoChunks: any = []
-
-        videoRecorder.current.ondataavailable = (event: any) => {
-            if (event.data.size > 0) {
-                videoChunks.push(event.data)
-            }
-        }
-
-        videoRecorder.current.onstart = (event: any) => {
-            setRecording(true);
-            trackScreen && RecordingApi.Instance.start();
-        }
-
-        videoRecorder.current.onstop = () => {
-            // if we have a last portion
-            if (videoChunks.length > 1) {
-                // append the current portion to the video pieces
-                setVideos(videos => [...videos, { videoChunks: videoChunks, endTime: recordingTimerRef.current }])
-            }
-
-            // reset the temporary chunks
-            videoChunks = []
-            setRecording(false);
-            setFinished(true);
-            trackScreen && RecordingApi.Instance.pause();
-        }
-
-        // recording paused
-        videoRecorder.current.onpause = (event: any) => {
-            // append the current portion to the video pieces
-            setVideos(videos => [...videos, { videoChunks: videoChunks, endTime: recordingTimerRef.current }])
-
-            // reset the temporary chunks
-            videoChunks = []
-            setRecording(false);
-            trackScreen && RecordingApi.Instance.pause();
-        }
-
-        videoRecorder.current.onresume = async (event: any) => {
-            await startShowingStream();
-            setRecording(true);
-            trackScreen && RecordingApi.Instance.resume();
-        }
-
-        videoRecorder.current.start(200)
-    }
-
-
-    const stop = () => {
-        if (videoRecorder.current) {
-            if (videoRecorder.current.state !== "inactive") {
-                videoRecorder.current.stop();
-                // recorder.current.stream.getTracks().forEach((track: any) => track.stop())
-            }
-        }
-    }
-
-    const pause = () => {
-        if (videoRecorder.current) {
-            if (videoRecorder.current.state === "recording") {
-                videoRecorder.current.pause();
-            }
-        }
-    }
-
-    const startOrResume = (e: React.PointerEvent) => {
-        // the code to start or resume does not get triggered if we start dragging the button
-        setupMoveUpEvents({}, e, returnTrue, returnFalse, e => {
-            if (!videoRecorder.current || videoRecorder.current.state === "inactive") {
-                record();
-            } else if (videoRecorder.current.state === "paused") {
-                videoRecorder.current.resume();
-            }
-            return true; // cancels propagation to documentView to avoid selecting it.
-        }, false, false);
-    }
-
-    const clearPrevious = () => {
-        const numVideos = videos.length
-        setRecordingTimer(numVideos == 1 ? 0 : videos[numVideos - 2].endTime)
-        setVideoProgressHelper(numVideos == 1 ? 0 : videos[numVideos - 2].endTime)
-        setVideos(videos.filter((_, idx) => idx !== numVideos - 1));
-    }
-
-    const handleOnTimeUpdate = () => {
-        if (playing) {
-            setVideoProgressHelper(videoElementRef.current!.currentTime)
-        }
-    };
-
-    const millisecondToMinuteSecond = (milliseconds: number) => {
-        const toTwoDigit = (digit: number) => {
-            return String(digit).length == 1 ? "0" + digit : digit
-        }
-        const minutes = Math.floor((milliseconds % (1000 * 60 * 60)) / (1000 * 60));
-        const seconds = Math.floor((milliseconds % (1000 * 60)) / 1000);
-        return toTwoDigit(minutes) + " : " + toTwoDigit(seconds);
-    }
-
-    return (
-        <div className="recording-container">
-            <div className="video-wrapper">
-                <video id={`video-${props.id}`}
-                    autoPlay
-                    muted
-                    onTimeUpdate={handleOnTimeUpdate}
-                />
-                <div className="recording-sign">
-                    <span className="dot" />
-                    <p className="timer">{millisecondToMinuteSecond(recordingTimer * 10)}</p>
-                </div>
-                <div className="controls">
-
-                    <div className="controls-inner-container">
-                        <div className="record-button-wrapper">
-                            {recording ?
-                                <button className="stop-button" onPointerDown={pause} /> :
-                                <button className="record-button" onPointerDown={startOrResume} />
-                            }
-                        </div>
-
-                        {!recording && (videos.length > 0 ?
-
-                            <div className="options-wrapper video-edit-wrapper">
-                                {/* <IconContext.Provider value={{ color: "grey", className: "video-edit-buttons" }}>
-                                                      <MdBackspace onClick={clearPrevious} />
-                                                </IconContext.Provider> */}
-                                <IconContext.Provider value={{ color: "#cc1c08", className: "video-edit-buttons" }}>
-                                    <FaCheckCircle onClick={stop} />
-                                </IconContext.Provider>
-                            </div>
-
-                            : <div className="options-wrapper track-screen-wrapper">
-                                <label className="track-screen">
-                                    <input type="checkbox" checked={trackScreen} onChange={(e) => { setTrackScreen(e.target.checked) }} />
-                                    <span className="checkmark"></span>
-                                    Track Screen
-                                </label>
-                            </div>)}
-
-                    </div>
-
-                    <ProgressBar
-                        progress={progress}
-                        marks={videos.map((elt) => elt.endTime / MAXTIME * 100)}
-                    // playSegment={playSegment}
-                    />
-                </div>
-            </div>
-        </div>)
-}
\ No newline at end of file
diff --git a/src/client/views/nodes/RecordingBox/index.ts b/src/client/views/nodes/RecordingBox/index.ts
deleted file mode 100644
index ff21eaed6..000000000
--- a/src/client/views/nodes/RecordingBox/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './RecordingView'
-export * from './RecordingBox'
\ No newline at end of file
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 0c7d7dc31..dbb567d3a 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -26,7 +26,7 @@ import { FormattedTextBox } from "./formattedText/FormattedTextBox";
 import "./ScreenshotBox.scss";
 import { VideoBox } from "./VideoBox";
 declare class MediaRecorder {
-      constructor(e: any, options?: any);  // whatever MediaRecorder has
+    constructor(e: any, options?: any);  // whatever MediaRecorder has
 }
 
 // interface VideoTileProps {
@@ -107,226 +107,225 @@ declare class MediaRecorder {
 
 @observer
 export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
-      public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScreenshotBox, fieldKey); }
-      private _audioRec: any;
-      private _videoRec: any;
-      @observable private _videoRef: HTMLVideoElement | null = null;
-      @observable _screenCapture = false;
-      @computed get recordingStart() { return Cast(this.dataDoc[this.props.fieldKey + "-recordingStart"], DateField)?.date.getTime(); }
+    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScreenshotBox, fieldKey); }
+    private _audioRec: any;
+    private _videoRec: any;
+    @observable private _videoRef: HTMLVideoElement | null = null;
+    @observable _screenCapture = false;
+    @computed get recordingStart() { return Cast(this.dataDoc[this.props.fieldKey + "-recordingStart"], DateField)?.date.getTime(); }
 
-      constructor(props: any) {
-            super(props);
-            if (this.rootDoc.videoWall) {
-                  this.rootDoc.nativeWidth = undefined;
-                  this.rootDoc.nativeHeight = undefined;
-                  this.layoutDoc.popOff = 0;
-                  this.layoutDoc.popOut = 1;
-            } else {
-                  this.setupDictation();
-            }
-      }
-      getAnchor = () => {
-            const startTime = Cast(this.layoutDoc._currentTimecode, "number", null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
-            return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow", "_timecodeToHide",
-                  startTime, startTime === undefined ? undefined : startTime + 3)
-                  || this.rootDoc;
-      }
+    constructor(props: any) {
+        super(props);
+        if (this.rootDoc.videoWall) {
+            this.rootDoc.nativeWidth = undefined;
+            this.rootDoc.nativeHeight = undefined;
+            this.layoutDoc.popOff = 0;
+            this.layoutDoc.popOut = 1;
+        } else {
+            this.setupDictation();
+        }
+    }
+    getAnchor = () => {
+        const startTime = Cast(this.layoutDoc._currentTimecode, "number", null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
+        return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow", "_timecodeToHide",
+            startTime, startTime === undefined ? undefined : startTime + 3)
+            || this.rootDoc;
+    }
 
-      videoLoad = () => {
-            const aspect = this._videoRef!.videoWidth / this._videoRef!.videoHeight;
-            const nativeWidth = Doc.NativeWidth(this.layoutDoc);
-            const nativeHeight = Doc.NativeHeight(this.layoutDoc);
-            if (!nativeWidth || !nativeHeight) {
-                  if (!nativeWidth) Doc.SetNativeWidth(this.dataDoc, 1200);
-                  Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 1200) / aspect);
-                  this.layoutDoc._height = (this.layoutDoc[WidthSym]() || 0) / aspect;
-            }
-      }
+    videoLoad = () => {
+        const aspect = this._videoRef!.videoWidth / this._videoRef!.videoHeight;
+        const nativeWidth = Doc.NativeWidth(this.layoutDoc);
+        const nativeHeight = Doc.NativeHeight(this.layoutDoc);
+        if (!nativeWidth || !nativeHeight) {
+            if (!nativeWidth) Doc.SetNativeWidth(this.dataDoc, 1200);
+            Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 1200) / aspect);
+            this.layoutDoc._height = (this.layoutDoc[WidthSym]() || 0) / aspect;
+        }
+    }
 
-      componentDidMount() {
-            this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = 0;
-            this.props.setContentView?.(this); // this tells the DocumentView that this ScreenshotBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
-            // this.rootDoc.videoWall && reaction(() => ({ width: this.props.PanelWidth(), height: this.props.PanelHeight() }),
-            //     ({ width, height }) => {
-            //         if (this._camera) {
-            //             const angle = -Math.abs(1 - width / height);
-            //             const xz = [0, (this._numScreens - 2) / Math.abs(1 + angle)];
-            //             this._camera.position.set(this._numScreens / 2 + xz[1] * Math.sin(angle), this._numScreens / 2, xz[1] * Math.cos(angle));
-            //             this._camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
-            //             (this._camera as any).updateProjectionMatrix();
-            //         }
-            //     });
-      }
-      componentWillUnmount() {
-            const ind = DocUtils.ActiveRecordings.indexOf(this);
-            ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1));
-      }
+    componentDidMount() {
+        this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = 0;
+        this.props.setContentView?.(this); // this tells the DocumentView that this ScreenshotBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
+        // this.rootDoc.videoWall && reaction(() => ({ width: this.props.PanelWidth(), height: this.props.PanelHeight() }),
+        //     ({ width, height }) => {
+        //         if (this._camera) {
+        //             const angle = -Math.abs(1 - width / height);
+        //             const xz = [0, (this._numScreens - 2) / Math.abs(1 + angle)];
+        //             this._camera.position.set(this._numScreens / 2 + xz[1] * Math.sin(angle), this._numScreens / 2, xz[1] * Math.cos(angle));
+        //             this._camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
+        //             (this._camera as any).updateProjectionMatrix();
+        //         }
+        //     });
+    }
+    componentWillUnmount() {
+        const ind = DocUtils.ActiveRecordings.indexOf(this);
+        ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1));
+    }
 
-      specificContextMenu = (e: React.MouseEvent): void => {
-            const subitems = [{ description: "Screen Capture", event: this.toggleRecording, icon: "expand-arrows-alt" as any }];
-            ContextMenu.Instance.addItem({ description: "Options...", subitems, icon: "video" });
-      }
+    specificContextMenu = (e: React.MouseEvent): void => {
+        const subitems = [{ description: "Screen Capture", event: this.toggleRecording, icon: "expand-arrows-alt" as any }];
+        ContextMenu.Instance.addItem({ description: "Options...", subitems, icon: "video" });
+    }
 
-      @computed get content() {
-            if (this.rootDoc.videoWall) return (null);
-            return <video className={"videoBox-content"} key="video"
-                  ref={r => {
-                        this._videoRef = r;
-                        setTimeout(() => {
-                              if (this.rootDoc.mediaState === "pendingRecording" && this._videoRef) {
-                                    this.toggleRecording();
-                              }
-                        }, 100);
-                  }}
-                  autoPlay={this._screenCapture}
-                  style={{ width: this._screenCapture ? "100%" : undefined, height: this._screenCapture ? "100%" : undefined }}
-                  onCanPlay={this.videoLoad}
-                  controls={true}
-                  onClick={e => e.preventDefault()}>
-                  <source type="video/mp4" />
-                  Not supported.
-            </video>;
-      }
+    @computed get content() {
+        if (this.rootDoc.videoWall) return (null);
+        return <video className={"videoBox-content"} key="video"
+            ref={r => {
+                this._videoRef = r;
+                setTimeout(() => {
+                    if (this.rootDoc.mediaState === "pendingRecording" && this._videoRef) {
+                        this.toggleRecording();
+                    }
+                }, 100);
+            }}
+            autoPlay={this._screenCapture}
+            style={{ width: this._screenCapture ? "100%" : undefined, height: this._screenCapture ? "100%" : undefined }}
+            onCanPlay={this.videoLoad}
+            controls={true}
+            onClick={e => e.preventDefault()}>
+            <source type="video/mp4" />
+            Not supported.
+        </video>;
+    }
 
-      // _numScreens = 5;
-      // _camera: Camera | undefined;
-      // @observable _raised = [] as { coord: Vector2, off: Vector3 }[];
-      // @action setRaised = (r: { coord: Vector2, off: Vector3 }[]) => this._raised = r;
-      @computed get threed() {
-            // if (this.rootDoc.videoWall) {
-            //     const screens: any[] = [];
-            //     const colors = ["yellow", "red", "orange", "brown", "maroon", "gray"];
-            //     let count = 0;
-            //     numberRange(this._numScreens).forEach(x => numberRange(this._numScreens).forEach(y => screens.push(
-            //         <VideoTile rootDoc={this.rootDoc} color={colors[count++ % colors.length]} x={x} y={y} raised={this._raised} setRaised={this.setRaised} />)));
-            //     return <Canvas key="canvas" id="CANCAN" style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }} gl={{ antialias: false }} colorManagement={false} onCreated={props => {
-            //         this._camera = props.camera;
-            //         props.camera.position.set(this._numScreens / 2, this._numScreens / 2, this._numScreens - 2);
-            //         props.camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
-            //     }}>
-            //         {/* <ambientLight />*/}
-            //         <pointLight position={[10, 10, 10]} intensity={1} />
-            //         {screens}
-            //     </ Canvas>;
-            // }
-            return (null);
-      }
-      toggleRecording = async () => {
-            if (!this._screenCapture) {
-                  this._audioRec = new MediaRecorder(await navigator.mediaDevices.getUserMedia({ audio: true }));
-                  const aud_chunks: any = [];
-                  this._audioRec.ondataavailable = (e: any) => aud_chunks.push(e.data);
-                  this._audioRec.onstop = async (e: any) => {
-                        const [{ result }] = await Networking.UploadFilesToServer(aud_chunks);
-                        if (!(result instanceof Error)) {
-                              this.dataDoc[this.props.fieldKey + "-audio"] = new AudioField(result.accessPaths.agnostic.client);
-                        }
-                  };
-                  this._videoRef!.srcObject = await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
-                  this._videoRec = new MediaRecorder(this._videoRef!.srcObject);
-                  const vid_chunks: any = [];
-                  this._videoRec.onstart = () => this.dataDoc[this.props.fieldKey + "-recordingStart"] = new DateField(new Date());
-                  this._videoRec.ondataavailable = (e: any) => vid_chunks.push(e.data);
-                  this._videoRec.onstop = async (e: any) => {
-                        console.log("screenshotbox: upload")
-                        const file = new File(vid_chunks, `${this.rootDoc[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() });
-                        const [{ result }] = await Networking.UploadFilesToServer(file);
-                        this.dataDoc[this.fieldKey + "-duration"] = (new Date().getTime() - this.recordingStart!) / 1000;
-                        if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox
-                              this.dataDoc.type = DocumentType.VID;
-                              this.layoutDoc.layout = VideoBox.LayoutString(this.fieldKey);
-                              this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = undefined;
-                              this.layoutDoc._fitWidth = undefined;
-                              this.dataDoc[this.props.fieldKey] = new VideoField(result.accessPaths.agnostic.client);
-                        } else alert("video conversion failed");
-                  };
-                  this._audioRec.start();
-                  this._videoRec.start();
-                  runInAction(() => {
-                        this._screenCapture = true;
-                        this.dataDoc.mediaState = "recording";
-                  });
-                  DocUtils.ActiveRecordings.push(this);
-            } else {
-                  this._audioRec?.stop();
-                  this._videoRec?.stop();
-                  runInAction(() => {
-                        this._screenCapture = false;
-                        this.dataDoc.mediaState = "paused";
-                  });
-                  const ind = DocUtils.ActiveRecordings.indexOf(this);
-                  ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1));
+    // _numScreens = 5;
+    // _camera: Camera | undefined;
+    // @observable _raised = [] as { coord: Vector2, off: Vector3 }[];
+    // @action setRaised = (r: { coord: Vector2, off: Vector3 }[]) => this._raised = r;
+    @computed get threed() {
+        // if (this.rootDoc.videoWall) {
+        //     const screens: any[] = [];
+        //     const colors = ["yellow", "red", "orange", "brown", "maroon", "gray"];
+        //     let count = 0;
+        //     numberRange(this._numScreens).forEach(x => numberRange(this._numScreens).forEach(y => screens.push(
+        //         <VideoTile rootDoc={this.rootDoc} color={colors[count++ % colors.length]} x={x} y={y} raised={this._raised} setRaised={this.setRaised} />)));
+        //     return <Canvas key="canvas" id="CANCAN" style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }} gl={{ antialias: false }} colorManagement={false} onCreated={props => {
+        //         this._camera = props.camera;
+        //         props.camera.position.set(this._numScreens / 2, this._numScreens / 2, this._numScreens - 2);
+        //         props.camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
+        //     }}>
+        //         {/* <ambientLight />*/}
+        //         <pointLight position={[10, 10, 10]} intensity={1} />
+        //         {screens}
+        //     </ Canvas>;
+        // }
+        return (null);
+    }
+    toggleRecording = async () => {
+        if (!this._screenCapture) {
+            this._audioRec = new MediaRecorder(await navigator.mediaDevices.getUserMedia({ audio: true }));
+            const aud_chunks: any = [];
+            this._audioRec.ondataavailable = (e: any) => aud_chunks.push(e.data);
+            this._audioRec.onstop = async (e: any) => {
+                const [{ result }] = await Networking.UploadFilesToServer(aud_chunks);
+                if (!(result instanceof Error)) {
+                    this.dataDoc[this.props.fieldKey + "-audio"] = new AudioField(result.accessPaths.agnostic.client);
+                }
+            };
+            this._videoRef!.srcObject = await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
+            this._videoRec = new MediaRecorder(this._videoRef!.srcObject);
+            const vid_chunks: any = [];
+            this._videoRec.onstart = () => this.dataDoc[this.props.fieldKey + "-recordingStart"] = new DateField(new Date());
+            this._videoRec.ondataavailable = (e: any) => vid_chunks.push(e.data);
+            this._videoRec.onstop = async (e: any) => {
+                const file = new File(vid_chunks, `${this.rootDoc[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() });
+                const [{ result }] = await Networking.UploadFilesToServer(file);
+                this.dataDoc[this.fieldKey + "-duration"] = (new Date().getTime() - this.recordingStart!) / 1000;
+                if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox
+                    this.dataDoc.type = DocumentType.VID;
+                    this.layoutDoc.layout = VideoBox.LayoutString(this.fieldKey);
+                    this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = undefined;
+                    this.layoutDoc._fitWidth = undefined;
+                    this.dataDoc[this.props.fieldKey] = new VideoField(result.accessPaths.agnostic.client);
+                } else alert("video conversion failed");
+            };
+            this._audioRec.start();
+            this._videoRec.start();
+            runInAction(() => {
+                this._screenCapture = true;
+                this.dataDoc.mediaState = "recording";
+            });
+            DocUtils.ActiveRecordings.push(this);
+        } else {
+            this._audioRec?.stop();
+            this._videoRec?.stop();
+            runInAction(() => {
+                this._screenCapture = false;
+                this.dataDoc.mediaState = "paused";
+            });
+            const ind = DocUtils.ActiveRecordings.indexOf(this);
+            ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1));
 
-                  CaptureManager.Instance.open(this.rootDoc);
-            }
-      }
+            CaptureManager.Instance.open(this.rootDoc);
+        }
+    }
 
-      setupDictation = () => {
-            if (this.dataDoc[this.fieldKey + "-dictation"]) return;
-            const dictationText = CurrentUserUtils.GetNewTextDoc("dictation",
-                  NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10,
-                  NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
-            dictationText._autoHeight = false;
-            const dictationTextProto = Doc.GetProto(dictationText);
-            dictationTextProto.recordingSource = this.dataDoc;
-            dictationTextProto.recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}-recordingStart"]`);
-            dictationTextProto.mediaState = ComputedField.MakeFunction("self.recordingSource.mediaState");
-            this.dataDoc[this.fieldKey + "-dictation"] = dictationText;
-      }
-      contentFunc = () => [this.threed, this.content];
-      videoPanelHeight = () => NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"], this.layoutDoc[HeightSym]()) / NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"], this.layoutDoc[WidthSym]()) * this.props.PanelWidth();
-      formattedPanelHeight = () => Math.max(0, this.props.PanelHeight() - this.videoPanelHeight());
-      render() {
-            TraceMobx();
-            return <div className="videoBox" onContextMenu={this.specificContextMenu} style={{ width: "100%", height: "100%" }} >
-                  <div className="videoBox-viewer" >
-                        <div style={{ position: "relative", height: this.videoPanelHeight() }}>
-                              <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
-                                    PanelHeight={this.videoPanelHeight}
-                                    PanelWidth={this.props.PanelWidth}
-                                    focus={this.props.focus}
-                                    isSelected={this.props.isSelected}
-                                    isAnnotationOverlay={true}
-                                    select={emptyFunction}
-                                    isContentActive={returnFalse}
-                                    scaling={returnOne}
-                                    whenChildContentsActiveChanged={emptyFunction}
-                                    removeDocument={returnFalse}
-                                    moveDocument={returnFalse}
-                                    addDocument={returnFalse}
-                                    CollectionView={undefined}
-                                    ScreenToLocalTransform={this.props.ScreenToLocalTransform}
-                                    renderDepth={this.props.renderDepth + 1}
-                                    ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
-                                    {this.contentFunc}
-                              </CollectionFreeFormView></div>
-                        <div style={{ position: "relative", height: this.formattedPanelHeight() }}>
-                              {!(this.dataDoc[this.fieldKey + "-dictation"] instanceof Doc) ? (null) :
-                                    <FormattedTextBox {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
-                                          Document={this.dataDoc[this.fieldKey + "-dictation"]}
-                                          fieldKey={"text"}
-                                          PanelHeight={this.formattedPanelHeight}
-                                          isAnnotationOverlay={true}
-                                          select={emptyFunction}
-                                          isContentActive={emptyFunction}
-                                          scaling={returnOne}
-                                          xPadding={25}
-                                          yPadding={10}
-                                          whenChildContentsActiveChanged={emptyFunction}
-                                          removeDocument={returnFalse}
-                                          moveDocument={returnFalse}
-                                          addDocument={returnFalse}
-                                          CollectionView={undefined}
-                                          renderDepth={this.props.renderDepth + 1}
-                                          ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
-                                    </FormattedTextBox>}
-                        </div>
-                  </div>
-                  {!this.props.isSelected() ? (null) : <div className="screenshotBox-uiButtons">
-                        <div className="screenshotBox-recorder" key="snap" onPointerDown={this.toggleRecording} >
-                              <FontAwesomeIcon icon="file" size="lg" />
-                        </div>
-                  </div>}
-            </div >;
-      }
+    setupDictation = () => {
+        if (this.dataDoc[this.fieldKey + "-dictation"]) return;
+        const dictationText = CurrentUserUtils.GetNewTextDoc("dictation",
+            NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10,
+            NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
+        dictationText._autoHeight = false;
+        const dictationTextProto = Doc.GetProto(dictationText);
+        dictationTextProto.recordingSource = this.dataDoc;
+        dictationTextProto.recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}-recordingStart"]`);
+        dictationTextProto.mediaState = ComputedField.MakeFunction("self.recordingSource.mediaState");
+        this.dataDoc[this.fieldKey + "-dictation"] = dictationText;
+    }
+    contentFunc = () => [this.threed, this.content];
+    videoPanelHeight = () => NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"], this.layoutDoc[HeightSym]()) / NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"], this.layoutDoc[WidthSym]()) * this.props.PanelWidth();
+    formattedPanelHeight = () => Math.max(0, this.props.PanelHeight() - this.videoPanelHeight());
+    render() {
+        TraceMobx();
+        return <div className="videoBox" onContextMenu={this.specificContextMenu} style={{ width: "100%", height: "100%" }} >
+            <div className="videoBox-viewer" >
+                <div style={{ position: "relative", height: this.videoPanelHeight() }}>
+                    <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
+                        PanelHeight={this.videoPanelHeight}
+                        PanelWidth={this.props.PanelWidth}
+                        focus={this.props.focus}
+                        isSelected={this.props.isSelected}
+                        isAnnotationOverlay={true}
+                        select={emptyFunction}
+                        isContentActive={returnFalse}
+                        scaling={returnOne}
+                        whenChildContentsActiveChanged={emptyFunction}
+                        removeDocument={returnFalse}
+                        moveDocument={returnFalse}
+                        addDocument={returnFalse}
+                        CollectionView={undefined}
+                        ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+                        renderDepth={this.props.renderDepth + 1}
+                        ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
+                        {this.contentFunc}
+                    </CollectionFreeFormView></div>
+                <div style={{ position: "relative", height: this.formattedPanelHeight() }}>
+                    {!(this.dataDoc[this.fieldKey + "-dictation"] instanceof Doc) ? (null) :
+                        <FormattedTextBox {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
+                            Document={this.dataDoc[this.fieldKey + "-dictation"]}
+                            fieldKey={"text"}
+                            PanelHeight={this.formattedPanelHeight}
+                            isAnnotationOverlay={true}
+                            select={emptyFunction}
+                            isContentActive={emptyFunction}
+                            scaling={returnOne}
+                            xPadding={25}
+                            yPadding={10}
+                            whenChildContentsActiveChanged={emptyFunction}
+                            removeDocument={returnFalse}
+                            moveDocument={returnFalse}
+                            addDocument={returnFalse}
+                            CollectionView={undefined}
+                            renderDepth={this.props.renderDepth + 1}
+                            ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
+                        </FormattedTextBox>}
+                </div>
+            </div>
+            {!this.props.isSelected() ? (null) : <div className="screenshotBox-uiButtons">
+                <div className="screenshotBox-recorder" key="snap" onPointerDown={this.toggleRecording} >
+                    <FontAwesomeIcon icon="file" size="lg" />
+                </div>
+            </div>}
+        </div >;
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 5d2fae1ed..ef3b0d105 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -7,7 +7,7 @@ import * as rp from 'request-promise';
 import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
 import { InkTool } from "../../../fields/InkField";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { AudioField, ImageField, RecordingField, VideoField } from "../../../fields/URLField";
+import { AudioField, ImageField, VideoField } from "../../../fields/URLField";
 import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from "../../documents/DocumentTypes";
@@ -28,9 +28,8 @@ import { AnchorMenu } from "../pdf/AnchorMenu";
 import { StyleProp } from "../StyleProvider";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./VideoBox.scss";
+import { Image } from "wikijs";
 import { List } from "../../../fields/List";
-import { RecordingApi } from "../../util/RecordingApi";
-import { RecordingBox } from "./RecordingBox";
 const path = require('path');
 
 /**
@@ -47,1264 +46,936 @@ const path = require('path');
 
 @observer
 export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
-      public static LayoutString(fieldKey: string) { return FieldView.LayoutString(VideoBox, fieldKey); }
-      /**
-       * Uploads an image buffer to the server and stores with specified filename.  by default the image
-       * is stored at multiple resolutions each retrieved by using the filename appended with _o, _s, _m, _l (indicating original, small, medium, or large)
-       * @param imageUri the bytes of the image
-       * @param returnedFilename the base filename to store the image on the server
-       * @param nosuffix optionally suppress creating multiple resolution images
-       */
-      public static async convertDataUri(imageUri: string, returnedFilename: string, nosuffix = false, replaceRootFilename?: string) {
-            try {
-                  const posting = Utils.prepend("/uploadURI");
-                  const returnedUri = await rp.post(posting, {
-                        body: {
-                              uri: imageUri,
-                              name: returnedFilename,
-                              nosuffix,
-                              replaceRootFilename
-                        },
-                        json: true,
-                  });
-                  return returnedUri;
-
-            } catch (e) {
-                  console.log("VideoBox :" + e);
-            }
-      }
-
-      static _youtubeIframeCounter: number = 0;
-      static heightPercent = 80; // height of video relative to videoBox when timeline is open
-      static numThumbnails = 20;
-      private _disposers: { [name: string]: IReactionDisposer } = {};
-      private _youtubePlayer: YT.Player | undefined = undefined;
-      private _videoRef: HTMLVideoElement | null = null; // <video> ref
-      private _contentRef: HTMLDivElement | null = null; // ref to div that wraps video and controls for full screen
-      private _youtubeIframeId: number = -1;
-      private _youtubeContentCreated = false;
-      private _audioPlayer: HTMLAudioElement | null = null;
-      private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); // outermost div
-      private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
-      private _playRegionTimer: any = null; // timeout for playback
-      private _controlsFadeTimer: any = null; // timeout for controls fade
-
-      public onMakeAnchor: () => Opt<Doc> = () => undefined; // Method to get anchor from text search
-
-      @observable _stackedTimeline: any; // CollectionStackedTimeline ref
-      @observable static _nativeControls: boolean; // default html controls
-      @observable _marqueeing: number[] | undefined; // coords for marquee selection
-      @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
-      @observable _screenCapture = false;
-      @observable _clicking = false; // used for transition between showing/hiding timeline
-      @observable _forceCreateYouTubeIFrame = false;
-      @observable _playTimer?: NodeJS.Timeout = undefined;
-      @observable _fullScreen = false;
-      @observable _playing = false;
-      @observable _finished: boolean = false; // has playback reached end of clip
-      @observable _volume: number = 1;
-      @observable _muted: boolean = false;
-      @observable _controlsTransform?: { X: number, Y: number };
-      @observable _controlsVisible: boolean = true;
-
-      @computed get links() { return DocListCast(this.dataDoc.links); }
-      @computed get heightPercent() { return NumCast(this.layoutDoc._timelineHeightPercent, 100); } // current percent of video relative to VideoBox height
-      // @computed get rawDuration() { return NumCast(this.dataDoc[this.fieldKey + "-duration"]); }
-      @observable rawDuration: number = 0;
-
-
-      @computed get youtubeVideoId() {
-            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("/")) : "";
-      }
-
-
-      // returns the path of the audio file
-      @computed get audiopath() {
-            const field = Cast(this.props.Document[this.props.fieldKey + '-audio'], AudioField, null);
-            const vfield = Cast(this.dataDoc[this.fieldKey], VideoField, null);
-            return field?.url.href ?? vfield?.url.href ?? "";
-      }
-
-
-      @computed private get timeline() { return this._stackedTimeline; }
-      private get transition() { return this._clicking ? "left 0.5s, width 0.5s, height 0.5s" : ""; } // css transition for hiding/showing timeline
-      public get player(): HTMLVideoElement | null { return this._videoRef; }
-
-
-      componentDidMount() {
-            this.props.setContentView?.(this); // this tells the DocumentView that this VideoBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the VideoBox when making a link.
-            if (this.youtubeVideoId) {
-                  const youtubeaspect = 400 / 315;
-                  const nativeWidth = Doc.NativeWidth(this.layoutDoc);
-                  const nativeHeight = Doc.NativeHeight(this.layoutDoc);
-                  if (!nativeWidth || !nativeHeight) {
-                        if (!nativeWidth) Doc.SetNativeWidth(this.dataDoc, 600);
-                        Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 600) / youtubeaspect);
-                        this.layoutDoc._height = NumCast(this.layoutDoc._width) / youtubeaspect;
-                  }
-            }
-            this.player && this.setPlayheadTime(0);
-      }
-
-      componentWillUnmount() {
-            this.removeCurrentlyPlaying();
-            this.Pause();
-            Object.keys(this._disposers).forEach(d => this._disposers[d]?.());
-      }
-
-
-      // plays video
-      @action public Play = (update: boolean = true) => {
-            if (this._playRegionTimer) return;
-            this._playing = true;
-            const eleTime = this.player?.currentTime || 0;
-            if (this.timeline) {
-                  let start = eleTime >= this.timeline.trimEnd || eleTime <= this.timeline.trimStart ? this.timeline.trimStart : eleTime;
-
-                  if (this._finished) {
-                        // restarts video if reached end on previous play
-                        this._finished = false;
-                        start = this.timeline.trimStart;
-                  }
-
-                  try {
-                        const posting = Utils.prepend("/uploadURI");
-                        const returnedUri = await rp.post(posting, {
-                              body: {
-                                    uri: imageUri,
-                                    name: returnedFilename,
-                                    nosuffix,
-                                    replaceRootFilename
-                              },
-                              json: true,
-                        });
-                        return returnedUri;
-
-                  } catch (e) {
-                        console.log("VideoBox :" + e);
-                  }
+    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(VideoBox, fieldKey); }
+    /**
+     * Uploads an image buffer to the server and stores with specified filename.  by default the image
+     * is stored at multiple resolutions each retrieved by using the filename appended with _o, _s, _m, _l (indicating original, small, medium, or large)
+     * @param imageUri the bytes of the image
+     * @param returnedFilename the base filename to store the image on the server
+     * @param nosuffix optionally suppress creating multiple resolution images
+     */
+    public static async convertDataUri(imageUri: string, returnedFilename: string, nosuffix = false, replaceRootFilename?: string) {
+        try {
+            const posting = Utils.prepend("/uploadURI");
+            const returnedUri = await rp.post(posting, {
+                body: {
+                    uri: imageUri,
+                    name: returnedFilename,
+                    nosuffix,
+                    replaceRootFilename
+                },
+                json: true,
+            });
+            return returnedUri;
+
+        } catch (e) {
+            console.log("VideoBox :" + e);
+        }
+    }
+
+    static _youtubeIframeCounter: number = 0;
+    static heightPercent = 80; // height of video relative to videoBox when timeline is open
+    static numThumbnails = 20;
+    private _disposers: { [name: string]: IReactionDisposer } = {};
+    private _youtubePlayer: YT.Player | undefined = undefined;
+    private _videoRef: HTMLVideoElement | null = null; // <video> ref
+    private _contentRef: HTMLDivElement | null = null; // ref to div that wraps video and controls for full screen
+    private _youtubeIframeId: number = -1;
+    private _youtubeContentCreated = false;
+    private _audioPlayer: HTMLAudioElement | null = null;
+    private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); // outermost div
+    private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
+    private _playRegionTimer: any = null; // timeout for playback
+    private _controlsFadeTimer: any = null; // timeout for controls fade
+
+    public onMakeAnchor: () => Opt<Doc> = () => undefined; // Method to get anchor from text search
+
+    @observable _stackedTimeline: any; // CollectionStackedTimeline ref
+    @observable static _nativeControls: boolean; // default html controls
+    @observable _marqueeing: number[] | undefined; // coords for marquee selection
+    @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
+    @observable _screenCapture = false;
+    @observable _clicking = false; // used for transition between showing/hiding timeline
+    @observable _forceCreateYouTubeIFrame = false;
+    @observable _playTimer?: NodeJS.Timeout = undefined;
+    @observable _fullScreen = false;
+    @observable _playing = false;
+    @observable _finished: boolean = false; // has playback reached end of clip
+    @observable _volume: number = 1;
+    @observable _muted: boolean = false;
+    @observable _controlsTransform?: { X: number, Y: number };
+    @observable _controlsVisible: boolean = true;
+
+    @computed get links() { return DocListCast(this.dataDoc.links); }
+    @computed get heightPercent() { return NumCast(this.layoutDoc._timelineHeightPercent, 100); } // current percent of video relative to VideoBox height
+    // @computed get rawDuration() { return NumCast(this.dataDoc[this.fieldKey + "-duration"]); }
+    @observable rawDuration: number = 0;
+
+
+    @computed get youtubeVideoId() {
+        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("/")) : "";
+    }
+
+
+    // returns the path of the audio file
+    @computed get audiopath() {
+        const field = Cast(this.props.Document[this.props.fieldKey + '-audio'], AudioField, null);
+        const vfield = Cast(this.dataDoc[this.fieldKey], VideoField, null);
+        return field?.url.href ?? vfield?.url.href ?? "";
+    }
+
+
+    @computed private get timeline() { return this._stackedTimeline; }
+    private get transition() { return this._clicking ? "left 0.5s, width 0.5s, height 0.5s" : ""; } // css transition for hiding/showing timeline
+    public get player(): HTMLVideoElement | null { return this._videoRef; }
+
+
+    componentDidMount() {
+        this.props.setContentView?.(this); // this tells the DocumentView that this VideoBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the VideoBox when making a link.
+        if (this.youtubeVideoId) {
+            const youtubeaspect = 400 / 315;
+            const nativeWidth = Doc.NativeWidth(this.layoutDoc);
+            const nativeHeight = Doc.NativeHeight(this.layoutDoc);
+            if (!nativeWidth || !nativeHeight) {
+                if (!nativeWidth) Doc.SetNativeWidth(this.dataDoc, 600);
+                Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 600) / youtubeaspect);
+                this.layoutDoc._height = NumCast(this.layoutDoc._width) / youtubeaspect;
+            }
+        }
+        this.player && this.setPlayheadTime(0);
+    }
+
+    componentWillUnmount() {
+        this.removeCurrentlyPlaying();
+        this.Pause();
+        Object.keys(this._disposers).forEach(d => this._disposers[d]?.());
+    }
+
+
+    // plays video
+    @action public Play = (update: boolean = true) => {
+        if (this._playRegionTimer) return;
+        this._playing = true;
+        const eleTime = this.player?.currentTime || 0;
+        if (this.timeline) {
+            let start = eleTime >= this.timeline.trimEnd || eleTime <= this.timeline.trimStart ? this.timeline.trimStart : eleTime;
+
+            if (this._finished) {
+                // restarts video if reached end on previous play
+                this._finished = false;
+                start = this.timeline.trimStart;
             }
 
-      static _youtubeIframeCounter: number = 0;
-      static heightPercent = 80; // height of video relative to videoBox when timeline is open
-      private _disposers: { [name: string]: IReactionDisposer } = {};
-      private _youtubePlayer: YT.Player | undefined = undefined;
-      private _videoRef: HTMLVideoElement | null = null; // <video> ref
-      private _contentRef: HTMLDivElement | null = null; // ref to div that wraps video and controls for full screen
-      private _youtubeIframeId: number = -1;
-      private _youtubeContentCreated = false;
-      private _audioPlayer: HTMLAudioElement | null = null;
-      private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); // outermost div
-      private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
-      private _playRegionTimer: any = null; // timeout for playback
-      @observable _stackedTimeline: any; // CollectionStackedTimeline ref
-      @observable static _nativeControls: boolean; // default html controls
-      @observable _marqueeing: number[] | undefined; // coords for marquee selection
-      @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
-      @observable _screenCapture = false;
-      @observable _clicking = false; // used for transition between showing/hiding timeline
-      @observable _forceCreateYouTubeIFrame = false;
-      @observable _playTimer?: NodeJS.Timeout = undefined;
-      @observable _fullScreen = false;
-      @observable _playing = false;
-      @observable _finished: boolean = false; // has playback reached end of clip
-      @observable _volume: number = 1;
-      @observable _muted: boolean = false;
-
-      // pauses video
-      @action public Pause = (update: boolean = true) => {
-            this._playing = false;
-            this.removeCurrentlyPlaying();
-            try {
-                  update && this.player?.pause();
-                  update && this._audioPlayer?.pause();
-                  update && this._youtubePlayer?.pauseVideo();
-                  this._youtubePlayer && this._playTimer && clearInterval(this._playTimer);
-                  this._youtubePlayer?.seekTo(this._youtubePlayer?.getCurrentTime(), true);
-            } catch (e) {
-                  console.log("Video Pause Exception:", e);
-            }
-            this._youtubePlayer && SelectionManager.DeselectAll(); // if we don't deselect the player, then we get an annoying YouTube spinner I guess telling us we're paused.
-            this._playTimer = undefined;
-            this.updateTimecode();
-            if (!this._finished) {
-                  clearTimeout(this._playRegionTimer); // if paused in the middle of playback, prevents restart on next play
-            }
-            this._playRegionTimer = undefined;
-      }
-
-      // toggles video full screen
-      @action public FullScreen = () => {
-            if (document.fullscreenElement === this._contentRef) {
-                  this._fullScreen = false;
-                  this.player && this._contentRef && document.exitFullscreen();
-            }
-            else {
-                  this._fullScreen = true;
-                  this.player && this._contentRef && this._contentRef.requestFullscreen();
-            }
             try {
-                  this._youtubePlayer && this.props.addDocTab(this.rootDoc, "add");
+                this._audioPlayer && this.player && (this._audioPlayer.currentTime = this.player?.currentTime);
+                update && this.player && this.playFrom(start, undefined, true);
+                update && this._audioPlayer?.play();
+                update && this._youtubePlayer?.playVideo();
+                this._youtubePlayer && !this._playTimer && (this._playTimer = setInterval(this.updateTimecode, 5));
             } catch (e) {
-                  console.log("Video FullScreen Exception:", e);
-            }
-      }
-
-      // fades out controls in fullscreen after mouse stops moving
-      @action controlsFade = (e: PointerEvent) => {
-            e.stopPropagation();
-            this._controlsVisible = true;
-            clearTimeout(this._controlsFadeTimer);
-            this._controlsFadeTimer = setTimeout(action(() => this._controlsVisible = false), 3000);
-      }
-
-
-      // drag controls around window in fulls screen
-      @action controlsDrag = (e: React.PointerEvent) => {
-            e.preventDefault();
-            e.stopPropagation();
-            const eleStyle = getComputedStyle(e.target as Element);
-            this._controlsTransform = { X: parseInt(eleStyle.left), Y: parseInt(eleStyle.top) };
-
-            setupMoveUpEvents(e.target,
-                  e,
-                  action((e, down, delta) => {
-                        if (this._controlsTransform) {
-                              this._controlsTransform.X = Math.max(0, Math.min(delta[0] + this._controlsTransform.X, window.innerWidth));
-                              this._controlsTransform.Y = Math.max(0, Math.min(delta[1] + this._controlsTransform.Y, window.innerHeight));
-                        }
-                        return false;
-                  }),
-                  emptyFunction,
-                  emptyFunction)
-      }
-
-
-      // creates and links snapshot photo of current video frame
-      @action public Snapshot = (downX?: number, downY?: number, cb?: (filename: string, x: number | undefined, y: number | undefined) => void) => {
-            const width = NumCast(this.layoutDoc._width);
+                console.log("Video Play Exception:", e);
+            }
+        }
+        this.updateTimecode();
+    }
+
+    // goes to time
+    @action public Seek(time: number) {
+        try {
+            this._youtubePlayer?.seekTo(Math.round(time), true);
+        } catch (e) {
+            console.log("Video Seek Exception:", e);
+        }
+        this.player && (this.player.currentTime = time);
+        this._audioPlayer && (this._audioPlayer.currentTime = time);
+    }
+
+    // pauses video
+    @action public Pause = (update: boolean = true) => {
+        this._playing = false;
+        this.removeCurrentlyPlaying();
+        try {
+            update && this.player?.pause();
+            update && this._audioPlayer?.pause();
+            update && this._youtubePlayer?.pauseVideo();
+            this._youtubePlayer && this._playTimer && clearInterval(this._playTimer);
+            this._youtubePlayer?.seekTo(this._youtubePlayer?.getCurrentTime(), true);
+        } catch (e) {
+            console.log("Video Pause Exception:", e);
+        }
+        this._youtubePlayer && SelectionManager.DeselectAll(); // if we don't deselect the player, then we get an annoying YouTube spinner I guess telling us we're paused.
+        this._playTimer = undefined;
+        this.updateTimecode();
+        if (!this._finished) {
+            clearTimeout(this._playRegionTimer); // if paused in the middle of playback, prevents restart on next play
+        }
+        this._playRegionTimer = undefined;
+    }
+
+    // toggles video full screen
+    @action public FullScreen = () => {
+        if (document.fullscreenElement === this._contentRef) {
+            this._fullScreen = false;
+            this.player && this._contentRef && document.exitFullscreen();
+        }
+        else {
+            this._fullScreen = true;
+            this.player && this._contentRef && this._contentRef.requestFullscreen();
+        }
+        try {
+            this._youtubePlayer && this.props.addDocTab(this.rootDoc, "add");
+        } catch (e) {
+            console.log("Video FullScreen Exception:", e);
+        }
+    }
+
+    // fades out controls in fullscreen after mouse stops moving
+    @action controlsFade = (e: PointerEvent) => {
+        e.stopPropagation();
+        this._controlsVisible = true;
+        clearTimeout(this._controlsFadeTimer);
+        this._controlsFadeTimer = setTimeout(action(() => this._controlsVisible = false), 3000);
+    }
+
+
+    // drag controls around window in fulls screen
+    @action controlsDrag = (e: React.PointerEvent) => {
+        e.preventDefault();
+        e.stopPropagation();
+        const eleStyle = getComputedStyle(e.target as Element);
+        this._controlsTransform = { X: parseInt(eleStyle.left), Y: parseInt(eleStyle.top) };
+
+        setupMoveUpEvents(e.target,
+            e,
+            action((e, down, delta) => {
+                if (this._controlsTransform) {
+                    this._controlsTransform.X = Math.max(0, Math.min(delta[0] + this._controlsTransform.X, window.innerWidth));
+                    this._controlsTransform.Y = Math.max(0, Math.min(delta[1] + this._controlsTransform.Y, window.innerHeight));
+                }
+                return false;
+            }),
+            emptyFunction,
+            emptyFunction)
+    }
+
+
+    // creates and links snapshot photo of current video frame
+    @action public Snapshot = (downX?: number, downY?: number, cb?: (filename: string, x: number | undefined, y: number | undefined) => void) => {
+        const width = NumCast(this.layoutDoc._width);
+        const canvas = document.createElement('canvas');
+        canvas.width = 640;
+        canvas.height = 640 * Doc.NativeHeight(this.layoutDoc) / (Doc.NativeWidth(this.layoutDoc) || 1);
+        const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
+        if (ctx) {
+            this._videoRef && ctx.drawImage(this._videoRef, 0, 0, canvas.width, canvas.height);
+        }
+
+        if (!this._videoRef) {
+            const b = Docs.Create.LabelDocument({
+                x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y, 1),
+                _width: 150, _height: 50, title: (this.layoutDoc._currentTimecode || 0).toString(),
+                _isLinkButton: true
+            });
+            this.props.addDocument?.(b);
+            DocUtils.MakeLink({ doc: b }, { doc: this.rootDoc }, "video snapshot");
+            Networking.PostToServer("/youtubeScreenshot", {
+                id: this.youtubeVideoId,
+                timecode: this.layoutDoc._currentTimecode
+            }).then(response => {
+                const resolved = response?.accessPaths?.agnostic?.client;
+                if (resolved) {
+                    this.props.removeDocument?.(b);
+                    this.createRealSummaryLink(resolved);
+                }
+            });
+        } else {
+            //convert to desired file format
+            const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
+            // if you want to preview the captured image,
+            const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, "");
+            const encodedFilename = encodeURIComponent("snapshot" + retitled + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_"));
+            const filename = basename(encodedFilename);
+            VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) =>
+                returnedFilename && (cb ?? this.createRealSummaryLink)(returnedFilename, downX, downY));
+        }
+    }
+
+    updateIcon = () => {
+        const makeIcon = (returnedfilename: string) => {
+            this.dataDoc.icon = new ImageField(returnedfilename);
+            this.dataDoc["icon-nativeWidth"] = this.layoutDoc[WidthSym]();
+            this.dataDoc["icon-nativeHeight"] = this.layoutDoc[HeightSym]();
+        };
+        this.Snapshot(undefined, undefined, makeIcon);
+    }
+
+    // creates link for snapshot
+    createRealSummaryLink = (imagePath: string, downX?: number, downY?: number) => {
+        const url = !imagePath.startsWith("/") ? Utils.CorsProxy(imagePath) : imagePath;
+        const width = NumCast(this.layoutDoc._width) || 1;
+        const height = NumCast(this.layoutDoc._height);
+        const imageSummary = Docs.Create.ImageDocument(url, {
+            _nativeWidth: Doc.NativeWidth(this.layoutDoc), _nativeHeight: Doc.NativeHeight(this.layoutDoc),
+            x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y), _isLinkButton: true,
+            _width: 150, _height: height / width * 150, title: "--snapshot" + NumCast(this.layoutDoc._currentTimecode) + " image-"
+        });
+        Doc.SetNativeWidth(Doc.GetProto(imageSummary), Doc.NativeWidth(this.layoutDoc));
+        Doc.SetNativeHeight(Doc.GetProto(imageSummary), Doc.NativeHeight(this.layoutDoc));
+        this.props.addDocument?.(imageSummary);
+        const link = DocUtils.MakeLink({ doc: imageSummary }, { doc: this.getAnchor() }, "video snapshot");
+        link && (Doc.GetProto(link.anchor2 as Doc).timecodeToHide = NumCast((link.anchor2 as Doc).timecodeToShow) + 3);
+        setTimeout(() =>
+            (downX !== undefined && downY !== undefined) && DocumentManager.Instance.getFirstDocumentView(imageSummary)?.startDragging(downX, downY, "move", true));
+    }
+
+
+    getAnchor = () => {
+        const timecode = Cast(this.layoutDoc._currentTimecode, "number", null);
+        const marquee = AnchorMenu.Instance.GetAnchor?.();
+        return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow"/* videoStart */, "_timecodeToHide" /* videoEnd */, timecode ? timecode : undefined, undefined, marquee) || this.rootDoc;
+    }
+
+
+    // sets video info on load
+    videoLoad = action(() => {
+        const aspect = this.player!.videoWidth / this.player!.videoHeight;
+        Doc.SetNativeWidth(this.dataDoc, this.player!.videoWidth);
+        Doc.SetNativeHeight(this.dataDoc, this.player!.videoHeight);
+        this.layoutDoc._height = NumCast(this.layoutDoc._width) / aspect;
+        if (Number.isFinite(this.player!.duration)) {
+            this.rawDuration = this.player!.duration;
+        } else this.rawDuration = NumCast(this.dataDoc[this.fieldKey + "-duration"]);
+    });
+
+
+    // updates video time
+    @action
+    updateTimecode = () => {
+        this.player && (this.layoutDoc._currentTimecode = this.player.currentTime);
+        try {
+            this._youtubePlayer && (this.layoutDoc._currentTimecode = this._youtubePlayer.getCurrentTime?.());
+        } catch (e) {
+            console.log("Video Timecode Exception:", e);
+        }
+    }
+
+
+    // extracts video thumbnails and saves them as field of doc
+    getVideoThumbnails = () => {
+        const video = document.createElement('video');
+        const thumbnailPromises: Promise<any>[] = [];
+
+        video.onloadedmetadata = () => {
+            video.currentTime = 0;
+        };
+
+        video.onseeked = () => {
             const canvas = document.createElement('canvas');
-            canvas.width = 640;
-            canvas.height = 640 * Doc.NativeHeight(this.layoutDoc) / (Doc.NativeWidth(this.layoutDoc) || 1);
-            const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
-            if (ctx) {
-                  this._videoRef && ctx.drawImage(this._videoRef, 0, 0, canvas.width, canvas.height);
+            canvas.height = video.videoHeight;
+            canvas.width = video.videoWidth;
+            const ctx = canvas.getContext('2d');
+            ctx?.drawImage(video, 0, 0, canvas.width, canvas.height);
+            const imgUrl = canvas.toDataURL();
+            const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, "");
+            const encodedFilename = encodeURIComponent("thumbnail" + retitled + "_" + video.currentTime.toString().replace(/\./, "_"));
+            const filename = basename(encodedFilename);
+            thumbnailPromises.push(VideoBox.convertDataUri(imgUrl, filename));
+            const newTime = video.currentTime + video.duration / (VideoBox.numThumbnails - 1);
+            if (newTime < video.duration) {
+                video.currentTime = newTime;
             }
-
-            if (!this._videoRef) {
-                  const b = Docs.Create.LabelDocument({
-                        x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y, 1),
-                        _width: 150, _height: 50, title: (this.layoutDoc._currentTimecode || 0).toString(),
-                        _isLinkButton: true
-                  });
-                  this.props.addDocument?.(b);
-                  DocUtils.MakeLink({ doc: b }, { doc: this.rootDoc }, "video snapshot");
-                  Networking.PostToServer("/youtubeScreenshot", {
-                        id: this.youtubeVideoId,
-                        timecode: this.layoutDoc._currentTimecode
-                  }).then(response => {
-                        const resolved = response?.accessPaths?.agnostic?.client;
-                        if (resolved) {
-                              this.props.removeDocument?.(b);
-                              this.createRealSummaryLink(resolved);
-                        }
-                  });
-            } else {
-                  //convert to desired file format
-                  const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
-                  // if you want to preview the captured image,
-                  const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, "");
-                  const encodedFilename = encodeURIComponent("snapshot" + retitled + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_"));
-                  const filename = basename(encodedFilename);
-                  VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) =>
-                        returnedFilename && (cb ?? this.createRealSummaryLink)(returnedFilename, downX, downY));
-            }
-      }
-
-      updateIcon = () => {
-            const makeIcon = (returnedfilename: string) => {
-                  this.dataDoc.icon = new ImageField(returnedfilename);
-                  this.dataDoc["icon-nativeWidth"] = this.layoutDoc[WidthSym]();
-                  this.dataDoc["icon-nativeHeight"] = this.layoutDoc[HeightSym]();
-            };
-            this.Snapshot(undefined, undefined, makeIcon);
-      }
-
-      // creates link for snapshot
-      createRealSummaryLink = (imagePath: string, downX?: number, downY?: number) => {
-            const url = !imagePath.startsWith("/") ? Utils.CorsProxy(imagePath) : imagePath;
-            const width = NumCast(this.layoutDoc._width) || 1;
-            const height = NumCast(this.layoutDoc._height);
-            const imageSummary = Docs.Create.ImageDocument(url, {
-                  _nativeWidth: Doc.NativeWidth(this.layoutDoc), _nativeHeight: Doc.NativeHeight(this.layoutDoc),
-                  x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y), _isLinkButton: true,
-                  _width: 150, _height: height / width * 150, title: "--snapshot" + NumCast(this.layoutDoc._currentTimecode) + " image-"
+            else {
+                Promise.all(thumbnailPromises).then(thumbnails => { this.dataDoc.thumbnails = new List<string>(thumbnails); });
+            }
+        }
+
+        const field = Cast(this.dataDoc[this.fieldKey], VideoField);
+        field && (video.src = field.url.href);
+    }
+
+
+    // sets video element ref
+    @action
+    setVideoRef = (vref: HTMLVideoElement | null) => {
+        this._videoRef = vref;
+        if (vref) {
+            this._videoRef!.ontimeupdate = this.updateTimecode;
+            // @ts-ignore
+            // vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen);
+            this._disposers.reactionDisposer?.();
+            this._disposers.reactionDisposer = reaction(() => NumCast(this.layoutDoc._currentTimecode),
+                time => !this._playing && (vref.currentTime = time), { fireImmediately: true });
+
+            (!this.dataDoc.thumbnails || this.dataDoc.thumbnails.length != VideoBox.numThumbnails) && this.getVideoThumbnails();
+        }
+    }
+
+    // set ref for div that wraps video and controls for fullscreen
+    @action
+    setContentRef = (cref: HTMLDivElement | null) => {
+        this._contentRef = cref;
+        if (cref) {
+            cref.onfullscreenchange = action((e) => {
+                this._fullScreen = (document.fullscreenElement === cref);
+                if (this._fullScreen) {
+                    document.addEventListener('pointermove', this.controlsFade);
+                    this._controlsVisible = true;
+                    this._controlsFadeTimer = setTimeout(action(() => this._controlsVisible = false), 3000)
+                }
+                else {
+                    document.removeEventListener('pointermove', this.controlsFade);
+                }
             });
-            Doc.SetNativeWidth(Doc.GetProto(imageSummary), Doc.NativeWidth(this.layoutDoc));
-            Doc.SetNativeHeight(Doc.GetProto(imageSummary), Doc.NativeHeight(this.layoutDoc));
-            this.props.addDocument?.(imageSummary);
-            const link = DocUtils.MakeLink({ doc: imageSummary }, { doc: this.getAnchor() }, "video snapshot");
-            link && (Doc.GetProto(link.anchor2 as Doc).timecodeToHide = NumCast((link.anchor2 as Doc).timecodeToShow) + 3);
-            setTimeout(() =>
-                  (downX !== undefined && downY !== undefined) && DocumentManager.Instance.getFirstDocumentView(imageSummary)?.startDragging(downX, downY, "move", true));
-      }
-
-
-      // returns the path of the audio file
-      @computed get audiopath() {
-            const field = Cast(this.props.Document[this.props.fieldKey + '-audio'], AudioField, null);
-            const vfield = Cast(this.dataDoc[this.fieldKey], VideoField, null);
-            return field?.url.href ?? vfield?.url.href ?? "";
-      }
-
-      // returns the presentation data if it exists, null otherwise
-      @computed get presentation() {
-            const data = this.dataDoc[this.fieldKey + '-presentation'];
-            return data ? JSON.parse(data) : null;
-      }
-
-
-      @computed private get timeline() { return this._stackedTimeline; }
-      private get transition() { return this._clicking ? "left 0.5s, width 0.5s, height 0.5s" : ""; } // css transition for hiding/showing timeline
-      public get player(): HTMLVideoElement | null { return this._videoRef; }
-
-
-      // updates video time
-      @action
-      updateTimecode = () => {
-            this.player && (this.layoutDoc._currentTimecode = this.player.currentTime);
-            try {
-                  this._youtubePlayer && (this.layoutDoc._currentTimecode = this._youtubePlayer.getCurrentTime?.());
-            } catch (e) {
-                  console.log("Video Timecode Exception:", e);
-            }
-      }
-
-
-      // extracts video thumbnails and saves them as field of doc
-      getVideoThumbnails = () => {
-            const video = document.createElement('video');
-            const thumbnailPromises: Promise<any>[] = [];
-
-            video.onloadedmetadata = () => {
-                  video.currentTime = 0;
-            };
-
-            video.onseeked = () => {
-                  const canvas = document.createElement('canvas');
-                  canvas.height = video.videoHeight;
-                  canvas.width = video.videoWidth;
-                  const ctx = canvas.getContext('2d');
-                  ctx?.drawImage(video, 0, 0, canvas.width, canvas.height);
-                  const imgUrl = canvas.toDataURL();
-                  const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, "");
-                  const encodedFilename = encodeURIComponent("thumbnail" + retitled + "_" + video.currentTime.toString().replace(/\./, "_"));
-                  const filename = basename(encodedFilename);
-                  thumbnailPromises.push(VideoBox.convertDataUri(imgUrl, filename));
-                  const newTime = video.currentTime + video.duration / (VideoBox.numThumbnails - 1);
-                  if (newTime < video.duration) {
-                        video.currentTime = newTime;
-                  }
-                  else {
-                        Promise.all(thumbnailPromises).then(thumbnails => { this.dataDoc.thumbnails = new List<string>(thumbnails); });
-                  }
-            }
-
-            const field = Cast(this.dataDoc[this.fieldKey], VideoField);
-            field && (video.src = field.url.href);
-      }
-
-
-      // sets video element ref
-      @action
-      setVideoRef = (vref: HTMLVideoElement | null) => {
-            this._videoRef = vref;
-            if (vref) {
-                  this._videoRef!.ontimeupdate = this.updateTimecode;
-                  // @ts-ignore
-                  // vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen);
-                  this._disposers.reactionDisposer?.();
-                  this._disposers.reactionDisposer = reaction(() => NumCast(this.layoutDoc._currentTimecode),
-                        time => !this._playing && (vref.currentTime = time), { fireImmediately: true });
-
-                  (!this.dataDoc.thumbnails || this.dataDoc.thumbnails.length != VideoBox.numThumbnails) && this.getVideoThumbnails();
-            }
-      }
-
-      // set ref for div that wraps video and controls for fullscreen
-      @action
-      setContentRef = (cref: HTMLDivElement | null) => {
-            this._contentRef = cref;
-            if (cref) {
-                  cref.onfullscreenchange = action((e) => {
-                        this._fullScreen = (document.fullscreenElement === cref);
-                        if (this._fullScreen) {
-                              document.addEventListener('pointermove', this.controlsFade);
-                              this._controlsVisible = true;
-                              this._controlsFadeTimer = setTimeout(action(() => this._controlsVisible = false), 3000)
-                        }
-                        else {
-                              document.removeEventListener('pointermove', this.controlsFade);
-                        }
-                  });
-            }
-      }
-
-
-      // context menu
-      specificContextMenu = (e: React.MouseEvent): void => {
-            const field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
-            if (field) {
-                  const url = field.url.href;
-                  const subitems: ContextMenuProps[] = [];
-                  subitems.push({ description: "Full Screen", event: this.FullScreen, icon: "expand" });
-                  subitems.push({ description: "Take Snapshot", event: this.Snapshot, icon: "expand-arrows-alt" });
-                  this.rootDoc.type === DocumentType.SCREENSHOT && subitems.push({
-                        description: "Screen Capture", event: (async () => {
-                              runInAction(() => this._screenCapture = !this._screenCapture);
-                              this._videoRef!.srcObject = !this._screenCapture ? undefined : await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
-                        }), icon: "expand-arrows-alt"
-                  });
-                  subitems.push({ description: (this.layoutDoc.dontAutoFollowLinks ? "" : "Don't") + " follow links when encountered", event: () => this.layoutDoc.dontAutoFollowLinks = !this.layoutDoc.dontAutoFollowLinks, icon: "expand-arrows-alt" });
-                  subitems.push({ description: (this.layoutDoc.dontAutoPlayFollowedLinks ? "" : "Don't") + " play when link is selected", event: () => this.layoutDoc.dontAutoPlayFollowedLinks = !this.layoutDoc.dontAutoPlayFollowedLinks, icon: "expand-arrows-alt" });
-                  subitems.push({ description: (this.layoutDoc.autoPlayAnchors ? "Don't auto play" : "Auto play") + " anchors onClick", event: () => this.layoutDoc.autoPlayAnchors = !this.layoutDoc.autoPlayAnchors, icon: "expand-arrows-alt" });
-                  // subitems.push({ description: "Toggle Native Controls", event: action(() => VideoBox._nativeControls = !VideoBox._nativeControls), icon: "expand-arrows-alt" });
-                  // subitems.push({ description: "Start Trim All", event: () => this.startTrim(TrimScope.All), icon: "expand-arrows-alt" });
-                  // subitems.push({ description: "Start Trim Clip", event: () => this.startTrim(TrimScope.Clip), icon: "expand-arrows-alt" });
-                  // subitems.push({ description: "Stop Trim", event: () => this.finishTrim(), icon: "expand-arrows-alt" });
-                  subitems.push({ description: "Copy path", event: () => { Utils.CopyText(url); }, icon: "expand-arrows-alt" });
-                  ContextMenu.Instance.addItem({ description: "Options...", subitems: subitems, icon: "video" });
-            }
-      }
-
-
-      // ref for updating time
-      setAudioRef = (e: HTMLAudioElement | null) => this._audioPlayer = e;
-
-      // renders the video and audio
-      @computed get content() {
-            const field = Cast(this.dataDoc[this.fieldKey], VideoField);
-            const interactive = CurrentUserUtils.SelectedTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive";
-            const classname = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
-            return !field ? <div key="loading">Loading</div> :
-                  <div className="videoBox-contentContainer" key="container" style={{ mixBlendMode: "multiply", cursor: this._fullScreen && !this._controlsVisible ? 'none' : 'pointer' }}>
-                        <div className={classname} ref={this.setContentRef} onPointerDown={(e) => this._fullScreen && e.stopPropagation()}>
-                              {this._fullScreen && <div className="videoBox-ui" onPointerDown={this.controlsDrag}
-                                    style={{ left: this._controlsTransform && this._controlsTransform.X, top: this._controlsTransform && this._controlsTransform.Y, visibility: this._controlsVisible ? 'visible' : 'hidden', opacity: this._controlsVisible ? 1 : 0 }}>
-                                    {this.UIButtons}
-                              </div>}
-                              <video key="video" autoPlay={this._screenCapture} ref={this.setVideoRef} style={this._fullScreen ? this.fullScreenSize() : {}}
-                                    onCanPlay={this.videoLoad}
-                                    controls={VideoBox._nativeControls}
-                                    onPlay={() => this.Play()}
-                                    onSeeked={this.updateTimecode}
-                                    onPause={() => this.Pause()}
-                                    onClick={this._fullScreen ? () => this.playing() ? this.Pause() : this.Play() : e => e.preventDefault()}>
-                                    <source src={field.url.href} type="video/mp4" />
-                                    Not supported.
-                              </video>
-                              {!this.audiopath || this.audiopath === field.url.href ? (null) :
-                                    <audio ref={this.setAudioRef} className={`audiobox-control${this.props.isContentActive() ? "-interactive" : ""}`}>
-                                          <source src={this.audiopath} type="audio/mpeg" />
-                                          Not supported.
-                                    </audio>}
-                        </div>
-                  </div>;
-      }
-
-
-      @action youtubeIframeLoaded = (e: any) => {
-            if (!this._youtubeContentCreated) {
-                  this._forceCreateYouTubeIFrame = !this._forceCreateYouTubeIFrame;
-                  return;
-            }
-            else this._youtubeContentCreated = false;
-
-            this.loadYouTube(e.target);
-      }
-
-      loadYouTube = (iframe: any) => {
-            let started = true;
-            const onYoutubePlayerStateChange = (event: any) => runInAction(() => {
-                  if (started && event.data === YT.PlayerState.PLAYING) {
-                        started = false;
-                        this._youtubePlayer?.unMute();
-                        //this.Pause();
-                        return;
-                  }
-                  this.player && this.setPlayheadTime(0);
-            }
-
-      componentWillUnmount() {
-                  this.removeCurrentlyPlaying();
-                  this.Pause();
-                  Object.keys(this._disposers).forEach(d => this._disposers[d]?.());
-            }
-
-
-      // plays video
-      @action public Play = (update: boolean = true) => {
-                  // if (Doc.UserDoc().presentationMode === 'watching' && !this._playing) {
-                  //     console.log('VideoBox : Play : presentation mode', this._playing);
-                  //     return;
-                  // }
-
-                  // if presentation isn't null, call followmovements on the recording api
-                  if (this.presentation) {
-                        console.log("presentation isn't null")
-                        const err = RecordingApi.Instance.playMovements(this.presentation, this.player?.currentTime || 0, this);
-                        err && console.log(err)
-                  } else {
-                        console.log("presentation is null")
-                  }
-
-                  this._playing = true;
-                  const eleTime = this.player?.currentTime || 0;
-                  if (this.timeline) {
-                        let start = eleTime >= this.timeline.trimEnd || eleTime <= this.timeline.trimStart ? this.timeline.trimStart : eleTime;
-
-                        if (this._finished) {
-                              // restarts video if reached end on previous play
-                              this._finished = false;
-                              start = this.timeline.trimStart;
-                        }
-
-                        try {
-                              this._audioPlayer && this.player && (this._audioPlayer.currentTime = this.player?.currentTime);
-                              update && this.player && this.playFrom(start, undefined, true);
-                              update && this._audioPlayer?.play();
-                              update && this._youtubePlayer?.playVideo();
-                              this._youtubePlayer && !this._playTimer && (this._playTimer = setInterval(this.updateTimecode, 5));
-                        } catch (e) {
-                              console.log("Video Play Exception:", e);
-                        }
-                  }
-                  this.updateTimecode();
-            }
-
-            // goes to time
-            @action public Seek(time: number) {
-                  try {
-                        this._youtubePlayer?.seekTo(Math.round(time), true);
-                  } catch (e) {
-                        console.log("Video Seek Exception:", e);
-                  }
-                  this.player && (this.player.currentTime = time);
-                  this._audioPlayer && (this._audioPlayer.currentTime = time);
-                  // TODO: revisit this and clean it
-                  if ((this.player?.currentTime || -1) < this.rawDuration) {
-                        this._finished = false;
-                  }
-            }
-
-            // pauses video
-            @action public Pause = (update: boolean = true) => {
-                  if (this.presentation) {
-                        console.log('VideoBox : Pause');
-                        const err = RecordingApi.Instance.pauseMovements();
-                        err && console.log(err);
-                  }
-
-                  this._playing = false;
-                  this.removeCurrentlyPlaying();
-                  try {
-                        update && this.player?.pause();
-                        update && this._audioPlayer?.pause();
-                        update && this._youtubePlayer?.pauseVideo();
-                        this._youtubePlayer && this._playTimer && clearInterval(this._playTimer);
-                        this._youtubePlayer?.seekTo(this._youtubePlayer?.getCurrentTime(), true);
-                  } catch (e) {
-                        console.log("Video Pause Exception:", e);
-                  }
-                  this._youtubePlayer && SelectionManager.DeselectAll(); // if we don't deselect the player, then we get an annoying YouTube spinner I guess telling us we're paused.
-                  this._playTimer = undefined;
-                  this.updateTimecode();
-                  if (!this._finished) clearTimeout(this._playRegionTimer); // if paused in the middle of playback, prevents restart on next play
-            }
-
-            // toggles video full screen
-            @action public FullScreen = () => {
-                  if (document.fullscreenElement === this._contentRef) {
-                        this._fullScreen = false;
-                        this.player && this._contentRef && document.exitFullscreen();
-                  }
-                  else {
-                        this._fullScreen = true;
-                        this.player && this._contentRef && this._contentRef.requestFullscreen();
-
-                  }
-                  try {
-                        this._youtubePlayer && this.props.addDocTab(this.rootDoc, "add");
-                  } catch (e) {
-                        console.log("Video FullScreen Exception:", e);
-                  }
-            }
-
-
-            // creates and links snapshot photo of current video frame
-            @action public Snapshot = (downX?: number, downY?: number, cb?: (filename: string, x: number | undefined, y: number | undefined) => void) => {
-                  const width = NumCast(this.layoutDoc._width);
-                  const canvas = document.createElement('canvas');
-                  canvas.width = 640;
-                  canvas.height = 640 * Doc.NativeHeight(this.layoutDoc) / (Doc.NativeWidth(this.layoutDoc) || 1);
-                  const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
-                  if (ctx) {
-                        this._videoRef && ctx.drawImage(this._videoRef, 0, 0, canvas.width, canvas.height);
-                  }
-
-                  if (!this._videoRef) {
-                        const b = Docs.Create.LabelDocument({
-                              x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y, 1),
-                              _width: 150, _height: 50, title: (this.layoutDoc._currentTimecode || 0).toString(),
-                              _isLinkButton: true
-                        });
-                        this.props.addDocument?.(b);
-                        DocUtils.MakeLink({ doc: b }, { doc: this.rootDoc }, "video snapshot");
-                        Networking.PostToServer("/youtubeScreenshot", {
-                              id: this.youtubeVideoId,
-                              timecode: this.layoutDoc._currentTimecode
-                        }).then(response => {
-                              const resolved = response?.accessPaths?.agnostic?.client;
-                              if (resolved) {
-                                    this.props.removeDocument?.(b);
-                                    this.createRealSummaryLink(resolved);
-                              }
-                        });
-                  } else {
-                        //convert to desired file format
-                        const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
-                        // if you want to preview the captured image,
-                        const retitled = StrCast(this.rootDoc.title).replace(/[ -\.]/g, "");
-                        const encodedFilename = encodeURIComponent("snapshot" + retitled + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_"));
-                        const filename = basename(encodedFilename);
-                        VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) =>
-                              returnedFilename && (cb ?? this.createRealSummaryLink)(returnedFilename, downX, downY));
-                  }
-            }
-
-            updateIcon = () => {
-                  const makeIcon = (returnedfilename: string) => {
-                        this.dataDoc.icon = new ImageField(returnedfilename);
-                        this.dataDoc["icon-nativeWidth"] = this.layoutDoc[WidthSym]();
-                        this.dataDoc["icon-nativeHeight"] = this.layoutDoc[HeightSym]();
-                  };
-                  this.Snapshot(undefined, undefined, makeIcon);
-            }
-
-            // creates link for snapshot
-            createRealSummaryLink = (imagePath: string, downX?: number, downY?: number) => {
-                  const url = !imagePath.startsWith("/") ? Utils.CorsProxy(imagePath) : imagePath;
-                  const width = NumCast(this.layoutDoc._width) || 1;
-                  const height = NumCast(this.layoutDoc._height);
-                  const imageSummary = Docs.Create.ImageDocument(url, {
-                        _nativeWidth: Doc.NativeWidth(this.layoutDoc), _nativeHeight: Doc.NativeHeight(this.layoutDoc),
-                        x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y), _isLinkButton: true,
-                        _width: 150, _height: height / width * 150, title: "--snapshot" + NumCast(this.layoutDoc._currentTimecode) + " image-"
-                  });
-                  Doc.SetNativeWidth(Doc.GetProto(imageSummary), Doc.NativeWidth(this.layoutDoc));
-                  Doc.SetNativeHeight(Doc.GetProto(imageSummary), Doc.NativeHeight(this.layoutDoc));
-                  this.props.addDocument?.(imageSummary);
-                  const link = DocUtils.MakeLink({ doc: imageSummary }, { doc: this.getAnchor() }, "video snapshot");
-                  link && (Doc.GetProto(link.anchor2 as Doc).timecodeToHide = NumCast((link.anchor2 as Doc).timecodeToShow) + 3);
-                  setTimeout(() =>
-                        (downX !== undefined && downY !== undefined) && DocumentManager.Instance.getFirstDocumentView(imageSummary)?.startDragging(downX, downY, "move", true));
-            }
-
-
-            getAnchor = () => {
-                  const timecode = Cast(this.layoutDoc._currentTimecode, "number", null);
-                  const marquee = AnchorMenu.Instance.GetAnchor?.();
-                  return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow"/* videoStart */, "_timecodeToHide" /* videoEnd */, timecode ? timecode : undefined, undefined, marquee) || this.rootDoc;
-            }
-
-
-            // sets video info on load
-            videoLoad = action(() => {
-                  const aspect = this.player!.videoWidth / this.player!.videoHeight;
-                  Doc.SetNativeWidth(this.dataDoc, this.player!.videoWidth);
-                  Doc.SetNativeHeight(this.dataDoc, this.player!.videoHeight);
-                  this.layoutDoc._height = NumCast(this.layoutDoc._width) / aspect;
-                  if (Number.isFinite(this.player!.duration)) {
-                        this.rawDuration = this.player!.duration;
-                  } else this.rawDuration = NumCast(this.dataDoc[this.fieldKey + "-duration"]);
+        }
+    }
+
+
+    // context menu
+    specificContextMenu = (e: React.MouseEvent): void => {
+        const field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
+        if (field) {
+            const url = field.url.href;
+            const subitems: ContextMenuProps[] = [];
+            subitems.push({ description: "Full Screen", event: this.FullScreen, icon: "expand" });
+            subitems.push({ description: "Take Snapshot", event: this.Snapshot, icon: "expand-arrows-alt" });
+            this.rootDoc.type === DocumentType.SCREENSHOT && subitems.push({
+                description: "Screen Capture", event: (async () => {
+                    runInAction(() => this._screenCapture = !this._screenCapture);
+                    this._videoRef!.srcObject = !this._screenCapture ? undefined : await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
+                }), icon: "expand-arrows-alt"
             });
-
-
-            // updates video time
-            @action
-            updateTimecode = () => {
-                  this.player && (this.layoutDoc._currentTimecode = this.player.currentTime);
-                  try {
-                        this._youtubePlayer && (this.layoutDoc._currentTimecode = this._youtubePlayer.getCurrentTime?.());
-                  } catch (e) {
-                        console.log("Video Timecode Exception:", e);
-                  }
-            }
-
-
-            // renders video controls
-            componentUI = (boundsLeft: number, boundsTop: number) => {
-                  const bounds = this.props.docViewPath().lastElement().getBounds();
-                  const left = bounds?.left || 0;
-                  const right = bounds?.right || 0;
-                  const top = bounds?.top || 0;
-                  const height = (bounds?.bottom || 0) - top;
-                  const width = Math.max(right - left, 100);
-                  const uiHeight = Math.max(25, Math.min(50, height / 10));
-                  const uiMargin = Math.min(10, height / 20);
-                  const vidHeight = height * this.heightPercent / 100;
-                  const yPos = top + vidHeight - uiHeight - uiMargin;
-                  const xPos = uiHeight / vidHeight > 0.4 ? right + 10 : left + 10;
-                  return this._fullScreen || (right - left) < 50 ? null : <div className="videoBox-ui-wrapper" style={{ clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)` }}>
-                        <div className="videoBox-ui" style={{ left: xPos, top: yPos, height: uiHeight, width: width - 20, transition: this._clicking ? "top 0.5s" : "" }}>
-                              {this.UIButtons}
-                        </div>
-                  </div>
-            }
-
-            @computed get UIButtons() {
-                  const bounds = this.props.docViewPath().lastElement().getBounds();
-                  const width = (bounds?.right || 0) - (bounds?.left || 0);
-                  const curTime = NumCast(this.layoutDoc._currentTimecode) - (this.timeline?.clipStart || 0);
-                  return <>
-                        <div className="videobox-button"
-                              title={this._playing ? "play" : "pause"}
-                              onPointerDown={this.onPlayDown}>
-                              <FontAwesomeIcon icon={this._playing ? "pause" : "play"} />
-                        </div>
-
-                        {this.timeline && width > 150 && <div className="timecode-controls">
-                              <div className="timecode-current">
-                                    {formatTime(curTime)}
-                              </div>
-
-                              {this._fullScreen || (this.heightPercent === 100 && width > 200) ?
-                                    <div className="timeline-slider">
-                                          <input type="range" step="0.1" min={this.timeline.clipStart} max={this.timeline.clipEnd} value={curTime}
-                                                className="toolbar-slider time-progress"
-                                                onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
-                                                onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setPlayheadTime(Number(e.target.value))}
-                                          />
-                                    </div>
-                                    :
-                                    <div>/</div>}
-
-                              <div className="timecode-end">
-                                    {formatTime(this.timeline.clipDuration)}
-                              </div>
-                        </div>
-                        }
-
-                  }
-                        ContextMenu.Instance.addItem({description: "Options...", subitems: subitems, icon: "video" });
-            }
-      }
-
-                        {
-                              !this._fullScreen && width > 300 && <div className="videobox-button"
-                                    title={"show timeline"}
-                                    onPointerDown={this.onTimelineHdlDown}>
-                                    <FontAwesomeIcon icon="eye" />
-                              </div>
-                        }
-
-                        {
-                              !this._fullScreen && width > 300 && <div className="videobox-button"
-                                    title={this.timeline?.IsTrimming !== TrimScope.None ? "finish trimming" : "start trim"}
-                                    onPointerDown={this.onClipPointerDown}>
-                                    <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? "check" : "cut"} />
-                              </div>
-                        }
-
-                        <div className="videobox-button"
-                              title={this._muted ? "unmute" : "mute"}
-                              onPointerDown={(e) => { e.stopPropagation(); this.toggleMute(); }}>
-                              <FontAwesomeIcon icon={this._muted ? "volume-mute" : "volume-up"} />
-                        </div>
-                        {
-                              width > 300 && <input type="range" style={{ width: `min(25%, 50px)` }} step="0.1" min="0" max="1" value={this._muted ? 0 : this._volume}
-                                    className="toolbar-slider volume"
-                                    onPointerDown={(e: React.PointerEvent) => e.stopPropagation()}
-                                    onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setVolume(Number(e.target.value))}
-                              />
-                        }
-
-                        {
-                              !this._fullScreen && this.heightPercent !== 100 && width > 300 &&
-                              <>
-                                    <div className="videobox-button" title="zoom">
-                                          <FontAwesomeIcon icon="search-plus" />
-                                    </div>
-                                    <input type="range" step="0.1" min="1" max="5" value={this.timeline?._zoomFactor}
-                                          className="toolbar-slider zoom"
-                                          onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
-                                          onChange={(e: React.ChangeEvent<HTMLInputElement>) => { this.zoom(Number(e.target.value)); }}
-                                    />
-                              </>
-                        }
-                  </>
-            }
-
-            @action youtubeIframeLoaded = (e: any) => {
-                  if (!this._youtubeContentCreated) {
-                        this._forceCreateYouTubeIFrame = !this._forceCreateYouTubeIFrame;
-                        return;
-                  }
-                  else this._youtubeContentCreated = false;
-
-                  this.loadYouTube(e.target);
-            }
-
-            loadYouTube = (iframe: any) => {
-                  let started = true;
-                  const onYoutubePlayerStateChange = (event: any) => runInAction(() => {
-                        if (started && event.data === YT.PlayerState.PLAYING) {
-                              started = false;
-                              this._youtubePlayer?.unMute();
-                              //this.Pause();
-                              return;
-                        }
-                        if (event.data === YT.PlayerState.PLAYING && !this._playing) this.Play(false);
-                        if (event.data === YT.PlayerState.PAUSED && this._playing) this.Pause(false);
-                  });
-                  const onYoutubePlayerReady = (event: any) => {
-                        this._disposers.reactionDisposer?.();
-                        this._disposers.youtubeReactionDisposer?.();
-                        this._disposers.reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek(NumCast(this.layoutDoc._currentTimecode)));
-                        this._disposers.youtubeReactionDisposer = reaction(
-                              () => CurrentUserUtils.SelectedTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
-                              (interactive) => iframe.style.pointerEvents = interactive ? "all" : "none", { fireImmediately: true });
-                  };
-                  if (typeof (YT) === undefined) setTimeout(() => this.loadYouTube(iframe), 100);
-                  else {
-                        (YT as any)?.ready(() => {
-                              this._youtubePlayer = new YT.Player(`${this.youtubeVideoId + this._youtubeIframeId}-player`, {
-                                    events: {
-                                          'onReady': this.props.dontRegisterView ? undefined : onYoutubePlayerReady,
-                                          'onStateChange': this.props.dontRegisterView ? undefined : onYoutubePlayerStateChange,
-                                    }
-                              });
-                        });
-                  }
-            }
-
-
-            // for play button
-
-            onPlayDown = () => {
-                  console.log("PLAY DOWN");
-                  this._playing ? this.Pause() : this.Play();
-            }
-
-            // for fullscreen button
-            onFullDown = (e: React.PointerEvent) => {
-                  this.FullScreen();
-                  e.stopPropagation();
-                  e.preventDefault();
-            }
-
-            // for snapshot button
-            onSnapshotDown = (e: React.PointerEvent) => {
-                  setupMoveUpEvents(this, e, (e) => {
-                        this.Snapshot(e.clientX, e.clientY);
-                        return true;
-                  }, emptyFunction, () => this.Snapshot());
-            }
-
-            // for show/hide timeline button, transitions between show/hide
-            @action
-            onTimelineHdlDown = (e: React.PointerEvent) => {
-                  this._clicking = true;
-                  setupMoveUpEvents(this, e,
-                        action(encodeURIComponent => {
-                              this._clicking = false;
-                              if (this.props.isContentActive()) {
-                                    // const local = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformPoint(e.clientX, e.clientY);
-                                    // this.layoutDoc._timelineHeightPercent = Math.max(0, Math.min(100, local[1] / this.props.PanelHeight() * 100));
-
-                                    this.layoutDoc._timelineHeightPercent = 80;
-                              }
-                              return false;
-                        }), emptyFunction,
-                        () => {
-                              this.layoutDoc._timelineHeightPercent = this.heightPercent !== 100 ? 100 : VideoBox.heightPercent;
-                              setTimeout(action(() => this._clicking = false), 500);
-                        }, this.props.isContentActive(), this.props.isContentActive());
-            }
-
-
-            // removes video from currently playing display
-            @action
-            removeCurrentlyPlaying = () => {
-                  if (CollectionStackedTimeline.CurrentlyPlaying) {
-                        const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc);
-                        index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1);
-                  }
-            }
-
-            // adds video to currently playing display
-            @action
-            addCurrentlyPlaying = () => {
-                  if (!CollectionStackedTimeline.CurrentlyPlaying) {
-                        CollectionStackedTimeline.CurrentlyPlaying = [];
-                  }
-                  if (CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc) === -1) {
-                        CollectionStackedTimeline.CurrentlyPlaying.push(this.layoutDoc);
-                  }
-            }
-
-
-            @computed get youtubeContent() {
-                  this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
-                  this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
-                  const classname = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
-                  const start = untracked(() => Math.round(NumCast(this.layoutDoc._currentTimecode)));
-                  return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
-                        onPointerLeave={this.updateTimecode}
-                        onLoad={this.youtubeIframeLoaded} className={classname} width={Doc.NativeWidth(this.layoutDoc) || 640} height={Doc.NativeHeight(this.layoutDoc) || 390}
-                        src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=0&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._nativeControls ? 1 : 0}`} />;
-            }
-
-
-            // for annotating, adds doc with time info
-            @action.bound
-            addDocWithTimecode(doc: Doc | Doc[]): boolean {
-                  const docs = doc instanceof Doc ? [doc] : doc;
-                  const curTime = NumCast(this.layoutDoc._currentTimecode);
-                  docs.forEach(doc => doc._timecodeToHide = (doc._timecodeToShow = curTime) + 1);
-                  return this.addDocument(doc);
-            }
-
-
-            // play back the audio from seekTimeInSeconds, fullPlay tells whether clip is being played to end vs link range
-            @action
-            playFrom = (seekTimeInSeconds: number, endTime?: number, fullPlay: boolean = false) => {
-                  clearTimeout(this._playRegionTimer);
-                  if (Number.isNaN(this.player?.duration)) {
-                        setTimeout(() => this.playFrom(seekTimeInSeconds, endTime), 500);
-                  }
-                  else if (this.player) {
-                        // trimBounds override requested playback bounds
-                        const end = Math.min(this.timeline?.trimEnd ?? this.rawDuration, endTime ?? this.timeline?.trimEnd ?? this.rawDuration);
-                        const start = Math.max(this.timeline?.trimStart ?? 0, seekTimeInSeconds);
-                        const playRegionDuration = end - start;
-                        // checks if times are within clip range
-                        if (seekTimeInSeconds >= 0 && (this.timeline?.trimStart || 0) <= end && seekTimeInSeconds <= (this.timeline?.trimEnd || this.rawDuration)) {
-                              this.player.currentTime = start;
-                              this._audioPlayer && (this._audioPlayer.currentTime = seekTimeInSeconds);
-                              this.player.play();
-                              this._audioPlayer?.play();
-                              this._playing = true;
-                              this.addCurrentlyPlaying();
-                              this._playRegionTimer = setTimeout(
-                                    () => {
-                                          // need to keep track of if end of clip is reached so on next play, clip restarts
-                                          if (fullPlay) {
-                                                Doc.UserDoc().presentationMode = 'none';
-                                                this._finished = true;
-                                          }
-                                          // removes from currently playing if playback has reached end of range marker
-                                          else this.removeCurrentlyPlaying();
-                                          this.Pause();
-                                    }, playRegionDuration * 1000);
-                        } else {
-                              this.Pause();
-                        }
-                  }
-            }
-
-
-            // ends trim, hides trim controls and displays new clip
-            @undoBatch
-            finishTrim = action(() => {
-                  this.Pause();
-                  this.setPlayheadTime(Math.max(Math.min(this.timeline?.trimEnd || 0, this.player!.currentTime), this.timeline?.trimStart || 0));
-                  this.timeline?.StopTrimming();
+            subitems.push({ description: (this.layoutDoc.dontAutoFollowLinks ? "" : "Don't") + " follow links when encountered", event: () => this.layoutDoc.dontAutoFollowLinks = !this.layoutDoc.dontAutoFollowLinks, icon: "expand-arrows-alt" });
+            subitems.push({ description: (this.layoutDoc.dontAutoPlayFollowedLinks ? "" : "Don't") + " play when link is selected", event: () => this.layoutDoc.dontAutoPlayFollowedLinks = !this.layoutDoc.dontAutoPlayFollowedLinks, icon: "expand-arrows-alt" });
+            subitems.push({ description: (this.layoutDoc.autoPlayAnchors ? "Don't auto play" : "Auto play") + " anchors onClick", event: () => this.layoutDoc.autoPlayAnchors = !this.layoutDoc.autoPlayAnchors, icon: "expand-arrows-alt" });
+            // subitems.push({ description: "Toggle Native Controls", event: action(() => VideoBox._nativeControls = !VideoBox._nativeControls), icon: "expand-arrows-alt" });
+            // subitems.push({ description: "Start Trim All", event: () => this.startTrim(TrimScope.All), icon: "expand-arrows-alt" });
+            // subitems.push({ description: "Start Trim Clip", event: () => this.startTrim(TrimScope.Clip), icon: "expand-arrows-alt" });
+            // subitems.push({ description: "Stop Trim", event: () => this.finishTrim(), icon: "expand-arrows-alt" });
+            subitems.push({ description: "Copy path", event: () => { Utils.CopyText(url); }, icon: "expand-arrows-alt" });
+            ContextMenu.Instance.addItem({ description: "Options...", subitems: subitems, icon: "video" });
+        }
+    }
+
+
+    // ref for updating time
+    setAudioRef = (e: HTMLAudioElement | null) => this._audioPlayer = e;
+
+    // renders the video and audio
+    @computed get content() {
+        const field = Cast(this.dataDoc[this.fieldKey], VideoField);
+        const interactive = CurrentUserUtils.SelectedTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive";
+        const classname = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
+        return !field ? <div key="loading">Loading</div> :
+            <div className="videoBox-contentContainer" key="container" style={{ mixBlendMode: "multiply", cursor: this._fullScreen && !this._controlsVisible ? 'none' : 'pointer' }}>
+                <div className={classname} ref={this.setContentRef} onPointerDown={(e) => this._fullScreen && e.stopPropagation()}>
+                    {this._fullScreen && <div className="videoBox-ui" onPointerDown={this.controlsDrag}
+                        style={{ left: this._controlsTransform && this._controlsTransform.X, top: this._controlsTransform && this._controlsTransform.Y, visibility: this._controlsVisible ? 'visible' : 'hidden', opacity: this._controlsVisible ? 1 : 0 }}>
+                        {this.UIButtons}
+                    </div>}
+                    <video key="video" autoPlay={this._screenCapture} ref={this.setVideoRef} style={this._fullScreen ? this.fullScreenSize() : {}}
+                        onCanPlay={this.videoLoad}
+                        controls={VideoBox._nativeControls}
+                        onPlay={() => this.Play()}
+                        onSeeked={this.updateTimecode}
+                        onPause={() => this.Pause()}
+                        onClick={this._fullScreen ? () => this.playing() ? this.Pause() : this.Play() : e => e.preventDefault()}>
+                        <source src={field.url.href} type="video/mp4" />
+                        Not supported.
+                    </video>
+                    {!this.audiopath || this.audiopath === field.url.href ? (null) :
+                        <audio ref={this.setAudioRef} className={`audiobox-control${this.props.isContentActive() ? "-interactive" : ""}`}>
+                            <source src={this.audiopath} type="audio/mpeg" />
+                            Not supported.
+                        </audio>}
+                </div>
+            </div>;
+    }
+
+
+    @action youtubeIframeLoaded = (e: any) => {
+        if (!this._youtubeContentCreated) {
+            this._forceCreateYouTubeIFrame = !this._forceCreateYouTubeIFrame;
+            return;
+        }
+        else this._youtubeContentCreated = false;
+
+        this.loadYouTube(e.target);
+    }
+
+    loadYouTube = (iframe: any) => {
+        let started = true;
+        const onYoutubePlayerStateChange = (event: any) => runInAction(() => {
+            if (started && event.data === YT.PlayerState.PLAYING) {
+                started = false;
+                this._youtubePlayer?.unMute();
+                //this.Pause();
+                return;
+            }
+            if (event.data === YT.PlayerState.PLAYING && !this._playing) this.Play(false);
+            if (event.data === YT.PlayerState.PAUSED && this._playing) this.Pause(false);
+        });
+        const onYoutubePlayerReady = (event: any) => {
+            this._disposers.reactionDisposer?.();
+            this._disposers.youtubeReactionDisposer?.();
+            this._disposers.reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek(NumCast(this.layoutDoc._currentTimecode)));
+            this._disposers.youtubeReactionDisposer = reaction(
+                () => CurrentUserUtils.SelectedTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
+                (interactive) => iframe.style.pointerEvents = interactive ? "all" : "none", { fireImmediately: true });
+        };
+        if (typeof (YT) === undefined) setTimeout(() => this.loadYouTube(iframe), 100);
+        else {
+            (YT as any)?.ready(() => {
+                this._youtubePlayer = new YT.Player(`${this.youtubeVideoId + this._youtubeIframeId}-player`, {
+                    events: {
+                        'onReady': this.props.dontRegisterView ? undefined : onYoutubePlayerReady,
+                        'onStateChange': this.props.dontRegisterView ? undefined : onYoutubePlayerStateChange,
+                    }
+                });
             });
-
-            // displays trim controls to start trimming clip
-            startTrim = (scope: TrimScope) => {
-                  this.Pause();
-                  this.timeline?.StartTrimming(scope);
-            }
-
-            // for trim button, double click displays full clip, single displays curr trim bounds
-            onClipPointerDown = (e: React.PointerEvent) => {
-                  // if timeline isn't shown, show first then trim
-                  this.heightPercent >= 100 && this.onTimelineHdlDown(e);
-                  this.timeline && setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => {
-                        if (doubleTap) {
-                              this.startTrim(TrimScope.All);
-                        } else if (this.timeline) {
-                              this.Pause();
-                              this.timeline.IsTrimming !== TrimScope.None ? this.finishTrim() : this.startTrim(TrimScope.Clip);
-                        }
-                  }));
-            }
-
-
-            // for volume slider sets volume
-            @action
-            setVolume = (volume: number) => {
-                  if (this.player) {
-                        this._volume = volume;
-                        this.player.volume = volume;
-                        if (this._muted) {
-                              this.toggleMute();
-                        }
-                  }
-            }
-
-            // toggles video mute
-            @action
-            toggleMute = () => {
-                  if (this.player) {
-                        this._muted = !this._muted;
-                        this.player.muted = this._muted;
-                  }
-            }
-
-
-            // stretches vertically or horizontally depending on video orientation so video fits full screen
-            fullScreenSize() {
-                  if (this._videoRef && this._videoRef.videoHeight / this._videoRef.videoWidth > 1) {
-                        return { height: "100%" };
-                  }
-                  else {
-                        return { width: "100%" };
-                  }
-            }
-
-
-            // for zoom slider, sets timeline waveform zoom
-            zoom = (zoom: number) => {
-                  this.timeline?.setZoom(zoom);
-            }
-
-
-            // plays link
-            playLink = (doc: Doc) => {
-                  const startTime = Math.max(0, (this._stackedTimeline?.anchorStart(doc) || 0));
-                  const endTime = this.timeline?.anchorEnd(doc);
-                  if (startTime !== undefined) {
-                        if (!this.layoutDoc.dontAutoPlayFollowedLinks) endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime);
-                        else this.Seek(startTime);
-                  }
-            }
-
-
-            // starts marquee selection
-            marqueeDown = (e: React.PointerEvent) => {
-                  if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
-                        setupMoveUpEvents(this, e, action(e => {
-                              MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
-                              this._marqueeing = [e.clientX, e.clientY];
-                              return true;
-                        }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false);
-                  }
-            }
-
-            // ends marquee selection
-            @action
-            finishMarquee = () => {
-                  this._marqueeing = undefined;
-                  this.props.select(true);
-            }
-
-            timelineWhenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive));
-
-            timelineScreenToLocal = () => this.props.ScreenToLocalTransform().scale(this.scaling()).translate(0, -this.heightPercent / 100 * this.props.PanelHeight());
-
-            setPlayheadTime = (time: number) => this.player!.currentTime = this.layoutDoc._currentTimecode = time;
-
-            timelineHeight = () => this.props.PanelHeight() * (100 - this.heightPercent) / 100;
-
-            playing = () => this._playing;
-
-            contentFunc = () => [this.youtubeVideoId ? this.youtubeContent : this.content];
-
-            scaling = () => this.props.scaling?.() || 1;
-
-            panelWidth = () => this.props.PanelWidth() * this.heightPercent / 100;
-            panelHeight = () => this.layoutDoc._fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : this.props.PanelHeight() * this.heightPercent / 100;
-
-            screenToLocalTransform = () => {
-                  const offset = (this.props.PanelWidth() - this.panelWidth()) / 2 / this.scaling();
-                  return this.props.ScreenToLocalTransform().translate(-offset, 0).scale(100 / this.heightPercent);
-            }
-
-            marqueeFitScaling = () => (this.props.scaling?.() || 1) * this.heightPercent / 100;
-            marqueeOffset = () => [this.panelWidth() / 2 * (1 - this.heightPercent / 100) / (this.heightPercent / 100), 0];
-
-            timelineDocFilter = () => [`_timelineLabel:true,${Utils.noRecursionHack}:x`];
-
-
-            // renders video controls
-            @computed get uIButtons() {
-                  const curTime = NumCast(this.layoutDoc._currentTimecode) - (this.timeline?.clipStart || 0);
-                  return <div className="videoBox-ui" style={this._fullScreen || this.heightPercent === 100 ? { fontSize: "40px", minWidth: "80%" } : {}}>
-                        <div className="videobox-button"
-                              title={this._playing ? "play" : "pause"}
-                              onPointerDown={this.onPlayDown}>
-                              <FontAwesomeIcon icon={this._playing ? "pause" : "play"} />
-                        </div>
-
-                        {this.timeline && <div className="timecode-controls">
-                              <div className="timecode-current">
-                                    {formatTime(curTime)}
-                              </div>
-
-                              {this._fullScreen || this.heightPercent === 100 ?
-                                    <div className="timeline-slider">
-                                          <input type="range" step="0.1" min={this.timeline.clipStart} max={this.timeline.clipEnd} value={curTime}
-                                                className="toolbar-slider time-progress"
-                                                onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
-                                                onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setPlayheadTime(Number(e.target.value))}
-                                          />
-                                    </div>
-                                    :
-                                    <div>/</div>}
-
-                              <div className="timecode-end">
-                                    {formatTime(this.timeline.clipDuration)}
-                              </div>
-                        </div>}
-
-                        <div className="videobox-button"
-                              title={"full screen"}
-                              onPointerDown={this.onFullDown}>
-                              <FontAwesomeIcon icon="expand" />
-                        </div>
-
-                        {!this._fullScreen && <div className="videobox-button"
-                              title={"show timeline"}
-                              onPointerDown={this.onTimelineHdlDown}>
-                              <FontAwesomeIcon icon="eye" />
-                        </div>}
-
-                        {!this._fullScreen && <div className="videobox-button"
-                              title={this.timeline?.IsTrimming !== TrimScope.None ? "finish trimming" : "start trim"}
-                              onPointerDown={this.onClipPointerDown}>
-                              <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? "check" : "cut"} />
-                        </div>}
-
-                        <div className="videobox-button show-slider"
-                              title={this._muted ? "unmute" : "mute"}
-                              onPointerDown={(e) => { e.stopPropagation(); this.toggleMute(); }}>
-                              <FontAwesomeIcon icon={this._muted ? "volume-mute" : "volume-up"} />
-                        </div>
-                        <input type="range" step="0.1" min="0" max="1" value={this._muted ? 0 : this._volume}
-                              className="toolbar-slider volume"
-                              onPointerDown={(e: React.PointerEvent) => e.stopPropagation()}
-                              onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setVolume(Number(e.target.value))}
-                        />
-
-                        {!this._fullScreen && this.heightPercent !== 100 &&
-                              <>
-                                    <div className="videobox-button" title="zoom">
-                                          <FontAwesomeIcon icon="search-plus" />
-                                    </div>
-                                    <input type="range" step="0.1" min="1" max="5" value={this.timeline?._zoomFactor}
-                                          className="toolbar-slider zoom"
-                                          onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
-                                          onChange={(e: React.ChangeEvent<HTMLInputElement>) => { this.zoom(Number(e.target.value)); }}
-                                    />
-                              </>}
-                  </div>;
+        }
+    }
+
+
+    // for play button
+    onPlayDown = () => this._playing ? this.Pause() : this.Play();
+
+    // for fullscreen button
+    onFullDown = (e: React.PointerEvent) => {
+        this.FullScreen();
+        e.stopPropagation();
+        e.preventDefault();
+    }
+
+    // for snapshot button
+    onSnapshotDown = (e: React.PointerEvent) => {
+        setupMoveUpEvents(this, e, (e) => {
+            this.Snapshot(e.clientX, e.clientY);
+            return true;
+        }, emptyFunction, () => this.Snapshot());
+    }
+
+    // for show/hide timeline button, transitions between show/hide
+    @action
+    onTimelineHdlDown = (e: React.PointerEvent) => {
+        this._clicking = true;
+        setupMoveUpEvents(this, e,
+            action(encodeURIComponent => {
+                this._clicking = false;
+                if (this.props.isContentActive()) {
+                    // const local = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformPoint(e.clientX, e.clientY);
+                    // this.layoutDoc._timelineHeightPercent = Math.max(0, Math.min(100, local[1] / this.props.PanelHeight() * 100));
+
+                    this.layoutDoc._timelineHeightPercent = 80;
+                }
+                return false;
+            }), emptyFunction,
+            () => {
+                this.layoutDoc._timelineHeightPercent = this.heightPercent !== 100 ? 100 : VideoBox.heightPercent;
+                setTimeout(action(() => this._clicking = false), 500);
+            }, this.props.isContentActive(), this.props.isContentActive());
+    }
+
+
+    // removes video from currently playing display
+    @action
+    removeCurrentlyPlaying = () => {
+        if (CollectionStackedTimeline.CurrentlyPlaying) {
+            const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc);
+            index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1);
+        }
+    }
+
+    // adds video to currently playing display
+    @action
+    addCurrentlyPlaying = () => {
+        if (!CollectionStackedTimeline.CurrentlyPlaying) {
+            CollectionStackedTimeline.CurrentlyPlaying = [];
+        }
+        if (CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc) === -1) {
+            CollectionStackedTimeline.CurrentlyPlaying.push(this.layoutDoc);
+        }
+    }
+
+
+    @computed get youtubeContent() {
+        this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
+        this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
+        const classname = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
+        const start = untracked(() => Math.round(NumCast(this.layoutDoc._currentTimecode)));
+        return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
+            onPointerLeave={this.updateTimecode}
+            onLoad={this.youtubeIframeLoaded} className={classname} width={Doc.NativeWidth(this.layoutDoc) || 640} height={Doc.NativeHeight(this.layoutDoc) || 390}
+            src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=0&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._nativeControls ? 1 : 0}`} />;
+    }
+
+
+    // for annotating, adds doc with time info
+    @action.bound
+    addDocWithTimecode(doc: Doc | Doc[]): boolean {
+        const docs = doc instanceof Doc ? [doc] : doc;
+        const curTime = NumCast(this.layoutDoc._currentTimecode);
+        docs.forEach(doc => doc._timecodeToHide = (doc._timecodeToShow = curTime) + 1);
+        return this.addDocument(doc);
+    }
+
+
+    // play back the audio from seekTimeInSeconds, fullPlay tells whether clip is being played to end vs link range
+    @action
+    playFrom = (seekTimeInSeconds: number, endTime?: number, fullPlay: boolean = false) => {
+        clearTimeout(this._playRegionTimer);
+        this._playRegionTimer = undefined;
+        if (Number.isNaN(this.player?.duration)) {
+            setTimeout(() => this.playFrom(seekTimeInSeconds, endTime), 500);
+        }
+        else if (this.player) {
+            // trimBounds override requested playback bounds
+            const end = Math.min(this.timeline?.trimEnd ?? this.rawDuration, endTime ?? this.timeline?.trimEnd ?? this.rawDuration);
+            const start = Math.max(this.timeline?.trimStart ?? 0, seekTimeInSeconds);
+            const playRegionDuration = end - start;
+            // checks if times are within clip range
+            if (seekTimeInSeconds >= 0 && (this.timeline?.trimStart || 0) <= end && seekTimeInSeconds <= (this.timeline?.trimEnd || this.rawDuration)) {
+                this.player.currentTime = start;
+                this._audioPlayer && (this._audioPlayer.currentTime = seekTimeInSeconds);
+                this.player.play();
+                this._audioPlayer?.play();
+                this._playing = true;
+                this.addCurrentlyPlaying();
+                this._playRegionTimer = setTimeout(
+                    () => {
+                        // need to keep track of if end of clip is reached so on next play, clip restarts
+                        if (fullPlay) this._finished = true;
+                        // removes from currently playing if playback has reached end of range marker
+                        else this.removeCurrentlyPlaying();
+                        this.Pause();
+                    }, playRegionDuration * 1000);
+            } else {
+                this.Pause();
+            }
+        }
+    }
+
+
+    // ends trim, hides trim controls and displays new clip
+    @undoBatch
+    finishTrim = action(() => {
+        this.Pause();
+        this.setPlayheadTime(Math.max(Math.min(this.timeline?.trimEnd || 0, this.player!.currentTime), this.timeline?.trimStart || 0));
+        this.timeline?.StopTrimming();
+    });
+
+    // displays trim controls to start trimming clip
+    startTrim = (scope: TrimScope) => {
+        this.Pause();
+        this.timeline?.StartTrimming(scope);
+    }
+
+    // for trim button, double click displays full clip, single displays curr trim bounds
+    onClipPointerDown = (e: React.PointerEvent) => {
+        // if timeline isn't shown, show first then trim
+        this.heightPercent >= 100 && this.onTimelineHdlDown(e);
+        this.timeline && setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => {
+            if (doubleTap) {
+                this.startTrim(TrimScope.All);
+            } else if (this.timeline) {
+                this.Pause();
+                this.timeline.IsTrimming !== TrimScope.None ? this.finishTrim() : this.startTrim(TrimScope.Clip);
             }
-
-            // renders CollectionStackedTimeline
-            @computed get renderTimeline() {
-                  return <div className="videoBox-stackPanel" style={{ transition: this.transition, height: `${100 - this.heightPercent}%` }}>
-                        <CollectionStackedTimeline ref={action((r: any) => this._stackedTimeline = r)} {...this.props}
-                              fieldKey={this.annotationKey}
-                              dictationKey={this.fieldKey + "-dictation"}
-                              mediaPath={this.audiopath}
-                              renderDepth={this.props.renderDepth + 1}
-                              startTag={"_timecodeToShow" /* videoStart */}
-                              endTag={"_timecodeToHide" /* videoEnd */}
-                              bringToFront={emptyFunction}
-                              CollectionView={undefined}
-                              playFrom={this.playFrom}
-                              setTime={this.setPlayheadTime}
-                              playing={this.playing}
-                              isAnyChildContentActive={this.isAnyChildContentActive}
-                              whenChildContentsActiveChanged={this.timelineWhenChildContentsActiveChanged}
-                              moveDocument={this.moveDocument}
-                              addDocument={this.addDocument}
-                              removeDocument={this.removeDocument}
-                              ScreenToLocalTransform={this.timelineScreenToLocal}
-                              Play={this.Play}
-                              Pause={this.Pause}
-                              playLink={this.playLink}
-                              PanelHeight={this.timelineHeight}
-                              rawDuration={this.rawDuration}
+        }));
+    }
+
+
+    // for volume slider sets volume
+    @action
+    setVolume = (volume: number) => {
+        if (this.player) {
+            this._volume = volume;
+            this.player.volume = volume;
+            if (this._muted) {
+                this.toggleMute();
+            }
+        }
+    }
+
+    // toggles video mute
+    @action
+    toggleMute = () => {
+        if (this.player) {
+            this._muted = !this._muted;
+            this.player.muted = this._muted;
+        }
+    }
+
+
+    // stretches vertically or horizontally depending on video orientation so video fits full screen
+    fullScreenSize() {
+        if (this._videoRef && this._videoRef.videoHeight / this._videoRef.videoWidth > 1) {
+            return { height: "100%" };
+        }
+        else {
+            return { width: "100%" };
+        }
+    }
+
+
+    // for zoom slider, sets timeline waveform zoom
+    zoom = (zoom: number) => {
+        this.timeline?.setZoom(zoom);
+    }
+
+
+    // plays link
+    playLink = (doc: Doc) => {
+        const startTime = Math.max(0, (this._stackedTimeline?.anchorStart(doc) || 0));
+        const endTime = this.timeline?.anchorEnd(doc);
+        if (startTime !== undefined) {
+            if (!this.layoutDoc.dontAutoPlayFollowedLinks) endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime);
+            else this.Seek(startTime);
+        }
+    }
+
+
+    // starts marquee selection
+    marqueeDown = (e: React.PointerEvent) => {
+        if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+            setupMoveUpEvents(this, e, action(e => {
+                MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+                this._marqueeing = [e.clientX, e.clientY];
+                return true;
+            }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false);
+        }
+    }
+
+    // ends marquee selection
+    @action
+    finishMarquee = () => {
+        this._marqueeing = undefined;
+        this.props.select(true);
+    }
+
+    timelineWhenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive));
+
+    timelineScreenToLocal = () => this.props.ScreenToLocalTransform().scale(this.scaling()).translate(0, -this.heightPercent / 100 * this.props.PanelHeight());
+
+    setPlayheadTime = (time: number) => this.player!.currentTime = this.layoutDoc._currentTimecode = time;
+
+    timelineHeight = () => this.props.PanelHeight() * (100 - this.heightPercent) / 100;
+
+    playing = () => this._playing;
+
+    contentFunc = () => [this.youtubeVideoId ? this.youtubeContent : this.content];
+
+    scaling = () => this.props.scaling?.() || 1;
+
+    panelWidth = () => this.props.PanelWidth() * this.heightPercent / 100;
+    panelHeight = () => this.layoutDoc._fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : this.props.PanelHeight() * this.heightPercent / 100;
+
+    screenToLocalTransform = () => {
+        const offset = (this.props.PanelWidth() - this.panelWidth()) / 2 / this.scaling();
+        return this.props.ScreenToLocalTransform().translate(-offset, 0).scale(100 / this.heightPercent);
+    }
+
+    marqueeFitScaling = () => (this.props.scaling?.() || 1) * this.heightPercent / 100;
+    marqueeOffset = () => [this.panelWidth() / 2 * (1 - this.heightPercent / 100) / (this.heightPercent / 100), 0];
+
+    timelineDocFilter = () => [`_timelineLabel:true,${Utils.noRecursionHack}:x`];
+
+
+    // renders video controls
+    componentUI = (boundsLeft: number, boundsTop: number) => {
+        const bounds = this.props.docViewPath().lastElement().getBounds();
+        const left = bounds?.left || 0;
+        const right = bounds?.right || 0;
+        const top = bounds?.top || 0;
+        const height = (bounds?.bottom || 0) - top;
+        const width = Math.max(right - left, 100);
+        const uiHeight = Math.max(25, Math.min(50, height / 10));
+        const uiMargin = Math.min(10, height / 20);
+        const vidHeight = height * this.heightPercent / 100;
+        const yPos = top + vidHeight - uiHeight - uiMargin;
+        const xPos = uiHeight / vidHeight > 0.4 ? right + 10 : left + 10;
+        return this._fullScreen || (right - left) < 50 ? null : <div className="videoBox-ui-wrapper" style={{ clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)` }}>
+            <div className="videoBox-ui" style={{ left: xPos, top: yPos, height: uiHeight, width: width - 20, transition: this._clicking ? "top 0.5s" : "" }}>
+                {this.UIButtons}
+            </div>
+        </div>
+    }
+
+    @computed get UIButtons() {
+        const bounds = this.props.docViewPath().lastElement().getBounds();
+        const width = (bounds?.right || 0) - (bounds?.left || 0);
+        const curTime = NumCast(this.layoutDoc._currentTimecode) - (this.timeline?.clipStart || 0);
+        return <>
+            <div className="videobox-button"
+                title={this._playing ? "play" : "pause"}
+                onPointerDown={this.onPlayDown}>
+                <FontAwesomeIcon icon={this._playing ? "pause" : "play"} />
+            </div>
+
+            {this.timeline && width > 150 && <div className="timecode-controls">
+                <div className="timecode-current">
+                    {formatTime(curTime)}
+                </div>
+
+                {this._fullScreen || (this.heightPercent === 100 && width > 200) ?
+                    <div className="timeline-slider">
+                        <input type="range" step="0.1" min={this.timeline.clipStart} max={this.timeline.clipEnd} value={curTime}
+                            className="toolbar-slider time-progress"
+                            onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
+                            onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setPlayheadTime(Number(e.target.value))}
                         />
-                  </div>;
-            }
-
-            // renders annotation layer
-            @computed get annotationLayer() {
-                  return <div className="videoBox-annotationLayer" style={{ transition: this.transition, height: `${this.heightPercent}%` }} ref={this._annotationLayer} />;
-            }
-
-            savedAnnotations = () => this._savedAnnotations;
-            render() {
-                  const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
-                  const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / this.scaling()}px` : borderRad;
-                  return (<div className="videoBox" onContextMenu={this.specificContextMenu} ref={this._mainCont}
-                        style={{
-                              pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined,
-                              borderRadius,
-                              overflow: this.props.docViewPath?.().slice(-1)[0].fitWidth ? "auto" : undefined
-                        }} onWheel={e => { e.stopPropagation(); e.preventDefault(); }}>
-                        <div className="videoBox-viewer" onPointerDown={this.marqueeDown} >
-                              <div style={{
-                                    position: "absolute", transition: this.transition,
-                                    width: this.panelWidth(),
-                                    height: this.panelHeight(),
-                                    top: 0,
-                                    left: (this.props.PanelWidth() - this.panelWidth()) / 2
-                              }}>
-                                    <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
-                                          renderDepth={this.props.renderDepth + 1}
-                                          fieldKey={this.annotationKey}
-                                          CollectionView={undefined}
-                                          isAnnotationOverlay={true}
-                                          annotationLayerHostsContent={true}
-                                          PanelWidth={this.panelWidth}
-                                          PanelHeight={this.panelHeight}
-                                          ScreenToLocalTransform={this.screenToLocalTransform}
-                                          docFilters={this.timelineDocFilter}
-                                          select={emptyFunction}
-                                          scaling={returnOne}
-                                          whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                                          removeDocument={this.removeDocument}
-                                          moveDocument={this.moveDocument}
-                                          addDocument={this.addDocWithTimecode}>
-                                          {this.contentFunc}
-                                    </CollectionFreeFormView>
-                              </div>
-                              {this.annotationLayer}
-                              {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
-                                    <MarqueeAnnotator
-                                          rootDoc={this.rootDoc}
-                                          scrollTop={0}
-                                          down={this._marqueeing}
-                                          scaling={this.marqueeFitScaling}
-                                          docView={this.props.docViewPath().slice(-1)[0]}
-                                          containerOffset={this.marqueeOffset}
-                                          addDocument={this.addDocWithTimecode}
-                                          finishMarquee={this.finishMarquee}
-                                          savedAnnotations={this.savedAnnotations}
-                                          annotationLayer={this._annotationLayer.current}
-                                          mainCont={this._mainCont.current}
-                                    />}
-                              {this.renderTimeline}
-                        </div>
-                  </div >);
-            }
-      }
+                    </div>
+                    :
+                    <div>/</div>}
+
+                <div className="timecode-end">
+                    {formatTime(this.timeline.clipDuration)}
+                </div>
+            </div>
+            }
+
+            <div className="videobox-button"
+                title={"full screen"}
+                onPointerDown={this.onFullDown}>
+                <FontAwesomeIcon icon="expand" />
+            </div>
+
+            {
+                !this._fullScreen && width > 300 && <div className="videobox-button"
+                    title={"show timeline"}
+                    onPointerDown={this.onTimelineHdlDown}>
+                    <FontAwesomeIcon icon="eye" />
+                </div>
+            }
+
+            {
+                !this._fullScreen && width > 300 && <div className="videobox-button"
+                    title={this.timeline?.IsTrimming !== TrimScope.None ? "finish trimming" : "start trim"}
+                    onPointerDown={this.onClipPointerDown}>
+                    <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? "check" : "cut"} />
+                </div>
+            }
+
+            <div className="videobox-button"
+                title={this._muted ? "unmute" : "mute"}
+                onPointerDown={(e) => { e.stopPropagation(); this.toggleMute(); }}>
+                <FontAwesomeIcon icon={this._muted ? "volume-mute" : "volume-up"} />
+            </div>
+            {
+                width > 300 && <input type="range" style={{ width: `min(25%, 50px)` }} step="0.1" min="0" max="1" value={this._muted ? 0 : this._volume}
+                    className="toolbar-slider volume"
+                    onPointerDown={(e: React.PointerEvent) => e.stopPropagation()}
+                    onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setVolume(Number(e.target.value))}
+                />
+            }
+
+            {
+                !this._fullScreen && this.heightPercent !== 100 && width > 300 &&
+                <>
+                    <div className="videobox-button" title="zoom">
+                        <FontAwesomeIcon icon="search-plus" />
+                    </div>
+                    <input type="range" step="0.1" min="1" max="5" value={this.timeline?._zoomFactor}
+                        className="toolbar-slider zoom"
+                        onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
+                        onChange={(e: React.ChangeEvent<HTMLInputElement>) => { this.zoom(Number(e.target.value)); }}
+                    />
+                </>
+            }
+        </>
+    }
+
+    // renders CollectionStackedTimeline
+    @computed get renderTimeline() {
+        return <div className="videoBox-stackPanel" style={{ transition: this.transition, height: `${100 - this.heightPercent}%` }}>
+            <CollectionStackedTimeline ref={action((r: any) => this._stackedTimeline = r)} {...this.props}
+                fieldKey={this.annotationKey}
+                dictationKey={this.fieldKey + "-dictation"}
+                mediaPath={this.audiopath}
+                renderDepth={this.props.renderDepth + 1}
+                startTag={"_timecodeToShow" /* videoStart */}
+                endTag={"_timecodeToHide" /* videoEnd */}
+                bringToFront={emptyFunction}
+                CollectionView={undefined}
+                playFrom={this.playFrom}
+                setTime={this.setPlayheadTime}
+                playing={this.playing}
+                isAnyChildContentActive={this.isAnyChildContentActive}
+                whenChildContentsActiveChanged={this.timelineWhenChildContentsActiveChanged}
+                moveDocument={this.moveDocument}
+                addDocument={this.addDocument}
+                removeDocument={this.removeDocument}
+                ScreenToLocalTransform={this.timelineScreenToLocal}
+                Play={this.Play}
+                Pause={this.Pause}
+                playLink={this.playLink}
+                PanelHeight={this.timelineHeight}
+                rawDuration={this.rawDuration}
+            />
+        </div>;
+    }
+
+    // renders annotation layer
+    @computed get annotationLayer() {
+        return <div className="videoBox-annotationLayer" style={{ transition: this.transition, height: `${this.heightPercent}%` }} ref={this._annotationLayer} />;
+    }
+
+    savedAnnotations = () => this._savedAnnotations;
+    render() {
+        const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
+        const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / this.scaling()}px` : borderRad;
+        return (<div className="videoBox" onContextMenu={this.specificContextMenu} ref={this._mainCont}
+            style={{
+                pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined,
+                borderRadius,
+                overflow: this.props.docViewPath?.().slice(-1)[0].fitWidth ? "auto" : undefined
+            }} onWheel={e => { e.stopPropagation(); e.preventDefault(); }}>
+            <div className="videoBox-viewer" onPointerDown={this.marqueeDown} >
+                <div style={{
+                    position: "absolute", transition: this.transition,
+                    width: this.panelWidth(),
+                    height: this.panelHeight(),
+                    top: 0,
+                    left: (this.props.PanelWidth() - this.panelWidth()) / 2
+                }}>
+                    <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
+                        renderDepth={this.props.renderDepth + 1}
+                        fieldKey={this.annotationKey}
+                        CollectionView={undefined}
+                        isAnnotationOverlay={true}
+                        annotationLayerHostsContent={true}
+                        PanelWidth={this.panelWidth}
+                        PanelHeight={this.panelHeight}
+                        ScreenToLocalTransform={this.screenToLocalTransform}
+                        docFilters={this.timelineDocFilter}
+                        select={emptyFunction}
+                        scaling={returnOne}
+                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                        removeDocument={this.removeDocument}
+                        moveDocument={this.moveDocument}
+                        addDocument={this.addDocWithTimecode}>
+                        {this.contentFunc}
+                    </CollectionFreeFormView>
+                </div>
+                {this.annotationLayer}
+                {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
+                    <MarqueeAnnotator
+                        rootDoc={this.rootDoc}
+                        scrollTop={0}
+                        down={this._marqueeing}
+                        scaling={this.marqueeFitScaling}
+                        docView={this.props.docViewPath().slice(-1)[0]}
+                        containerOffset={this.marqueeOffset}
+                        addDocument={this.addDocWithTimecode}
+                        finishMarquee={this.finishMarquee}
+                        savedAnnotations={this.savedAnnotations}
+                        annotationLayer={this._annotationLayer.current}
+                        mainCont={this._mainCont.current}
+                    />}
+                {this.renderTimeline}
+            </div>
+        </div >);
+    }
+}
 
 VideoBox._nativeControls = false;
\ No newline at end of file
diff --git a/src/client/views/nodes/WebBoxRenderer.js b/src/client/views/nodes/WebBoxRenderer.js
index f3f1bcf5c..d9524dd6e 100644
--- a/src/client/views/nodes/WebBoxRenderer.js
+++ b/src/client/views/nodes/WebBoxRenderer.js
@@ -214,8 +214,8 @@ var ForeignHtmlRenderer = function (styleSheets) {
                 .replace(/noscript/g, "div").replace(/<div class="mediaset"><\/div>/g, "")  // when scripting isn't available (ie, rendering web pages here), <noscript> tags should become <div>'s.  But for Brown CS, there's a layout problem if you leave the empty <mediaset> tag
                 .replace(/<link[^>]*>/g, "")  // don't need to keep any linked style sheets because we've already processed all style sheets above
                 .replace(/srcset="([^ "]*)[^"]*"/g, "src=\"$1\""); // instead of converting each item in the srcset to a data url, just convert the first one and use that
-            let urlsFoundInHtml = getImageUrlsFromFromHtml(contentHtml).filter(url => !url.startsWith("data:"));
-            const fetchedResources = webUrl ? await getMultipleResourcesAsBase64(webUrl, urlsFoundInHtml) : [];
+            let urlsFoundInHtml = getImageUrlsFromFromHtml(contentHtml);
+            const fetchedResources = await getMultipleResourcesAsBase64(webUrl, urlsFoundInHtml);
             for (let i = 0; i < fetchedResources.length; i++) {
                 const r = fetchedResources[i];
                 if (r.resourceUrl) {
diff --git a/src/client/views/nodes/button/FontIconBadge.tsx b/src/client/views/nodes/button/FontIconBadge.tsx
index df17d603f..cf86b5e07 100644
--- a/src/client/views/nodes/button/FontIconBadge.tsx
+++ b/src/client/views/nodes/button/FontIconBadge.tsx
@@ -6,31 +6,31 @@ import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../../Utils
 import { DragManager } from "../../../util/DragManager";
 import "./FontIconBadge.scss";
 
-interface FontIconBadgeProps {    
-    value: string | undefined;
+interface FontIconBadgeProps {
+    collection: Doc | undefined;
 }
 
 @observer
 export class FontIconBadge extends React.Component<FontIconBadgeProps> {
     _notifsRef = React.createRef<HTMLDivElement>();
 
-    // onPointerDown = (e: React.PointerEvent) => {
-    //     setupMoveUpEvents(this, e,
-    //         (e: PointerEvent) => {
-    //             const dragData = new DragManager.DocumentDragData([this.props.collection!]);
-    //             DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y);
-    //             return true;
-    //         },
-    //         returnFalse, emptyFunction, false);
-    // }
+    onPointerDown = (e: React.PointerEvent) => {
+        setupMoveUpEvents(this, e,
+            (e: PointerEvent) => {
+                const dragData = new DragManager.DocumentDragData([this.props.collection!]);
+                DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y);
+                return true;
+            },
+            returnFalse, emptyFunction, false);
+    }
 
     render() {
-        if (this.props.value === undefined) return (null);
+        if (!(this.props.collection instanceof Doc)) return (null);
+        const length = DocListCast(this.props.collection.data).filter(d => GetEffectiveAcl(d) !== AclPrivate).length; //  Object.keys(d).length).length; // filter out any documents that we can't read
         return <div className="fontIconBadge-container" ref={this._notifsRef}>
-            <div className="fontIconBadge" style={{ "display": "initial" }}
-               // onPointerDown={this.onPointerDown} 
-               >
-                {this.props.value}
+            <div className="fontIconBadge" style={length > 0 ? { "display": "initial" } : { "display": "none" }}
+                onPointerDown={this.onPointerDown} >
+                {length}
             </div>
         </div>;
     }
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 97e6eddfe..42c650a09 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -521,7 +521,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                     <div className={`menuButton ${this.type}`} style={{ color, backgroundColor }}>
                         {this.icon === "pres-trail" ? trailsIcon : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />}
                         {menuLabel}
-                        <FontIconBadge value={ScriptCast(this.Document.badgeValue,null)?.script.run({self:this.Document}).result} />
+                        <FontIconBadge collection={Cast(this.rootDoc.watchedDocuments, Doc, null)} />
                     </div >
                 );
                 break;
@@ -708,6 +708,8 @@ ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
 });
 
 
+
+
 export function checkInksToGroup() {
     // console.log("getting here to inks group");
     if (CurrentUserUtils.SelectedTool === InkTool.Write) {
@@ -791,7 +793,8 @@ export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
  **/
 
 ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boolean) {
-    InkTranscription.Instance?.createInkGroup();
+    createInkGroup();
+
     if (checkResult) {
         return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ?
             Colors.MEDIUM_BLUE : "transparent";
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 16a523b40..7a500ac88 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -255,7 +255,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             const removeSelection = (json: string | undefined) => json?.indexOf("\"storedMarks\"") === -1 ?
                 json?.replace(/"selection":.*/, "") : json?.replace(/"selection":"\"storedMarks\""/, "\"storedMarks\"");
 
-            if ([AclEdit, AclAdmin, AclSelfEdit].includes(effectiveAcl)) {
+            if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin || effectiveAcl === AclSelfEdit) {
                 const accumTags = [] as string[];
                 state.tr.doc.nodesBetween(0, state.doc.content.size, (node: any, pos: number, parent: any) => {
                     if (node.type === schema.nodes.dashField && node.attrs.fieldKey.startsWith("#")) {
@@ -382,11 +382,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         }
     }
 
-    // creates links between terms in a document and published documents (myPublishedDocs) that have titles starting with an '@'
+    // creates links between terms in a document and documents which have a matching Id
     hyperlinkTerm = (tr: any, target: Doc, newAutoLinks: Set<Doc>) => {
         const editorView = this._editorView;
         if (editorView && (editorView as any).docView && !Doc.AreProtosEqual(target, this.rootDoc)) {
-            const autoLinkTerm = StrCast(target.title).replace(/^@/, ""); 
+            const autoLinkTerm = StrCast(target.title).replace(/^@/, "");
             const flattened1 = this.findInNode(editorView, editorView.state.doc, autoLinkTerm);
             var alink: Doc | undefined;
             flattened1.forEach((flat, i) => {
@@ -495,7 +495,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 // embed document when dragg marked as embed
             } else if (de.embedKey) {
                 const target = dragData.droppedDocuments[0];
-                target._fitContentsToBox = true;
+                target._fitToBox = true;
                 const node = schema.nodes.dashDoc.create({
                     width: target[WidthSym](),
                     height: target[HeightSym](),
@@ -1411,9 +1411,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             const listNode = this._editorView?.state.doc.nodeAt(clickPos.pos);
             if (olistNode && olistNode.type === this._editorView?.state.schema.nodes.ordered_list && listNode) {
                 if (!highlightOnly) {
-                    if (selectOrderedList) {
+                    if (selectOrderedList || (!collapse && listNode.attrs.visibility)) {
                         this._editorView.dispatch(this._editorView.state.tr.setSelection(new NodeSelection(selectOrderedList ? $olistPos! : listPos!)));
-                    } else {
+                    } else if (!listNode.attrs.visibility || downNode === listNode) {
                         const tr = this._editorView.state.tr.setNodeMarkup(clickPos.pos, listNode.type, { ...listNode.attrs, visibility: !listNode.attrs.visibility });
                         this._editorView.dispatch(tr.setSelection(TextSelection.create(tr.doc, clickPos.pos)));
                     }
@@ -1504,9 +1504,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             this._rules!.EnteringStyle = false;
         }
         e.stopPropagation();
-        for (var i = state.selection.from; i <= state.selection.to; i++) {
+        for (var i = state.selection.from; i < state.selection.to; i++) {
             const node = state.doc.resolve(i);
-            if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark &&
+            if (node?.marks?.().some(mark => mark.type === schema.marks.user_mark &&
                 mark.attrs.userid !== Doc.CurrentUserEmail) &&
                 [AclAugment, AclSelfEdit].includes(GetEffectiveAcl(this.rootDoc))) {
                 e.preventDefault();
@@ -1523,7 +1523,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             case "Tab": e.preventDefault(); break;
             default: if (this._lastTimedMark?.attrs.userid === Doc.CurrentUserEmail) break;
             case " ":
-                [AclEdit, AclAdmin, AclSelfEdit].includes(GetEffectiveAcl(this.dataDoc)) && this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({}))
+                this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({}))
                     .addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
         }
         this.startUndoTypingBatch();
@@ -1557,7 +1557,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }
         }
     }
-    fitContentsToBox = () => this.props.Document._fitContentsToBox;
+    fitToBox = () => this.props.Document._fitToBox;
     sidebarContentScaling = () => (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
         if (!this.layoutDoc._showSidebar) this.toggleSidebar();
@@ -1632,7 +1632,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     ScreenToLocalTransform={this.sidebarScreenToLocal}
                     renderDepth={this.props.renderDepth + 1}
                     setHeight={this.setSidebarHeight}
-                    fitContentsToBox={this.fitContentsToBox}
+                    fitContentsToDoc={this.fitToBox}
                     noSidebar={true}
                     fieldKey={this.layoutDoc.sidebarViewType === "translation" ? `${this.fieldKey}-translation` : `${this.fieldKey}-annotations`} />;
         };
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index c66cb502e..fb49b0698 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -44,7 +44,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     }
 
     const canEdit = (state: any) => {
-        switch (GetEffectiveAcl(props.DataDoc)) {
+        switch (GetEffectiveAcl(props.Document)) {
             case AclAugment: return false;
             case AclSelfEdit:
                 for (var i = state.selection.from; i < state.selection.to; i++) {
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 1dd6fef9b..e32f2826f 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -10,16 +10,17 @@ import { InkTool } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
 import { PrefetchProxy } from "../../../../fields/Proxy";
 import { listSpec } from "../../../../fields/Schema";
+import { ScriptField } from "../../../../fields/ScriptField";
 import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
 import { emptyFunction, returnFalse, returnOne, returnTrue, setupMoveUpEvents } from '../../../../Utils';
 import { Docs } from "../../../documents/Documents";
 import { DocumentType } from "../../../documents/DocumentTypes";
 import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
 import { DocumentManager } from "../../../util/DocumentManager";
+import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { undoBatch, UndoManager } from "../../../util/UndoManager";
 import { CollectionDockingView } from "../../collections/CollectionDockingView";
-import { MarqueeViewBounds } from "../../collections/collectionFreeForm";
 import { CollectionView, CollectionViewType } from "../../collections/CollectionView";
 import { TabDocView } from "../../collections/TabDocView";
 import { ViewBoxBaseComponent } from "../../DocComponent";
@@ -29,21 +30,12 @@ import { CollectionFreeFormDocumentView } from "../CollectionFreeFormDocumentVie
 import { FieldView, FieldViewProps } from '../FieldView';
 import "./PresBox.scss";
 import { PresEffect, PresMovement, PresStatus } from "./PresEnums";
-import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
 
-export interface PinProps {
+export class PinProps {
     audioRange?: boolean;
+    unpin?: boolean;
     setPosition?: boolean;
     hidePresBox?: boolean;
-    pinWithView?: PinViewProps;
-    pinDocView?: boolean;  // whether the current view specs of the document should be saved the pinned document
-    panelWidth?: number;  // panel width and height of the document (used to compute the bounds of the pinned view area)
-    panelHeight?: number
-}
-
-export interface PinViewProps {
-  bounds: MarqueeViewBounds;
-  scale: number;
 }
 
 @observer
@@ -106,11 +98,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @observable private openMovementDropdown: boolean = false;
     @observable private openEffectDropdown: boolean = false;
     @observable private presentTools: boolean = false;
-    @computed get isTreeOrStack() {return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(StrCast(this.layoutDoc._viewType) as any) }
-    @computed get isTree() { return this.layoutDoc._viewType === CollectionViewType.Tree;}
-    @computed get presFieldKey() { return StrCast(this.layoutDoc.presFieldKey, "data"); }
-    @computed get childDocs() { return DocListCast(this.rootDoc[this.presFieldKey]); }
-    @observable _treeViewMap: Map<Doc, number> = new Map();
+    // @computed get childDocs() { return DocListCast(this.rootDoc[this.fieldKey]); }
+    // TODO: [AL] add childDocs() and treeMap fields
+    // @computed get childDocs() { return DocListCast(this.rootDoc.presentationaLinearizedDocuments); }
+    //_treeViewMap:Map<number[], Doc>
+    @computed get childDocs() { return this.layoutDoc._viewType === CollectionViewType.Tree ? DocListCast(this.rootDoc.presentationLinearizedDocuments) : DocListCast(this.rootDoc[this.fieldKey]); }
+    @observable _treeViewMap: Map<number, Doc> = new Map<number, Doc>();
 
     @computed get tagDocs() {
         const tagDocs: Doc[] = [];
@@ -139,6 +132,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             Doc.UserDoc().presElement = new PrefetchProxy(Docs.Create.PresElementBoxDocument({
                 title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"
             }));
+            // this script will be called by each presElement to get rendering-specific info that the PresBox knows about but which isn't written to the PresElement
+            // this is a design choice -- we could write this data to the presElements which would require a reaction to keep it up to date, and it would prevent
+            // the preselement docs from being part of multiple presentations since they would all have the same field, or we'd have to keep per-presentation data
+            // stored on each pres element.
+            (this.presElement as Doc).lookupField = ScriptField.MakeFunction("lookupPresBoxField(container, field, data)",
+                { field: "string", data: Doc.name, container: Doc.name });
         }
         this.props.Document.presentationFieldKey = this.fieldKey; // provide info to the presElement script so that it can look up rendering information about the presBox
 
@@ -158,10 +157,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
     @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
 
-    _unmounting = false;
     @action
     componentWillUnmount() {
-        this._unmounting = true;
         document.removeEventListener("keydown", PresBox.keyEventsWrapper, true);
         this._presKeyEventsActive = false;
         this.resetPresentation();
@@ -172,7 +169,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @action
     componentDidMount() {
-        this._unmounting = false;
+        this.rootDoc.presBox = this.rootDoc;
         this.rootDoc._forceRenderEngine = "timeline";
         this.layoutDoc.presStatus = PresStatus.Edit;
         this.layoutDoc._gridGap = 0;
@@ -331,40 +328,22 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     navigateToView = (targetDoc: Doc, activeItem: Doc) => {
         clearTimeout(this._navTimer);
         const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document;
-        if (bestTarget) console.log(bestTarget.title, bestTarget.type);
-        else console.log("no best target");
-        if (bestTarget)  this._navTimer = PresBox.navigateToDoc(bestTarget, activeItem, false);
-    }
-
-    // navigates to the bestTarget document by making sure it is on screen,
-    // then it applies the view specs stored in activeItem to 
-    @action
-    static navigateToDoc(bestTarget:Doc, activeItem:Doc, jumpToDoc:boolean)  {
-        if (bestTarget.type === DocumentType.PDF || bestTarget.type === DocumentType.WEB || bestTarget.type === DocumentType.RTF || bestTarget._viewType === CollectionViewType.Stacking) {
-            bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
-            bestTarget._scrollTop = activeItem.presPinViewScroll;
-        } else if (bestTarget.type === DocumentType.COMPARISON) {
-            bestTarget._clipWidth = activeItem.presPinClipWidth;
-        } else if ([DocumentType.AUDIO, DocumentType.VID].includes(bestTarget.type as any)) {
-            bestTarget._currentTimecode = activeItem.presStartTime;
-        } else {
-            const contentBounds= Cast(activeItem.contentBounds, listSpec("number"));
-            bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
-            if (contentBounds) {
-                bestTarget._panX = (contentBounds[0] + contentBounds[2])/2;
-                bestTarget._panY = (contentBounds[1] + contentBounds[3])/2;
-                const dv = DocumentManager.Instance.getDocumentView(bestTarget);
-                if (dv) {
-                    bestTarget._viewScale =  Math.min(dv.props.PanelHeight() / (contentBounds[3] - contentBounds[1]),
-                            dv.props.PanelWidth() / (contentBounds[2]- contentBounds[0]));
-                };
+        bestTarget && runInAction(() => {
+            if (bestTarget.type === DocumentType.PDF || bestTarget.type === DocumentType.WEB || bestTarget.type === DocumentType.RTF || bestTarget._viewType === CollectionViewType.Stacking) {
+                bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
+                bestTarget._scrollTop = activeItem.presPinViewScroll;
+            } else if (bestTarget.type === DocumentType.COMPARISON) {
+                bestTarget._clipWidth = activeItem.presPinClipWidth;
+            } else if (bestTarget.type === DocumentType.VID) {
+                bestTarget._currentTimecode = activeItem.presStartTime;
             } else {
+                bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
                 bestTarget._panX = activeItem.presPinViewX;
                 bestTarget._panY = activeItem.presPinViewY;
                 bestTarget._viewScale = activeItem.presPinViewScale;
             }
-        }
-        return setTimeout(() => bestTarget._viewTransition = undefined, activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 510);
+            this._navTimer = setTimeout(() => bestTarget._viewTransition = undefined, activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 510);
+        });
     }
 
     /**
@@ -422,17 +401,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             // openInTab(targetDoc);
         } else if (curDoc.presMovement === PresMovement.Pan && targetDoc) {
             LightboxView.SetLightboxDoc(undefined);
-            await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext ? [srcContext]:[], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right
+            await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, [srcContext], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right
         } else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) {
             LightboxView.SetLightboxDoc(undefined);
             //awaiting jump so that new scale can be found, since jumping is async     
-            await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext ? [srcContext]:[], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right    
+            await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, [srcContext], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right    
         }
         // After navigating to the document, if it is added as a presPinView then it will
         // adjust the pan and scale to that of the pinView when it was added.
         if (activeItem.presPinView) {
-            console.log(targetDoc.title);
-            console.log("presPinView in PresBox.tsx:420");
             // if targetDoc is not displayed but one of its aliases is, then we need to modify that alias, not the original target
             this.navigateToView(targetDoc, activeItem);
         }
@@ -644,17 +621,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
      * Called when the user changes the view type
      * Either 'List' (stacking) or 'Slides' (carousel)
      */
-    @undoBatch
+    // @undoBatch
     viewChanged = action((e: React.ChangeEvent) => {
         //@ts-ignore
         const viewType = e.target.selectedOptions[0].value as CollectionViewType;
-        this.layoutDoc.presFieldKey = this.fieldKey+(viewType === CollectionViewType.Tree ?"-linearized":"");
         // pivot field may be set by the user in timeline view (or some other way) -- need to reset it here
         [CollectionViewType.Tree || CollectionViewType.Stacking].includes(viewType) && (this.rootDoc._pivotField = undefined);
         this.rootDoc._viewType = viewType;
-        if (this.isTreeOrStack) {
-            this.layoutDoc._gridGap = 0;
-        }
+        if ([CollectionViewType.Tree || CollectionViewType.Stacking].includes(viewType)) this.layoutDoc._gridGap = 0;
     });
 
     /**
@@ -728,7 +702,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         });
         return true;
     }
-    childLayoutTemplate = () => !this.isTreeOrStack ? undefined : this.presElement;
+    childLayoutTemplate = () => ![CollectionViewType.Stacking, CollectionViewType.Tree].includes(this.rootDoc._viewType as any) ? undefined : this.presElement;
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc);
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
@@ -827,13 +801,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     //regular click
     @action
-    regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, selectPres = true) => {
+    regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean) => {
         this._selectedArray.clear();
         this._selectedArray.set(doc, undefined);
         this._eleArray.splice(0, this._eleArray.length, ref);
         this._dragArray.splice(0, this._dragArray.length, drag);
         focus && this.selectElement(doc);
-        selectPres && this.selectPres();
+        this.selectPres();
     }
 
     modifierSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, cmdClick: boolean, shiftClick: boolean) => {
@@ -1002,7 +976,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     /**
      * Method called for viewing paths which adds a single line with
      * points at the center of each document added.
-     * Design choice: When this is called it sets _fitContentsToBox as true so the
+     * Design choice: When this is called it sets _fitToBox as true so the
      * user can have an overview of all of the documents in the collection.
      * (Design needed for when documents in presentation trail are in another
      * collection)
@@ -1389,7 +1363,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     const scroll = targetDoc._scrollTop;
                                     activeItem.presPinView = true;
                                     activeItem.presPinViewScroll = scroll;
-                                } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
+                                } else if (targetDoc.type === DocumentType.VID) {
                                     activeItem.presStartTime = targetDoc._currentTimecode;
                                     activeItem.presEndTime = NumCast(targetDoc._currentTimecode) + 0.1;
                                 } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG) {
@@ -1412,7 +1386,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF) {
                                 const scroll = targetDoc._scrollTop;
                                 activeItem.presPinViewScroll = scroll;
-                            } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
+                            } else if (targetDoc.type === DocumentType.VID) {
                                 activeItem.presStartTime = targetDoc._currentTimecode;
                                 activeItem.presStartTime = NumCast(targetDoc._currentTimecode) + 0.1;
                             } else if (targetDoc.type === DocumentType.COMPARISON) {
@@ -1805,16 +1779,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 doc = Docs.Create.FreeformDocument([], { title: input ? input : "Blank slide", _width: 400, _height: 225, x: x, y: y });
                 break;
             case 'title':
-                doc = Docs.Create.FreeformDocument([title, subtitle], { title: input ? input : "Title slide", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([title, subtitle], { title: input ? input : "Title slide", _width: 400, _height: 225, _fitToBox: true, x: x, y: y });
                 break;
             case 'header':
-                doc = Docs.Create.FreeformDocument([header], { title: input ? input : "Section header", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([header], { title: input ? input : "Section header", _width: 400, _height: 225, _fitToBox: true, x: x, y: y });
                 break;
             case 'content':
-                doc = Docs.Create.FreeformDocument([contentTitle, content], { title: input ? input : "Title and content", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([contentTitle, content], { title: input ? input : "Title and content", _width: 400, _height: 225, _fitToBox: true, x: x, y: y });
                 break;
             case 'twoColumns':
-                doc = Docs.Create.FreeformDocument([contentTitle, content1, content2], { title: input ? input : "Title and two columns", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([contentTitle, content1, content2], { title: input ? input : "Title and two columns", _width: 400, _height: 225, _fitToBox: true, x: x, y: y });
                 break;
             default:
                 break;
@@ -2480,27 +2454,27 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         for (let i = 0; i < index.length; i++) {
             indexNum += (index[i] * (10 ** (-i)));
         }
-        if (this._treeViewMap.get(treeViewDoc) !== indexNum) {
-            this._treeViewMap.set(treeViewDoc, indexNum);
-            const sorted = this.sort(this._treeViewMap);
-            const curList = DocListCast(this.dataDoc[this.presFieldKey]);
-            if (sorted.length !== curList.length || sorted.some((doc,ind) => doc !== curList[ind])) {
-                this.dataDoc[this.presFieldKey] = new List<Doc>(sorted); // this is a flat array of Docs
-            }
-        }
+        this._treeViewMap.set(indexNum, treeViewDoc);
+        this.props.Document.presentationLinearizedDocuments = new List<Doc>(this.sort(this._treeViewMap)); // this is a flat array of Docs
         return this.childDocs;
     }
 
     RemFromMap = (treeViewDoc: Doc, index: number[]): Doc[] => {
-        if (!this._unmounting && this.isTree) {
-            this._treeViewMap.delete(treeViewDoc);
-            this.dataDoc[this.presFieldKey] = new List<Doc>(this.sort(this._treeViewMap));
+        var indexNum = 0;
+        for (let i = 0; i < index.length; i++) {
+            indexNum += (index[i] * (10 ** (-i)));
+        }
+        console.log(String(index), treeViewDoc)
+        this._treeViewMap.delete(indexNum);
+        const overlay = (Doc.UserDoc().myOverlayDocs as Doc);
+        if (!DocListCast(overlay[Doc.LayoutFieldKey(overlay)]).includes(this.rootDoc)) {
+            this.props.Document.presentationLinearizedDocuments = new List<Doc>(this.sort(this._treeViewMap));
         }
         return this.childDocs;
     }
 
     // TODO: [AL] implement sort function for an array of numbers (e.g. arr[1,2,4] v arr[1,2,1])
-    sort = (treeViewMap: Map<Doc, number>) => [...treeViewMap.entries()].sort((a: [Doc, number], b: [Doc, number]) => a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0).map(kv => kv[0]);
+    sort = (treeViewMap: Map<number, Doc>) => [...treeViewMap.entries()].sort().map(kv => kv[1]);
 
     render() {
         // calling this method for keyEvents
@@ -2512,7 +2486,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const presEnd: boolean = !this.layoutDoc.presLoop && (this.itemIndex === this.childDocs.length - 1);
         const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0);
         return CurrentUserUtils.OverlayDocs.includes(this.rootDoc) ?
-            <div className="miniPres" onClick={e => e.stopPropagation()}>
+            <div className="miniPres">
                 <div className="presPanelOverlay" style={{ display: "inline-flex", height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}>
                     <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : undefined }}
                         onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, () => this.layoutDoc.presLoop = !this.layoutDoc.presLoop, false, false)}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
@@ -2556,8 +2530,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 removeDocument={returnFalse}
                                 dontRegisterView={true}
                                 focus={this.selectElement}
-                                scriptContext={this}
                                 ScreenToLocalTransform={this.getTransform}
+                                // TODO: [AL] implement AddToMap, RemFromMap functions (outside this)
                                 AddToMap={this.AddToMap}
                                 RemFromMap={this.RemFromMap}
                                 hierarchyIndex={[]}
@@ -2576,10 +2550,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             </div>;
     }
 }
-
-ScriptingGlobals.add(function navigateToDoc(bestTarget:Doc, activeItem:Doc) {
-    const srcContext = Cast(bestTarget.context, Doc, null) ?? Cast(Cast(bestTarget.annotationOn, Doc, null)?.context, Doc, null);
-    const openInTab = (doc: Doc, finished?: () => void) => {CollectionDockingView.AddSplit(doc, "right"); finished?.(); };
-    DocumentManager.Instance.jumpToDocument(bestTarget, true, openInTab, srcContext ? [srcContext]:[], 
-        undefined, undefined, undefined, () => PresBox.navigateToDoc(bestTarget, activeItem, true), undefined, true, NumCast(activeItem.presZoom));
+// this func communicates with PresBoxElement to send information of the doc
+ScriptingGlobals.add(function lookupPresBoxField(presBoxDoc: Doc, field: string, presEleDoc: Doc) {
+    if (field === 'indexInPres') return DocListCast(presBoxDoc._viewType === CollectionViewType.Tree ? presBoxDoc.presentationLinearizedDocuments : presBoxDoc[StrCast(presBoxDoc.presentationFieldKey)]).indexOf(presEleDoc);
+    if (field === 'presCollapsedHeight') return [CollectionViewType.Tree || CollectionViewType.Stacking].includes(presBoxDoc._viewType as any) ? 35 : 31;
+    if (field === 'presStatus') return presBoxDoc.presStatus;
+    if (field === '_itemIndex') return presBoxDoc._itemIndex;
+    if (field === 'presBox') return presBoxDoc;
+    return undefined;
 });
\ No newline at end of file
diff --git a/src/client/views/nodes/trails/PresElementBox.scss b/src/client/views/nodes/trails/PresElementBox.scss
index 969f034a8..789a6d9f3 100644
--- a/src/client/views/nodes/trails/PresElementBox.scss
+++ b/src/client/views/nodes/trails/PresElementBox.scss
@@ -5,157 +5,155 @@ $slide-background: #d5dce2;
 $slide-active: #5B9FDD;
 
 .presItem-container {
-      cursor: grab;
-      display: flex;
-      grid-template-columns: 20px auto;
-      font-family: Roboto;
-      letter-spacing: normal;
-      position: relative;
-      pointer-events: all;
-      width: 100%;
-      height: 100%;
-      font-weight: 400;
-      -webkit-touch-callout: none;
-      -webkit-user-select: none;
-      -khtml-user-select: none;
-      -moz-user-select: none;
-      -ms-user-select: none;
-      user-select: none;
-      align-items: center;
+    cursor: grab;
+    display: grid;
+    grid-template-columns: 20px auto;
+    font-family: Roboto;
+    letter-spacing: normal;
+    position: relative;
+    pointer-events: all;
+    width: 100%;
+    height: 100%;
+    font-weight: 400;
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+    align-items: center;
 
-      //     .presItem-number {
-      //         margin-top: 3.5px;
-      //         font-size: 12px;
-      //         font-weight: 700;
-      //         text-align: center;
-      //         justify-self: center;
-      //         align-self: flex-start;
-      //         position: relative;
-      //         display: inline-block;
-      //         overflow: hidden;
-      //     }
+    .presItem-number {
+        margin-top: 3.5px;
+        font-size: 12px;
+        font-weight: 700;
+        text-align: center;
+        justify-self: center;
+        align-self: flex-start;
+        position: relative;
+        display: inline-block;
+        overflow: hidden;
+    }
 
 }
 
 .presItem-slide {
-      position: relative;
-      height: 100%;
-      width: 100%;
-      border-bottom: .5px solid grey;
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      grid-template-rows: 16px 10px auto;
-      grid-template-columns: max-content max-content max-content max-content auto;
+    position: relative;
+    background-color: #d5dce2;
+    border-radius: 5px;
+    height: calc(100% - 7px);
+    width: 100%;
+    display: grid;
+    grid-template-rows: 16px 10px auto;
+    grid-template-columns: max-content max-content max-content max-content auto;
 
-      .presItem-name {
-            display: flex;
-            min-width: 20px;
-            z-index: 300;
-            top: 2px;
-            align-self: center;
-            font-size: 11px;
-            font-family: Roboto;
-            font-weight: 500;
-            position: relative;
-            padding-left: 10px;
-            padding-right: 10px;
-            letter-spacing: normal;
-            width: max-content;
-            text-overflow: ellipsis;
-            overflow: hidden;
-            white-space: pre;
-      }
+    .presItem-name {
+        min-width: 20px;
+        z-index: 300;
+        top: 2px;
+        align-self: center;
+        font-size: 11px;
+        font-family: Roboto;
+        font-weight: 500;
+        position: relative;
+        padding-left: 10px;
+        padding-right: 10px;
+        letter-spacing: normal;
+        width: max-content;
+        text-overflow: ellipsis;
+        overflow: hidden;
+        white-space: pre;
+    }
 
-      .presItem-docName {
-            min-width: 20px;
-            z-index: 300;
-            align-self: center;
-            font-size: 9px;
-            font-family: Roboto;
-            font-weight: 300;
-            position: relative;
-            padding-left: 10px;
-            padding-right: 10px;
-            letter-spacing: normal;
-            width: max-content;
-            text-overflow: ellipsis;
-            overflow: hidden;
-            white-space: pre;
-            grid-row: 2;
-            grid-column: 1/6;
-      }
+    .presItem-docName {
+        min-width: 20px;
+        z-index: 300;
+        align-self: center;
+        font-size: 9px;
+        font-family: Roboto;
+        font-weight: 300;
+        position: relative;
+        padding-left: 10px;
+        padding-right: 10px;
+        letter-spacing: normal;
+        width: max-content;
+        text-overflow: ellipsis;
+        overflow: hidden;
+        white-space: pre;
+        grid-row: 2;
+        grid-column: 1/6;
+    }
 
-      .presItem-time {
-            align-self: center;
-            position: relative;
-            padding-right: 10px;
-            top: 1px;
-            font-size: 10;
-            font-weight: 300;
-            font-family: Roboto;
-            z-index: 300;
-            letter-spacing: normal;
-      }
+    .presItem-time {
+        align-self: center;
+        position: relative;
+        padding-right: 10px;
+        top: 1px;
+        font-size: 10;
+        font-weight: 300;
+        font-family: Roboto;
+        z-index: 300;
+        letter-spacing: normal;
+    }
 
-      .presItem-embedded {
-            overflow: hidden;
-            grid-row: 3;
-            grid-column: 1/8;
-            position: relative;
-            display: flex;
-            width: auto;
-            justify-content: center;
-            margin: auto;
-            margin-bottom: 2px;
-            border-bottom-left-radius: 5px;
-            border-bottom-right-radius: 5px;
-      }
+    .presItem-embedded {
+        overflow: hidden;
+        grid-row: 3;
+        grid-column: 1/8;
+        position: relative;
+        display: flex;
+        width: auto;
+        justify-content: center;
+        margin: auto;
+        margin-bottom: 2px;
+        border-bottom-left-radius: 5px;
+        border-bottom-right-radius: 5px;
+    }
 
-      .presItem-embeddedMask {
-            width: 100%;
-            height: 100%;
-            position: absolute;
-            border-radius: 3px;
-            top: 0;
-            left: 0;
-            z-index: 1;
-            overflow: hidden;
-      }
+    .presItem-embeddedMask {
+        width: 100%;
+        height: 100%;
+        position: absolute;
+        border-radius: 3px;
+        top: 0;
+        left: 0;
+        z-index: 1;
+        overflow: hidden;
+    }
 
 
-      .presItem-slideButtons {
-            display: flex;
-            grid-column: 7;
-            grid-row: 1/3;
-            width: max-content;
-            justify-self: right;
-            justify-content: flex-end;
+    .presItem-slideButtons {
+        display: flex;
+        grid-column: 7;
+        grid-row: 1/3;
+        width: max-content;
+        justify-self: right;
+        justify-content: flex-end;
 
-            .slideButton {
-                  cursor: pointer;
-                  position: relative;
-                  border-radius: 100px;
-                  z-index: 300;
-                  width: 18px;
-                  height: 18px;
-                  display: flex;
-                  font-size: 12px;
-                  justify-self: center;
-                  align-self: center;
-                  background-color: rgba(0, 0, 0, 0.5);
-                  color: white;
-                  justify-content: center;
-                  align-items: center;
-                  transition: 0.2s;
-                  margin-right: 3px;
-            }
+        .slideButton {
+            cursor: pointer;
+            position: relative;
+            border-radius: 100px;
+            z-index: 300;
+            width: 18px;
+            height: 18px;
+            display: flex;
+            font-size: 12px;
+            justify-self: center;
+            align-self: center;
+            background-color: rgba(0, 0, 0, 0.5);
+            color: white;
+            justify-content: center;
+            align-items: center;
+            transition: 0.2s;
+            margin-right: 3px;
+        }
 
-            .slideButton:hover {
-                  background-color: rgba(0, 0, 0, 1);
-                  transform: scale(1.2);
-            }
-      }
+        .slideButton:hover {
+            background-color: rgba(0, 0, 0, 1);
+            transform: scale(1.2);
+        }
+    }
 }
 
 // .presItem-slide:hover {
@@ -194,7 +192,7 @@ $slide-active: #5B9FDD;
 // }
 
 .presItem-slide.active {
-    box-shadow: 0 0 0px 2.5px $dark-blue;
+    box-shadow: 0 0 0px 1.5px $dark-blue;
 }
 
 .presItem-slide.group {
@@ -203,7 +201,7 @@ $slide-active: #5B9FDD;
 
 .presItem-slide.activeGroup {
     border-radius: 5px;
-    box-shadow: 0 0 0px 2.5px $dark-blue;
+    box-shadow: 0 0 0px 1.5px $dark-blue;
 }
 
 .presItem-groupSlideContainer {
@@ -239,38 +237,38 @@ $slide-active: #5B9FDD;
 }
 
 .presItem-multiDrag {
-      font-family: Roboto;
-      font-weight: 600;
-      color: white;
-      text-align: center;
-      justify-content: center;
-      align-content: center;
-      width: 100px;
-      height: 30px;
-      position: absolute;
-      background-color: $dark-blue;
-      z-index: 4000;
-      border-radius: 10px;
-      box-shadow: black 0.4vw 0.4vw 0.8vw;
-      line-height: 30px;
+    font-family: Roboto;
+    font-weight: 600;
+    color: white;
+    text-align: center;
+    justify-content: center;
+    align-content: center;
+    width: 100px;
+    height: 30px;
+    position: absolute;
+    background-color: $dark-blue;
+    z-index: 4000;
+    border-radius: 10px;
+    box-shadow: black 0.4vw 0.4vw 0.8vw;
+    line-height: 30px;
 }
 
 .presItem-miniSlide {
-      font-weight: 700;
-      font-size: 12;
-      grid-column: 1/8;
-      align-self: center;
-      justify-self: center;
-      background-color: #d5dce2;
-      width: 26px;
-      text-align: center;
-      height: 26px;
-      line-height: 28px;
-      border-radius: 100%;
+    font-weight: 700;
+    font-size: 12;
+    grid-column: 1/8;
+    align-self: center;
+    justify-self: center;
+    background-color: #d5dce2;
+    width: 26px;
+    text-align: center;
+    height: 26px;
+    line-height: 28px;
+    border-radius: 100%;
 }
 
 .presItem-miniSlide.active {
-      box-shadow: 0 0 0px 1.5px $dark-blue;
+    box-shadow: 0 0 0px 1.5px $dark-blue;
 }
 
 .expandButton {
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index 50df00612..bbabcb2de 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -2,18 +2,17 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { Tooltip } from "@material-ui/core";
 import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
 import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../../fields/Doc";
+import { DataSym, Doc, DocListCast, Opt } from "../../../../fields/Doc";
 import { Id } from "../../../../fields/FieldSymbols";
 import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
 import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from "../../../../Utils";
-import { Docs, DocUtils } from "../../../documents/Documents";
+import { DocUtils } from "../../../documents/Documents";
 import { DocumentType } from "../../../documents/DocumentTypes";
 import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
 import { DocumentManager } from "../../../util/DocumentManager";
 import { DragManager } from "../../../util/DragManager";
 import { Transform } from "../../../util/Transform";
 import { undoBatch } from "../../../util/UndoManager";
-import { CollectionViewType } from "../../collections/CollectionView";
 import { ViewBoxBaseComponent } from '../../DocComponent';
 import { EditableView } from "../../EditableView";
 import { Colors } from "../../global/globalEnums";
@@ -24,7 +23,7 @@ import { PresBox } from "./PresBox";
 import "./PresElementBox.scss";
 import { PresMovement } from "./PresEnums";
 import React = require("react");
-import { List } from "../../../../fields/List";
+import { CollectionViewType } from "../../collections/CollectionView";
 /**
  * This class models the view a document added to presentation will have in the presentation.
  * It involves some functionality for its buttons and options.
@@ -35,10 +34,12 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     _heightDisposer: IReactionDisposer | undefined;
 
     @observable _dragging = false;
-    @computed get indexInPres() { return DocListCast(this.presBox[StrCast(this.presBox.presFieldKey, "data")]).indexOf(this.rootDoc); }  // the index field is where this document is in the presBox display list (since this value is different for each presentation element, the value can't be stored on the layout template which is used by all display elements)
-    @computed get collapsedHeight() { return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(this.presBox._viewType as any) ? 35 : 31; } // the collapsed height changes depending on the state of the presBox.  We could store this on the presentation element template if it's used by only one presentation - but if it's shared by multiple, then this value must be looked up
-    @computed get presStatus() { return this.presBox.presStatus; }
-    @computed get presBox() { return (this.props.DocumentView?.().props.treeViewDoc ?? this.props.ContainingCollectionDoc)!; }
+    // these fields are conditionally computed fields on the layout document that take this document as a parameter
+    @computed get indexInPres() { return Number(this.lookupField("indexInPres")); }  // the index field is where this document is in the presBox display list (since this value is different for each presentation element, the value can't be stored on the layout template which is used by all display elements)
+    @computed get collapsedHeight() { return Number(this.lookupField("presCollapsedHeight")); } // the collapsed height changes depending on the state of the presBox.  We could store this on the presentation element template if it's used by only one presentation - but if it's shared by multiple, then this value must be looked up
+    @computed get presStatus() { return StrCast(this.lookupField("presStatus")); }
+    @computed get itemIndex() { return NumCast(this.lookupField("_itemIndex")); }
+    @computed get presBox() { return Cast(this.lookupField("presBox"), Doc, null); }
     @computed get targetDoc() { return Cast(this.rootDoc.presentationTargetDoc, Doc, null) || this.rootDoc; }
 
     componentDidMount() {
@@ -76,8 +77,8 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return !this.rootDoc.presExpandInlineButton || !this.targetDoc ? (null) :
             <div className="presItem-embedded" style={{ height: this.embedHeight(), width: this.embedWidth() }}>
                 <DocumentView
-                    Document={this.rootDoc}
-                    DataDoc={undefined}//this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
+                    Document={this.targetDoc}
+                    DataDoc={this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
                     styleProvider={this.styleProvider}
                     docViewPath={returnEmptyDoclist}
                     rootSelected={returnTrue}
@@ -86,7 +87,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     isContentActive={this.props.isContentActive}
                     addDocTab={returnFalse}
                     pinToPres={returnFalse}
-                    fitContentsToBox={returnTrue}
+                    fitContentsToDoc={returnTrue}
                     PanelWidth={this.embedWidth}
                     PanelHeight={this.embedHeight}
                     ScreenToLocalTransform={Transform.Identity}
@@ -166,11 +167,11 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         e.preventDefault();
         if (element && !(e.ctrlKey || e.metaKey)) {
             if (PresBox.Instance._selectedArray.has(this.rootDoc)) {
-                PresBox.Instance._selectedArray.size === 1 && PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false);
+                PresBox.Instance._selectedArray.size === 1 && PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false);
                 setupMoveUpEvents(this, e, this.startDrag, emptyFunction, emptyFunction);
             } else {
                 setupMoveUpEvents(this, e, ((e: PointerEvent) => {
-                    PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false);
+                    PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false);
                     return this.startDrag(e);
                 }), emptyFunction, emptyFunction);
             }
@@ -196,7 +197,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         dragData.moveDocument = this.props.docViewPath().lastElement()?.props.moveDocument;
         const dragItem: HTMLElement[] = [];
         if (dragArray.length === 1) {
-            const doc = this._itemRef.current || dragArray[0];
+            const doc = dragArray[0];
             doc.className = miniView ? "presItem-miniSlide" : "presItem-slide";
             dragItem.push(doc);
         } else if (dragArray.length >= 1) {
@@ -260,12 +261,11 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @undoBatch
     removeItem = action((e: React.MouseEvent) => {
-      e.stopPropagation();
         this.props.removeDocument?.(this.rootDoc);
         if (PresBox.Instance._selectedArray.has(this.rootDoc)) {
             PresBox.Instance._selectedArray.delete(this.rootDoc);
         }
-        this.removeAllRecordingInOverlay() 
+        e.stopPropagation();
     });
 
     // set the value/title of the individual pres element
@@ -285,230 +285,196 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     @action
     updateView = (targetDoc: Doc, activeItem: Doc) => {
-        switch (targetDoc.type) {
-            case DocumentType.PDF: case  DocumentType.WEB: case DocumentType.RTF :
-                const scroll = targetDoc._scrollTop;
-                activeItem.presPinViewScroll = scroll;
-            break;
-            case DocumentType.VID: case DocumentType.AUDIO:
-                activeItem.presStartTime = targetDoc._currentTimecode;
-            break;
-            case  DocumentType.COMPARISON :
-                const clipWidth = targetDoc._clipWidth;
-                activeItem.presPinClipWidth = clipWidth;
-            break;
-            default:
-                const x = targetDoc._panX;
-                const y = targetDoc._panY;
-                const scale = targetDoc._viewScale;
-                activeItem.presPinViewX = x;
-                activeItem.presPinViewY = y;
-                activeItem.presPinViewScale = scale;
+        if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF) {
+            const scroll = targetDoc._scrollTop;
+            activeItem.presPinViewScroll = scroll;
+        } else if (targetDoc.type === DocumentType.VID) {
+            activeItem.presPinTimecode = targetDoc._currentTimecode;
+        } else if (targetDoc.type === DocumentType.COMPARISON) {
+            const clipWidth = targetDoc._clipWidth;
+            activeItem.presPinClipWidth = clipWidth;
+        } else {
+            const x = targetDoc._panX;
+            const y = targetDoc._panY;
+            const scale = targetDoc._viewScale;
+            activeItem.presPinViewX = x;
+            activeItem.presPinViewY = y;
+            activeItem.presPinViewScale = scale;
         }
     }
 
-      @computed get recordingIsInOverlay() {
-            let isInOverlay = false
-            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
-                  if (doc.slides === this.rootDoc) {
-                        isInOverlay = true
-                        return
-                  }
-            })
-            return isInOverlay
-      }
-
-      removeAllRecordingInOverlay = () => {
-            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
-                  if (doc.slides === this.rootDoc) {
-                        Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
-                  }
-            })
-      }
-
-      @undoBatch
-      @action
-      hideRecording = (e: React.MouseEvent) => {
-            e.stopPropagation()
-            this.removeAllRecordingInOverlay()
-      }
-
-      @undoBatch
-      @action
-      showRecording = (activeItem: Doc) => {
-            this.removeAllRecordingInOverlay()
-            if (activeItem.recording) {
-                  // if we already have an existing recording
-                  Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, Cast(activeItem.recording, Doc, null));
-
-            }
-      }
-
-      @undoBatch
-      @action
-      startRecording = (activeItem: Doc) => {
-            // Remove every recording that already exists in overlay view
-            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
-                  if (doc.slides !== null) {
-                        Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
-                  }
-            })
-
-            if (activeItem.recording) {
-                  // if we already have an existing recording
-                  Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, Cast(activeItem.recording, Doc, null));
-
-            } else {
-                  // if we dont have any recording
-                  const recording = Docs.Create.WebCamDocument("", {
-                        _width: 400, _height: 200,
-                        // hideDocumentButtonBar: true,
-                        hideDecorationTitle: true,
-                        hideOpenButton: true,
-                        // hideDeleteButton: true,
-                        cloneFieldFilter: new List<string>(["system"])
-                  });
-
-                  // attach the recording to the slide, and attach the slide to the recording
-                  recording.slides = activeItem
-                  activeItem.recording = recording
-
-                  // make recording box appear in the bottom right corner of the screen
-                  recording.x = window.innerWidth - recording[WidthSym]() - 20;
-                  recording.y = window.innerHeight - recording[HeightSym]() - 20;
-                  Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, recording);
-            }
-      }
-
-      @computed
-      get toolbarWidth(): number {
-            const presBoxDocView = DocumentManager.Instance.getDocumentView(this.presBox);
-            let width: number = NumCast(this.presBox._width);
-            if (presBoxDocView) width = presBoxDocView.props.PanelWidth();
-            if (width === 0) width = 300;
-            return width;
-      }
+    @computed
+    get toolbarWidth(): number {
+        const presBoxDocView = DocumentManager.Instance.getDocumentView(this.presBox);
+        let width: number = NumCast(this.presBox._width);
+        if (presBoxDocView) width = presBoxDocView.props.PanelWidth();
+        if (width === 0) width = 300;
+        return width;
+    }
 
-      @computed get mainItem() {
-            const isSelected: boolean = PresBox.Instance?._selectedArray.has(this.rootDoc);
-            const toolbarWidth: number = this.toolbarWidth;
-            const showMore: boolean = this.toolbarWidth >= 300;
-            const miniView: boolean = this.toolbarWidth <= 110;
-            const presBox: Doc = this.presBox; //presBox
-            const presBoxColor: string = StrCast(presBox._backgroundColor);
-            const presColorBool: boolean = presBoxColor ? (presBoxColor !== Colors.WHITE && presBoxColor !== "transparent") : false;
-            const targetDoc: Doc = this.targetDoc;
-            const activeItem: Doc = this.rootDoc;
-            return (
-                  <div className={`presItem-container`}
-                        key={this.props.Document[Id] + this.indexInPres}
-                        ref={this._itemRef}
+    @computed get mainItem() {
+        const isSelected: boolean = PresBox.Instance._selectedArray.has(this.rootDoc);
+        const toolbarWidth: number = this.toolbarWidth;
+        const showMore: boolean = this.toolbarWidth >= 300;
+        const miniView: boolean = this.toolbarWidth <= 110;
+        const presBox: Doc = this.presBox; //presBox
+        const presBoxColor: string = StrCast(presBox._backgroundColor);
+        const presColorBool: boolean = presBoxColor ? (presBoxColor !== Colors.WHITE && presBoxColor !== "transparent") : false;
+        const targetDoc: Doc = this.targetDoc;
+        const activeItem: Doc = this.rootDoc;
+        const isGroup: boolean = BoolCast(targetDoc._isGroup);
+        return (
+            <div className={`presItem-container`}
+                key={this.props.Document[Id] + this.indexInPres}
+                ref={this._itemRef}
+                style={{
+                    backgroundColor: presColorBool ? isSelected ? "rgba(250,250,250,0.3)" : "transparent" : isSelected ? Colors.LIGHT_BLUE : "transparent",
+                    opacity: this._dragging ? 0.3 : 1
+                }}
+                onClick={e => {
+                    e.stopPropagation();
+                    e.preventDefault();
+                    PresBox.Instance.modifierSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
+                }}
+                onDoubleClick={action(e => {
+                    if (isGroup) {
+                        this.rootDoc.presExpandGroup = !this.rootDoc.presExpandGroup;
+                    } else {
+                        this.toggleProperties();
+                        PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, true);
+                    }
+                })}
+                onPointerOver={this.onPointerOver}
+                onPointerLeave={this.onPointerLeave}
+                onPointerDown={this.headerDown}
+                onPointerUp={this.headerUp}
+            >
+                {miniView ?
+                    // when width is LESS than 110 px 
+                    <div className={`presItem-miniSlide ${isSelected ? "active" : ""}`} ref={miniView ? this._dragRef : null}>
+                        {`${this.indexInPres + 1}.`}
+                    </div>
+                    :
+                    // when width is MORE than 110 px 
+                    <div className="presItem-number">
+                        {`${this.indexInPres + 1}.`}
+                    </div>}
+                {isGroup ?
+                    <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "activeGroup" : "group"}`}
                         style={{
-                              backgroundColor: presColorBool ? isSelected ? "rgba(250,250,250,0.3)" : "transparent" : isSelected ? Colors.LIGHT_BLUE : "transparent",
-                              opacity: this._dragging ? 0.3 : 1
-                        }}
-                        onClick={e => {
-                              e.stopPropagation();
-                              e.preventDefault();
-                              PresBox.Instance.modifierSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
-                              this.showRecording(activeItem);
-                        }}
-                        onDoubleClick={action(e => {
-                              this.toggleProperties();
-                              PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, true);
-                        })}
-                        onPointerOver={this.onPointerOver}
-                        onPointerLeave={this.onPointerLeave}
-                        onPointerDown={this.headerDown}
-                        onPointerUp={this.headerUp}
-                  >
-                        {/* {miniView ?
-                              // when width is LESS than 110 px 
-                              <div className={`presItem-miniSlide ${isSelected ? "active" : ""}`} ref={miniView ? this._dragRef : null}>
-                                    {`${this.indexInPres + 1}.`}
-                              </div>
-                              :
-                              // when width is MORE than 110 px 
-                              <div className="presItem-number">
-                                    {`${this.indexInPres + 1}.`}
-                              </div>} */}
-                        {/* <div className="presItem-number">
-                              {`${this.indexInPres + 1}.`}
-                        </div> */}
-                        {miniView ? (null) : <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "active" : ""}`}
-                              style={{
-                                    backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
-                                    boxShadow: presBoxColor && presBoxColor !== "white" && presBoxColor !== "transparent" ? isSelected ? "0 0 0px 1.5px" + presBoxColor : undefined : undefined
-                              }}>
-                              <div className="presItem-name" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105, cursor: isSelected ? 'text' : 'grab' }}>
-                                    <div>{`${this.indexInPres + 1}.  `}</div>
-                                    <EditableView
-                                          ref={this._titleRef}
-                                          editing={!isSelected ? false : undefined}
-                                          contents={activeItem.title}
-                                          overflow={'ellipsis'}
-                                          GetValue={() => StrCast(activeItem.title)}
-                                          SetValue={this.onSetValue}
-                                    />
-                              </div>
-                              {/* <Tooltip title={<><div className="dash-tooltip">{"Movement speed"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.transition}</div></Tooltip> */}
-                              {/* <Tooltip title={<><div className="dash-tooltip">{"Duration"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.duration}</div></Tooltip> */}
-                              <div className={"presItem-slideButtons"}>
-                                    <Tooltip title={<><div className="dash-tooltip">{"Update view"}</div></>}>
-                                          <div className="slideButton"
-                                                onClick={() => this.updateView(targetDoc, activeItem)}
-                                                style={{ fontWeight: 700, display: activeItem.presPinView ? "flex" : "none" }}>V</div>
-                                    </Tooltip>
-
-                                    {this.recordingIsInOverlay ?
-                                          <Tooltip title={<><div className="dash-tooltip">{"Hide Recording"}</div></>}>
-                                                <div className="slideButton"
-                                                      onClick={this.hideRecording}
-                                                      style={{ fontWeight: 700 }}>
-                                                      <FontAwesomeIcon icon={"video-slash"} onPointerDown={e => e.stopPropagation()} />
-                                                </div>
-                                          </Tooltip> :
-                                          <Tooltip title={<><div className="dash-tooltip">{"Start recording"}</div></>}>
-                                                <div className="slideButton"
-                                                      onClick={() => this.startRecording(activeItem)}
-                                                      style={{ fontWeight: 700 }}>
-                                                      <FontAwesomeIcon icon={"video"} onPointerDown={e => e.stopPropagation()} />
-                                                </div>
-                                          </Tooltip>
-                                    }
-
-
-                                    {/* {this.indexInPres === 0 ? (null) : <Tooltip title={<><div className="dash-tooltip">{activeItem.groupWithUp ? "Ungroup" : "Group with up"}</div></>}>
-                                          <div className="slideButton"
-                                                onClick={() => activeItem.groupWithUp = !activeItem.groupWithUp}
-                                                style={{
-                                                      zIndex: 1000 - this.indexInPres,
-                                                      fontWeight: 700,
-                                                      backgroundColor: activeItem.groupWithUp ? presColorBool ? presBoxColor : Colors.MEDIUM_BLUE : undefined,
-                                                      height: activeItem.groupWithUp ? 53 : 18,
-                                                      transform: activeItem.groupWithUp ? "translate(0, -17px)" : undefined
-                                                }}>
-                                                <div style={{ transform: activeItem.groupWithUp ? "rotate(180deg) translate(0, -17.5px)" : "rotate(0deg)" }}>
-                                                      <FontAwesomeIcon icon={"arrow-up"} onPointerDown={e => e.stopPropagation()} />
-                                                </div>
-                                          </div>
-                                    </Tooltip>} */}
-                                <Tooltip title={<><div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? "Minimize" : "Expand"}</div></>}><div className={"slideButton"} onClick={e => { e.stopPropagation(); this.presExpandDocumentClick(); }}>
-                                    <FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? "eye-slash" : "eye"} onPointerDown={e => e.stopPropagation()} />
-                                </div></Tooltip>
-                                    <Tooltip title={<><div className="dash-tooltip">{"Remove from presentation"}</div></>}><div
-                                          className={"slideButton"}
-                                          onClick={this.removeItem}>
-                                          <FontAwesomeIcon icon={"trash"} onPointerDown={e => e.stopPropagation()} />
-                                    </div></Tooltip>
-                              </div>
-                              {/* <div className="presItem-docName" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105 }}>{activeItem.presPinView ? (<><i>View of </i> {targetDoc.title}</>) : targetDoc.title}</div> */}
-                              {this.renderEmbeddedInline}
-                        </div>}
-                  </div >);
-      }
+                            backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
+                            boxShadow: presBoxColor && presBoxColor !== "white" && presBoxColor !== "transparent" ? isSelected ? "0 0 0px 1.5px" + presBoxColor : undefined : undefined
+                        }}>
+                        <div className="presItem-name" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105, cursor: isSelected ? 'text' : 'grab' }}>
+                            <EditableView
+                                ref={this._titleRef}
+                                editing={!isSelected ? false : undefined}
+                                contents={activeItem.title}
+                                overflow={'ellipsis'}
+                                GetValue={() => StrCast(activeItem.title)}
+                                SetValue={this.onSetValue}
+                            />
+                        </div>
+                        <div className={"presItem-slideButtons"}>
+                            <Tooltip title={<><div className="dash-tooltip">{"Update view"}</div></>}>
+                                <div className="slideButton"
+                                    onClick={() => this.updateView(targetDoc, activeItem)}
+                                    style={{ fontWeight: 700, display: activeItem.presPinView ? "flex" : "none" }}>V</div>
+                            </Tooltip>
+                            {this.indexInPres === 0 ? (null) : <Tooltip title={<><div className="dash-tooltip">{activeItem.groupWithUp ? "Ungroup" : "Group with up"}</div></>}>
+                                <div className="slideButton"
+                                    onClick={() => activeItem.groupWithUp = !activeItem.groupWithUp}
+                                    style={{
+                                        zIndex: 1000 - this.indexInPres,
+                                        fontWeight: 700,
+                                        backgroundColor: activeItem.groupWithUp ? presColorBool ? presBoxColor : Colors.MEDIUM_BLUE : undefined,
+                                        height: activeItem.groupWithUp ? 53 : 18,
+                                        transform: activeItem.groupWithUp ? "translate(0, -17px)" : undefined
+                                    }}>
+                                    <div style={{ transform: activeItem.groupWithUp ? "rotate(180deg) translate(0, -17.5px)" : "rotate(0deg)" }}>
+                                        <FontAwesomeIcon icon={"arrow-up"} onPointerDown={e => e.stopPropagation()} />
+                                    </div>
+                                </div>
+                            </Tooltip>}
+                            <Tooltip title={<><div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? "Minimize" : "Expand"}</div></>}><div className={"slideButton"} onClick={e => { e.stopPropagation(); this.presExpandDocumentClick(); }}>
+                                <FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? "eye-slash" : "eye"} onPointerDown={e => e.stopPropagation()} />
+                            </div></Tooltip>
+                            <Tooltip title={<><div className="dash-tooltip">{"Remove from presentation"}</div></>}><div
+                                className={"slideButton"}
+                                onClick={this.removeItem}>
+                                <FontAwesomeIcon icon={"trash"} onPointerDown={e => e.stopPropagation()} />
+                            </div></Tooltip>
+                            <div className="group"></div>
+                        </div>
+                        <div className="presItem-groupSlideContainer" style={{ top: 28, height: 'calc(100% - 28px)' }}>
+                            {this.rootDoc.presExpandGroup ? this.renderGroupSlides : (null)}
+                        </div>
+                        <div className="presItem-docName" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105 }}>{activeItem.presPinView ? (<><i>View of </i> {targetDoc.title}</>) : targetDoc.title}</div>
+                        <div className="expandButton"
+                            onClick={e => {
+                                if (isGroup) {
+                                    this.rootDoc.presExpandGroup = !this.rootDoc.presExpandGroup;
+                                }
+                            }}
+                        >
+                            <FontAwesomeIcon icon={"caret-down"} style={{ transform: this.rootDoc.presExpandGroup ? "rotate(180deg)" : "rotate(0deg)" }} />
+                        </div>
+                    </div>
+                    : (null)}
+                {miniView || isGroup ? (null) : <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "active" : ""}`}
+                    style={{
+                        backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
+                        boxShadow: presBoxColor && presBoxColor !== "white" && presBoxColor !== "transparent" ? isSelected ? "0 0 0px 1.5px" + presBoxColor : undefined : undefined
+                    }}>
+                    <div className="presItem-name" style={{ maxWidth: showMore ? (toolbarWidth - (this.presBox._viewType === CollectionViewType.Stacking ? 195 : 220)) : toolbarWidth - (this.presBox._viewType === CollectionViewType.Stacking ? 105 : 145), cursor: isSelected ? 'text' : 'grab' }}>
+                        <EditableView
+                            ref={this._titleRef}
+                            editing={!isSelected ? false : undefined}
+                            contents={activeItem.title}
+                            overflow={'ellipsis'}
+                            GetValue={() => StrCast(activeItem.title)}
+                            SetValue={this.onSetValue}
+                        />
+                    </div>
+                    <Tooltip title={<><div className="dash-tooltip">{"Movement speed"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.transition}</div></Tooltip>
+                    <Tooltip title={<><div className="dash-tooltip">{"Duration"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.duration}</div></Tooltip>
+                    <div className={"presItem-slideButtons"}>
+                        <Tooltip title={<><div className="dash-tooltip">{"Update view"}</div></>}>
+                            <div className="slideButton"
+                                onClick={() => this.updateView(targetDoc, activeItem)}
+                                style={{ fontWeight: 700, display: activeItem.presPinView ? "flex" : "none" }}>V</div>
+                        </Tooltip>
+                        {this.indexInPres === 0 ? (null) : <Tooltip title={<><div className="dash-tooltip">{activeItem.groupWithUp ? "Ungroup" : "Group with up"}</div></>}>
+                            <div className="slideButton"
+                                onClick={() => activeItem.groupWithUp = !activeItem.groupWithUp}
+                                style={{
+                                    zIndex: 1000 - this.indexInPres,
+                                    fontWeight: 700,
+                                    backgroundColor: activeItem.groupWithUp ? presColorBool ? presBoxColor : Colors.MEDIUM_BLUE : undefined,
+                                    height: activeItem.groupWithUp ? 53 : 18,
+                                    transform: activeItem.groupWithUp ? "translate(0, -17px)" : undefined
+                                }}>
+                                <div style={{ transform: activeItem.groupWithUp ? "rotate(180deg) translate(0, -17.5px)" : "rotate(0deg)" }}>
+                                    <FontAwesomeIcon icon={"arrow-up"} onPointerDown={e => e.stopPropagation()} />
+                                </div>
+                            </div>
+                        </Tooltip>}
+                        <Tooltip title={<><div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? "Minimize" : "Expand"}</div></>}><div className={"slideButton"} onClick={e => { e.stopPropagation(); this.presExpandDocumentClick(); }}>
+                            <FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? "eye-slash" : "eye"} onPointerDown={e => e.stopPropagation()} />
+                        </div></Tooltip>
+                        <Tooltip title={<><div className="dash-tooltip">{"Remove from presentation"}</div></>}><div
+                            className={"slideButton"}
+                            onClick={this.removeItem}>
+                            <FontAwesomeIcon icon={"trash"} onPointerDown={e => e.stopPropagation()} />
+                        </div></Tooltip>
+                    </div>
+                    <div className="presItem-docName" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105 }}>{activeItem.presPinView ? (<><i>View of </i> {targetDoc.title}</>) : targetDoc.title}</div>
+                    {this.renderEmbeddedInline}
+                </div>}
+            </div >);
+    }
 
-      render() {
-            return !(this.rootDoc instanceof Doc) || this.targetDoc instanceof Promise ? (null) : this.mainItem;
-      }
+    render() {
+        return !(this.rootDoc instanceof Doc) || this.targetDoc instanceof Promise ? (null) : this.mainItem;
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 0ded1bb3c..f706be6c6 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -274,8 +274,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
         }
     }
 
-    @observable private _scrollTimer: any;
-
     onScroll = (e: React.UIEvent<HTMLElement>) => {
         if (this._mainCont.current && !this._forcedScroll) {
             this._ignoreScroll = true; // the pdf scrolled, so we need to tell the Doc to scroll but we don't want the doc to then try to set the PDF scroll pos (which would interfere with the smooth scroll animation)
@@ -283,11 +281,6 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 this.props.layoutDoc._scrollTop = this._mainCont.current.scrollTop;
             }
             this._ignoreScroll = false;
-            if (this._scrollTimer) clearTimeout(this._scrollTimer); // wait until a scrolling pause, then create an anchor to audio
-            this._scrollTimer = setTimeout(() => {
-                DocUtils.MakeLinkToActiveAudio(() => this.props.DocumentView?.().ComponentView?.getAnchor!()!, false);
-                this._scrollTimer = undefined;
-            }, 200);
         }
     }
 
diff --git a/src/client/views/topbar/TopBar.scss b/src/client/views/topbar/TopBar.scss
index c5b340514..923f1892e 100644
--- a/src/client/views/topbar/TopBar.scss
+++ b/src/client/views/topbar/TopBar.scss
@@ -1,44 +1,29 @@
 @import "../global/globalCssVariables";
 
-
 .topbar-container {
-      display: flex;
-      flex-direction: column;
-      font-size: 10px;
-      line-height: 1;
-      overflow-y: auto;
-      overflow-x: visible;
-      background: $dark-gray;
-      overflow: visible;
-      z-index: 1000;
-      height: $topbar-height;
-      background-color: $dark-gray;
-
-      .topbar-inner-container {
-            display: flex;
-            flex-direction: row;
-            position: relative;
-            display: grid;
-            grid-auto-columns: 33.3% 33.3% 33.3%;
-            align-items: center;
-
-            // &:first-child {
-            //       height: 20px;
-            // }
-      }
-
-      .topbar-button-text {
-            color: $white;
-            padding: 10px;
-            size: 15;
-
-            &:hover {
-                  font-weight: 500;
-            }
-      }
-
-      .topbar-button-icon {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    position: relative;
+    font-size: 10px;
+    line-height: 1;
+    overflow-y: auto;
+    overflow-x: visible;
+    background: $dark-gray;
+    overflow: visible;
+    z-index: 1000;
+
+    .topbar-bar {
+        height: $topbar-height;
+        display: grid;
+        grid-auto-columns: 33.3% 33.3% 33.3%;
+        align-items: center;
+        background-color: $dark-gray;
+
+        .topBar-icon {
             cursor: pointer;
+            font-size: 12.5px;
+            font-family: 'Roboto';
             width: fit-content;
             display: flex;
             justify-content: center;
@@ -46,22 +31,21 @@
             align-items: center;
             justify-self: center;
             align-self: center;
+            border-radius: $standard-border-radius;
             padding: 5px;
             transition: linear 0.2s;
-            color: $white;
+            color: $black;
+            background-color: $light-gray;
 
             &:hover {
-                  background-color: darken($color: $light-gray, $amount: 20);
+                background-color: darken($color: $light-gray, $amount: 20);
             }
-      }
+        }
 
-      .topbar-title {
-            color: $white;
-            font-size: 17;
-            font-weight: 500;
-      }
 
-      .topbar-center {
+
+
+        .topbar-center {
             grid-column: 2;
             display: inline-flex;
             justify-content: center;
@@ -69,23 +53,42 @@
             gap: 5px;
 
             .topbar-dashboards {
-                  display: flex;
-                  flex-direction: row;
-                  gap: 5px;
+                display: flex;
+                flex-direction: row;
+                gap: 5px;
+            }
+
+            .topbar-lozenge-dashboard {
+                display: flex;
+
+
+
+                .topbar-dashSelect {
+                    border: none;
+                    background-color: $dark-gray;
+                    color: $white;
+                    font-family: 'Roboto';
+                    font-size: 17;
+                    font-weight: 500;
+
+                    &:hover {
+                        cursor: pointer;
+                    }
+                }
             }
-      }
+        }
 
 
-      .topbar-right {
+        .topbar-right {
             grid-column: 3;
             position: relative;
             display: flex;
             justify-content: flex-end;
             gap: 5px;
             margin-right: 5px;
-      }
+        }
 
-      .topbar-left {
+        .topbar-left {
             grid-column: 1;
             color: black;
             font-family: 'Roboto';
@@ -95,122 +98,123 @@
             gap: 5px;
 
             .topBar-icon:hover {
-                  background-color: $close-red;
+                background-color: $close-red;
             }
 
             .topbar-lozenge-user,
             .topbar-lozenge {
-                  height: 23;
-                  font-size: 12;
-                  color: white;
-                  font-family: 'Roboto';
-                  font-weight: 400;
-                  padding: 4px;
-                  align-self: center;
-                  margin-left: 7px;
-                  display: flex;
-                  align-items: center;
-
-                  .topbar-dashSelect {
-                        border: none;
-                        background-color: transparent;
-                        color: black;
-                        font-family: 'Roboto';
-                        font-size: 17;
-                        font-weight: 500;
-
-                        &:hover {
-                              cursor: pointer;
-                        }
-                  }
+                height: 23;
+                font-size: 12;
+                color: white;
+                font-family: 'Roboto';
+                font-weight: 400;
+                padding: 4px;
+                align-self: center;
+                margin-left: 7px;
+                display: flex;
+                align-items: center;
+
+                .topbar-dashSelect {
+                    border: none;
+                    background-color: transparent;
+                    color: black;
+                    font-family: 'Roboto';
+                    font-size: 17;
+                    font-weight: 500;
+
+                    &:hover {
+                        cursor: pointer;
+                    }
+                }
             }
 
             .topbar-logoff {
-                  border-radius: 3px;
-                  background: olivedrab;
-                  color: white;
-                  display: none;
-                  margin-left: 5px;
-                  padding: 1px 2px 1px 2px;
-                  cursor: pointer;
+                border-radius: 3px;
+                background: olivedrab;
+                color: white;
+                display: none;
+                margin-left: 5px;
+                padding: 1px 2px 1px 2px;
+                cursor: pointer;
             }
 
             .topbar-logoff {
-                  background: red;
+                background: red;
             }
 
             .topbar-lozenge-user:hover {
-                  .topbar-logoff {
-                        display: inline-block;
-                  }
+                .topbar-logoff {
+                    display: inline-block;
+                }
             }
-      }
+        }
 
-      .topbar-barChild {
+        .topbar-barChild {
 
             &.topbar-collection {
-                  flex: 0 1 auto;
-                  margin-left: 2px;
-                  margin-right: 2px
+                flex: 0 1 auto;
+                margin-left: 2px;
+                margin-right: 2px
             }
 
             &.topbar-input {
-                  margin: 5px;
-                  border-radius: 20px;
-                  border: $dark-gray;
-                  display: block;
-                  width: 130px;
-                  -webkit-transition: width 0.4s;
-                  transition: width 0.4s;
-                  /* align-self: stretch; */
-                  outline: none;
-
-                  &:focus {
-                        width: 500px;
-                        outline: none;
-                  }
+                margin: 5px;
+                border-radius: 20px;
+                border: $dark-gray;
+                display: block;
+                width: 130px;
+                -webkit-transition: width 0.4s;
+                transition: width 0.4s;
+                /* align-self: stretch; */
+                outline: none;
+
+                &:focus {
+                    width: 500px;
+                    outline: none;
+                }
             }
 
             &.topbar-filter {
-                  align-self: stretch;
+                align-self: stretch;
 
-                  button {
-                        transform: none;
+                button {
+                    transform: none;
 
-                        &:hover {
-                              transform: none;
-                        }
-                  }
+                    &:hover {
+                        transform: none;
+                    }
+                }
             }
 
             &.topbar-submit {
-                  margin-left: 2px;
-                  margin-right: 2px
+                margin-left: 2px;
+                margin-right: 2px
             }
 
             &.topbar-close {
-                  color: $white;
-                  max-height: $topbar-height;
+                color: $white;
+                max-height: $topbar-height;
             }
-      }
+        }
+    }
 }
 
 .topbar-results {
-      display: flex;
-      flex-direction: column;
-      top: 300px;
-      display: flex;
-      flex-direction: column;
-      height: 100%;
-      overflow: visible;
-
-      .no-result {
-            width: 500px;
-            background: $light-gray;
-            padding: 10px;
-            height: 50px;
-            text-transform: uppercase;
-            text-align: left;
-            font-weight: bold;
-      }
+    display: flex;
+    flex-direction: column;
+    top: 300px;
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+    overflow: visible;
+
+    .no-result {
+        width: 500px;
+        background: $light-gray;
+        padding: 10px;
+        height: 50px;
+        text-transform: uppercase;
+        text-align: left;
+        font-weight: bold;
+    }
 }
\ No newline at end of file
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 0db3950a2..be248ab92 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -3,18 +3,13 @@ import { Tooltip } from "@material-ui/core";
 import { action } from "mobx";
 import { observer } from "mobx-react";
 import * as React from 'react';
-import { AclAdmin, Doc, DocListCast } from '../../../fields/Doc';
+import { Doc, DocListCast } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
-import { Cast, StrCast } from '../../../fields/Types';
-import { GetEffectiveAcl } from "../../../fields/util";
+import { StrCast } from '../../../fields/Types';
 import { Utils } from '../../../Utils';
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { DocumentManager } from "../../util/DocumentManager";
-import { SelectionManager } from "../../util/SelectionManager";
 import { SettingsManager } from "../../util/SettingsManager";
-import { SharingManager } from "../../util/SharingManager";
 import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { ContextMenu } from "../ContextMenu";
 import { Borders, Colors } from "../global/globalEnums";
 import { MainView } from "../MainView";
 import "./TopBar.scss";
@@ -25,63 +20,64 @@ import "./TopBar.scss";
  */
 @observer
 export class TopBar extends React.Component {
-      navigateToHome = () => {
-            CurrentUserUtils.CaptureDashboardThumbnail()?.then(() => {
-                Doc.UserDoc().activePage = "home";
-                CurrentUserUtils.closeActiveDashboard(); // bcz: if we do this, we need some other way to keep track, for user convenience, of the last dashboard in use
-            });
-      }
-      render() {
-            const activeDashboard = Cast(Doc.UserDoc().activeDashboard, Doc, null)
-            return (
-                  //TODO:glr Add support for light / dark mode
-                  <div style={{ pointerEvents: "all", background: Colors.DARK_GRAY, borderBottom: Borders.STANDARD }} className="topbar-container">
-                        <div className="topbar-inner-container">
-                              <div className="topbar-left">
-                                    {activeDashboard ? <div className="topbar-button-text" onClick={e => {
-                                    ContextMenu.Instance.addItem({ description: "Logout", event: () => window.location.assign(Utils.prepend("/logout")), icon: "edit" });
-                                    ContextMenu.Instance.displayMenu(e.clientX +5, e.clientY + 10);
-                                    }}>{Doc.CurrentUserEmail}</div> : (null)}
-                              </div>
-                              <div className="topbar-center" >
-                                    <div className="topbar-title" onClick={() => SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(activeDashboard)!, false)}>
-                                          {activeDashboard ? StrCast(activeDashboard.title) : "Dash"}
-                                    </div>
-                                    <div className="topbar-button-icon" onClick={e => {
-                                        const dashView = DocumentManager.Instance.getDocumentView(activeDashboard);
-                                        ContextMenu.Instance.addItem({ description: "Open Dashboard View", event: this.navigateToHome, icon: "edit" });
-                                        ContextMenu.Instance.addItem({ description: "Snapshot Dashboard", event: async () => {
-                                            const batch = UndoManager.StartBatch("snapshot");
-                                            await CurrentUserUtils.snapshotDashboard(Doc.UserDoc());
-                                            batch.end();
-                                        }, icon: "edit" });
-                                        dashView?.showContextMenu(e.clientX+20, e.clientY+30);
-                                    }}>
-                                    <FontAwesomeIcon color="white" size="lg" icon="bars" />
-                                    </div>
-                                    <Tooltip title={<div className="dash-tooltip">Browsing mode for directly navigating to documents</div>} placement="bottom">
-                                        <div className="topbar-icon" style={{ background: MainView.Instance._exploreMode ? Colors.LIGHT_BLUE : undefined }} onClick={action(() => MainView.Instance._exploreMode = !MainView.Instance._exploreMode)}>
-                                            <FontAwesomeIcon color={MainView.Instance._exploreMode ? "red":"white"} icon="eye"  size="lg" />
-                                        </div>
-                                    </Tooltip>
-                              </div>
-                              <div className="topbar-right" >
-                                    <div className="topbar-button-text" onClick={() => {SharingManager.Instance.open(undefined, activeDashboard)}}>
-                                        {/* TODO: if this is my dashboard, display share
-                                        if this is a shared dashboard, display "view original or view annotated" */}
-                                        { Doc.GetProto(CurrentUserUtils.ActiveDashboard)?.author ===  Doc.CurrentUserEmail ?  "Share": "view original" }
-                                    </div>
-                                    <div className="topbar-button-icon" onClick={() => window.open(
-                                          "https://brown-dash.github.io/Dash-Documentation/", "_blank")}>
-                                          <FontAwesomeIcon icon="question-circle" />
-                                    </div>
-                                    <div className="topbar-button-icon" onClick={() => SettingsManager.Instance.open()}>
-                                          <FontAwesomeIcon icon="cog" />
-                                    </div>
-                              </div>
+    render() {
+        const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
+        return (
+            //TODO:glr Add support for light / dark mode
+            <div style={{ pointerEvents: "all" }} className="topbar-container">
+                <div className="topbar-bar" style={{ background: Colors.DARK_GRAY, borderBottom: Borders.STANDARD }}>
+                    <div className="topbar-left">
+                        <div className="topbar-lozenge-user">
+                            {`${Doc.CurrentUserEmail}`}
                         </div>
-                  </div>
-                        
-            );
-      }
+                        <div className="topbar-icon" onClick={() => window.location.assign(Utils.prepend("/logout"))}>
+                            {"Log out"}
+                        </div>
+                    </div>
+                    <div className="topbar-center" >
+                        <div className="topbar-lozenge-dashboard">
+                            <select className="topbar-dashSelect" onChange={undoBatch(e => CurrentUserUtils.openDashboard(Doc.UserDoc(), myDashboards[Number(e.target.value)]))}
+                                value={myDashboards.indexOf(CurrentUserUtils.ActiveDashboard)}
+                                style={{ color: Colors.WHITE }}>
+                                {myDashboards.map((dash, i) => <option key={dash[Id]} value={i}> {StrCast(dash.title)} </option>)}
+                            </select>
+                        </div>
+                        <div className="topbar-dashboards">
+                            <Tooltip title={<div className="dash-tooltip">Create a new dashboard </div>} placement="bottom"><div className="topbar-icon" onClick={async () => {
+                                const batch = UndoManager.StartBatch("new dash");
+                                await CurrentUserUtils.createNewDashboard(Doc.UserDoc());
+                                batch.end();
+                            }}>
+                                {"New"}<FontAwesomeIcon icon="plus" />
+                            </div>
+                            </Tooltip>
+                            <Tooltip title={<div className="dash-tooltip">Work on a copy of the dashboard layout</div>} placement="bottom">
+                                <div className="topbar-icon" onClick={async () => {
+                                    const batch = UndoManager.StartBatch("snapshot");
+                                    await CurrentUserUtils.snapshotDashboard(Doc.UserDoc());
+                                    batch.end();
+                                }}>
+                                    {"Snapshot"}<FontAwesomeIcon icon="camera" />
+                                </div>
+                            </Tooltip>
+                            <Tooltip title={<div className="dash-tooltip">Browsing mode for directly navigating to documents</div>} placement="bottom">
+                                <div className="topbar-icon" style={{ background: MainView.Instance._exploreMode ? Colors.LIGHT_BLUE : undefined }} onClick={action(() => MainView.Instance._exploreMode = !MainView.Instance._exploreMode)}>
+                                    {"Explore"}<FontAwesomeIcon icon="map" />
+                                </div>
+                            </Tooltip>
+                        </div>
+                    </div>
+                    <div className="topbar-right" >
+                        <div className="topbar-icon" onClick={() => window.open(
+                            "https://brown-dash.github.io/Dash-Documentation/", "_blank")}>
+                            {"Help"}<FontAwesomeIcon icon="question-circle" />
+                        </div>
+                        <div className="topbar-icon" onClick={() => SettingsManager.Instance.open()}>
+                            {"Settings"}<FontAwesomeIcon icon="cog" />
+                        </div>
+                    </div>
+                </div>
+            </div >
+        );
+    }
 }
\ No newline at end of file
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 7b72787d1..6abc27b23 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1436,7 +1436,6 @@ ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) { return Doc.cop
 ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) { return Doc.delegateDragFactory(dragFactory); });
 ScriptingGlobals.add(function copyField(field: any) { return Field.Copy(field); });
 ScriptingGlobals.add(function docList(field: any) { return DocListCast(field); });
-ScriptingGlobals.add(function addDocToList(doc: Doc, field: string, added:Doc) { return Doc.AddDocToList(doc,field, added); });
 ScriptingGlobals.add(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); });
 ScriptingGlobals.add(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); });
 ScriptingGlobals.add(function undo() { SelectionManager.DeselectAll(); return UndoManager.Undo(); });
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
index bf40a0d7b..7e2aa5681 100644
--- a/src/fields/Types.ts
+++ b/src/fields/Types.ts
@@ -76,10 +76,6 @@ export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal
     return defaultVal === null ? undefined : defaultVal;
 }
 
-export function DocCast(field: FieldResult, defaultVal?: Doc) {
-    return Cast(field, Doc, null) ?? defaultVal;
-}
-
 export function NumCast(field: FieldResult, defaultVal: number | null = 0) {
     return Cast(field, "number", defaultVal);
 }
diff --git a/src/fields/URLField.ts b/src/fields/URLField.ts
index 3e7542e46..1d4bbaed0 100644
--- a/src/fields/URLField.ts
+++ b/src/fields/URLField.ts
@@ -53,7 +53,6 @@ export abstract class URLField extends ObjectField {
 export const nullAudio = "https://actions.google.com/sounds/v1/alarms/beep_short.ogg";
 
 @scriptingGlobal @Deserializable("audio") export class AudioField extends URLField { }
-@scriptingGlobal @Deserializable("recording") export class RecordingField extends URLField { }
 @scriptingGlobal @Deserializable("image") export class ImageField extends URLField { }
 @scriptingGlobal @Deserializable("video") export class VideoField extends URLField { }
 @scriptingGlobal @Deserializable("pdf") export class PdfField extends URLField { }
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index be39e0709..e532becb5 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -36,8 +36,8 @@ export const documentSchema = createSchema({
     _nativeHeight: "number",    // "
     _width: "number",           // width of document in its container's coordinate system
     _height: "number",          // "
-    _xPadding: "number",        // pixels of padding on left/right of collectionfreeformview contents when fitContentsToBox is set
-    _yPadding: "number",        // pixels of padding on top/bottom of collectionfreeformview contents when fitContentsToBox is set
+    _xPadding: "number",        // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
+    _yPadding: "number",        // pixels of padding on top/bottom of collectionfreeformview contents when fitToBox is set
     _xMargin: "number",         // margin added on left/right of most documents to add separation from their container
     _yMargin: "number",         // margin added on top/bottom of most documents to add separation from their container
     _overflow: "string",        // sets overflow behvavior for CollectionFreeForm views
@@ -59,7 +59,7 @@ export const documentSchema = createSchema({
     borderRounding: "string",   // border radius rounding of document
     boxShadow: "string",        // the amount of shadow around the perimeter of a document
     color: "string",            // foreground color of document
-    fitContentsToBox: "boolean",// whether freeform view contents should be zoomed/panned to fill the area of the document view box
+    fitToBox: "boolean",        // whether freeform view contents should be zoomed/panned to fill the area of the document view
     fontSize: "string",
     hidden: "boolean",          // whether a document should not be displayed
     isInkMask: "boolean",       // is the document a mask (ie, sits on top of other documents, has an unbounded width/height that is dark, and content uses 'hard-light' mix-blend-mode to let other documents pop through)
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index 64baa351c..418464f0e 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -19,7 +19,7 @@ import React = require('react');
 
 @observer
 export class AudioUpload extends React.Component {
-    @observable public _audioCol: Doc = FieldValue(Cast(Docs.Create.FreeformDocument([Cast(Docs.Create.AudioDocument(nullAudio, { title: "mobile audio", _width: 500, _height: 100 }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitContentsToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
+    @observable public _audioCol: Doc = FieldValue(Cast(Docs.Create.FreeformDocument([Cast(Docs.Create.AudioDocument(nullAudio, { title: "mobile audio", _width: 500, _height: 100 }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
 
     /**
      * Handles the onclick functionality for the 'Restart' button
@@ -36,7 +36,7 @@ export class AudioUpload extends React.Component {
                     title: "mobile audio",
                     _width: 500,
                     _height: 100
-                }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitContentsToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
+                }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
     }
 
     /** 
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index e7b7056a1..2c667ba3e 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -17,239 +17,239 @@ import { SolrManager } from "./SearchManager";
 const fs = require('fs');
 
 export enum Directory {
-      parsed_files = "parsed_files",
-      images = "images",
-      videos = "videos",
-      pdfs = "pdfs",
-      text = "text",
-      pdf_thumbnails = "pdf_thumbnails",
-      audio = "audio",
+    parsed_files = "parsed_files",
+    images = "images",
+    videos = "videos",
+    pdfs = "pdfs",
+    text = "text",
+    pdf_thumbnails = "pdf_thumbnails",
+    audio = "audio",
 }
 
 export function serverPathToFile(directory: Directory, filename: string) {
-      return normalize(`${filesDirectory}/${directory}/${filename}`);
+    return normalize(`${filesDirectory}/${directory}/${filename}`);
 }
 
 export function pathToDirectory(directory: Directory) {
-      return normalize(`${filesDirectory}/${directory}`);
+    return normalize(`${filesDirectory}/${directory}`);
 }
 
 export function clientPathToFile(directory: Directory, filename: string) {
-      return `/files/${directory}/${filename}`;
+    return `/files/${directory}/${filename}`;
 }
 
 export default class UploadManager extends ApiManager {
 
-      protected initialize(register: Registration): void {
+    protected initialize(register: Registration): void {
 
-            register({
-                  method: Method.POST,
-                  subscription: "/uploadFormData",
-                  secureHandler: async ({ req, res }) => {
-                        const form = new formidable.IncomingForm();
-                        form.keepExtensions = true;
-                        form.uploadDir = pathToDirectory(Directory.parsed_files);
-                        return new Promise<void>(resolve => {
-                              form.parse(req, async (_err, _fields, files) => {
-                                    const results: Upload.FileResponse[] = [];
-                                    for (const key in files) {
-                                          const f = files[key];
-                                          if (!Array.isArray(f)) {
-                                                const result = await DashUploadUtils.upload(f);
-                                                result && !(result.result instanceof Error) && results.push(result);
-                                          }
-                                    }
-                                    _success(res, results);
-                                    resolve();
-                              });
-                        });
-                  }
-            });
+        register({
+            method: Method.POST,
+            subscription: "/uploadFormData",
+            secureHandler: async ({ req, res }) => {
+                const form = new formidable.IncomingForm();
+                form.keepExtensions = true;
+                form.uploadDir = pathToDirectory(Directory.parsed_files);
+                return new Promise<void>(resolve => {
+                    form.parse(req, async (_err, _fields, files) => {
+                        const results: Upload.FileResponse[] = [];
+                        for (const key in files) {
+                            const f = files[key];
+                            if (!Array.isArray(f)) {
+                                const result = await DashUploadUtils.upload(f);
+                                result && !(result.result instanceof Error) && results.push(result);
+                            }
+                        }
+                        _success(res, results);
+                        resolve();
+                    });
+                });
+            }
+        });
 
-            register({
-                  method: Method.POST,
-                  subscription: "/uploadYoutubeVideo",
-                  secureHandler: async ({ req, res }) => {
-                        //req.readableBuffer.head.data
-                        return new Promise<void>(async resolve => {
-                              req.addListener("data", async (args) => {
-                                    console.log(args);
-                                    const payload = String.fromCharCode.apply(String, args);
-                                    const videoId = JSON.parse(payload).videoId;
-                                    const results: Upload.FileResponse[] = [];
-                                    const result = await DashUploadUtils.uploadYoutube(videoId);
-                                    result && !(result.result instanceof Error) && results.push(result);
-                                    _success(res, results);
-                                    resolve();
-                              });
-                        });
-                  }
-            });
+        register({
+            method: Method.POST,
+            subscription: "/uploadYoutubeVideo",
+            secureHandler: async ({ req, res }) => {
+                //req.readableBuffer.head.data
+                return new Promise<void>(async resolve => {
+                    req.addListener("data", async (args) => {
+                        console.log(args);
+                        const payload = String.fromCharCode.apply(String, args);
+                        const videoId = JSON.parse(payload).videoId;
+                        const results: Upload.FileResponse[] = [];
+                        const result = await DashUploadUtils.uploadYoutube(videoId);
+                        result && !(result.result instanceof Error) && results.push(result);
+                        _success(res, results);
+                        resolve();
+                    });
+                });
+            }
+        });
 
-            register({
-                  method: Method.POST,
-                  subscription: new RouteSubscriber("youtubeScreenshot"),
-                  secureHandler: async ({ req, res }) => {
-                        const { id, timecode } = req.body;
-                        const convert = (raw: string) => {
-                              const number = Math.floor(Number(raw));
-                              const seconds = number % 60;
-                              const minutes = (number - seconds) / 60;
-                              return `${minutes}m${seconds}s`;
-                        };
-                        const suffix = timecode ? `&t=${convert(timecode)}` : ``;
-                        const targetUrl = `https://www.youtube.com/watch?v=${id}${suffix}`;
-                        const buffer = await captureYoutubeScreenshot(targetUrl);
-                        if (!buffer) {
-                              return res.send();
+        register({
+            method: Method.POST,
+            subscription: new RouteSubscriber("youtubeScreenshot"),
+            secureHandler: async ({ req, res }) => {
+                const { id, timecode } = req.body;
+                const convert = (raw: string) => {
+                    const number = Math.floor(Number(raw));
+                    const seconds = number % 60;
+                    const minutes = (number - seconds) / 60;
+                    return `${minutes}m${seconds}s`;
+                };
+                const suffix = timecode ? `&t=${convert(timecode)}` : ``;
+                const targetUrl = `https://www.youtube.com/watch?v=${id}${suffix}`;
+                const buffer = await captureYoutubeScreenshot(targetUrl);
+                if (!buffer) {
+                    return res.send();
+                }
+                const resolvedName = `youtube_capture_${id}_${suffix}.png`;
+                const resolvedPath = serverPathToFile(Directory.images, resolvedName);
+                return new Promise<void>(resolve => {
+                    writeFile(resolvedPath, buffer, async error => {
+                        if (error) {
+                            return res.send();
                         }
-                        const resolvedName = `youtube_capture_${id}_${suffix}.png`;
-                        const resolvedPath = serverPathToFile(Directory.images, resolvedName);
-                        return new Promise<void>(resolve => {
-                              writeFile(resolvedPath, buffer, async error => {
-                                    if (error) {
-                                          return res.send();
-                                    }
-                                    await DashUploadUtils.outputResizedImages(() => createReadStream(resolvedPath), resolvedName, pathToDirectory(Directory.images));
-                                    res.send({
-                                          accessPaths: {
-                                                agnostic: DashUploadUtils.getAccessPaths(Directory.images, resolvedName)
-                                          }
-                                    } as Upload.FileInformation);
-                                    resolve();
-                              });
-                        });
-                  }
-            });
+                        await DashUploadUtils.outputResizedImages(() => createReadStream(resolvedPath), resolvedName, pathToDirectory(Directory.images));
+                        res.send({
+                            accessPaths: {
+                                agnostic: DashUploadUtils.getAccessPaths(Directory.images, resolvedName)
+                            }
+                        } as Upload.FileInformation);
+                        resolve();
+                    });
+                });
+            }
+        });
 
-            register({
-                  method: Method.POST,
-                  subscription: "/uploadRemoteImage",
-                  secureHandler: async ({ req, res }) => {
+        register({
+            method: Method.POST,
+            subscription: "/uploadRemoteImage",
+            secureHandler: async ({ req, res }) => {
 
-                        const { sources } = req.body;
-                        if (Array.isArray(sources)) {
-                              const results = await Promise.all(sources.map(source => DashUploadUtils.UploadImage(source)));
-                              return res.send(results);
-                        }
-                        res.send();
-                  }
-            });
+                const { sources } = req.body;
+                if (Array.isArray(sources)) {
+                    const results = await Promise.all(sources.map(source => DashUploadUtils.UploadImage(source)));
+                    return res.send(results);
+                }
+                res.send();
+            }
+        });
 
-            register({
-                  method: Method.POST,
-                  subscription: "/uploadDoc",
-                  secureHandler: ({ req, res }) => {
+        register({
+            method: Method.POST,
+            subscription: "/uploadDoc",
+            secureHandler: ({ req, res }) => {
 
-                        const form = new formidable.IncomingForm();
-                        form.keepExtensions = true;
-                        // let path = req.body.path;
-                        const ids: { [id: string]: string } = {};
-                        let remap = true;
-                        const getId = (id: string): string => {
-                              if (!remap) return id;
-                              if (id.endsWith("Proto")) return id;
-                              if (id in ids) {
-                                    return ids[id];
-                              } else {
-                                    return ids[id] = v4();
-                              }
-                        };
-                        const mapFn = (doc: any) => {
-                              if (doc.id) {
-                                    doc.id = getId(doc.id);
-                              }
-                              for (const key in doc.fields) {
-                                    if (!doc.fields.hasOwnProperty(key)) { continue; }
-                                    const field = doc.fields[key];
-                                    if (field === undefined || field === null) { continue; }
+                const form = new formidable.IncomingForm();
+                form.keepExtensions = true;
+                // let path = req.body.path;
+                const ids: { [id: string]: string } = {};
+                let remap = true;
+                const getId = (id: string): string => {
+                    if (!remap) return id;
+                    if (id.endsWith("Proto")) return id;
+                    if (id in ids) {
+                        return ids[id];
+                    } else {
+                        return ids[id] = v4();
+                    }
+                };
+                const mapFn = (doc: any) => {
+                    if (doc.id) {
+                        doc.id = getId(doc.id);
+                    }
+                    for (const key in doc.fields) {
+                        if (!doc.fields.hasOwnProperty(key)) { continue; }
+                        const field = doc.fields[key];
+                        if (field === undefined || field === null) { continue; }
 
-                                    if (field.__type === "Doc") {
-                                          mapFn(field);
-                                    } else if (field.__type === "proxy" || field.__type === "prefetch_proxy") {
-                                          field.fieldId = getId(field.fieldId);
-                                    } else if (field.__type === "script" || field.__type === "computed") {
-                                          if (field.captures) {
-                                                field.captures.fieldId = getId(field.captures.fieldId);
-                                          }
-                                    } else if (field.__type === "list") {
-                                          mapFn(field);
-                                    } else if (typeof field === "string") {
-                                          const re = /("(?:dataD|d)ocumentId"\s*:\s*")([\w\-]*)"/g;
-                                          doc.fields[key] = (field as any).replace(re, (match: any, p1: string, p2: string) => {
-                                                return `${p1}${getId(p2)}"`;
-                                          });
-                                    } else if (field.__type === "RichTextField") {
-                                          const re = /("href"\s*:\s*")(.*?)"/g;
-                                          field.Data = field.Data.replace(re, (match: any, p1: string, p2: string) => {
-                                                return `${p1}${getId(p2)}"`;
-                                          });
-                                    }
-                              }
-                        };
-                        return new Promise<void>(resolve => {
-                              form.parse(req, async (_err, fields, files) => {
-                                    remap = fields.remap !== "false";
-                                    let id: string = "";
+                        if (field.__type === "Doc") {
+                            mapFn(field);
+                        } else if (field.__type === "proxy" || field.__type === "prefetch_proxy") {
+                            field.fieldId = getId(field.fieldId);
+                        } else if (field.__type === "script" || field.__type === "computed") {
+                            if (field.captures) {
+                                field.captures.fieldId = getId(field.captures.fieldId);
+                            }
+                        } else if (field.__type === "list") {
+                            mapFn(field);
+                        } else if (typeof field === "string") {
+                            const re = /("(?:dataD|d)ocumentId"\s*:\s*")([\w\-]*)"/g;
+                            doc.fields[key] = (field as any).replace(re, (match: any, p1: string, p2: string) => {
+                                return `${p1}${getId(p2)}"`;
+                            });
+                        } else if (field.__type === "RichTextField") {
+                            const re = /("href"\s*:\s*")(.*?)"/g;
+                            field.Data = field.Data.replace(re, (match: any, p1: string, p2: string) => {
+                                return `${p1}${getId(p2)}"`;
+                            });
+                        }
+                    }
+                };
+                return new Promise<void>(resolve => {
+                    form.parse(req, async (_err, fields, files) => {
+                        remap = fields.remap !== "false";
+                        let id: string = "";
+                        try {
+                            for (const name in files) {
+                                const f = files[name];
+                                const path_2 = Array.isArray(f) ? "" : f.path;
+                                const zip = new AdmZip(path_2);
+                                zip.getEntries().forEach((entry: any) => {
+                                    if (!entry.entryName.startsWith("files/")) return;
+                                    let directory = dirname(entry.entryName) + "/";
+                                    const extension = extname(entry.entryName);
+                                    const base = basename(entry.entryName).split(".")[0];
                                     try {
-                                          for (const name in files) {
-                                                const f = files[name];
-                                                const path_2 = Array.isArray(f) ? "" : f.path;
-                                                const zip = new AdmZip(path_2);
-                                                zip.getEntries().forEach((entry: any) => {
-                                                      if (!entry.entryName.startsWith("files/")) return;
-                                                      let directory = dirname(entry.entryName) + "/";
-                                                      const extension = extname(entry.entryName);
-                                                      const base = basename(entry.entryName).split(".")[0];
-                                                      try {
-                                                            zip.extractEntryTo(entry.entryName, publicDirectory, true, false);
-                                                            directory = "/" + directory;
+                                        zip.extractEntryTo(entry.entryName, publicDirectory, true, false);
+                                        directory = "/" + directory;
 
-                                                            createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + "_o" + extension));
-                                                            createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + "_s" + extension));
-                                                            createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + "_m" + extension));
-                                                            createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + "_l" + extension));
-                                                      } catch (e) {
-                                                            console.log(e);
-                                                      }
-                                                });
-                                                const json = zip.getEntry("doc.json");
-                                                try {
-                                                      const data = JSON.parse(json.getData().toString("utf8"));
-                                                      const datadocs = data.docs;
-                                                      id = getId(data.id);
-                                                      const docs = Object.keys(datadocs).map(key => datadocs[key]);
-                                                      docs.forEach(mapFn);
-                                                      await Promise.all(docs.map((doc: any) => new Promise<void>(res => {
-                                                            Database.Instance.replace(doc.id, doc, (err, r) => {
-                                                                  err && console.log(err);
-                                                                  res();
-                                                            }, true);
-                                                      })));
-                                                } catch (e) { console.log(e); }
-                                                unlink(path_2, () => { });
-                                          }
-                                          SolrManager.update();
-                                          res.send(JSON.stringify(id || "error"));
-                                    } catch (e) { console.log(e); }
-                                    resolve();
-                              });
-                        });
-                  }
-            });
+                                        createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + "_o" + extension));
+                                        createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + "_s" + extension));
+                                        createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + "_m" + extension));
+                                        createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + "_l" + extension));
+                                    } catch (e) {
+                                        console.log(e);
+                                    }
+                                });
+                                const json = zip.getEntry("doc.json");
+                                try {
+                                    const data = JSON.parse(json.getData().toString("utf8"));
+                                    const datadocs = data.docs;
+                                    id = getId(data.id);
+                                    const docs = Object.keys(datadocs).map(key => datadocs[key]);
+                                    docs.forEach(mapFn);
+                                    await Promise.all(docs.map((doc: any) => new Promise<void>(res => {
+                                        Database.Instance.replace(doc.id, doc, (err, r) => {
+                                            err && console.log(err);
+                                            res();
+                                        }, true);
+                                    })));
+                                } catch (e) { console.log(e); }
+                                unlink(path_2, () => { });
+                            }
+                            SolrManager.update();
+                            res.send(JSON.stringify(id || "error"));
+                        } catch (e) { console.log(e); }
+                        resolve();
+                    });
+                });
+            }
+        });
 
-            register({
-                  method: Method.POST,
-                  subscription: "/inspectImage",
-                  secureHandler: async ({ req, res }) => {
+        register({
+            method: Method.POST,
+            subscription: "/inspectImage",
+            secureHandler: async ({ req, res }) => {
 
-                        const { source } = req.body;
-                        if (typeof source === "string") {
-                              return res.send(await DashUploadUtils.InspectImage(source));
-                        }
-                        res.send({});
-                  }
-            });
+                const { source } = req.body;
+                if (typeof source === "string") {
+                    return res.send(await DashUploadUtils.InspectImage(source));
+                }
+                res.send({});
+            }
+        });
 
         register({
             method: Method.POST,
@@ -296,17 +296,17 @@ export default class UploadManager extends ApiManager {
                                 .pipe(createWriteStream(path).on("error", e => console.log("Resizing write: " + e)));
                         });
 
-                              }
-                              res.send(clientPathToFile(Directory.images, filename + ext));
-                        });
-                  }
-            });
+                    }
+                    res.send(clientPathToFile(Directory.images, filename + ext));
+                });
+            }
+        });
 
-      }
+    }
 
 }
 function delay(ms: number) {
-      return new Promise(resolve => setTimeout(resolve, ms));
+    return new Promise(resolve => setTimeout(resolve, ms));
 }
 /**
  * On success, returns a buffer containing the bytes of a screenshot
@@ -315,25 +315,25 @@ function delay(ms: number) {
  * On failure, returns undefined.
  */
 async function captureYoutubeScreenshot(targetUrl: string) {
-      // const browser = await launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
-      // const page = await browser.newPage();
-      // // await page.setViewport({ width: 1920, height: 1080 });
+    // const browser = await launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
+    // const page = await browser.newPage();
+    // // await page.setViewport({ width: 1920, height: 1080 });
 
-      // // await page.goto(targetUrl, { waitUntil: 'domcontentloaded' as any });
+    // // await page.goto(targetUrl, { waitUntil: 'domcontentloaded' as any });
 
-      // const videoPlayer = await page.$('.html5-video-player');
-      // videoPlayer && await page.focus("video");
-      // await delay(7000);
-      // const ad = await page.$('.ytp-ad-skip-button-text');
-      // await ad?.click();
-      // await videoPlayer?.click();
-      // await delay(1000);
-      // // hide youtube player controls.
-      // await page.evaluate(() => (document.querySelector('.ytp-chrome-bottom') as HTMLElement).style.display = 'none');
+    // const videoPlayer = await page.$('.html5-video-player');
+    // videoPlayer && await page.focus("video");
+    // await delay(7000);
+    // const ad = await page.$('.ytp-ad-skip-button-text');
+    // await ad?.click();
+    // await videoPlayer?.click();
+    // await delay(1000);
+    // // hide youtube player controls.
+    // await page.evaluate(() => (document.querySelector('.ytp-chrome-bottom') as HTMLElement).style.display = 'none');
 
-      // const buffer = await videoPlayer?.screenshot({ encoding: "binary" });
-      // await browser.close();
+    // const buffer = await videoPlayer?.screenshot({ encoding: "binary" });
+    // await browser.close();
 
-      // return buffer;
-      return null;
+    // return buffer;
+    return null;
 }
\ No newline at end of file
diff --git a/tslint.json b/tslint.json
new file mode 100644
index 000000000..83255a798
--- /dev/null
+++ b/tslint.json
@@ -0,0 +1,32 @@
+{
+    "linterOptions": {
+        "exclude": [
+            "./src/client/northstar/**",
+            "**/*.js"
+        ]
+    },
+    "rules": {
+        "no-return-await": true,
+        "no-string-literal": true,
+        "prefer-object-spread": true,
+        "prefer-for-of": true,
+        "no-unnecessary-type-assertion": true,
+        "triple-equals": true,
+        "prefer-const": true,
+        "no-unnecessary-callback-wrapper": true,
+        "class-name": true,
+        "arrow-return-shorthand": true,
+        "semicolon": [
+            true,
+            "always"
+        ],
+        "curly": [
+            true,
+            "ignore-same-line"
+        ],
+        "no-tautology-expression": true,
+        "unnecessary-constructor": true
+    },
+    "defaultSeverity": "warning",
+    "jsRules": {}
+}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From 9819886735988e448d09a292b3369e19f4a79a61 Mon Sep 17 00:00:00 2001
From: mehekj <mehek.jethani@gmail.com>
Date: Mon, 20 Jun 2022 18:42:04 -0400
Subject: Revert "Revert "Merge branch 'master' into temporalmedia-mehek""

This reverts commit 3415f672292bb349c7d9ec66564933a746ee3b25.
---
 .eslintrc.json                                     |   13 +
 .prettierrc.json                                   |    6 +
 .vscode/settings.json                              |    2 +-
 package-lock.json                                  | 7273 +++++++++-----------
 package.json                                       |    3 +
 src/Utils.ts                                       |   12 +-
 src/client/Network.ts                              |   74 +-
 src/client/documents/DocumentTypes.ts              |    1 +
 src/client/documents/Documents.ts                  |   47 +-
 src/client/goldenLayout.js                         |    7 +-
 src/client/util/CurrentUserUtils.ts                |  134 +-
 src/client/util/DragManager.ts                     |    2 +-
 src/client/util/History.ts                         |    6 +-
 src/client/util/RecordingApi.ts                    |  269 +
 src/client/util/Scripting.ts                       |    2 +-
 src/client/views/DashboardView.scss                |   48 +
 src/client/views/DashboardView.tsx                 |   69 +
 src/client/views/DocComponent.tsx                  |    6 +-
 src/client/views/DocumentButtonBar.tsx             |   52 +-
 src/client/views/DocumentDecorations.tsx           | 1049 +--
 src/client/views/GestureOverlay.tsx                |    9 +-
 src/client/views/InkTangentHandles.tsx             |    3 +-
 src/client/views/InkTranscription.tsx              |  184 +-
 src/client/views/LightboxView.tsx                  |    2 -
 src/client/views/Main.tsx                          |    3 +
 src/client/views/MainView.scss                     |   15 +-
 src/client/views/MainView.tsx                      |   52 +-
 src/client/views/MarqueeAnnotator.tsx              |   10 +-
 src/client/views/OverlayView.scss                  |   12 +
 src/client/views/OverlayView.tsx                   |   14 +-
 src/client/views/PropertiesButtons.tsx             |   14 +-
 src/client/views/PropertiesView.tsx                |    2 +-
 src/client/views/ScriptingRepl.tsx                 |    3 +-
 .../views/collections/CollectionDockingView.tsx    |  135 +-
 .../collections/CollectionDockingViewMenu.scss     |   34 -
 .../collections/CollectionDockingViewMenu.tsx      |   48 -
 src/client/views/collections/CollectionMenu.tsx    |    7 +-
 .../collections/CollectionStackedTimeline.tsx      |    1 +
 .../views/collections/CollectionStackingView.scss  |    5 +-
 .../views/collections/CollectionTimeView.tsx       |    2 +-
 src/client/views/collections/TabDocView.tsx        |  146 +-
 src/client/views/collections/TreeView.tsx          |   15 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  158 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |  124 +-
 .../collectionLinear/CollectionLinearView.tsx      |    1 +
 .../CollectionMulticolumnView.tsx                  |    3 +-
 .../CollectionMultirowView.tsx                     |    3 +-
 .../collectionSchema/CollectionSchemaView.tsx      |    2 +-
 src/client/views/linking/LinkMenuItem.tsx          |    4 +
 src/client/views/nodes/AudioBox.tsx                |   85 +-
 .../views/nodes/CollectionFreeFormDocumentView.tsx |    4 +-
 src/client/views/nodes/DocumentContentsView.tsx    |    3 +-
 src/client/views/nodes/DocumentView.tsx            |   21 +-
 src/client/views/nodes/FieldView.tsx               |    1 +
 src/client/views/nodes/FilterBox.tsx               |    2 +-
 src/client/views/nodes/ImageBox.tsx                |    2 +-
 src/client/views/nodes/LinkDocPreview.tsx          |    2 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |    6 +-
 src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx |   14 +-
 src/client/views/nodes/PDFBox.tsx                  |   44 +-
 .../views/nodes/RecordingBox/ProgressBar.scss      |   26 +
 .../views/nodes/RecordingBox/ProgressBar.tsx       |   45 +
 .../views/nodes/RecordingBox/RecordingBox.tsx      |   61 +
 .../views/nodes/RecordingBox/RecordingView.scss    |  207 +
 .../views/nodes/RecordingBox/RecordingView.tsx     |  282 +
 src/client/views/nodes/RecordingBox/index.ts       |    2 +
 src/client/views/nodes/ScreenshotBox.tsx           |  429 +-
 src/client/views/nodes/VideoBox.tsx                | 2173 +++---
 src/client/views/nodes/WebBoxRenderer.js           |    4 +-
 src/client/views/nodes/button/FontIconBadge.tsx    |   32 +-
 src/client/views/nodes/button/FontIconBox.tsx      |    7 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   22 +-
 .../formattedText/ProsemirrorExampleTransfer.ts    |    2 +-
 src/client/views/nodes/trails/PresBox.tsx          |  152 +-
 src/client/views/nodes/trails/PresElementBox.scss  |  328 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |  438 +-
 src/client/views/pdf/PDFViewer.tsx                 |    7 +
 src/client/views/topbar/TopBar.scss                |  276 +-
 src/client/views/topbar/TopBar.tsx                 |  126 +-
 src/fields/Doc.ts                                  |    1 +
 src/fields/Types.ts                                |    4 +
 src/fields/URLField.ts                             |    1 +
 src/fields/documentSchemas.ts                      |    6 +-
 src/mobile/AudioUpload.tsx                         |    4 +-
 src/server/ApiManagers/UploadManager.ts            |  462 +-
 tslint.json                                        |   32 -
 86 files changed, 8191 insertions(+), 7208 deletions(-)
 create mode 100644 .eslintrc.json
 create mode 100644 .prettierrc.json
 create mode 100644 src/client/util/RecordingApi.ts
 create mode 100644 src/client/views/DashboardView.scss
 create mode 100644 src/client/views/DashboardView.tsx
 delete mode 100644 src/client/views/collections/CollectionDockingViewMenu.scss
 delete mode 100644 src/client/views/collections/CollectionDockingViewMenu.tsx
 create mode 100644 src/client/views/nodes/RecordingBox/ProgressBar.scss
 create mode 100644 src/client/views/nodes/RecordingBox/ProgressBar.tsx
 create mode 100644 src/client/views/nodes/RecordingBox/RecordingBox.tsx
 create mode 100644 src/client/views/nodes/RecordingBox/RecordingView.scss
 create mode 100644 src/client/views/nodes/RecordingBox/RecordingView.tsx
 create mode 100644 src/client/views/nodes/RecordingBox/index.ts
 delete mode 100644 tslint.json

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

diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 000000000..5cc0ab6dc
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,13 @@
+{
+  "extends": ["airbnb", "prettier", "plugin:node/recommended"],
+  "plugins": ["prettier"],
+  "rules": {
+    "prettier/prettier": "error",
+    "no-unused-vars": "warn",
+    "no-console": "off",
+    "func-names": "off",
+    "no-process-exit": "off",
+    "object-shorthand": "off",
+    "class-methods-use-this": "off"
+  }
+}
diff --git a/.prettierrc.json b/.prettierrc.json
new file mode 100644
index 000000000..a07280e32
--- /dev/null
+++ b/.prettierrc.json
@@ -0,0 +1,6 @@
+{
+  "trailingComma": "es5",
+  "tabWidth": 4,
+  "semi": false,
+  "singleQuote": false
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 27acfd1a2..a6e6ad360 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -6,7 +6,7 @@
         "**/CVS": true,
         "**/.DS_Store": true,
     },
-    "editor.formatOnSave": true,
+    "editor.formatOnSave": false,
     "editor.detectIndentation": false,
     "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false,
     "typescript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": true,
diff --git a/package-lock.json b/package-lock.json
index 6683f979e..9dc7f1b04 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5,220 +5,167 @@
     "requires": true,
     "dependencies": {
         "@babel/code-frame": {
-            "version": "7.8.3",
-            "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz",
-            "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==",
+            "version": "7.16.7",
+            "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz",
+            "integrity": "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==",
             "requires": {
-                "@babel/highlight": "^7.8.3"
+                "@babel/highlight": "^7.16.7"
             }
         },
         "@babel/generator": {
-            "version": "7.9.6",
-            "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.6.tgz",
-            "integrity": "sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ==",
-            "requires": {
-                "@babel/types": "^7.9.6",
-                "jsesc": "^2.5.1",
-                "lodash": "^4.17.13",
-                "source-map": "^0.5.0"
-            },
-            "dependencies": {
-                "@babel/types": {
-                    "version": "7.9.6",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.6.tgz",
-                    "integrity": "sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==",
-                    "requires": {
-                        "@babel/helper-validator-identifier": "^7.9.5",
-                        "lodash": "^4.17.13",
-                        "to-fast-properties": "^2.0.0"
-                    }
-                }
+            "version": "7.18.2",
+            "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.2.tgz",
+            "integrity": "sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==",
+            "requires": {
+                "@babel/types": "^7.18.2",
+                "@jridgewell/gen-mapping": "^0.3.0",
+                "jsesc": "^2.5.1"
             }
         },
         "@babel/helper-annotate-as-pure": {
-            "version": "7.10.4",
-            "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz",
-            "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==",
+            "version": "7.16.7",
+            "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz",
+            "integrity": "sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==",
             "requires": {
-                "@babel/types": "^7.10.4"
-            },
-            "dependencies": {
-                "@babel/helper-validator-identifier": {
-                    "version": "7.10.4",
-                    "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
-                    "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw=="
-                },
-                "@babel/types": {
-                    "version": "7.10.5",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz",
-                    "integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==",
-                    "requires": {
-                        "@babel/helper-validator-identifier": "^7.10.4",
-                        "lodash": "^4.17.19",
-                        "to-fast-properties": "^2.0.0"
-                    }
-                },
-                "lodash": {
-                    "version": "4.17.19",
-                    "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
-                    "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
-                }
+                "@babel/types": "^7.16.7"
             }
         },
+        "@babel/helper-environment-visitor": {
+            "version": "7.18.2",
+            "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.2.tgz",
+            "integrity": "sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ=="
+        },
         "@babel/helper-function-name": {
-            "version": "7.9.5",
-            "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz",
-            "integrity": "sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==",
+            "version": "7.17.9",
+            "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz",
+            "integrity": "sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==",
             "requires": {
-                "@babel/helper-get-function-arity": "^7.8.3",
-                "@babel/template": "^7.8.3",
-                "@babel/types": "^7.9.5"
+                "@babel/template": "^7.16.7",
+                "@babel/types": "^7.17.0"
             }
         },
-        "@babel/helper-get-function-arity": {
-            "version": "7.8.3",
-            "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz",
-            "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==",
+        "@babel/helper-hoist-variables": {
+            "version": "7.16.7",
+            "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz",
+            "integrity": "sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==",
             "requires": {
-                "@babel/types": "^7.8.3"
+                "@babel/types": "^7.16.7"
             }
         },
         "@babel/helper-module-imports": {
-            "version": "7.10.3",
-            "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.3.tgz",
-            "integrity": "sha512-Jtqw5M9pahLSUWA+76nhK9OG8nwYXzhQzVIGFoNaHnXF/r4l7kz4Fl0UAW7B6mqC5myoJiBP5/YQlXQTMfHI9w==",
+            "version": "7.16.7",
+            "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz",
+            "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==",
             "requires": {
-                "@babel/types": "^7.10.3"
-            },
-            "dependencies": {
-                "@babel/helper-validator-identifier": {
-                    "version": "7.10.3",
-                    "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.3.tgz",
-                    "integrity": "sha512-bU8JvtlYpJSBPuj1VUmKpFGaDZuLxASky3LhaKj3bmpSTY6VWooSM8msk+Z0CZoErFye2tlABF6yDkT3FOPAXw=="
-                },
-                "@babel/types": {
-                    "version": "7.10.3",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.3.tgz",
-                    "integrity": "sha512-nZxaJhBXBQ8HVoIcGsf9qWep3Oh3jCENK54V4mRF7qaJabVsAYdbTtmSD8WmAp1R6ytPiu5apMwSXyxB1WlaBA==",
-                    "requires": {
-                        "@babel/helper-validator-identifier": "^7.10.3",
-                        "lodash": "^4.17.13",
-                        "to-fast-properties": "^2.0.0"
-                    }
-                }
+                "@babel/types": "^7.16.7"
             }
         },
         "@babel/helper-plugin-utils": {
-            "version": "7.16.7",
-            "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz",
-            "integrity": "sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA=="
+            "version": "7.17.12",
+            "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz",
+            "integrity": "sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA=="
         },
         "@babel/helper-split-export-declaration": {
-            "version": "7.8.3",
-            "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz",
-            "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==",
+            "version": "7.16.7",
+            "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz",
+            "integrity": "sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==",
             "requires": {
-                "@babel/types": "^7.8.3"
+                "@babel/types": "^7.16.7"
             }
         },
         "@babel/helper-validator-identifier": {
-            "version": "7.9.5",
-            "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz",
-            "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g=="
+            "version": "7.16.7",
+            "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz",
+            "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw=="
         },
         "@babel/highlight": {
-            "version": "7.9.0",
-            "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
-            "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
+            "version": "7.17.12",
+            "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.17.12.tgz",
+            "integrity": "sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==",
             "requires": {
-                "@babel/helper-validator-identifier": "^7.9.0",
+                "@babel/helper-validator-identifier": "^7.16.7",
                 "chalk": "^2.0.0",
                 "js-tokens": "^4.0.0"
             }
         },
         "@babel/parser": {
-            "version": "7.9.6",
-            "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.6.tgz",
-            "integrity": "sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q=="
+            "version": "7.18.4",
+            "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.4.tgz",
+            "integrity": "sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow=="
         },
         "@babel/plugin-syntax-jsx": {
-            "version": "7.16.7",
-            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz",
-            "integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==",
+            "version": "7.17.12",
+            "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.17.12.tgz",
+            "integrity": "sha512-spyY3E3AURfxh/RHtjx5j6hs8am5NbUBGfcZ2vB3uShSpZdQyXSf5rR5Mk76vbtlAZOelyVQ71Fg0x9SG4fsog==",
             "requires": {
-                "@babel/helper-plugin-utils": "^7.16.7"
+                "@babel/helper-plugin-utils": "^7.17.12"
             }
         },
         "@babel/runtime": {
-            "version": "7.9.2",
-            "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz",
-            "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==",
+            "version": "7.18.3",
+            "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.3.tgz",
+            "integrity": "sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==",
             "requires": {
                 "regenerator-runtime": "^0.13.4"
             }
         },
         "@babel/runtime-corejs3": {
-            "version": "7.17.7",
-            "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.7.tgz",
-            "integrity": "sha512-TvliGJjhxis5m7xIMvlXH/xG8Oa/LK0SCUCyfKD6nLi42n5fB4WibDJ0g9trmmBB6hwpMNx+Lzbxy9/4gpMaVw==",
+            "version": "7.18.3",
+            "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.18.3.tgz",
+            "integrity": "sha512-l4ddFwrc9rnR+EJsHsh+TJ4A35YqQz/UqcjtlX2ov53hlJYG5CxtQmNZxyajwDVmCxwy++rtvGU5HazCK4W41Q==",
             "requires": {
                 "core-js-pure": "^3.20.2",
                 "regenerator-runtime": "^0.13.4"
             }
         },
         "@babel/template": {
-            "version": "7.8.6",
-            "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz",
-            "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==",
+            "version": "7.16.7",
+            "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz",
+            "integrity": "sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==",
             "requires": {
-                "@babel/code-frame": "^7.8.3",
-                "@babel/parser": "^7.8.6",
-                "@babel/types": "^7.8.6"
+                "@babel/code-frame": "^7.16.7",
+                "@babel/parser": "^7.16.7",
+                "@babel/types": "^7.16.7"
             }
         },
         "@babel/traverse": {
-            "version": "7.9.6",
-            "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.6.tgz",
-            "integrity": "sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg==",
-            "requires": {
-                "@babel/code-frame": "^7.8.3",
-                "@babel/generator": "^7.9.6",
-                "@babel/helper-function-name": "^7.9.5",
-                "@babel/helper-split-export-declaration": "^7.8.3",
-                "@babel/parser": "^7.9.6",
-                "@babel/types": "^7.9.6",
+            "version": "7.18.2",
+            "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.2.tgz",
+            "integrity": "sha512-9eNwoeovJ6KH9zcCNnENY7DMFwTU9JdGCFtqNLfUAqtUHRCOsTOqWoffosP8vKmNYeSBUv3yVJXjfd8ucwOjUA==",
+            "requires": {
+                "@babel/code-frame": "^7.16.7",
+                "@babel/generator": "^7.18.2",
+                "@babel/helper-environment-visitor": "^7.18.2",
+                "@babel/helper-function-name": "^7.17.9",
+                "@babel/helper-hoist-variables": "^7.16.7",
+                "@babel/helper-split-export-declaration": "^7.16.7",
+                "@babel/parser": "^7.18.0",
+                "@babel/types": "^7.18.2",
                 "debug": "^4.1.0",
-                "globals": "^11.1.0",
-                "lodash": "^4.17.13"
+                "globals": "^11.1.0"
             },
             "dependencies": {
-                "@babel/types": {
-                    "version": "7.9.6",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.6.tgz",
-                    "integrity": "sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==",
-                    "requires": {
-                        "@babel/helper-validator-identifier": "^7.9.5",
-                        "lodash": "^4.17.13",
-                        "to-fast-properties": "^2.0.0"
-                    }
-                },
                 "debug": {
-                    "version": "4.1.1",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
-                    "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+                    "version": "4.3.4",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
                     "requires": {
-                        "ms": "^2.1.1"
+                        "ms": "2.1.2"
                     }
+                },
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
                 }
             }
         },
         "@babel/types": {
-            "version": "7.9.5",
-            "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.5.tgz",
-            "integrity": "sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg==",
+            "version": "7.18.4",
+            "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.4.tgz",
+            "integrity": "sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==",
             "requires": {
-                "@babel/helper-validator-identifier": "^7.9.5",
-                "lodash": "^4.17.13",
+                "@babel/helper-validator-identifier": "^7.16.7",
                 "to-fast-properties": "^2.0.0"
             }
         },
@@ -228,9 +175,9 @@
             "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw=="
         },
         "@emotion/babel-plugin": {
-            "version": "11.7.2",
-            "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.7.2.tgz",
-            "integrity": "sha512-6mGSCWi9UzXut/ZAN6lGFu33wGR3SJisNl3c0tvlmb8XChH1b2SUvxvnOh7hvLpqyRdHHU9AiazV3Cwbk5SXKQ==",
+            "version": "11.9.2",
+            "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.9.2.tgz",
+            "integrity": "sha512-Pr/7HGH6H6yKgnVFNEj2MVlreu3ADqftqjqwUvDy/OJzKFgxKeTQ+eeUf20FOTuHVkDON2iNa25rAXVYtWJCjw==",
             "requires": {
                 "@babel/helper-module-imports": "^7.12.13",
                 "@babel/plugin-syntax-jsx": "^7.12.13",
@@ -246,45 +193,15 @@
                 "stylis": "4.0.13"
             },
             "dependencies": {
-                "@babel/helper-module-imports": {
-                    "version": "7.16.7",
-                    "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz",
-                    "integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==",
-                    "requires": {
-                        "@babel/types": "^7.16.7"
-                    }
-                },
-                "@babel/helper-validator-identifier": {
-                    "version": "7.16.7",
-                    "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz",
-                    "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw=="
-                },
-                "@babel/runtime": {
-                    "version": "7.17.7",
-                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz",
-                    "integrity": "sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==",
-                    "requires": {
-                        "regenerator-runtime": "^0.13.4"
-                    }
-                },
-                "@babel/types": {
-                    "version": "7.17.0",
-                    "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz",
-                    "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==",
-                    "requires": {
-                        "@babel/helper-validator-identifier": "^7.16.7",
-                        "to-fast-properties": "^2.0.0"
-                    }
-                },
                 "@emotion/memoize": {
                     "version": "0.7.5",
                     "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz",
                     "integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ=="
                 },
                 "@emotion/serialize": {
-                    "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz",
-                    "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==",
+                    "version": "1.0.3",
+                    "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.3.tgz",
+                    "integrity": "sha512-2mSSvgLfyV3q+iVh3YWgNlUc2a9ZlDU7DjuP5MjK3AXRR0dYigCrP99aeFtaB2L/hjfEZdSThn5dsZ0ufqbvsA==",
                     "requires": {
                         "@emotion/hash": "^0.8.0",
                         "@emotion/memoize": "^0.7.4",
@@ -299,19 +216,14 @@
                     "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
                 },
                 "csstype": {
-                    "version": "3.0.11",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
-                    "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
                 },
                 "escape-string-regexp": {
                     "version": "4.0.0",
                     "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
                     "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
-                },
-                "stylis": {
-                    "version": "4.0.13",
-                    "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
-                    "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
                 }
             }
         },
@@ -352,13 +264,13 @@
             }
         },
         "@emotion/css": {
-            "version": "11.7.1",
-            "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.7.1.tgz",
-            "integrity": "sha512-RUUgPlMZunlc7SE5A6Hg+VWRzb2cU6O9xlV78KCFgcnl25s7Qz/20oQg71iKudpLqk7xj0vhbJlwcJJMT0BOZg==",
+            "version": "11.9.0",
+            "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.9.0.tgz",
+            "integrity": "sha512-S9UjCxSrxEHawOLnWw4upTwfYKb0gVQdatHejn3W9kPyXxmKv3HmjVfJ84kDLmdX8jR20OuDQwaJ4Um24qD9vA==",
             "requires": {
                 "@emotion/babel-plugin": "^11.7.1",
                 "@emotion/cache": "^11.7.1",
-                "@emotion/serialize": "^1.0.0",
+                "@emotion/serialize": "^1.0.3",
                 "@emotion/sheet": "^1.0.3",
                 "@emotion/utils": "^1.0.0"
             },
@@ -376,9 +288,9 @@
                     }
                 },
                 "@emotion/serialize": {
-                    "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz",
-                    "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==",
+                    "version": "1.0.3",
+                    "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.3.tgz",
+                    "integrity": "sha512-2mSSvgLfyV3q+iVh3YWgNlUc2a9ZlDU7DjuP5MjK3AXRR0dYigCrP99aeFtaB2L/hjfEZdSThn5dsZ0ufqbvsA==",
                     "requires": {
                         "@emotion/hash": "^0.8.0",
                         "@emotion/memoize": "^0.7.4",
@@ -398,14 +310,9 @@
                     "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
                 },
                 "csstype": {
-                    "version": "3.0.11",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
-                    "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
-                },
-                "stylis": {
-                    "version": "4.0.13",
-                    "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
-                    "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
                 }
             }
         },
@@ -415,11 +322,11 @@
             "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
         },
         "@emotion/is-prop-valid": {
-            "version": "0.8.8",
-            "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
-            "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.2.tgz",
+            "integrity": "sha512-3QnhqeL+WW88YjYbQL5gUIkthuMw7a0NGbZ7wfFVk2kg/CK5w8w5FFa0RzWjyY1+sujN0NWbtSHH6OJmWHtJpQ==",
             "requires": {
-                "@emotion/memoize": "0.7.4"
+                "@emotion/memoize": "^0.7.4"
             }
         },
         "@emotion/memoize": {
@@ -428,27 +335,19 @@
             "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw=="
         },
         "@emotion/react": {
-            "version": "11.8.2",
-            "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.8.2.tgz",
-            "integrity": "sha512-+1bcHBaNJv5nkIIgnGKVsie3otS0wF9f1T1hteF3WeVvMNQEtfZ4YyFpnphGoot3ilU/wWMgP2SgIDuHLE/wAA==",
+            "version": "11.9.0",
+            "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.9.0.tgz",
+            "integrity": "sha512-lBVSF5d0ceKtfKCDQJveNAtkC7ayxpVlgOohLgXqRwqWr9bOf4TZAFFyIcNngnV6xK6X4x2ZeXq7vliHkoVkxQ==",
             "requires": {
                 "@babel/runtime": "^7.13.10",
                 "@emotion/babel-plugin": "^11.7.1",
                 "@emotion/cache": "^11.7.1",
-                "@emotion/serialize": "^1.0.2",
+                "@emotion/serialize": "^1.0.3",
                 "@emotion/utils": "^1.1.0",
                 "@emotion/weak-memoize": "^0.2.5",
                 "hoist-non-react-statics": "^3.3.1"
             },
             "dependencies": {
-                "@babel/runtime": {
-                    "version": "7.17.7",
-                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz",
-                    "integrity": "sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==",
-                    "requires": {
-                        "regenerator-runtime": "^0.13.4"
-                    }
-                },
                 "@emotion/cache": {
                     "version": "11.7.1",
                     "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz",
@@ -462,9 +361,9 @@
                     }
                 },
                 "@emotion/serialize": {
-                    "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz",
-                    "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==",
+                    "version": "1.0.3",
+                    "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.3.tgz",
+                    "integrity": "sha512-2mSSvgLfyV3q+iVh3YWgNlUc2a9ZlDU7DjuP5MjK3AXRR0dYigCrP99aeFtaB2L/hjfEZdSThn5dsZ0ufqbvsA==",
                     "requires": {
                         "@emotion/hash": "^0.8.0",
                         "@emotion/memoize": "^0.7.4",
@@ -484,14 +383,9 @@
                     "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
                 },
                 "csstype": {
-                    "version": "3.0.11",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
-                    "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
-                },
-                "stylis": {
-                    "version": "4.0.13",
-                    "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
-                    "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
                 }
             }
         },
@@ -524,26 +418,10 @@
                 "@emotion/utils": "^1.1.0"
             },
             "dependencies": {
-                "@babel/runtime": {
-                    "version": "7.17.7",
-                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz",
-                    "integrity": "sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==",
-                    "requires": {
-                        "regenerator-runtime": "^0.13.4"
-                    }
-                },
-                "@emotion/is-prop-valid": {
-                    "version": "1.1.2",
-                    "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.2.tgz",
-                    "integrity": "sha512-3QnhqeL+WW88YjYbQL5gUIkthuMw7a0NGbZ7wfFVk2kg/CK5w8w5FFa0RzWjyY1+sujN0NWbtSHH6OJmWHtJpQ==",
-                    "requires": {
-                        "@emotion/memoize": "^0.7.4"
-                    }
-                },
                 "@emotion/serialize": {
-                    "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz",
-                    "integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==",
+                    "version": "1.0.3",
+                    "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.3.tgz",
+                    "integrity": "sha512-2mSSvgLfyV3q+iVh3YWgNlUc2a9ZlDU7DjuP5MjK3AXRR0dYigCrP99aeFtaB2L/hjfEZdSThn5dsZ0ufqbvsA==",
                     "requires": {
                         "@emotion/hash": "^0.8.0",
                         "@emotion/memoize": "^0.7.4",
@@ -558,9 +436,9 @@
                     "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
                 },
                 "csstype": {
-                    "version": "3.0.11",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
-                    "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
                 }
             }
         },
@@ -648,51 +526,23 @@
             "integrity": "sha512-RwLIB4TZw0M9gvy5u+TusAA0afbwM4JQIimNH/j3ygd6aIvYPQLqXMhC9ErY26J23rDPyDZldIfPq/HpTTJ/tQ==",
             "requires": {
                 "prop-types": "^15.8.1"
-            },
-            "dependencies": {
-                "prop-types": {
-                    "version": "15.8.1",
-                    "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
-                    "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
-                    "requires": {
-                        "loose-envify": "^1.4.0",
-                        "object-assign": "^4.1.1",
-                        "react-is": "^16.13.1"
-                    }
-                }
             }
         },
         "@googlemaps/js-api-loader": {
-            "version": "1.12.8",
-            "resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.12.8.tgz",
-            "integrity": "sha512-h1RNVIJkHWL4UrdJo8fwECJwSRg8CSIfTEtPdzvThcji7tAPvVBDVBPzvPKhvl8e8mWEGPhnYOt6kPfSxJL+vQ==",
+            "version": "1.14.3",
+            "resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.14.3.tgz",
+            "integrity": "sha512-6iIb+qpGgQpgIHmIFO44WhE1rDUxPVHuezNFL30wRJnkvhwFm94tD291UvNg9L05hLDSoL16jd0lbqqmdy4C5g==",
             "requires": {
                 "fast-deep-equal": "^3.1.3"
-            },
-            "dependencies": {
-                "fast-deep-equal": {
-                    "version": "3.1.3",
-                    "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
-                    "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
-                }
             }
         },
         "@googlemaps/markerclusterer": {
-            "version": "1.0.12",
-            "resolved": "https://registry.npmjs.org/@googlemaps/markerclusterer/-/markerclusterer-1.0.12.tgz",
-            "integrity": "sha512-DSmN2w1ZoI+PBWfNCKYcr+zalP0uaaKIU08+UgebJYZ96X+J6CdUpD+xOVwBV1OJDiIXkneHquOZnT+V6dm+Sg==",
+            "version": "2.0.7",
+            "resolved": "https://registry.npmjs.org/@googlemaps/markerclusterer/-/markerclusterer-2.0.7.tgz",
+            "integrity": "sha512-/xADL31ERrNoVsF+O2A+H+obxOki4DHPQkU/STS5gFG6ZZVpvNV4WLisW2aOlOsH3rUXSq3MFjqFOEDXSQcvog==",
             "requires": {
-                "@turf/clusters-dbscan": "^6.4.0",
-                "@turf/clusters-kmeans": "^6.4.0",
                 "fast-deep-equal": "^3.1.3",
                 "supercluster": "^7.1.3"
-            },
-            "dependencies": {
-                "fast-deep-equal": {
-                    "version": "3.1.3",
-                    "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
-                    "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
-                }
             }
         },
         "@hig/flyout": {
@@ -716,9 +566,9 @@
             }
         },
         "@hig/theme-data": {
-            "version": "2.24.0",
-            "resolved": "https://registry.npmjs.org/@hig/theme-data/-/theme-data-2.24.0.tgz",
-            "integrity": "sha512-/71T3kezuom7z60Bxyxwxo0oHqb2B59jCbdWrgRgUYorK0vO6zjFmQvRmPSgxq2i1bQxQLx12iuzW9cM494pMQ==",
+            "version": "2.26.0",
+            "resolved": "https://registry.npmjs.org/@hig/theme-data/-/theme-data-2.26.0.tgz",
+            "integrity": "sha512-jEiCieyafBePgFLlIPRPIhNPulWELeOgWJUY4wxdvozRdFUEq50ydx0ysuYPQOh4ucy0jI7Q8FcsBd+aEe7WXQ==",
             "requires": {
                 "tinycolor2": "^1.4.1"
             }
@@ -756,20 +606,63 @@
             "resolved": "https://registry.npmjs.org/@icons/material/-/material-0.2.4.tgz",
             "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw=="
         },
+        "@jridgewell/gen-mapping": {
+            "version": "0.3.1",
+            "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz",
+            "integrity": "sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==",
+            "requires": {
+                "@jridgewell/set-array": "^1.0.0",
+                "@jridgewell/sourcemap-codec": "^1.4.10",
+                "@jridgewell/trace-mapping": "^0.3.9"
+            }
+        },
+        "@jridgewell/resolve-uri": {
+            "version": "3.0.7",
+            "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz",
+            "integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA=="
+        },
+        "@jridgewell/set-array": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.1.tgz",
+            "integrity": "sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ=="
+        },
+        "@jridgewell/source-map": {
+            "version": "0.3.2",
+            "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
+            "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
+            "requires": {
+                "@jridgewell/gen-mapping": "^0.3.0",
+                "@jridgewell/trace-mapping": "^0.3.9"
+            }
+        },
+        "@jridgewell/sourcemap-codec": {
+            "version": "1.4.13",
+            "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz",
+            "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w=="
+        },
+        "@jridgewell/trace-mapping": {
+            "version": "0.3.13",
+            "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz",
+            "integrity": "sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==",
+            "requires": {
+                "@jridgewell/resolve-uri": "^3.0.3",
+                "@jridgewell/sourcemap-codec": "^1.4.10"
+            }
+        },
         "@log4js-node/log4js-api": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/@log4js-node/log4js-api/-/log4js-api-1.0.2.tgz",
             "integrity": "sha512-6SJfx949YEWooh/CUPpJ+F491y4BYJmknz4hUN1+RHvKoUEynKbRmhnwbk/VLmh4OthLLDNCyWXfbh4DG1cTXA=="
         },
         "@mapbox/node-pre-gyp": {
-            "version": "1.0.8",
-            "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz",
-            "integrity": "sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg==",
+            "version": "1.0.9",
+            "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.9.tgz",
+            "integrity": "sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==",
             "requires": {
-                "detect-libc": "^1.0.3",
+                "detect-libc": "^2.0.0",
                 "https-proxy-agent": "^5.0.0",
                 "make-dir": "^3.1.0",
-                "node-fetch": "^2.6.5",
+                "node-fetch": "^2.6.7",
                 "nopt": "^5.0.0",
                 "npmlog": "^5.0.1",
                 "rimraf": "^3.0.2",
@@ -777,69 +670,6 @@
                 "tar": "^6.1.11"
             },
             "dependencies": {
-                "ansi-regex": {
-                    "version": "5.0.1",
-                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
-                    "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
-                },
-                "are-we-there-yet": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
-                    "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
-                    "requires": {
-                        "delegates": "^1.0.0",
-                        "readable-stream": "^3.6.0"
-                    }
-                },
-                "emoji-regex": {
-                    "version": "8.0.0",
-                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
-                    "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
-                },
-                "gauge": {
-                    "version": "3.0.2",
-                    "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
-                    "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
-                    "requires": {
-                        "aproba": "^1.0.3 || ^2.0.0",
-                        "color-support": "^1.1.2",
-                        "console-control-strings": "^1.0.0",
-                        "has-unicode": "^2.0.1",
-                        "object-assign": "^4.1.1",
-                        "signal-exit": "^3.0.0",
-                        "string-width": "^4.2.3",
-                        "strip-ansi": "^6.0.1",
-                        "wide-align": "^1.1.2"
-                    }
-                },
-                "is-fullwidth-code-point": {
-                    "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
-                    "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
-                },
-                "lru-cache": {
-                    "version": "6.0.0",
-                    "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
-                    "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
-                    "requires": {
-                        "yallist": "^4.0.0"
-                    }
-                },
-                "make-dir": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
-                    "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
-                    "requires": {
-                        "semver": "^6.0.0"
-                    },
-                    "dependencies": {
-                        "semver": {
-                            "version": "6.3.0",
-                            "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
-                            "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
-                        }
-                    }
-                },
                 "node-fetch": {
                     "version": "2.6.7",
                     "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
@@ -848,79 +678,26 @@
                         "whatwg-url": "^5.0.0"
                     }
                 },
-                "npmlog": {
-                    "version": "5.0.1",
-                    "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
-                    "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
-                    "requires": {
-                        "are-we-there-yet": "^2.0.0",
-                        "console-control-strings": "^1.1.0",
-                        "gauge": "^3.0.0",
-                        "set-blocking": "^2.0.0"
-                    }
-                },
                 "semver": {
-                    "version": "7.3.5",
-                    "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
-                    "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+                    "version": "7.3.7",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
+                    "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
                     "requires": {
                         "lru-cache": "^6.0.0"
                     }
-                },
-                "string-width": {
-                    "version": "4.2.3",
-                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
-                    "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-                    "requires": {
-                        "emoji-regex": "^8.0.0",
-                        "is-fullwidth-code-point": "^3.0.0",
-                        "strip-ansi": "^6.0.1"
-                    }
-                },
-                "strip-ansi": {
-                    "version": "6.0.1",
-                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
-                    "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-                    "requires": {
-                        "ansi-regex": "^5.0.1"
-                    }
-                },
-                "tr46": {
-                    "version": "0.0.3",
-                    "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
-                    "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
-                },
-                "webidl-conversions": {
-                    "version": "3.0.1",
-                    "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
-                    "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
-                },
-                "whatwg-url": {
-                    "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
-                    "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
-                    "requires": {
-                        "tr46": "~0.0.3",
-                        "webidl-conversions": "^3.0.0"
-                    }
-                },
-                "yallist": {
-                    "version": "4.0.0",
-                    "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-                    "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
                 }
             }
         },
         "@material-ui/core": {
-            "version": "4.12.3",
-            "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.3.tgz",
-            "integrity": "sha512-sdpgI/PL56QVsEJldwEe4FFaFTLUqN+rd7sSZiRCdx2E/C7z5yK0y/khAWVBH24tXwto7I1hCzNWfJGZIYJKnw==",
+            "version": "4.12.4",
+            "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.4.tgz",
+            "integrity": "sha512-tr7xekNlM9LjA6pagJmL8QCgZXaubWUwkJnoYcMKd4gw/t4XiyvnTkjdGrUVicyB2BsdaAv1tvow45bPM4sSwQ==",
             "requires": {
                 "@babel/runtime": "^7.4.4",
-                "@material-ui/styles": "^4.11.4",
-                "@material-ui/system": "^4.12.1",
+                "@material-ui/styles": "^4.11.5",
+                "@material-ui/system": "^4.12.2",
                 "@material-ui/types": "5.1.0",
-                "@material-ui/utils": "^4.11.2",
+                "@material-ui/utils": "^4.11.3",
                 "@types/react-transition-group": "^4.2.0",
                 "clsx": "^1.0.4",
                 "hoist-non-react-statics": "^3.3.2",
@@ -931,9 +708,9 @@
             },
             "dependencies": {
                 "csstype": {
-                    "version": "3.0.11",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
-                    "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
                 },
                 "dom-helpers": {
                     "version": "5.2.1",
@@ -958,14 +735,14 @@
             }
         },
         "@material-ui/styles": {
-            "version": "4.11.4",
-            "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.4.tgz",
-            "integrity": "sha512-KNTIZcnj/zprG5LW0Sao7zw+yG3O35pviHzejMdcSGCdWbiO8qzRgOYL8JAxAsWBKOKYwVZxXtHWaB5T2Kvxew==",
+            "version": "4.11.5",
+            "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.5.tgz",
+            "integrity": "sha512-o/41ot5JJiUsIETME9wVLAJrmIWL3j0R0Bj2kCOLbSfqEkKf0fmaPt+5vtblUh5eXr2S+J/8J3DaCb10+CzPGA==",
             "requires": {
                 "@babel/runtime": "^7.4.4",
                 "@emotion/hash": "^0.8.0",
                 "@material-ui/types": "5.1.0",
-                "@material-ui/utils": "^4.11.2",
+                "@material-ui/utils": "^4.11.3",
                 "clsx": "^1.0.4",
                 "csstype": "^2.5.2",
                 "hoist-non-react-statics": "^3.3.2",
@@ -981,12 +758,12 @@
             }
         },
         "@material-ui/system": {
-            "version": "4.12.1",
-            "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.1.tgz",
-            "integrity": "sha512-lUdzs4q9kEXZGhbN7BptyiS1rLNHe6kG9o8Y307HCvF4sQxbCgpL2qi+gUk+yI8a2DNk48gISEQxoxpgph0xIw==",
+            "version": "4.12.2",
+            "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.2.tgz",
+            "integrity": "sha512-6CSKu2MtmiJgcCGf6nBQpM8fLkuB9F55EKfbdTC80NND5wpTmKzwdhLYLH3zL4cLlK0gVaaltW7/wMuyTnN0Lw==",
             "requires": {
                 "@babel/runtime": "^7.4.4",
-                "@material-ui/utils": "^4.11.2",
+                "@material-ui/utils": "^4.11.3",
                 "csstype": "^2.5.2",
                 "prop-types": "^15.7.2"
             }
@@ -997,9 +774,9 @@
             "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A=="
         },
         "@material-ui/utils": {
-            "version": "4.11.2",
-            "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.2.tgz",
-            "integrity": "sha512-Uul8w38u+PICe2Fg2pDKCaIG7kOyhowZ9vjiC1FsVwPABTW8vPPKfF6OvxRq3IiBaI1faOJmgdvMG7rMJARBhA==",
+            "version": "4.11.3",
+            "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.3.tgz",
+            "integrity": "sha512-ZuQPV4rBK/V1j2dIkSSEcH5uT6AaHuKWFfotADHsC0wVL1NLd2WkFCm4ZZbX33iO4ydl6V0GPngKm8HZQ2oujg==",
             "requires": {
                 "@babel/runtime": "^7.4.4",
                 "prop-types": "^15.7.2",
@@ -1007,27 +784,27 @@
             }
         },
         "@react-google-maps/api": {
-            "version": "2.7.0",
-            "resolved": "https://registry.npmjs.org/@react-google-maps/api/-/api-2.7.0.tgz",
-            "integrity": "sha512-Fb/l7aR69ObYvzhpt1d1931454ylRkTPKF1C8GfLvXtg19wc36zTev2Afbahd+qAdyKuQyDdf5/SQyMjYBHI/A==",
-            "requires": {
-                "@googlemaps/js-api-loader": "1.12.8",
-                "@googlemaps/markerclusterer": "1.0.12",
-                "@react-google-maps/infobox": "2.6.0",
-                "@react-google-maps/marker-clusterer": "2.6.0",
-                "@types/google.maps": "3.46.1",
+            "version": "2.12.0",
+            "resolved": "https://registry.npmjs.org/@react-google-maps/api/-/api-2.12.0.tgz",
+            "integrity": "sha512-BqsclJ9p2I6yZa++0R3oEcneECcH7PFsULhn8UDlWQ9uNAQR/aqvqGb9SXYvdpIAOJdk3E737iTrGhFg4j5Ucw==",
+            "requires": {
+                "@googlemaps/js-api-loader": "1.14.3",
+                "@googlemaps/markerclusterer": "2.0.7",
+                "@react-google-maps/infobox": "2.11.8",
+                "@react-google-maps/marker-clusterer": "2.11.8",
+                "@types/google.maps": "3.49.1",
                 "invariant": "2.2.4"
             }
         },
         "@react-google-maps/infobox": {
-            "version": "2.6.0",
-            "resolved": "https://registry.npmjs.org/@react-google-maps/infobox/-/infobox-2.6.0.tgz",
-            "integrity": "sha512-2dXVZsuZ2PjelVg+slpUSYPnvoSIySwf4P/wScGijjqu6bpvhyX9AivitsF7zHfbwnZ0gAPX1Q8xtQhKgawkEg=="
+            "version": "2.11.8",
+            "resolved": "https://registry.npmjs.org/@react-google-maps/infobox/-/infobox-2.11.8.tgz",
+            "integrity": "sha512-15y3dniys1Op5HREyLwiJI2pAdeeHRJT5b4WPrS+vyL5KF6iXK1P3y/9bXtnxHiKe2v6iPreaEhR3VgvfRA50Q=="
         },
         "@react-google-maps/marker-clusterer": {
-            "version": "2.6.0",
-            "resolved": "https://registry.npmjs.org/@react-google-maps/marker-clusterer/-/marker-clusterer-2.6.0.tgz",
-            "integrity": "sha512-EjeoCM+U/6W9qoGWqurcDbCubqTEDTFET7Jd3XcLHqTLvFAE473x2YbQg98mJXKNgPKcPTNEcTiYYGiIz9tEjA=="
+            "version": "2.11.8",
+            "resolved": "https://registry.npmjs.org/@react-google-maps/marker-clusterer/-/marker-clusterer-2.11.8.tgz",
+            "integrity": "sha512-OEcdbTXEgPwdpGvVuSrhIhjcZPhlzPz2TvaKCBLraf1PKiDyLOPhlTVRlbIziLiu0xN89pJcill1w9UVIVFF+Q=="
         },
         "@react-three/fiber": {
             "version": "6.2.3",
@@ -1044,16 +821,6 @@
                 "use-asset": "^1.0.4",
                 "utility-types": "^3.10.0",
                 "zustand": "^3.5.1"
-            },
-            "dependencies": {
-                "@babel/runtime": {
-                    "version": "7.17.7",
-                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz",
-                    "integrity": "sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==",
-                    "requires": {
-                        "regenerator-runtime": "^0.13.4"
-                    }
-                }
             }
         },
         "@rkusa/linebreak": {
@@ -1077,68 +844,6 @@
                 "defer-to-connect": "^2.0.1"
             }
         },
-        "@turf/clone": {
-            "version": "6.5.0",
-            "resolved": "https://registry.npmjs.org/@turf/clone/-/clone-6.5.0.tgz",
-            "integrity": "sha512-mzVtTFj/QycXOn6ig+annKrM6ZlimreKYz6f/GSERytOpgzodbQyOgkfwru100O1KQhhjSudKK4DsQ0oyi9cTw==",
-            "requires": {
-                "@turf/helpers": "^6.5.0"
-            }
-        },
-        "@turf/clusters-dbscan": {
-            "version": "6.5.0",
-            "resolved": "https://registry.npmjs.org/@turf/clusters-dbscan/-/clusters-dbscan-6.5.0.tgz",
-            "integrity": "sha512-SxZEE4kADU9DqLRiT53QZBBhu8EP9skviSyl+FGj08Y01xfICM/RR9ACUdM0aEQimhpu+ZpRVcUK+2jtiCGrYQ==",
-            "requires": {
-                "@turf/clone": "^6.5.0",
-                "@turf/distance": "^6.5.0",
-                "@turf/helpers": "^6.5.0",
-                "@turf/meta": "^6.5.0",
-                "density-clustering": "1.3.0"
-            }
-        },
-        "@turf/clusters-kmeans": {
-            "version": "6.5.0",
-            "resolved": "https://registry.npmjs.org/@turf/clusters-kmeans/-/clusters-kmeans-6.5.0.tgz",
-            "integrity": "sha512-DwacD5+YO8kwDPKaXwT9DV46tMBVNsbi1IzdajZu1JDSWoN7yc7N9Qt88oi+p30583O0UPVkAK+A10WAQv4mUw==",
-            "requires": {
-                "@turf/clone": "^6.5.0",
-                "@turf/helpers": "^6.5.0",
-                "@turf/invariant": "^6.5.0",
-                "@turf/meta": "^6.5.0",
-                "skmeans": "0.9.7"
-            }
-        },
-        "@turf/distance": {
-            "version": "6.5.0",
-            "resolved": "https://registry.npmjs.org/@turf/distance/-/distance-6.5.0.tgz",
-            "integrity": "sha512-xzykSLfoURec5qvQJcfifw/1mJa+5UwByZZ5TZ8iaqjGYN0vomhV9aiSLeYdUGtYRESZ+DYC/OzY+4RclZYgMg==",
-            "requires": {
-                "@turf/helpers": "^6.5.0",
-                "@turf/invariant": "^6.5.0"
-            }
-        },
-        "@turf/helpers": {
-            "version": "6.5.0",
-            "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.5.0.tgz",
-            "integrity": "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw=="
-        },
-        "@turf/invariant": {
-            "version": "6.5.0",
-            "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.5.0.tgz",
-            "integrity": "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg==",
-            "requires": {
-                "@turf/helpers": "^6.5.0"
-            }
-        },
-        "@turf/meta": {
-            "version": "6.5.0",
-            "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.5.0.tgz",
-            "integrity": "sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA==",
-            "requires": {
-                "@turf/helpers": "^6.5.0"
-            }
-        },
         "@types/adm-zip": {
             "version": "0.4.34",
             "resolved": "https://registry.npmjs.org/@types/adm-zip/-/adm-zip-0.4.34.tgz",
@@ -1175,14 +880,14 @@
             "dev": true
         },
         "@types/babel-types": {
-            "version": "7.0.7",
-            "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.7.tgz",
-            "integrity": "sha512-dBtBbrc+qTHy1WdfHYjBwRln4+LWqASWakLHsWHR2NWHIFkv4W3O070IGoGLEBrJBvct3r0L1BUPuvURi7kYUQ=="
+            "version": "7.0.11",
+            "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.11.tgz",
+            "integrity": "sha512-pkPtJUUY+Vwv6B1inAz55rQvivClHJxc9aVEPPmaq2cbyeMLCiDpbKpcKyX4LAwpNGi+SHBv0tHv6+0gXv0P2A=="
         },
         "@types/babylon": {
-            "version": "6.16.5",
-            "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.5.tgz",
-            "integrity": "sha512-xH2e58elpj1X4ynnKp9qSnWlsRTIs6n3tgLGNfwAGHwePw0mulHQllV34n0T25uYSu1k0hRKkWXF890B1yS47w==",
+            "version": "6.16.6",
+            "resolved": "https://registry.npmjs.org/@types/babylon/-/babylon-6.16.6.tgz",
+            "integrity": "sha512-G4yqdVlhr6YhzLXFKy5F7HtRBU8Y23+iWy7UKthMq/OSQnL1hbsoeXESQ2LY8zEDlknipDG3nRGhUC9tkwvy/w==",
             "requires": {
                 "@types/babel-types": "*"
             }
@@ -1190,7 +895,7 @@
         "@types/bcrypt-nodejs": {
             "version": "0.0.30",
             "resolved": "https://registry.npmjs.org/@types/bcrypt-nodejs/-/bcrypt-nodejs-0.0.30.tgz",
-            "integrity": "sha1-TN2WtJKTs5MhIuS34pVD415rrlg=",
+            "integrity": "sha512-gSWCu7EOXhcM0FYM8tanV0deaX1VM07sgBS8dSUmgnAQ5/3Xn6k+mWF+ZfE+c/OCW14IVnNdTOKcHgvL78oDFQ==",
             "dev": true
         },
         "@types/bezier-js": {
@@ -1215,11 +920,18 @@
             }
         },
         "@types/bson": {
-            "version": "4.0.2",
-            "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.2.tgz",
-            "integrity": "sha512-+uWmsejEHfmSjyyM/LkrP0orfE2m5Mx9Xel4tXNeqi1ldK5XMQcDsFkBmLDtuyKUbxj2jGDo0H240fbCRJZo7Q==",
+            "version": "4.0.5",
+            "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.5.tgz",
+            "integrity": "sha512-vVLwMUqhYJSQ/WKcE60eFqcyuWse5fGH+NMAXHuKrUAPoryq3ATxk5o4bgYNtg5aOM4APVg7Hnb3ASqUYG0PKg==",
             "requires": {
                 "@types/node": "*"
+            },
+            "dependencies": {
+                "@types/node": {
+                    "version": "17.0.41",
+                    "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.41.tgz",
+                    "integrity": "sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw=="
+                }
             }
         },
         "@types/cacheable-request": {
@@ -1231,6 +943,13 @@
                 "@types/keyv": "*",
                 "@types/node": "*",
                 "@types/responselike": "*"
+            },
+            "dependencies": {
+                "@types/node": {
+                    "version": "17.0.41",
+                    "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.41.tgz",
+                    "integrity": "sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw=="
+                }
             }
         },
         "@types/caseless": {
@@ -1240,9 +959,9 @@
             "dev": true
         },
         "@types/chai": {
-            "version": "4.3.0",
-            "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz",
-            "integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==",
+            "version": "4.3.1",
+            "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.1.tgz",
+            "integrity": "sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ==",
             "dev": true
         },
         "@types/color": {
@@ -1266,7 +985,8 @@
         "@types/color-name": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
-            "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
+            "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
+            "dev": true
         },
         "@types/connect": {
             "version": "3.4.35",
@@ -1287,9 +1007,9 @@
             }
         },
         "@types/cookie-parser": {
-            "version": "1.4.2",
-            "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.2.tgz",
-            "integrity": "sha512-uwcY8m6SDQqciHsqcKDGbo10GdasYsPCYkH3hVegj9qAah6pX5HivOnOuI3WYmyQMnOATV39zv/Ybs0bC/6iVg==",
+            "version": "1.4.3",
+            "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.3.tgz",
+            "integrity": "sha512-CqSKwFwefj4PzZ5n/iwad/bow2hTCh0FlNAeWLtQM3JA/NX/iYagIpWG2cf1bQKQ2c9gU2log5VUCrn7LDOs0w==",
             "dev": true,
             "requires": {
                 "@types/express": "*"
@@ -1365,9 +1085,9 @@
             }
         },
         "@types/eslint": {
-            "version": "8.4.1",
-            "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz",
-            "integrity": "sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA==",
+            "version": "8.4.3",
+            "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.3.tgz",
+            "integrity": "sha512-YP1S7YJRMPs+7KZKDb9G63n8YejIwW9BALq7a5j2+H4yl6iOv9CB29edho+cuFRrvmJbbaH2yiVChKLJVysDGw==",
             "dev": true,
             "requires": {
                 "@types/estree": "*",
@@ -1468,6 +1188,13 @@
             "requires": {
                 "@types/events": "*",
                 "@types/node": "*"
+            },
+            "dependencies": {
+                "@types/node": {
+                    "version": "17.0.41",
+                    "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.41.tgz",
+                    "integrity": "sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw=="
+                }
             }
         },
         "@types/geojson": {
@@ -1503,9 +1230,9 @@
             }
         },
         "@types/google.maps": {
-            "version": "3.46.1",
-            "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.46.1.tgz",
-            "integrity": "sha512-GAa5ZWYgXG50yLXybb7A824esGm/L0LKHS7qD0qkP0IA/Qp5r922P9tmYcbCkGEf3Zgf7Ukbp7l08/IGIJuQwQ=="
+            "version": "3.49.1",
+            "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.49.1.tgz",
+            "integrity": "sha512-Sbl5anucT7LUcUxXsRxkCozHdXIkUiY+Tyru+OVl5rot0+VIZuuulmABC7X+nF7rL7BRTAguSBSAD/e/AfIkkA=="
         },
         "@types/hoist-non-react-statics": {
             "version": "3.3.1",
@@ -1514,6 +1241,23 @@
             "requires": {
                 "@types/react": "*",
                 "hoist-non-react-statics": "^3.3.0"
+            },
+            "dependencies": {
+                "@types/react": {
+                    "version": "18.0.12",
+                    "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.12.tgz",
+                    "integrity": "sha512-duF1OTASSBQtcigUvhuiTB1Ya3OvSy+xORCiEf20H0P0lzx+/KeVsA99U5UjLXSbyo1DRJDlLKqTeM1ngosqtg==",
+                    "requires": {
+                        "@types/prop-types": "*",
+                        "@types/scheduler": "*",
+                        "csstype": "^3.0.2"
+                    }
+                },
+                "csstype": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
+                }
             }
         },
         "@types/html-minifier-terser": {
@@ -1543,12 +1287,24 @@
                 "@types/node": "*",
                 "@types/parse5": "*",
                 "@types/tough-cookie": "*"
-            }
+            },
+            "dependencies": {
+                "@types/node": {
+                    "version": "17.0.41",
+                    "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.41.tgz",
+                    "integrity": "sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw=="
+                }
+            }
+        },
+        "@types/json-buffer": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/@types/json-buffer/-/json-buffer-3.0.0.tgz",
+            "integrity": "sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ=="
         },
         "@types/json-schema": {
-            "version": "7.0.9",
-            "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz",
-            "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ=="
+            "version": "7.0.11",
+            "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
+            "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ=="
         },
         "@types/keygrip": {
             "version": "1.0.2",
@@ -1557,11 +1313,18 @@
             "dev": true
         },
         "@types/keyv": {
-            "version": "3.1.3",
-            "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz",
-            "integrity": "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==",
+            "version": "3.1.4",
+            "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
+            "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
             "requires": {
                 "@types/node": "*"
+            },
+            "dependencies": {
+                "@types/node": {
+                    "version": "17.0.41",
+                    "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.41.tgz",
+                    "integrity": "sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw=="
+                }
             }
         },
         "@types/libxmljs": {
@@ -1574,9 +1337,9 @@
             }
         },
         "@types/lodash": {
-            "version": "4.14.179",
-            "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.179.tgz",
-            "integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w==",
+            "version": "4.14.182",
+            "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz",
+            "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==",
             "dev": true
         },
         "@types/mime": {
@@ -1610,10 +1373,16 @@
             "version": "3.6.20",
             "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz",
             "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==",
-            "dev": true,
             "requires": {
                 "@types/bson": "*",
                 "@types/node": "*"
+            },
+            "dependencies": {
+                "@types/node": {
+                    "version": "17.0.41",
+                    "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.41.tgz",
+                    "integrity": "sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw=="
+                }
             }
         },
         "@types/mongoose": {
@@ -1628,7 +1397,8 @@
         "@types/node": {
             "version": "10.17.60",
             "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
-            "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="
+            "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==",
+            "dev": true
         },
         "@types/nodemailer": {
             "version": "4.6.8",
@@ -1665,9 +1435,9 @@
             "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g=="
         },
         "@types/passport": {
-            "version": "1.0.7",
-            "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.7.tgz",
-            "integrity": "sha512-JtswU8N3kxBYgo+n9of7C97YQBT+AYPP2aBfNGTzABqPAZnK/WOAaKfh3XesUYMZRrXFuoPc2Hv0/G/nQFveHw==",
+            "version": "1.0.8",
+            "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.8.tgz",
+            "integrity": "sha512-Gdcvis7+7G/Mobm+25AeFi+oe5teBhHzpbCOFWeN10Bj8tnoEE1L5lkraQjzmDEKkJQuM7xSJUGIFGl/giyRfQ==",
             "dev": true,
             "requires": {
                 "@types/express": "*"
@@ -1726,9 +1496,9 @@
             }
         },
         "@types/prop-types": {
-            "version": "15.7.4",
-            "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
-            "integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ=="
+            "version": "15.7.5",
+            "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
+            "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
         },
         "@types/prosemirror-commands": {
             "version": "1.0.4",
@@ -1795,9 +1565,9 @@
             }
         },
         "@types/prosemirror-model": {
-            "version": "1.16.1",
-            "resolved": "https://registry.npmjs.org/@types/prosemirror-model/-/prosemirror-model-1.16.1.tgz",
-            "integrity": "sha512-SrrCe2cHlYrQ9o55e2i/c3wt1yRajTTpRLvzfmB+2DWjWEbBLTByVWyjrdpKtQTxAaTeU2aeDGo1iuwl/jF27w==",
+            "version": "1.16.2",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-model/-/prosemirror-model-1.16.2.tgz",
+            "integrity": "sha512-1XPJopkKP3oHSBP61uuSuW13DIDZPWvAzP6Pv2/6mixk8EBPUeRGIW548DjJTicMo23gEg1zvCZy9asblQdWag==",
             "dev": true,
             "requires": {
                 "@types/orderedmap": "*"
@@ -1815,9 +1585,9 @@
             }
         },
         "@types/prosemirror-state": {
-            "version": "1.2.8",
-            "resolved": "https://registry.npmjs.org/@types/prosemirror-state/-/prosemirror-state-1.2.8.tgz",
-            "integrity": "sha512-mq9uyQWcpu8jeamO6Callrdvf/e1H/aRLR2kZWSpZrPHctEsxWHBbluD/wqVjXBRIOoMHLf6ZvOkrkmGLoCHVA==",
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-state/-/prosemirror-state-1.3.0.tgz",
+            "integrity": "sha512-nMdUF6w8B++NH4V54X+4GvDty7M02UfuHQW0s1AS25Z4ZrOW4RSY2+s57doXBbeMSjzYV/QoMxCY2sT3KQ2VdQ==",
             "dev": true,
             "requires": {
                 "@types/prosemirror-model": "*",
@@ -1826,18 +1596,18 @@
             }
         },
         "@types/prosemirror-transform": {
-            "version": "1.1.5",
-            "resolved": "https://registry.npmjs.org/@types/prosemirror-transform/-/prosemirror-transform-1.1.5.tgz",
-            "integrity": "sha512-Wr2HXaEF4JPklWpC17RTxE6PxyU54Taqk5FMhK1ojgcN93J+GpkYW8s0mD3rl7KfTmlhVwZPCHE9o0cYf2Go5A==",
+            "version": "1.4.2",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-transform/-/prosemirror-transform-1.4.2.tgz",
+            "integrity": "sha512-FZNzjYm6YUkb1XXOrw2193TiFzwM92ui1nycNaRSd5JDbugf9yBLkXm4Rq3HGJJxBBkRcUE8niqUW5aWlXQQiQ==",
             "dev": true,
             "requires": {
                 "@types/prosemirror-model": "*"
             }
         },
         "@types/prosemirror-view": {
-            "version": "1.23.1",
-            "resolved": "https://registry.npmjs.org/@types/prosemirror-view/-/prosemirror-view-1.23.1.tgz",
-            "integrity": "sha512-6e1B2oKUnhmZPUrsVvYjDqeVjE6jGezygjtoHsAK4ZENAxHzHqy5NT4jUvdPTWjCYeH0t2Y7pSfRPNrPIyQX4A==",
+            "version": "1.23.3",
+            "resolved": "https://registry.npmjs.org/@types/prosemirror-view/-/prosemirror-view-1.23.3.tgz",
+            "integrity": "sha512-T5dPDmZiXAazJVSvnx55D6h4mcpiH2q2wTyO9zIeOdox5zx964+zcDl9dFNaXG3qCGlERwMPckhBZL1HCxyygw==",
             "dev": true,
             "requires": {
                 "@types/prosemirror-model": "*",
@@ -1867,9 +1637,10 @@
             }
         },
         "@types/react": {
-            "version": "16.14.23",
-            "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.23.tgz",
-            "integrity": "sha512-WngBZLuSkP4IAgPi0HOsGCHo6dn3CcuLQnCfC17VbA7YBgipZiZoTOhObwl/93DsFW0Y2a/ZXeonpW4DxirEJg==",
+            "version": "16.14.26",
+            "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.26.tgz",
+            "integrity": "sha512-c/5CYyciOO4XdFcNhZW1O2woVx86k4T+DO2RorHZL7EhitkNQgSD/SgpdZJAUJa/qjVgOmTM44gHkAdZSXeQuQ==",
+            "dev": true,
             "requires": {
                 "@types/prop-types": "*",
                 "@types/scheduler": "*",
@@ -1877,9 +1648,10 @@
             },
             "dependencies": {
                 "csstype": {
-                    "version": "3.0.10",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
-                    "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
+                    "dev": true
                 }
             }
         },
@@ -1922,9 +1694,9 @@
             }
         },
         "@types/react-dom": {
-            "version": "16.9.14",
-            "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.14.tgz",
-            "integrity": "sha512-FIX2AVmPTGP30OUJ+0vadeIFJJ07Mh1m+U0rxfgyW34p3rTlXI+nlenvAxNn4BP36YyI9IJ/+UJ7Wu22N1pI7A==",
+            "version": "16.9.16",
+            "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.16.tgz",
+            "integrity": "sha512-Oqc0RY4fggGA3ltEgyPLc3IV9T73IGoWjkONbsyJ3ZBn+UPPCYpU2ec0i3cEbJuEdZtkqcCF2l1zf2pBdgUGSg==",
             "dev": true,
             "requires": {
                 "@types/react": "^16"
@@ -1939,6 +1711,14 @@
                 "@types/react": "*"
             }
         },
+        "@types/react-icons": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/@types/react-icons/-/react-icons-3.0.0.tgz",
+            "integrity": "sha512-Vefs6LkLqF61vfV7AiAqls+vpR94q67gunhMueDznG+msAkrYgRxl7gYjNem/kZ+as2l2mNChmF1jRZzzQQtMg==",
+            "requires": {
+                "react-icons": "*"
+            }
+        },
         "@types/react-measure": {
             "version": "2.0.8",
             "resolved": "https://registry.npmjs.org/@types/react-measure/-/react-measure-2.0.8.tgz",
@@ -1949,22 +1729,56 @@
             }
         },
         "@types/react-reconciler": {
-            "version": "0.26.4",
-            "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.4.tgz",
-            "integrity": "sha512-bdx4aIBkQRDAnzc23JBFeZmVpmfLJHfHikmQukEt9qs4bQtq9f+PDbNwhR9u74FkIUyIDz1I1qJ8OF6RwadKpw==",
+            "version": "0.26.7",
+            "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.7.tgz",
+            "integrity": "sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==",
             "requires": {
                 "@types/react": "*"
+            },
+            "dependencies": {
+                "@types/react": {
+                    "version": "18.0.12",
+                    "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.12.tgz",
+                    "integrity": "sha512-duF1OTASSBQtcigUvhuiTB1Ya3OvSy+xORCiEf20H0P0lzx+/KeVsA99U5UjLXSbyo1DRJDlLKqTeM1ngosqtg==",
+                    "requires": {
+                        "@types/prop-types": "*",
+                        "@types/scheduler": "*",
+                        "csstype": "^3.0.2"
+                    }
+                },
+                "csstype": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
+                }
             }
         },
         "@types/react-redux": {
-            "version": "7.1.22",
-            "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.22.tgz",
-            "integrity": "sha512-GxIA1kM7ClU73I6wg9IRTVwSO9GS+SAKZKe0Enj+82HMU6aoESFU2HNAdNi3+J53IaOHPiUfT3kSG4L828joDQ==",
+            "version": "7.1.24",
+            "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.24.tgz",
+            "integrity": "sha512-7FkurKcS1k0FHZEtdbbgN8Oc6b+stGSfZYjQGicofJ0j4U0qIn/jaSvnP2pLwZKiai3/17xqqxkkrxTgN8UNbQ==",
             "requires": {
                 "@types/hoist-non-react-statics": "^3.3.0",
                 "@types/react": "*",
                 "hoist-non-react-statics": "^3.3.0",
                 "redux": "^4.0.0"
+            },
+            "dependencies": {
+                "@types/react": {
+                    "version": "18.0.12",
+                    "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.12.tgz",
+                    "integrity": "sha512-duF1OTASSBQtcigUvhuiTB1Ya3OvSy+xORCiEf20H0P0lzx+/KeVsA99U5UjLXSbyo1DRJDlLKqTeM1ngosqtg==",
+                    "requires": {
+                        "@types/prop-types": "*",
+                        "@types/scheduler": "*",
+                        "csstype": "^3.0.2"
+                    }
+                },
+                "csstype": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
+                }
             }
         },
         "@types/react-select": {
@@ -1984,6 +1798,23 @@
             "integrity": "sha512-fVQXjy/EYDbgraScgjDONA291McKqGrw0R0NeK639fx2bS4T19TnXMjg3FjOPlkI3qYTQtFTPADlRYysaQIMpA==",
             "requires": {
                 "@types/react": "*"
+            },
+            "dependencies": {
+                "@types/react": {
+                    "version": "18.0.12",
+                    "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.12.tgz",
+                    "integrity": "sha512-duF1OTASSBQtcigUvhuiTB1Ya3OvSy+xORCiEf20H0P0lzx+/KeVsA99U5UjLXSbyo1DRJDlLKqTeM1ngosqtg==",
+                    "requires": {
+                        "@types/prop-types": "*",
+                        "@types/scheduler": "*",
+                        "csstype": "^3.0.2"
+                    }
+                },
+                "csstype": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
+                }
             }
         },
         "@types/react-transition-group": {
@@ -1992,6 +1823,23 @@
             "integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==",
             "requires": {
                 "@types/react": "*"
+            },
+            "dependencies": {
+                "@types/react": {
+                    "version": "18.0.12",
+                    "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.12.tgz",
+                    "integrity": "sha512-duF1OTASSBQtcigUvhuiTB1Ya3OvSy+xORCiEf20H0P0lzx+/KeVsA99U5UjLXSbyo1DRJDlLKqTeM1ngosqtg==",
+                    "requires": {
+                        "@types/prop-types": "*",
+                        "@types/scheduler": "*",
+                        "csstype": "^3.0.2"
+                    }
+                },
+                "csstype": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
+                }
             }
         },
         "@types/reactcss": {
@@ -2013,6 +1861,19 @@
                 "@types/node": "*",
                 "@types/tough-cookie": "*",
                 "form-data": "^2.5.0"
+            },
+            "dependencies": {
+                "form-data": {
+                    "version": "2.5.1",
+                    "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
+                    "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
+                    "dev": true,
+                    "requires": {
+                        "asynckit": "^0.4.0",
+                        "combined-stream": "^1.0.6",
+                        "mime-types": "^2.1.12"
+                    }
+                }
             }
         },
         "@types/request-promise": {
@@ -2031,6 +1892,13 @@
             "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
             "requires": {
                 "@types/node": "*"
+            },
+            "dependencies": {
+                "@types/node": {
+                    "version": "17.0.41",
+                    "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.41.tgz",
+                    "integrity": "sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw=="
+                }
             }
         },
         "@types/reveal": {
@@ -2123,7 +1991,7 @@
         "@types/strip-bom": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz",
-            "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=",
+            "integrity": "sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==",
             "dev": true
         },
         "@types/strip-json-comments": {
@@ -2152,23 +2020,23 @@
             "integrity": "sha512-6JqTgijtfXcTJik8NtiNxr2L90ex6ElM00qilOGeUcrEsJLOdzLJSIkXHUYS+KPAYQYtRJQKD6XaXds3HjS+gg=="
         },
         "@types/tough-cookie": {
-            "version": "4.0.1",
-            "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz",
-            "integrity": "sha512-Y0K95ThC3esLEYD6ZuqNek29lNX2EM1qxV8y2FTLUB0ff5wWrk7az+mLrnNFUnaXcgKye22+sFBRXOgpPILZNg=="
+            "version": "4.0.2",
+            "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz",
+            "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw=="
         },
         "@types/typescript": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/@types/typescript/-/typescript-2.0.0.tgz",
-            "integrity": "sha1-xDNTnJi64oaCswfqp6D9IRW4PCg=",
+            "integrity": "sha512-WMEWfMISiJ2QKyk5/dSdgL0ZwP//PZj0jmDU0hMh51FmLq4WIYzjlngsUQZXejQL+QtkXJUOGjb3G3UCvgZuSQ==",
             "dev": true,
             "requires": {
                 "typescript": "*"
             }
         },
         "@types/uglify-js": {
-            "version": "3.13.1",
-            "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.1.tgz",
-            "integrity": "sha512-O3MmRAk6ZuAKa9CHgg0Pr0+lUOqoMLpc9AS4R8ano2auvsg7IE8syF3Xh/NPr26TWklxYcqoEEFdzLLs1fV9PQ==",
+            "version": "3.13.3",
+            "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.13.3.tgz",
+            "integrity": "sha512-9dmBYXt/rKxedUXfCvXSxyiPvpDXLkiRlv17DnqdhS+pRustL1967rI1jZVt1xysTO+xJGMoZzcy3cWC9+b6Tw==",
             "dev": true,
             "requires": {
                 "source-map": "^0.6.1"
@@ -2240,14 +2108,6 @@
                 "@types/connect": "*",
                 "tapable": "^2.2.0",
                 "webpack": "^5"
-            },
-            "dependencies": {
-                "tapable": {
-                    "version": "2.2.1",
-                    "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
-                    "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
-                    "dev": true
-                }
             }
         },
         "@types/webpack-sources": {
@@ -2262,9 +2122,9 @@
             },
             "dependencies": {
                 "source-map": {
-                    "version": "0.7.3",
-                    "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
-                    "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+                    "version": "0.7.4",
+                    "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
+                    "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
                     "dev": true
                 }
             }
@@ -2275,6 +2135,23 @@
             "integrity": "sha512-e1DZGD+eH19BnllTWCGXAdrMa2kI53wEMuhn/d+wUmnu8//ZI6BiuK/EPdw07fI4+tlyo5qdPZdXdpkoXHJVOw==",
             "requires": {
                 "@types/react": "*"
+            },
+            "dependencies": {
+                "@types/react": {
+                    "version": "18.0.12",
+                    "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.12.tgz",
+                    "integrity": "sha512-duF1OTASSBQtcigUvhuiTB1Ya3OvSy+xORCiEf20H0P0lzx+/KeVsA99U5UjLXSbyo1DRJDlLKqTeM1ngosqtg==",
+                    "requires": {
+                        "@types/prop-types": "*",
+                        "@types/scheduler": "*",
+                        "csstype": "^3.0.2"
+                    }
+                },
+                "csstype": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
+                }
             }
         },
         "@types/xregexp": {
@@ -2287,12 +2164,20 @@
             }
         },
         "@types/yauzl": {
-            "version": "2.9.1",
-            "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.1.tgz",
-            "integrity": "sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA==",
+            "version": "2.10.0",
+            "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz",
+            "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==",
             "optional": true,
             "requires": {
                 "@types/node": "*"
+            },
+            "dependencies": {
+                "@types/node": {
+                    "version": "17.0.41",
+                    "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.41.tgz",
+                    "integrity": "sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw==",
+                    "optional": true
+                }
             }
         },
         "@types/youtube": {
@@ -2496,17 +2381,17 @@
         "Base64": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/Base64/-/Base64-0.2.1.tgz",
-            "integrity": "sha1-ujpCMHCOGGcFBl5mur3Uw1z2ACg="
+            "integrity": "sha512-reGEWshDmTDQDsCec/HduOO9Wyj6yMOupMfhIf3ugN1TDlK2NQW4DDJSqNNtp380SNcvRfXtO8HSCQot0d0SMw=="
         },
         "D": {
             "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/D/-/D-1.0.0.tgz",
-            "integrity": "sha512-nQvrCBu7K2pSSEtIM0EEF03FVjcczCXInMt3moLNFbjlWx6bZrX72uT6/1uAXDbnzGUAx9gTyDiQ+vrFi663oA=="
+            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA=="
         },
         "abab": {
-            "version": "2.0.3",
-            "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz",
-            "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==",
+            "version": "2.0.6",
+            "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
+            "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
             "dev": true
         },
         "abbrev": {
@@ -2529,32 +2414,17 @@
             "requires": {
                 "mime-types": "~2.1.34",
                 "negotiator": "0.6.3"
-            },
-            "dependencies": {
-                "mime-db": {
-                    "version": "1.51.0",
-                    "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
-                    "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g=="
-                },
-                "mime-types": {
-                    "version": "2.1.34",
-                    "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
-                    "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
-                    "requires": {
-                        "mime-db": "1.51.0"
-                    }
-                }
             }
         },
         "acorn": {
-            "version": "3.3.0",
-            "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
-            "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo="
+            "version": "8.7.1",
+            "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
+            "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A=="
         },
         "acorn-globals": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-3.1.0.tgz",
-            "integrity": "sha1-/YJw9x+7SZawBPqIDuXUZXOnMb8=",
+            "integrity": "sha512-uWttZCk96+7itPxK8xCzY86PnxKTMrReKDqrHzv42VQY0K30PUO8WY13WMOuI+cOdX4EIdzdvQ8k6jkuGRFMYw==",
             "requires": {
                 "acorn": "^4.0.4"
             },
@@ -2562,7 +2432,7 @@
                 "acorn": {
                     "version": "4.0.13",
                     "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
-                    "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c="
+                    "integrity": "sha512-fu2ygVGuMmlzG8ZeRJ0bvR41nsAkxxhbyk8bZ1SS521Z7vmgJFTQQlfz/Mp/nJexGBz+v8sC9bM6+lNgskt4Ug=="
                 }
             }
         },
@@ -2591,30 +2461,35 @@
         "after": {
             "version": "0.8.2",
             "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
-            "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
+            "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA=="
         },
         "agent-base": {
-            "version": "6.0.0",
-            "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz",
-            "integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==",
+            "version": "6.0.2",
+            "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+            "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
             "requires": {
                 "debug": "4"
             },
             "dependencies": {
                 "debug": {
-                    "version": "4.1.1",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
-                    "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+                    "version": "4.3.4",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
                     "requires": {
-                        "ms": "^2.1.1"
+                        "ms": "2.1.2"
                     }
+                },
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
                 }
             }
         },
         "ajv": {
-            "version": "6.12.2",
-            "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
-            "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==",
+            "version": "6.12.6",
+            "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+            "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
             "requires": {
                 "fast-deep-equal": "^3.1.1",
                 "fast-json-stable-stringify": "^2.0.0",
@@ -2636,9 +2511,9 @@
             },
             "dependencies": {
                 "ajv": {
-                    "version": "8.10.0",
-                    "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz",
-                    "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==",
+                    "version": "8.11.0",
+                    "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
+                    "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
                     "requires": {
                         "fast-deep-equal": "^3.1.1",
                         "json-schema-traverse": "^1.0.0",
@@ -2654,14 +2529,14 @@
             }
         },
         "ajv-keywords": {
-            "version": "3.4.1",
-            "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz",
-            "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ=="
+            "version": "3.5.2",
+            "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+            "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="
         },
         "align-text": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
-            "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
+            "integrity": "sha512-GrTZLRpmp6wIC2ztrWW9MjjTgSKccffgFagbNDOX95/dcjEcYZibYTeaOntySQLcdw1ztBoFkviiUvTMbb9MYg==",
             "requires": {
                 "kind-of": "^3.0.2",
                 "longest": "^1.0.1",
@@ -2676,7 +2551,7 @@
                 "kind-of": {
                     "version": "3.2.2",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                    "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+                    "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -2686,12 +2561,12 @@
         "amdefine": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
-            "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU="
+            "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg=="
         },
         "ansi-align": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz",
-            "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=",
+            "integrity": "sha512-TdlOggdA/zURfMYa7ABC66j+oqfMew58KpJMbUlH3bcZP1b+cBHIHDDn5uH9INsxrHBPjsqM0tDB4jPTF/vgJA==",
             "requires": {
                 "string-width": "^2.0.0"
             }
@@ -2723,9 +2598,9 @@
             "dev": true
         },
         "ansi-regex": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-            "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
+            "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw=="
         },
         "ansi-styles": {
             "version": "3.2.1",
@@ -2738,7 +2613,7 @@
         "any-promise": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
-            "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
+            "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
         },
         "anymatch": {
             "version": "2.0.0",
@@ -2752,7 +2627,7 @@
                 "normalize-path": {
                     "version": "2.1.1",
                     "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
-                    "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+                    "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
                     "requires": {
                         "remove-trailing-separator": "^1.0.1"
                     }
@@ -2760,9 +2635,9 @@
             }
         },
         "aproba": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-            "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
+            "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
         },
         "archiver": {
             "version": "3.1.1",
@@ -2812,28 +2687,12 @@
             }
         },
         "are-we-there-yet": {
-            "version": "1.1.5",
-            "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
-            "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
+            "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
             "requires": {
                 "delegates": "^1.0.0",
-                "readable-stream": "^2.0.6"
-            },
-            "dependencies": {
-                "readable-stream": {
-                    "version": "2.3.7",
-                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
-                    "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
-                    "requires": {
-                        "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"
-                    }
-                }
+                "readable-stream": "^3.6.0"
             }
         },
         "arg": {
@@ -2853,7 +2712,7 @@
         "arr-diff": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-            "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA="
+            "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA=="
         },
         "arr-flatten": {
             "version": "1.1.0",
@@ -2863,7 +2722,7 @@
         "arr-union": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
-            "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ="
+            "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q=="
         },
         "array-back": {
             "version": "2.0.0",
@@ -2886,9 +2745,9 @@
             },
             "dependencies": {
                 "@types/node": {
-                    "version": "12.12.37",
-                    "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.37.tgz",
-                    "integrity": "sha512-4mXKoDptrXAwZErQHrLzpe0FN/0Wmf5JRniSVIdwUrtDf9wnmEV1teCNLBo/TwuXhkK/bVegoEn/wmb+x0AuPg=="
+                    "version": "12.20.55",
+                    "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
+                    "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="
                 },
                 "glob": {
                     "version": "7.1.3",
@@ -2903,6 +2762,14 @@
                         "path-is-absolute": "^1.0.0"
                     }
                 },
+                "minimatch": {
+                    "version": "3.0.4",
+                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+                    "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+                    "requires": {
+                        "brace-expansion": "^1.1.7"
+                    }
+                },
                 "mocha": {
                     "version": "6.2.3",
                     "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.3.tgz",
@@ -2946,23 +2813,23 @@
         "array-equal": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
-            "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
+            "integrity": "sha512-H3LU5RLiSsGXPhN+Nipar0iR0IofH+8r89G2y1tBKxQ/agagKyAjhkAFDRBfodP2caPrNKHpAWNIM/c9yeL7uA==",
             "dev": true
         },
         "array-find-index": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
-            "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E="
+            "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw=="
         },
         "array-flatten": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
-            "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
+            "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
         },
         "array-union": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
-            "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+            "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==",
             "dev": true,
             "requires": {
                 "array-uniq": "^1.0.1"
@@ -2971,13 +2838,25 @@
         "array-uniq": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
-            "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
+            "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
             "dev": true
         },
         "array-unique": {
             "version": "0.3.2",
             "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-            "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="
+            "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ=="
+        },
+        "array.prototype.reduce": {
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz",
+            "integrity": "sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw==",
+            "requires": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.19.2",
+                "es-array-method-boxes-properly": "^1.0.0",
+                "is-string": "^1.0.7"
+            }
         },
         "arraybuffer.slice": {
             "version": "0.0.7",
@@ -2992,7 +2871,7 @@
         "asap": {
             "version": "2.0.6",
             "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
-            "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
+            "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="
         },
         "asn1": {
             "version": "0.2.6",
@@ -3005,7 +2884,7 @@
         "assert-plus": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-            "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+            "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="
         },
         "assertion-error": {
             "version": "1.1.0",
@@ -3015,12 +2894,12 @@
         "assign-symbols": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
-            "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c="
+            "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw=="
         },
         "async": {
-            "version": "2.6.3",
-            "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
-            "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+            "version": "2.6.4",
+            "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
+            "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
             "requires": {
                 "lodash": "^4.17.14"
             }
@@ -3033,7 +2912,7 @@
         "async-foreach": {
             "version": "0.1.3",
             "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz",
-            "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI="
+            "integrity": "sha512-VUeSMD8nEGBWaZK4lizI1sf3yEC7pnAQ/mrI7pC2fBz2s/tq5jWWEngTwaf0Gruu/OoXRGLGg1XFqpYBiGTYJA=="
         },
         "async-limiter": {
             "version": "1.0.1",
@@ -3044,7 +2923,7 @@
         "asynckit": {
             "version": "0.4.0",
             "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-            "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+            "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
         },
         "atob": {
             "version": "2.1.2",
@@ -3070,12 +2949,31 @@
                 "mkdirp": "^0.5.1",
                 "source-map-support": "^0.5.3",
                 "webpack-log": "^1.2.0"
+            },
+            "dependencies": {
+                "enhanced-resolve": {
+                    "version": "4.5.0",
+                    "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz",
+                    "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==",
+                    "dev": true,
+                    "requires": {
+                        "graceful-fs": "^4.1.2",
+                        "memory-fs": "^0.5.0",
+                        "tapable": "^1.0.0"
+                    }
+                },
+                "tapable": {
+                    "version": "1.1.3",
+                    "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+                    "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+                    "dev": true
+                }
             }
         },
         "aws-sign2": {
             "version": "0.7.0",
             "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
-            "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
+            "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA=="
         },
         "aws4": {
             "version": "1.11.0",
@@ -3088,40 +2986,17 @@
             "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
             "requires": {
                 "follow-redirects": "1.5.10"
-            },
-            "dependencies": {
-                "debug": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-                    "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
-                    "requires": {
-                        "ms": "2.0.0"
-                    }
-                },
-                "follow-redirects": {
-                    "version": "1.5.10",
-                    "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
-                    "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
-                    "requires": {
-                        "debug": "=3.1.0"
-                    }
-                },
-                "ms": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
-                }
             }
         },
         "babel": {
             "version": "6.23.0",
             "resolved": "https://registry.npmjs.org/babel/-/babel-6.23.0.tgz",
-            "integrity": "sha1-0NHn2APpdHZb7qMjLU4VPA77kPQ="
+            "integrity": "sha512-ZDcCaI8Vlct8PJ3DvmyqUz+5X2Ylz3ZuuItBe/74yXosk2dwyVo/aN7MCJ8HJzhnnJ+6yP4o+lDgG9MBe91DLA=="
         },
         "babel-code-frame": {
             "version": "6.26.0",
             "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
-            "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
+            "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==",
             "dev": true,
             "requires": {
                 "chalk": "^1.1.3",
@@ -3132,19 +3007,19 @@
                 "ansi-regex": {
                     "version": "2.1.1",
                     "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-                    "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+                    "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
                     "dev": true
                 },
                 "ansi-styles": {
                     "version": "2.2.1",
                     "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-                    "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+                    "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
                     "dev": true
                 },
                 "chalk": {
                     "version": "1.1.3",
                     "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-                    "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+                    "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
                     "dev": true,
                     "requires": {
                         "ansi-styles": "^2.2.1",
@@ -3157,7 +3032,7 @@
                 "js-tokens": {
                     "version": "3.0.2",
                     "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
-                    "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
+                    "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==",
                     "dev": true
                 },
                 "strip-ansi": {
@@ -3205,25 +3080,26 @@
             }
         },
         "babel-plugin-styled-components": {
-            "version": "1.10.7",
-            "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.10.7.tgz",
-            "integrity": "sha512-MBMHGcIA22996n9hZRf/UJLVVgkEOITuR2SvjHLb5dSTUyR4ZRGn+ngITapes36FI3WLxZHfRhkA1ffHxihOrg==",
+            "version": "2.0.7",
+            "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.7.tgz",
+            "integrity": "sha512-i7YhvPgVqRKfoQ66toiZ06jPNA3p6ierpfUuEWxNF+fV27Uv5gxBkf8KZLHUCc1nFA9j6+80pYoIpqCeyW3/bA==",
             "requires": {
-                "@babel/helper-annotate-as-pure": "^7.0.0",
-                "@babel/helper-module-imports": "^7.0.0",
+                "@babel/helper-annotate-as-pure": "^7.16.0",
+                "@babel/helper-module-imports": "^7.16.0",
                 "babel-plugin-syntax-jsx": "^6.18.0",
-                "lodash": "^4.17.11"
+                "lodash": "^4.17.11",
+                "picomatch": "^2.3.0"
             }
         },
         "babel-plugin-syntax-jsx": {
             "version": "6.18.0",
             "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
-            "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY="
+            "integrity": "sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw=="
         },
         "babel-runtime": {
             "version": "6.26.0",
             "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
-            "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+            "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
             "requires": {
                 "core-js": "^2.4.0",
                 "regenerator-runtime": "^0.11.0"
@@ -3244,7 +3120,7 @@
         "babel-types": {
             "version": "6.26.0",
             "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
-            "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
+            "integrity": "sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g==",
             "requires": {
                 "babel-runtime": "^6.26.0",
                 "esutils": "^2.0.2",
@@ -3267,12 +3143,12 @@
         "backo2": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
-            "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
+            "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA=="
         },
         "balanced-match": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
-            "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+            "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
         },
         "base": {
             "version": "0.11.2",
@@ -3291,7 +3167,7 @@
                 "define-property": {
                     "version": "1.0.0",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-                    "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+                    "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
                     "requires": {
                         "is-descriptor": "^1.0.0"
                     }
@@ -3327,17 +3203,17 @@
         "base16": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz",
-            "integrity": "sha1-4pf2DX7BAUp6lxo568ipjAtoHnA="
+            "integrity": "sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ=="
         },
         "base64-arraybuffer": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
-            "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
+            "integrity": "sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg=="
         },
         "base64-js": {
-            "version": "1.3.1",
-            "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
-            "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
+            "version": "1.5.1",
+            "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+            "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
         },
         "base64id": {
             "version": "2.0.0",
@@ -3352,18 +3228,18 @@
         "batch": {
             "version": "0.6.1",
             "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
-            "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=",
+            "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==",
             "dev": true
         },
         "bcrypt-nodejs": {
             "version": "0.0.3",
             "resolved": "https://registry.npmjs.org/bcrypt-nodejs/-/bcrypt-nodejs-0.0.3.tgz",
-            "integrity": "sha1-xgkX8m3CNWYVZsaBBhwwPCsohCs="
+            "integrity": "sha512-NmTbLm867btBHCBZ222FQXkQKzecB0KG6pTXFa6NeTVZaSnLfCsx7EK2PL3J+kX8xJThUquEBbhimRCKKZX9zA=="
         },
         "bcrypt-pbkdf": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
-            "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+            "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
             "requires": {
                 "tweetnacl": "^0.14.3"
             }
@@ -3371,7 +3247,7 @@
         "bezier-curve": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/bezier-curve/-/bezier-curve-1.0.0.tgz",
-            "integrity": "sha1-o9+v6rEqlMRicw1QeYxSqEBdc3k="
+            "integrity": "sha512-h6uZJ6qdFfswS1rIRericgouhTeiVi/MnH10OKtCu2IZzXa+ZcjaxRLHY4u/evRsJcxYbbiNkgWQj2Z4UIcpEQ=="
         },
         "bezier-js": {
             "version": "4.1.1",
@@ -3384,19 +3260,28 @@
             "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ=="
         },
         "bignumber.js": {
-            "version": "7.2.1",
-            "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz",
-            "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ=="
+            "version": "9.0.2",
+            "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz",
+            "integrity": "sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw=="
         },
         "binary-extensions": {
             "version": "1.13.1",
             "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
             "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw=="
         },
+        "bindings": {
+            "version": "1.5.0",
+            "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+            "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+            "optional": true,
+            "requires": {
+                "file-uri-to-path": "1.0.0"
+            }
+        },
         "bl": {
-            "version": "4.0.2",
-            "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz",
-            "integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==",
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+            "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
             "requires": {
                 "buffer": "^5.5.0",
                 "inherits": "^2.0.4",
@@ -3411,7 +3296,7 @@
         "block-stream": {
             "version": "0.0.9",
             "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
-            "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
+            "integrity": "sha512-OorbnJVPII4DuUKbjARAe8u8EfqOmkEEaSFIyoQ7OjTHn6kafxWl0wLgoZ2rXaYd7MyLcDaU4TmhfxtwgcccMQ==",
             "requires": {
                 "inherits": "~2.0.0"
             }
@@ -3427,20 +3312,22 @@
             "integrity": "sha512-6sTGh1OiUmuH8ftAYvUzALivoOmcnahinGmjZFI4puZVowXoKTn/bXtth7N1skW5AlezEOfjgFH4lNXHeNRQog=="
         },
         "body-parser": {
-            "version": "1.19.2",
-            "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz",
-            "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==",
+            "version": "1.20.0",
+            "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz",
+            "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==",
             "requires": {
                 "bytes": "3.1.2",
                 "content-type": "~1.0.4",
                 "debug": "2.6.9",
-                "depd": "~1.1.2",
-                "http-errors": "1.8.1",
+                "depd": "2.0.0",
+                "destroy": "1.2.0",
+                "http-errors": "2.0.0",
                 "iconv-lite": "0.4.24",
-                "on-finished": "~2.3.0",
-                "qs": "6.9.7",
-                "raw-body": "2.4.3",
-                "type-is": "~1.6.18"
+                "on-finished": "2.4.1",
+                "qs": "6.10.3",
+                "raw-body": "2.5.1",
+                "type-is": "~1.6.18",
+                "unpipe": "1.0.0"
             },
             "dependencies": {
                 "debug": {
@@ -3451,22 +3338,33 @@
                         "ms": "2.0.0"
                     }
                 },
+                "iconv-lite": {
+                    "version": "0.4.24",
+                    "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+                    "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+                    "requires": {
+                        "safer-buffer": ">= 2.1.2 < 3"
+                    }
+                },
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
                 },
                 "qs": {
-                    "version": "6.9.7",
-                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz",
-                    "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw=="
+                    "version": "6.10.3",
+                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
+                    "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
+                    "requires": {
+                        "side-channel": "^1.0.4"
+                    }
                 }
             }
         },
         "bonjour": {
             "version": "3.5.0",
             "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
-            "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=",
+            "integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==",
             "dev": true,
             "requires": {
                 "array-flatten": "^2.1.0",
@@ -3488,7 +3386,7 @@
         "boolbase": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
-            "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
+            "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
         },
         "bootstrap": {
             "version": "4.6.1",
@@ -3517,7 +3415,7 @@
                 "camelcase": {
                     "version": "4.1.0",
                     "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
-                    "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
+                    "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw=="
                 }
             }
         },
@@ -3550,22 +3448,17 @@
                 "extend-shallow": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-                    "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+                    "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
                     "requires": {
                         "is-extendable": "^0.1.0"
                     }
-                },
-                "is-extendable": {
-                    "version": "0.1.1",
-                    "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
-                    "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
                 }
             }
         },
         "browser-assert": {
             "version": "1.2.1",
             "resolved": "https://registry.npmjs.org/browser-assert/-/browser-assert-1.2.1.tgz",
-            "integrity": "sha1-mqpaKox0aFwq4Fv+Ru/WBvBowgA="
+            "integrity": "sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ=="
         },
         "browser-process-hrtime": {
             "version": "1.0.0",
@@ -3579,48 +3472,48 @@
             "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw=="
         },
         "browserslist": {
-            "version": "4.19.3",
-            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.3.tgz",
-            "integrity": "sha512-XK3X4xtKJ+Txj8G5c30B4gsm71s69lqXlkYui4s6EkKxuv49qjYlY6oVd+IFJ73d4YymtM3+djvvt/R/iJwwDg==",
+            "version": "4.20.4",
+            "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.4.tgz",
+            "integrity": "sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw==",
             "requires": {
-                "caniuse-lite": "^1.0.30001312",
-                "electron-to-chromium": "^1.4.71",
+                "caniuse-lite": "^1.0.30001349",
+                "electron-to-chromium": "^1.4.147",
                 "escalade": "^3.1.1",
-                "node-releases": "^2.0.2",
+                "node-releases": "^2.0.5",
                 "picocolors": "^1.0.0"
             }
         },
         "bson": {
-            "version": "4.6.1",
-            "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.1.tgz",
-            "integrity": "sha512-I1LQ7Hz5zgwR4QquilLNZwbhPw0Apx7i7X9kGMBTsqPdml/03Q9NBtD9nt/19ahjlphktQImrnderxqpzeVDjw==",
+            "version": "4.6.4",
+            "resolved": "https://registry.npmjs.org/bson/-/bson-4.6.4.tgz",
+            "integrity": "sha512-TdQ3FzguAu5HKPPlr0kYQCyrYUYh8tFM+CMTpxjNzVzxeiJY00Rtuj3LXLHSgiGvmaWlZ8PE+4KyM2thqE38pQ==",
             "requires": {
                 "buffer": "^5.6.0"
             }
         },
         "buffer": {
-            "version": "5.6.0",
-            "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz",
-            "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==",
+            "version": "5.7.1",
+            "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+            "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
             "requires": {
-                "base64-js": "^1.0.2",
-                "ieee754": "^1.1.4"
+                "base64-js": "^1.3.1",
+                "ieee754": "^1.1.13"
             }
         },
         "buffer-crc32": {
             "version": "0.2.13",
             "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
-            "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
+            "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="
         },
         "buffer-equal-constant-time": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
-            "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
+            "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
         },
         "buffer-from": {
-            "version": "1.1.1",
-            "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
-            "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+            "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
         },
         "buffer-indexof": {
             "version": "1.1.1",
@@ -3631,21 +3524,20 @@
         "buffer-shims": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz",
-            "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E="
+            "integrity": "sha512-Zy8ZXMyxIT6RMTeY7OP/bDndfj6bwCan7SS98CEndS6deHwWPpseeHlwarNcBim+etXnF9HBc1non5JgDaJU1g=="
         },
         "built-in-math-eval": {
             "version": "0.3.0",
             "resolved": "https://registry.npmjs.org/built-in-math-eval/-/built-in-math-eval-0.3.0.tgz",
-            "integrity": "sha1-JA3CHLOJQ5WIxhxGDrAHZJfvxBw=",
+            "integrity": "sha512-5XD5cujru60ooKJ4sGZqoH5v2Xvgw7ezV54gJX/OnPkgDKoH3BnlMEi8xW6hl8xaEjxKHebgrsawroeZnGwIMA==",
             "requires": {
                 "math-codegen": "^0.3.5"
             }
         },
         "builtin-modules": {
-            "version": "1.1.1",
-            "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
-            "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
-            "dev": true
+            "version": "3.3.0",
+            "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
+            "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw=="
         },
         "bytes": {
             "version": "3.1.2",
@@ -3673,6 +3565,12 @@
                 "y18n": "^4.0.0"
             },
             "dependencies": {
+                "chownr": {
+                    "version": "1.1.4",
+                    "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+                    "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+                    "dev": true
+                },
                 "lru-cache": {
                     "version": "4.1.5",
                     "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
@@ -3762,7 +3660,7 @@
         "caller-callsite": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
-            "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
+            "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==",
             "requires": {
                 "callsites": "^2.0.0"
             },
@@ -3770,14 +3668,14 @@
                 "callsites": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
-                    "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA="
+                    "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ=="
                 }
             }
         },
         "caller-path": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
-            "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
+            "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==",
             "requires": {
                 "caller-callsite": "^2.0.0"
             }
@@ -3794,13 +3692,6 @@
             "requires": {
                 "pascal-case": "^3.1.2",
                 "tslib": "^2.0.3"
-            },
-            "dependencies": {
-                "tslib": {
-                    "version": "2.3.1",
-                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
-                    "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
-                }
             }
         },
         "camelcase": {
@@ -3811,7 +3702,7 @@
         "camelcase-keys": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
-            "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
+            "integrity": "sha512-bA/Z/DERHKqoEOrp+qeGKw1QlvEQkGZSc0XaY6VnTxZr+Kv1G5zFwttpjv8qxZ/sBPT4nthwZaAcsAZTJlSKXQ==",
             "requires": {
                 "camelcase": "^2.0.0",
                 "map-obj": "^1.0.0"
@@ -3820,35 +3711,28 @@
                 "camelcase": {
                     "version": "2.1.1",
                     "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
-                    "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8="
+                    "integrity": "sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw=="
                 }
             }
         },
         "camelize": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz",
-            "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
+            "integrity": "sha512-W2lPwkBkMZwFlPCXhIlYgxu+7gC/NUlCtdK652DAJ1JdgV0sTrvuPFshNPrFa1TY2JOkLhgdeEBplB4ezEa+xg=="
         },
         "caniuse-lite": {
-            "version": "1.0.30001312",
-            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz",
-            "integrity": "sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ=="
+            "version": "1.0.30001351",
+            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001351.tgz",
+            "integrity": "sha512-u+Ll+RDaiQEproTQjZLjZwyfNgNezA1fERMT7/54npcz+PkbVJUAHXMUz4bkXQYRPWrcFNO0Fbi1mwjfXg6N5g=="
         },
         "canvas": {
-            "version": "2.9.0",
-            "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.9.0.tgz",
-            "integrity": "sha512-0l93g7uxp7rMyr7H+XRQ28A3ud0dKIUTIEkUe1Dxh4rjUYN7B93+SjC3r1PDKA18xcQN87OFGgUnyw7LSgNLSQ==",
+            "version": "2.9.1",
+            "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.9.1.tgz",
+            "integrity": "sha512-vSQti1uG/2gjv3x6QLOZw7TctfufaerTWbVe+NSduHxxLGB+qf3kFgQ6n66DSnuoINtVUjrLLIK2R+lxrBG07A==",
             "requires": {
                 "@mapbox/node-pre-gyp": "^1.0.0",
                 "nan": "^2.15.0",
                 "simple-get": "^3.0.3"
-            },
-            "dependencies": {
-                "nan": {
-                    "version": "2.15.0",
-                    "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
-                    "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ=="
-                }
             }
         },
         "capture-stack-trace": {
@@ -3859,12 +3743,12 @@
         "caseless": {
             "version": "0.12.0",
             "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
-            "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
+            "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="
         },
         "center-align": {
             "version": "0.1.3",
             "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
-            "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
+            "integrity": "sha512-Baz3aNe2gd2LP2qk5U+sDk/m4oSuwSDcBfayTCTBoWpfIGO5XFxPmjILQII4NGiZjD6DoDI6kf7gKaxkf7s3VQ==",
             "requires": {
                 "align-text": "^0.1.3",
                 "lazy-cache": "^1.0.3"
@@ -3902,12 +3786,12 @@
         "change-emitter": {
             "version": "0.1.6",
             "resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz",
-            "integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU="
+            "integrity": "sha512-YXzt1cQ4a2jqazhcuSWEOc1K2q8g9H6eWNsyZgi640LDzRWVQ2eDe+Y/kVdftH+vYdPF2rgDb3dLdpxE1jvAxw=="
         },
         "character-parser": {
             "version": "2.2.0",
             "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz",
-            "integrity": "sha1-x84o821LzZdE5f/CxfzeHHMmH8A=",
+            "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==",
             "requires": {
                 "is-regex": "^1.0.3"
             }
@@ -3915,12 +3799,12 @@
         "check-error": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
-            "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII="
+            "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA=="
         },
         "child_process": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz",
-            "integrity": "sha1-sffn/HPSXn/R1FWtyU4UODAYK1o="
+            "integrity": "sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g=="
         },
         "chokidar": {
             "version": "2.1.8",
@@ -3939,512 +3823,17 @@
                 "path-is-absolute": "^1.0.0",
                 "readdirp": "^2.2.1",
                 "upath": "^1.1.1"
-            },
-            "dependencies": {
-                "bindings": {
-                    "version": "1.5.0",
-                    "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
-                    "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
-                    "optional": true,
-                    "requires": {
-                        "file-uri-to-path": "1.0.0"
-                    }
-                },
-                "fsevents": {
-                    "version": "1.2.12",
-                    "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz",
-                    "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==",
-                    "optional": true,
-                    "requires": {
-                        "bindings": "^1.5.0",
-                        "nan": "^2.12.1",
-                        "node-pre-gyp": "*"
-                    },
-                    "dependencies": {
-                        "abbrev": {
-                            "version": "1.1.1",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "ansi-regex": {
-                            "version": "2.1.1",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "aproba": {
-                            "version": "1.2.0",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "are-we-there-yet": {
-                            "version": "1.1.5",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "delegates": "^1.0.0",
-                                "readable-stream": "^2.0.6"
-                            }
-                        },
-                        "balanced-match": {
-                            "version": "1.0.0",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "brace-expansion": {
-                            "version": "1.1.11",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "balanced-match": "^1.0.0",
-                                "concat-map": "0.0.1"
-                            }
-                        },
-                        "chownr": {
-                            "version": "1.1.4",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "code-point-at": {
-                            "version": "1.1.0",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "concat-map": {
-                            "version": "0.0.1",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "console-control-strings": {
-                            "version": "1.1.0",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "core-util-is": {
-                            "version": "1.0.2",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "debug": {
-                            "version": "3.2.6",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "ms": "^2.1.1"
-                            }
-                        },
-                        "deep-extend": {
-                            "version": "0.6.0",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "delegates": {
-                            "version": "1.0.0",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "detect-libc": {
-                            "version": "1.0.3",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "fs-minipass": {
-                            "version": "1.2.7",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "minipass": "^2.6.0"
-                            }
-                        },
-                        "fs.realpath": {
-                            "version": "1.0.0",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "gauge": {
-                            "version": "2.7.4",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "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": {
-                            "version": "7.1.6",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "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": {
-                            "version": "2.0.1",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "iconv-lite": {
-                            "version": "0.4.24",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "safer-buffer": ">= 2.1.2 < 3"
-                            }
-                        },
-                        "ignore-walk": {
-                            "version": "3.0.3",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "minimatch": "^3.0.4"
-                            }
-                        },
-                        "inflight": {
-                            "version": "1.0.6",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "once": "^1.3.0",
-                                "wrappy": "1"
-                            }
-                        },
-                        "inherits": {
-                            "version": "2.0.4",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "ini": {
-                            "version": "1.3.5",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "is-fullwidth-code-point": {
-                            "version": "1.0.0",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "number-is-nan": "^1.0.0"
-                            }
-                        },
-                        "isarray": {
-                            "version": "1.0.0",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "minimatch": {
-                            "version": "3.0.4",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "brace-expansion": "^1.1.7"
-                            }
-                        },
-                        "minimist": {
-                            "version": "1.2.5",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "minipass": {
-                            "version": "2.9.0",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "safe-buffer": "^5.1.2",
-                                "yallist": "^3.0.0"
-                            }
-                        },
-                        "minizlib": {
-                            "version": "1.3.3",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "minipass": "^2.9.0"
-                            }
-                        },
-                        "mkdirp": {
-                            "version": "0.5.3",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "minimist": "^1.2.5"
-                            }
-                        },
-                        "ms": {
-                            "version": "2.1.2",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "needle": {
-                            "version": "2.3.3",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "debug": "^3.2.6",
-                                "iconv-lite": "^0.4.4",
-                                "sax": "^1.2.4"
-                            }
-                        },
-                        "node-pre-gyp": {
-                            "version": "0.14.0",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "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.4.2"
-                            }
-                        },
-                        "nopt": {
-                            "version": "4.0.3",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "abbrev": "1",
-                                "osenv": "^0.1.4"
-                            }
-                        },
-                        "npm-bundled": {
-                            "version": "1.1.1",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "npm-normalize-package-bin": "^1.0.1"
-                            }
-                        },
-                        "npm-normalize-package-bin": {
-                            "version": "1.0.1",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "npm-packlist": {
-                            "version": "1.4.8",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "ignore-walk": "^3.0.1",
-                                "npm-bundled": "^1.0.1",
-                                "npm-normalize-package-bin": "^1.0.1"
-                            }
-                        },
-                        "npmlog": {
-                            "version": "4.1.2",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "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,
-                            "optional": true
-                        },
-                        "object-assign": {
-                            "version": "4.1.1",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "once": {
-                            "version": "1.4.0",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "wrappy": "1"
-                            }
-                        },
-                        "os-homedir": {
-                            "version": "1.0.2",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "os-tmpdir": {
-                            "version": "1.0.2",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "osenv": {
-                            "version": "0.1.5",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "os-homedir": "^1.0.0",
-                                "os-tmpdir": "^1.0.0"
-                            }
-                        },
-                        "path-is-absolute": {
-                            "version": "1.0.1",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "process-nextick-args": {
-                            "version": "2.0.1",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "rc": {
-                            "version": "1.2.8",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "deep-extend": "^0.6.0",
-                                "ini": "~1.3.0",
-                                "minimist": "^1.2.0",
-                                "strip-json-comments": "~2.0.1"
-                            }
-                        },
-                        "readable-stream": {
-                            "version": "2.3.7",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "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": {
-                            "version": "2.7.1",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "glob": "^7.1.3"
-                            }
-                        },
-                        "safe-buffer": {
-                            "version": "5.1.2",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "safer-buffer": {
-                            "version": "2.1.2",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "sax": {
-                            "version": "1.2.4",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "semver": {
-                            "version": "5.7.1",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "set-blocking": {
-                            "version": "2.0.0",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "signal-exit": {
-                            "version": "3.0.2",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "string-width": {
-                            "version": "1.0.2",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "code-point-at": "^1.0.0",
-                                "is-fullwidth-code-point": "^1.0.0",
-                                "strip-ansi": "^3.0.0"
-                            }
-                        },
-                        "string_decoder": {
-                            "version": "1.1.1",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "safe-buffer": "~5.1.0"
-                            }
-                        },
-                        "strip-ansi": {
-                            "version": "3.0.1",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "ansi-regex": "^2.0.0"
-                            }
-                        },
-                        "strip-json-comments": {
-                            "version": "2.0.1",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "tar": {
-                            "version": "4.4.13",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "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"
-                            }
-                        },
-                        "util-deprecate": {
-                            "version": "1.0.2",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "wide-align": {
-                            "version": "1.1.3",
-                            "bundled": true,
-                            "optional": true,
-                            "requires": {
-                                "string-width": "^1.0.2 || 2"
-                            }
-                        },
-                        "wrappy": {
-                            "version": "1.0.2",
-                            "bundled": true,
-                            "optional": true
-                        },
-                        "yallist": {
-                            "version": "3.1.1",
-                            "bundled": true,
-                            "optional": true
-                        }
-                    }
-                }
             }
         },
         "chownr": {
-            "version": "1.1.4",
-            "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
-            "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+            "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
         },
         "chrome": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/chrome/-/chrome-0.1.0.tgz",
-            "integrity": "sha1-9h2beS/v6MGUxwVt3BAscmqGQyk=",
+            "integrity": "sha512-6KYl20U4Taj6YipylsWr2etUvp9AElJKfGNSBmyGTymYmancnOb041ZNadolEZi2nboLXH7jMSqUmm4kpuTzfg==",
             "requires": {
                 "exeq": "^2.2.0",
                 "plist": "^1.1.0"
@@ -4464,7 +3853,7 @@
         "clamp": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz",
-            "integrity": "sha1-ZqDmQBGBbjcZaCj9yMjBRzEshjQ="
+            "integrity": "sha512-kgMuFyE78OC6Dyu3Dy7vcx4uy97EIbVxJB/B0eJ3bUNAkwdNcxYzgKltnyADiYwsR7SEqkkUPsEUT//OVS6XMA=="
         },
         "class-transformer": {
             "version": "0.2.3",
@@ -4485,7 +3874,7 @@
                 "define-property": {
                     "version": "0.2.5",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-                    "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+                    "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
                     "requires": {
                         "is-descriptor": "^0.1.0"
                     }
@@ -4493,14 +3882,14 @@
             }
         },
         "classnames": {
-            "version": "2.2.6",
-            "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
-            "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
+            "version": "2.3.1",
+            "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
+            "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
         },
         "clean-css": {
-            "version": "4.2.3",
-            "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
-            "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==",
+            "version": "5.3.0",
+            "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.0.tgz",
+            "integrity": "sha512-YYuuxv4H/iNb1Z/5IbMRoxgrzjWGhOEFfd+groZ5dMCVkpENiMZmwspdrzBo9286JjM1gZJPAyL7ZIdzuvu2AQ==",
             "requires": {
                 "source-map": "~0.6.0"
             },
@@ -4515,12 +3904,12 @@
         "cli-boxes": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
-            "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM="
+            "integrity": "sha512-3Fo5wu8Ytle8q9iCzS4D2MWVL2X7JVWRiS1BnXbTFDhS9c/REkM9vd1AmabsoZoY5/dGi5TT9iKL8Kb6DeBRQg=="
         },
         "clipboard": {
             "version": "1.7.1",
             "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-1.7.1.tgz",
-            "integrity": "sha1-Ng1taUbpmnof7zleQrqStem1oWs=",
+            "integrity": "sha512-smkaRaIQsrnKN1F3wd1/vY9Q+DeR4L8ZCXKeHCFC2j8RZuSBbuImcLdnIO4GTxmzJxQuDGNKkyfpGoPW7Ua5bQ==",
             "requires": {
                 "good-listener": "^1.2.2",
                 "select": "^1.1.2",
@@ -4545,7 +3934,7 @@
                 "camelcase": {
                     "version": "4.1.0",
                     "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
-                    "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
+                    "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw=="
                 },
                 "yargs-parser": {
                     "version": "7.0.0",
@@ -4568,9 +3957,9 @@
             },
             "dependencies": {
                 "ansi-regex": {
-                    "version": "4.1.0",
-                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
-                    "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
+                    "version": "4.1.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+                    "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
                 },
                 "string-width": {
                     "version": "3.1.0",
@@ -4595,7 +3984,7 @@
         "clj-fuzzy": {
             "version": "0.3.3",
             "resolved": "https://registry.npmjs.org/clj-fuzzy/-/clj-fuzzy-0.3.3.tgz",
-            "integrity": "sha1-seU0MJHFIC28UlMoY+HEp4RX8D0="
+            "integrity": "sha512-9cyh9A8+OphDZeKIG21MgyDHWDkWxTvagwvFLVjtdi6eToFENF7iDLlKwhHrnBQRSQwprKNhazG053nE/UgwfQ=="
         },
         "clone-deep": {
             "version": "4.0.1",
@@ -4610,7 +3999,7 @@
         "clone-response": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
-            "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
+            "integrity": "sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==",
             "requires": {
                 "mimic-response": "^1.0.0"
             },
@@ -4630,12 +4019,12 @@
         "code-point-at": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
-            "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
+            "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA=="
         },
         "collection-visit": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
-            "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+            "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==",
             "requires": {
                 "map-visit": "^1.0.0",
                 "object-visit": "^1.0.0"
@@ -4661,12 +4050,12 @@
         "color-name": {
             "version": "1.1.3",
             "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
-            "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
+            "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
         },
         "color-string": {
-            "version": "1.9.0",
-            "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz",
-            "integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==",
+            "version": "1.9.1",
+            "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+            "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
             "requires": {
                 "color-name": "^1.0.0",
                 "simple-swizzle": "^0.2.2"
@@ -4678,9 +4067,9 @@
             "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="
         },
         "colorette": {
-            "version": "2.0.16",
-            "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz",
-            "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g=="
+            "version": "2.0.17",
+            "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.17.tgz",
+            "integrity": "sha512-hJo+3Bkn0NCHybn9Tu35fIeoOKGOk5OCC32y4Hz2It+qlCO2Q3DeQ1hRn/tDDMQKRYUEzqsl7jbF6dYKjlE60g=="
         },
         "colors": {
             "version": "1.4.0",
@@ -4712,20 +4101,20 @@
             }
         },
         "commander": {
-            "version": "2.20.3",
-            "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
-            "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+            "version": "8.3.0",
+            "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+            "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="
         },
         "commondir": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
-            "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
+            "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
             "dev": true
         },
         "component-bind": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
-            "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
+            "integrity": "sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw=="
         },
         "component-emitter": {
             "version": "1.3.0",
@@ -4735,7 +4124,16 @@
         "component-inherit": {
             "version": "0.0.3",
             "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
-            "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
+            "integrity": "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA=="
+        },
+        "compress-brotli": {
+            "version": "1.3.8",
+            "resolved": "https://registry.npmjs.org/compress-brotli/-/compress-brotli-1.3.8.tgz",
+            "integrity": "sha512-lVcQsjhxhIXsuupfy9fmZUFtAIdBmXA7EGY6GBdgZ++qkM9zG4YFT8iU7FoBxzryNDMOpD1HIFHUSX4D87oqhQ==",
+            "requires": {
+                "@types/json-buffer": "~3.0.0",
+                "json-buffer": "~3.0.1"
+            }
         },
         "compress-commons": {
             "version": "2.1.1",
@@ -4791,7 +4189,7 @@
                 "bytes": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
-                    "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
+                    "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==",
                     "dev": true
                 },
                 "debug": {
@@ -4806,7 +4204,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
                     "dev": true
                 }
             }
@@ -4814,7 +4212,7 @@
         "concat-map": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-            "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+            "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
         },
         "concat-stream": {
             "version": "1.6.2",
@@ -4844,22 +4242,37 @@
             }
         },
         "configstore": {
-            "version": "3.1.2",
-            "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz",
-            "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==",
+            "version": "3.1.5",
+            "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.5.tgz",
+            "integrity": "sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==",
             "requires": {
-                "dot-prop": "^4.1.0",
+                "dot-prop": "^4.2.1",
                 "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"
+            },
+            "dependencies": {
+                "make-dir": {
+                    "version": "1.3.0",
+                    "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
+                    "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
+                    "requires": {
+                        "pify": "^3.0.0"
+                    }
+                },
+                "pify": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+                    "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="
+                }
             }
         },
         "connect-flash": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz",
-            "integrity": "sha1-2GMPJtlaf4UfmVax6MxnMvO2qjA="
+            "integrity": "sha512-2rcfELQt/ZMP+SM/pG8PyhJRaLKp+6Hk2IUBNkEit09X+vwn3QsAL3ZbYtxUn7NVPzbMTSLRDhqe0B/eh30RYA=="
         },
         "connect-history-api-fallback": {
             "version": "1.6.0",
@@ -4888,12 +4301,12 @@
                 "process-nextick-args": {
                     "version": "1.0.7",
                     "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
-                    "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
+                    "integrity": "sha512-yN0WQmuCX63LP/TMvAg31nvT6m4vDqJEiiv2CAZqWOGNWutc9DfDk1NPYYmKUFmaVM2UwDowH4u5AHWYP/jxKw=="
                 },
                 "readable-stream": {
                     "version": "2.2.7",
                     "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz",
-                    "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=",
+                    "integrity": "sha512-a6ibcfWFhgihuTw/chl+u3fB5ykBZFmnvpyZHebY0MCQE4vvYcsCLpCeaQ1BkH7HdJYavNSqF0WDLeo4IPHQaQ==",
                     "requires": {
                         "buffer-shims": "~1.0.0",
                         "core-util-is": "~1.0.0",
@@ -4917,7 +4330,7 @@
         "console-control-strings": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
-            "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
+            "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
         },
         "constantinople": {
             "version": "3.1.2",
@@ -5001,7 +4414,7 @@
         "cookie-signature": {
             "version": "1.0.6",
             "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
-            "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+            "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
         },
         "cookies": {
             "version": "0.8.0",
@@ -5010,13 +4423,6 @@
             "requires": {
                 "depd": "~2.0.0",
                 "keygrip": "~1.1.0"
-            },
-            "dependencies": {
-                "depd": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
-                    "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
-                }
             }
         },
         "copy-concurrently": {
@@ -5033,6 +4439,12 @@
                 "run-queue": "^1.0.0"
             },
             "dependencies": {
+                "aproba": {
+                    "version": "1.2.0",
+                    "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+                    "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+                    "dev": true
+                },
                 "rimraf": {
                     "version": "2.7.1",
                     "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@@ -5047,7 +4459,7 @@
         "copy-descriptor": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
-            "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="
+            "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw=="
         },
         "copy-webpack-plugin": {
             "version": "4.6.0",
@@ -5077,7 +4489,13 @@
                 "p-try": {
                     "version": "1.0.0",
                     "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
-                    "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+                    "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==",
+                    "dev": true
+                },
+                "serialize-javascript": {
+                    "version": "1.9.1",
+                    "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",
+                    "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==",
                     "dev": true
                 }
             }
@@ -5085,17 +4503,17 @@
         "core-js": {
             "version": "1.2.7",
             "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
-            "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
+            "integrity": "sha512-ZiPp9pZlgxpWRu0M+YWbm6+aQ84XEfH1JRXvfOc/fILWI0VKhLC2LX13X1NYq4fULzLMq7Hfh43CSo2/aIaUPA=="
         },
         "core-js-pure": {
-            "version": "3.21.1",
-            "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz",
-            "integrity": "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ=="
+            "version": "3.22.8",
+            "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.22.8.tgz",
+            "integrity": "sha512-bOxbZIy9S5n4OVH63XaLVXZ49QKicjowDx/UELyJ68vxfCRpYsbyh/WNZNfEfAk+ekA8vSjt+gCDpvh672bc3w=="
         },
         "core-util-is": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-            "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+            "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
         },
         "cors": {
             "version": "2.8.5",
@@ -5149,7 +4567,7 @@
         "create-error-class": {
             "version": "3.0.2",
             "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
-            "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
+            "integrity": "sha512-gYTKKexFO3kh200H1Nit76sRwRtOY32vQd3jpAQKpLtZqyNsSQNfI4N7o3eP2wUjV35pTWKRYqFUDBvUha/Pkw==",
             "requires": {
                 "capture-stack-trace": "^1.0.0"
             }
@@ -5208,32 +4626,13 @@
                     "requires": {
                         "whatwg-url": "^5.0.0"
                     }
-                },
-                "tr46": {
-                    "version": "0.0.3",
-                    "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
-                    "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
-                },
-                "webidl-conversions": {
-                    "version": "3.0.1",
-                    "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
-                    "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
-                },
-                "whatwg-url": {
-                    "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
-                    "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
-                    "requires": {
-                        "tr46": "~0.0.3",
-                        "webidl-conversions": "^3.0.0"
-                    }
                 }
             }
         },
         "cross-spawn": {
             "version": "3.0.1",
             "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz",
-            "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=",
+            "integrity": "sha512-eZ+m1WNhSZutOa/uRblAc9Ut5MQfukFrFMtPSm3bZCA888NmMd5AWXWdgRZ80zd+pTk1P2JrGjg9pUPTvl2PWQ==",
             "requires": {
                 "lru-cache": "^4.0.1",
                 "which": "^1.2.9"
@@ -5263,7 +4662,7 @@
         "crypto-random-string": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
-            "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4="
+            "integrity": "sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg=="
         },
         "css-box-model": {
             "version": "1.2.1",
@@ -5276,7 +4675,7 @@
         "css-color-keywords": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
-            "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU="
+            "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg=="
         },
         "css-in-js-utils": {
             "version": "2.0.1",
@@ -5304,37 +4703,24 @@
                 "postcss-modules-values": "^2.0.0",
                 "postcss-value-parser": "^3.3.0",
                 "schema-utils": "^1.0.0"
-            },
-            "dependencies": {
-                "schema-utils": {
-                    "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
-                    "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
-                    "dev": true,
-                    "requires": {
-                        "ajv": "^6.1.0",
-                        "ajv-errors": "^1.0.0",
-                        "ajv-keywords": "^3.1.0"
-                    }
-                }
             }
         },
         "css-select": {
-            "version": "4.2.1",
-            "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.1.tgz",
-            "integrity": "sha512-/aUslKhzkTNCQUB2qTX84lVmfia9NyjP3WpDGtj/WxhwBzWBYUV3DgUpurHTme8UTPcPlAD1DJ+b0nN/t50zDQ==",
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
+            "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
             "requires": {
                 "boolbase": "^1.0.0",
-                "css-what": "^5.1.0",
-                "domhandler": "^4.3.0",
+                "css-what": "^6.0.1",
+                "domhandler": "^4.3.1",
                 "domutils": "^2.8.0",
                 "nth-check": "^2.0.1"
             },
             "dependencies": {
                 "dom-serializer": {
-                    "version": "1.3.2",
-                    "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
-                    "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
+                    "version": "1.4.1",
+                    "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
+                    "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
                     "requires": {
                         "domelementtype": "^2.0.1",
                         "domhandler": "^4.2.0",
@@ -5342,14 +4728,14 @@
                     }
                 },
                 "domelementtype": {
-                    "version": "2.2.0",
-                    "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
-                    "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
+                    "version": "2.3.0",
+                    "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+                    "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
                 },
                 "domhandler": {
-                    "version": "4.3.0",
-                    "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz",
-                    "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==",
+                    "version": "4.3.1",
+                    "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
+                    "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
                     "requires": {
                         "domelementtype": "^2.2.0"
                     }
@@ -5391,9 +4777,9 @@
             }
         },
         "css-what": {
-            "version": "5.1.0",
-            "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz",
-            "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw=="
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+            "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw=="
         },
         "cssesc": {
             "version": "3.0.0",
@@ -5408,9 +4794,9 @@
             "dev": true
         },
         "cssstyle": {
-            "version": "2.2.0",
-            "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.2.0.tgz",
-            "integrity": "sha512-sEb3XFPx3jNnCAMtqrXPDeSgQr+jojtCeNf8cvMNMh1cG970+lljssvQDzPq6lmmJu2Vhqood/gtEomBiHOGnA==",
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz",
+            "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
             "dev": true,
             "requires": {
                 "cssom": "~0.3.6"
@@ -5425,14 +4811,14 @@
             }
         },
         "csstype": {
-            "version": "2.6.10",
-            "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.10.tgz",
-            "integrity": "sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w=="
+            "version": "2.6.20",
+            "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz",
+            "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA=="
         },
         "currently-unhandled": {
             "version": "0.4.1",
             "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
-            "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
+            "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==",
             "requires": {
                 "array-find-index": "^1.0.1"
             }
@@ -5440,18 +4826,31 @@
         "custom-event": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
-            "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU="
+            "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg=="
         },
         "cyclist": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
-            "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
+            "integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==",
             "dev": true
         },
+        "d": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+            "dev": true,
+            "requires": {
+                "es5-ext": "^0.10.50",
+                "type": "^1.0.1"
+            }
+        },
         "d3-array": {
-            "version": "1.2.4",
-            "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
-            "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
+            "version": "2.12.1",
+            "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
+            "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
+            "requires": {
+                "internmap": "^1.0.0"
+            }
         },
         "d3-axis": {
             "version": "2.1.0",
@@ -5510,16 +4909,6 @@
                 "d3-interpolate": "1.2.0 - 2",
                 "d3-time": "^2.1.1",
                 "d3-time-format": "2 - 3"
-            },
-            "dependencies": {
-                "d3-array": {
-                    "version": "2.12.1",
-                    "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
-                    "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
-                    "requires": {
-                        "internmap": "^1.0.0"
-                    }
-                }
             }
         },
         "d3-selection": {
@@ -5541,16 +4930,6 @@
             "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==",
             "requires": {
                 "d3-array": "2"
-            },
-            "dependencies": {
-                "d3-array": {
-                    "version": "2.12.1",
-                    "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
-                    "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
-                    "requires": {
-                        "internmap": "^1.0.0"
-                    }
-                }
             }
         },
         "d3-time-format": {
@@ -5593,7 +4972,7 @@
         "dashdash": {
             "version": "1.14.1",
             "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
-            "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+            "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
             "requires": {
                 "assert-plus": "^1.0.0"
             }
@@ -5607,6 +4986,34 @@
                 "abab": "^2.0.0",
                 "whatwg-mimetype": "^2.2.0",
                 "whatwg-url": "^7.0.0"
+            },
+            "dependencies": {
+                "tr46": {
+                    "version": "1.0.1",
+                    "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
+                    "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+                    "dev": true,
+                    "requires": {
+                        "punycode": "^2.1.0"
+                    }
+                },
+                "webidl-conversions": {
+                    "version": "4.0.2",
+                    "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+                    "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
+                    "dev": true
+                },
+                "whatwg-url": {
+                    "version": "7.1.0",
+                    "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
+                    "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
+                    "dev": true,
+                    "requires": {
+                        "lodash.sortby": "^4.7.0",
+                        "tr46": "^1.0.1",
+                        "webidl-conversions": "^4.0.2"
+                    }
+                }
             }
         },
         "date-fns": {
@@ -5617,7 +5024,7 @@
         "de-indent": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
-            "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0="
+            "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="
         },
         "debounce": {
             "version": "1.2.1",
@@ -5635,12 +5042,12 @@
         "decamelize": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
-            "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
+            "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="
         },
         "decode-uri-component": {
             "version": "0.2.0",
             "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
-            "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
+            "integrity": "sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og=="
         },
         "decompress-response": {
             "version": "4.2.1",
@@ -5677,9 +5084,9 @@
             "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
         },
         "deep-is": {
-            "version": "0.1.3",
-            "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
-            "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+            "version": "0.1.4",
+            "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+            "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
             "dev": true
         },
         "deepmerge": {
@@ -5742,11 +5149,12 @@
             "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="
         },
         "define-properties": {
-            "version": "1.1.3",
-            "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
-            "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
+            "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
             "requires": {
-                "object-keys": "^1.0.12"
+                "has-property-descriptors": "^1.0.0",
+                "object-keys": "^1.1.1"
             }
         },
         "define-property": {
@@ -5804,7 +5212,7 @@
                 "globby": {
                     "version": "6.1.0",
                     "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
-                    "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
+                    "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==",
                     "dev": true,
                     "requires": {
                         "array-union": "^1.0.1",
@@ -5817,11 +5225,17 @@
                         "pify": {
                             "version": "2.3.0",
                             "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-                            "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+                            "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
                             "dev": true
                         }
                     }
                 },
+                "pify": {
+                    "version": "4.0.1",
+                    "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+                    "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+                    "dev": true
+                },
                 "rimraf": {
                     "version": "2.7.1",
                     "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@@ -5836,7 +5250,7 @@
         "delayed-stream": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-            "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+            "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
         },
         "delegate": {
             "version": "3.2.0",
@@ -5846,18 +5260,13 @@
         "delegates": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
-            "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
+            "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
         },
         "denque": {
             "version": "1.5.1",
             "resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz",
             "integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw=="
         },
-        "density-clustering": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/density-clustering/-/density-clustering-1.3.0.tgz",
-            "integrity": "sha1-3J9ZyPCrl+FiSsZJMP0xlIF9ysU="
-        },
         "depcheck": {
             "version": "0.9.2",
             "resolved": "https://registry.npmjs.org/depcheck/-/depcheck-0.9.2.tgz",
@@ -5883,24 +5292,18 @@
             },
             "dependencies": {
                 "ansi-regex": {
-                    "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
-                    "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
+                    "version": "5.0.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                    "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
                 },
                 "ansi-styles": {
-                    "version": "4.2.1",
-                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
-                    "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+                    "version": "4.3.0",
+                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+                    "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
                     "requires": {
-                        "@types/color-name": "^1.1.1",
                         "color-convert": "^2.0.1"
                     }
                 },
-                "builtin-modules": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz",
-                    "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw=="
-                },
                 "cliui": {
                     "version": "6.0.0",
                     "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
@@ -5936,11 +5339,11 @@
                     }
                 },
                 "debug": {
-                    "version": "4.1.1",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
-                    "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+                    "version": "4.3.4",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
                     "requires": {
-                        "ms": "^2.1.1"
+                        "ms": "2.1.2"
                     }
                 },
                 "emoji-regex": {
@@ -5960,7 +5363,7 @@
                 "import-fresh": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
-                    "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
+                    "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==",
                     "requires": {
                         "caller-path": "^2.0.0",
                         "resolve-from": "^3.0.0"
@@ -5979,6 +5382,11 @@
                         "p-locate": "^4.1.0"
                     }
                 },
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+                },
                 "p-locate": {
                     "version": "4.1.0",
                     "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
@@ -5990,7 +5398,7 @@
                 "parse-json": {
                     "version": "4.0.0",
                     "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
-                    "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+                    "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==",
                     "requires": {
                         "error-ex": "^1.3.1",
                         "json-parse-better-errors": "^1.0.1"
@@ -6004,24 +5412,24 @@
                 "resolve-from": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
-                    "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
+                    "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw=="
                 },
                 "string-width": {
-                    "version": "4.2.0",
-                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
-                    "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+                    "version": "4.2.3",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+                    "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
                     "requires": {
                         "emoji-regex": "^8.0.0",
                         "is-fullwidth-code-point": "^3.0.0",
-                        "strip-ansi": "^6.0.0"
+                        "strip-ansi": "^6.0.1"
                     }
                 },
                 "strip-ansi": {
-                    "version": "6.0.0",
-                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
-                    "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+                    "version": "6.0.1",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                    "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
                     "requires": {
-                        "ansi-regex": "^5.0.0"
+                        "ansi-regex": "^5.0.1"
                     }
                 },
                 "wrap-ansi": {
@@ -6035,9 +5443,9 @@
                     }
                 },
                 "yargs": {
-                    "version": "15.3.1",
-                    "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz",
-                    "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==",
+                    "version": "15.4.1",
+                    "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
+                    "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
                     "requires": {
                         "cliui": "^6.0.0",
                         "decamelize": "^1.2.0",
@@ -6049,7 +5457,7 @@
                         "string-width": "^4.2.0",
                         "which-module": "^2.0.0",
                         "y18n": "^4.0.0",
-                        "yargs-parser": "^18.1.1"
+                        "yargs-parser": "^18.1.2"
                     }
                 },
                 "yargs-parser": {
@@ -6064,24 +5472,24 @@
             }
         },
         "depd": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
-            "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+            "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
         },
         "deps-regex": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/deps-regex/-/deps-regex-0.1.4.tgz",
-            "integrity": "sha1-UYZnt2kUYKXn4KNBvnbrfOgJAYQ="
+            "integrity": "sha512-3tzwGYogSJi8HoG93R5x9NrdefZQOXgHgGih/7eivloOq6yC6O+yoFxZnkgP661twvfILONfoKRdF9GQOGx2RA=="
         },
         "destroy": {
-            "version": "1.0.4",
-            "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
-            "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+            "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
         },
         "detect-libc": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
-            "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
+            "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w=="
         },
         "detect-node": {
             "version": "2.1.0",
@@ -6120,7 +5528,7 @@
                 "pify": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-                    "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+                    "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
                     "dev": true
                 }
             }
@@ -6128,7 +5536,7 @@
         "dns-equal": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
-            "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=",
+            "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==",
             "dev": true
         },
         "dns-packet": {
@@ -6144,7 +5552,7 @@
         "dns-txt": {
             "version": "2.0.2",
             "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz",
-            "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=",
+            "integrity": "sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ==",
             "dev": true,
             "requires": {
                 "buffer-indexof": "^1.0.0"
@@ -6153,7 +5561,7 @@
         "doctypes": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz",
-            "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk="
+            "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ=="
         },
         "dom-converter": {
             "version": "0.2.0",
@@ -6181,14 +5589,14 @@
             },
             "dependencies": {
                 "domelementtype": {
-                    "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz",
-                    "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ=="
+                    "version": "2.3.0",
+                    "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+                    "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
                 },
                 "entities": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
-                    "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw=="
+                    "version": "2.2.0",
+                    "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+                    "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="
                 }
             }
         },
@@ -6204,6 +5612,14 @@
             "dev": true,
             "requires": {
                 "webidl-conversions": "^4.0.2"
+            },
+            "dependencies": {
+                "webidl-conversions": {
+                    "version": "4.0.2",
+                    "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+                    "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
+                    "dev": true
+                }
             }
         },
         "domhandler": {
@@ -6214,6 +5630,11 @@
                 "domelementtype": "1"
             }
         },
+        "dommatrix": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/dommatrix/-/dommatrix-1.0.3.tgz",
+            "integrity": "sha512-l32Xp/TLgWb8ReqbVJAFIvXmY7go4nTxxlWiAFyhoQw9RKEOHBZNnyGvJWqDVSPmq3Y9HlM4npqF/T6VMOXhww=="
+        },
         "domutils": {
             "version": "1.7.0",
             "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
@@ -6230,19 +5651,12 @@
             "requires": {
                 "no-case": "^3.0.4",
                 "tslib": "^2.0.3"
-            },
-            "dependencies": {
-                "tslib": {
-                    "version": "2.3.1",
-                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
-                    "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
-                }
             }
         },
         "dot-prop": {
-            "version": "4.2.0",
-            "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
-            "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==",
+            "version": "4.2.1",
+            "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz",
+            "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==",
             "requires": {
                 "is-obj": "^1.0.0"
             }
@@ -6256,12 +5670,12 @@
         "double-bits": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/double-bits/-/double-bits-1.1.1.tgz",
-            "integrity": "sha1-WKu6RUlNpND6Nrc60RoobJGEscY="
+            "integrity": "sha512-BCLEIBq0O/DWoA7BsCu/R+RP0ZXiowP8BhtJT3qeuuQEBpnS8LK/Wo6UTJQv6v8mK1fj8n90YziHLwGdM5whSg=="
         },
         "duplexer3": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
-            "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
+            "integrity": "sha512-CEj8FwwNA4cVH2uFCoHUrmojhYh1vmCdOaneKJXwkeY1i9jnlslVo9dx+hQ5Hl9GnH/Bwy/IjxAyOePyPKYnzA=="
         },
         "duplexify": {
             "version": "3.7.1",
@@ -6295,7 +5709,7 @@
         "dynamic-dedupe": {
             "version": "0.3.0",
             "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz",
-            "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=",
+            "integrity": "sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==",
             "dev": true,
             "requires": {
                 "xtend": "^4.0.0"
@@ -6304,7 +5718,7 @@
         "ecc-jsbn": {
             "version": "0.1.2",
             "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
-            "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+            "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
             "requires": {
                 "jsbn": "~0.1.0",
                 "safer-buffer": "^2.1.0"
@@ -6321,12 +5735,12 @@
         "ee-first": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
-            "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+            "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
         },
         "electron-to-chromium": {
-            "version": "1.4.86",
-            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.86.tgz",
-            "integrity": "sha512-EVTZ+igi8x63pK4bPuA95PXIs2b2Cowi3WQwI9f9qManLiZJOD1Lash1J3W4TvvcUCcIR4o/rgi9o8UicXSO+w=="
+            "version": "1.4.149",
+            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.149.tgz",
+            "integrity": "sha512-SR6ZQ8gfXX7LIS3UTDXu1CbFMUnluP3TS+jP7QwDwOnoH5CcHbejFdaaaEnGxR6I76E55eFnUV9mxI8GKufkEg=="
         },
         "emoji-regex": {
             "version": "7.0.3",
@@ -6350,14 +5764,14 @@
         "encodeurl": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
-            "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
+            "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
         },
         "encoding": {
-            "version": "0.1.12",
-            "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
-            "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
+            "version": "0.1.13",
+            "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
+            "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
             "requires": {
-                "iconv-lite": "~0.4.13"
+                "iconv-lite": "^0.6.2"
             }
         },
         "end-of-stream": {
@@ -6425,7 +5839,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
                 },
                 "ws": {
                     "version": "7.4.6",
@@ -6447,14 +5861,13 @@
             }
         },
         "enhanced-resolve": {
-            "version": "4.1.1",
-            "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz",
-            "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==",
+            "version": "5.9.3",
+            "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz",
+            "integrity": "sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==",
             "dev": true,
             "requires": {
-                "graceful-fs": "^4.1.2",
-                "memory-fs": "^0.5.0",
-                "tapable": "^1.0.0"
+                "graceful-fs": "^4.2.4",
+                "tapable": "^2.2.0"
             }
         },
         "entities": {
@@ -6476,9 +5889,9 @@
             }
         },
         "errno": {
-            "version": "0.1.7",
-            "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
-            "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
+            "version": "0.1.8",
+            "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
+            "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
             "dev": true,
             "requires": {
                 "prr": "~1.0.1"
@@ -6493,23 +5906,53 @@
             }
         },
         "es-abstract": {
-            "version": "1.17.5",
-            "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz",
-            "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==",
+            "version": "1.20.1",
+            "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz",
+            "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==",
             "requires": {
+                "call-bind": "^1.0.2",
                 "es-to-primitive": "^1.2.1",
                 "function-bind": "^1.1.1",
+                "function.prototype.name": "^1.1.5",
+                "get-intrinsic": "^1.1.1",
+                "get-symbol-description": "^1.0.0",
                 "has": "^1.0.3",
-                "has-symbols": "^1.0.1",
-                "is-callable": "^1.1.5",
-                "is-regex": "^1.0.5",
-                "object-inspect": "^1.7.0",
+                "has-property-descriptors": "^1.0.0",
+                "has-symbols": "^1.0.3",
+                "internal-slot": "^1.0.3",
+                "is-callable": "^1.2.4",
+                "is-negative-zero": "^2.0.2",
+                "is-regex": "^1.1.4",
+                "is-shared-array-buffer": "^1.0.2",
+                "is-string": "^1.0.7",
+                "is-weakref": "^1.0.2",
+                "object-inspect": "^1.12.0",
                 "object-keys": "^1.1.1",
-                "object.assign": "^4.1.0",
-                "string.prototype.trimleft": "^2.1.1",
-                "string.prototype.trimright": "^2.1.1"
+                "object.assign": "^4.1.2",
+                "regexp.prototype.flags": "^1.4.3",
+                "string.prototype.trimend": "^1.0.5",
+                "string.prototype.trimstart": "^1.0.5",
+                "unbox-primitive": "^1.0.2"
+            },
+            "dependencies": {
+                "object.assign": {
+                    "version": "4.1.2",
+                    "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+                    "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+                    "requires": {
+                        "call-bind": "^1.0.0",
+                        "define-properties": "^1.1.3",
+                        "has-symbols": "^1.0.1",
+                        "object-keys": "^1.1.1"
+                    }
+                }
             }
         },
+        "es-array-method-boxes-properly": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz",
+            "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA=="
+        },
         "es-module-lexer": {
             "version": "0.9.3",
             "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
@@ -6526,10 +5969,32 @@
                 "is-symbol": "^1.0.2"
             }
         },
+        "es5-ext": {
+            "version": "0.10.61",
+            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz",
+            "integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==",
+            "dev": true,
+            "requires": {
+                "es6-iterator": "^2.0.3",
+                "es6-symbol": "^3.1.3",
+                "next-tick": "^1.1.0"
+            }
+        },
+        "es6-iterator": {
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+            "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
+            "dev": true,
+            "requires": {
+                "d": "1",
+                "es5-ext": "^0.10.35",
+                "es6-symbol": "^3.1.1"
+            }
+        },
         "es6-promise": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
-            "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q="
+            "integrity": "sha512-oj4jOSXvWglTsc3wrw86iom3LDPOx1nbipQk+jaG3dy+sMRM6ReSgVr/VlmBuF6lXUrflN9DCcQHeSbAwGUl4g=="
         },
         "es6-symbol": {
             "version": "3.1.3",
@@ -6537,6 +6002,7 @@
             "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
             "dev": true,
             "requires": {
+                "d": "^1.0.1",
                 "ext": "^1.1.2"
             }
         },
@@ -6548,17 +6014,17 @@
         "escape-html": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
-            "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+            "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
         },
         "escape-string-regexp": {
             "version": "1.0.5",
             "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-            "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
+            "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
         },
         "escodegen": {
-            "version": "1.14.1",
-            "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz",
-            "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==",
+            "version": "1.14.3",
+            "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
+            "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
             "dev": true,
             "requires": {
                 "esprima": "^4.0.1",
@@ -6623,7 +6089,7 @@
         "etag": {
             "version": "1.8.1",
             "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
-            "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+            "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
         },
         "event-target-shim": {
             "version": "5.0.1",
@@ -6643,18 +6109,15 @@
             "dev": true
         },
         "eventsource": {
-            "version": "1.1.0",
-            "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz",
-            "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==",
-            "dev": true,
-            "requires": {
-                "original": "^1.0.0"
-            }
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz",
+            "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==",
+            "dev": true
         },
         "execa": {
             "version": "0.7.0",
             "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
-            "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
+            "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==",
             "requires": {
                 "cross-spawn": "^5.0.1",
                 "get-stream": "^3.0.0",
@@ -6668,13 +6131,18 @@
                 "cross-spawn": {
                     "version": "5.1.0",
                     "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
-                    "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
+                    "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==",
                     "requires": {
                         "lru-cache": "^4.0.1",
                         "shebang-command": "^1.2.0",
                         "which": "^1.2.9"
                     }
                 },
+                "get-stream": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+                    "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ=="
+                },
                 "lru-cache": {
                     "version": "4.1.5",
                     "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
@@ -6694,12 +6162,12 @@
         "exenv": {
             "version": "1.2.2",
             "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
-            "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50="
+            "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw=="
         },
         "exeq": {
             "version": "2.4.0",
             "resolved": "https://registry.npmjs.org/exeq/-/exeq-2.4.0.tgz",
-            "integrity": "sha1-Td8qaEZIxCeteZNJzzO9dTWPiEo=",
+            "integrity": "sha512-B648qbDS00nQZv9UQGLT5RbZm/5dNBX10F8oWeXcgpFHSLm1249u95t/3sn2wXdQjLhlF+edAECdshFtSr1K0Q==",
             "requires": {
                 "bluebird": "^3.0.3",
                 "native-or-bluebird": "^1.2.0"
@@ -6708,7 +6176,7 @@
         "exif": {
             "version": "0.6.0",
             "resolved": "https://registry.npmjs.org/exif/-/exif-0.6.0.tgz",
-            "integrity": "sha1-YKYmaAdlQst+T1cZnUrG830sX0o=",
+            "integrity": "sha512-gEwM4uanNMfLnDNKclZ7jPEA99E3rpy4ntoS6QW8u6murZjl1o8qRaPdMoC46Syg3d9/QaET0bYKhWlTwJCPgg==",
             "requires": {
                 "debug": "^2.2"
             },
@@ -6724,7 +6192,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
                 }
             }
         },
@@ -6736,7 +6204,7 @@
         "expand-brackets": {
             "version": "2.1.4",
             "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-            "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+            "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==",
             "requires": {
                 "debug": "^2.3.3",
                 "define-property": "^0.2.5",
@@ -6758,7 +6226,7 @@
                 "define-property": {
                     "version": "0.2.5",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-                    "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+                    "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
                     "requires": {
                         "is-descriptor": "^0.1.0"
                     }
@@ -6766,20 +6234,15 @@
                 "extend-shallow": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-                    "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+                    "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
                     "requires": {
                         "is-extendable": "^0.1.0"
                     }
                 },
-                "is-extendable": {
-                    "version": "0.1.1",
-                    "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
-                    "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
-                },
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
                 }
             }
         },
@@ -6789,46 +6252,47 @@
             "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
         },
         "express": {
-            "version": "4.17.3",
-            "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz",
-            "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==",
+            "version": "4.18.1",
+            "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz",
+            "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==",
             "requires": {
                 "accepts": "~1.3.8",
                 "array-flatten": "1.1.1",
-                "body-parser": "1.19.2",
+                "body-parser": "1.20.0",
                 "content-disposition": "0.5.4",
                 "content-type": "~1.0.4",
-                "cookie": "0.4.2",
+                "cookie": "0.5.0",
                 "cookie-signature": "1.0.6",
                 "debug": "2.6.9",
-                "depd": "~1.1.2",
+                "depd": "2.0.0",
                 "encodeurl": "~1.0.2",
                 "escape-html": "~1.0.3",
                 "etag": "~1.8.1",
-                "finalhandler": "~1.1.2",
+                "finalhandler": "1.2.0",
                 "fresh": "0.5.2",
+                "http-errors": "2.0.0",
                 "merge-descriptors": "1.0.1",
                 "methods": "~1.1.2",
-                "on-finished": "~2.3.0",
+                "on-finished": "2.4.1",
                 "parseurl": "~1.3.3",
                 "path-to-regexp": "0.1.7",
                 "proxy-addr": "~2.0.7",
-                "qs": "6.9.7",
+                "qs": "6.10.3",
                 "range-parser": "~1.2.1",
                 "safe-buffer": "5.2.1",
-                "send": "0.17.2",
-                "serve-static": "1.14.2",
+                "send": "0.18.0",
+                "serve-static": "1.15.0",
                 "setprototypeof": "1.2.0",
-                "statuses": "~1.5.0",
+                "statuses": "2.0.1",
                 "type-is": "~1.6.18",
                 "utils-merge": "1.0.1",
                 "vary": "~1.1.2"
             },
             "dependencies": {
                 "cookie": {
-                    "version": "0.4.2",
-                    "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
-                    "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA=="
+                    "version": "0.5.0",
+                    "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
+                    "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
                 },
                 "debug": {
                     "version": "2.6.9",
@@ -6841,12 +6305,15 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
                 },
                 "qs": {
-                    "version": "6.9.7",
-                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz",
-                    "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw=="
+                    "version": "6.10.3",
+                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
+                    "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
+                    "requires": {
+                        "side-channel": "^1.0.4"
+                    }
                 },
                 "safe-buffer": {
                     "version": "5.2.1",
@@ -6858,17 +6325,17 @@
         "express-flash": {
             "version": "0.0.2",
             "resolved": "https://registry.npmjs.org/express-flash/-/express-flash-0.0.2.tgz",
-            "integrity": "sha1-I9GovPP5DXB5KOSJ+Whp7K0KzaI=",
+            "integrity": "sha512-QVUR0ZZRCaa8+iPHoUQaQJrQWcQuK/Q+19M7IUIdIEtvwhrA/ifHT7y1CVJI41YfGiOQnbGtn3uvd2vOdgu58A==",
             "requires": {
                 "connect-flash": "0.1.x"
             }
         },
         "express-session": {
-            "version": "1.17.2",
-            "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz",
-            "integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==",
+            "version": "1.17.3",
+            "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz",
+            "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==",
             "requires": {
-                "cookie": "0.4.1",
+                "cookie": "0.4.2",
                 "cookie-signature": "1.0.6",
                 "debug": "2.6.9",
                 "depd": "~2.0.0",
@@ -6878,6 +6345,11 @@
                 "uid-safe": "~2.1.5"
             },
             "dependencies": {
+                "cookie": {
+                    "version": "0.4.2",
+                    "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+                    "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA=="
+                },
                 "debug": {
                     "version": "2.6.9",
                     "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -6886,15 +6358,10 @@
                         "ms": "2.0.0"
                     }
                 },
-                "depd": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
-                    "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
-                },
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
                 },
                 "safe-buffer": {
                     "version": "5.2.1",
@@ -6915,21 +6382,21 @@
         "expressjs": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/expressjs/-/expressjs-1.0.1.tgz",
-            "integrity": "sha1-IgMoRpoY31rWFeK3oM6ZXxf7ru8="
+            "integrity": "sha512-eFnQ5bMJxTZ29XwRJPV8ee/OURBBMS6Fm+b5rvMMEyz6u2IxPEh2SRzMZt9WvgnV+SMLmnzkALE1DnGG1HxJCw=="
         },
         "ext": {
-            "version": "1.4.0",
-            "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
-            "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz",
+            "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==",
             "dev": true,
             "requires": {
-                "type": "^2.0.0"
+                "type": "^2.5.0"
             },
             "dependencies": {
                 "type": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz",
-                    "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==",
+                    "version": "2.6.0",
+                    "resolved": "https://registry.npmjs.org/type/-/type-2.6.0.tgz",
+                    "integrity": "sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ==",
                     "dev": true
                 }
             }
@@ -6942,10 +6409,20 @@
         "extend-shallow": {
             "version": "3.0.2",
             "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-            "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+            "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
             "requires": {
                 "assign-symbols": "^1.0.0",
                 "is-extendable": "^1.0.1"
+            },
+            "dependencies": {
+                "is-extendable": {
+                    "version": "1.0.1",
+                    "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"
+                    }
+                }
             }
         },
         "extglob": {
@@ -6966,7 +6443,7 @@
                 "define-property": {
                     "version": "1.0.0",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-                    "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+                    "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
                     "requires": {
                         "is-descriptor": "^1.0.0"
                     }
@@ -6974,7 +6451,7 @@
                 "extend-shallow": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-                    "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+                    "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
                     "requires": {
                         "is-extendable": "^0.1.0"
                     }
@@ -7004,11 +6481,6 @@
                         "is-data-descriptor": "^1.0.0",
                         "kind-of": "^6.0.2"
                     }
-                },
-                "is-extendable": {
-                    "version": "0.1.1",
-                    "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
-                    "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
                 }
             }
         },
@@ -7024,32 +6496,37 @@
             },
             "dependencies": {
                 "debug": {
-                    "version": "4.1.1",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
-                    "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+                    "version": "4.3.4",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
                     "requires": {
-                        "ms": "^2.1.1"
+                        "ms": "2.1.2"
                     }
                 },
                 "get-stream": {
-                    "version": "5.1.0",
-                    "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz",
-                    "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==",
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+                    "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
                     "requires": {
                         "pump": "^3.0.0"
                     }
+                },
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
                 }
             }
         },
         "extsprintf": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
-            "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
+            "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g=="
         },
         "fast-deep-equal": {
-            "version": "3.1.1",
-            "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
-            "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA=="
+            "version": "3.1.3",
+            "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+            "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
         },
         "fast-json-stable-stringify": {
             "version": "2.1.0",
@@ -7059,13 +6536,13 @@
         "fast-levenshtein": {
             "version": "2.0.6",
             "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
-            "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+            "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
             "dev": true
         },
         "fast-text-encoding": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.2.tgz",
-            "integrity": "sha512-5rQdinSsycpzvAoHga2EDn+LRX1d5xLFsuNG0Kg61JrAT/tASXcLL0nf/33v+sAxlQcfYmWbTURa1mmAf55jGw=="
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz",
+            "integrity": "sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig=="
         },
         "fastest-levenshtein": {
             "version": "1.0.12",
@@ -7082,9 +6559,9 @@
             }
         },
         "fbjs": {
-            "version": "0.8.17",
-            "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
-            "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
+            "version": "0.8.18",
+            "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.18.tgz",
+            "integrity": "sha512-EQaWFK+fEPSoibjNy8IxUtaFOMXcWsY0JaVrQoZR9zC8N2Ygf9iDITPWjUTVIax95b6I742JFLqASHfsag/vKA==",
             "requires": {
                 "core-js": "^1.0.0",
                 "isomorphic-fetch": "^2.1.1",
@@ -7092,17 +6569,25 @@
                 "object-assign": "^4.1.0",
                 "promise": "^7.1.1",
                 "setimmediate": "^1.0.5",
-                "ua-parser-js": "^0.7.18"
+                "ua-parser-js": "^0.7.30"
             }
         },
         "fd-slicer": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
-            "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
+            "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
             "requires": {
                 "pend": "~1.2.0"
             }
         },
+        "ffmpeg": {
+            "version": "0.0.4",
+            "resolved": "https://registry.npmjs.org/ffmpeg/-/ffmpeg-0.0.4.tgz",
+            "integrity": "sha512-3TgWUJJlZGQn+crJFyhsO/oNeRRnGTy6GhgS98oUCIfZrOW5haPPV7DUfOm3xJcHr5q3TJpjk2GudPutrNisRA==",
+            "requires": {
+                "when": ">= 0.0.1"
+            }
+        },
         "file-loader": {
             "version": "3.0.1",
             "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz",
@@ -7111,19 +6596,6 @@
             "requires": {
                 "loader-utils": "^1.0.2",
                 "schema-utils": "^1.0.0"
-            },
-            "dependencies": {
-                "schema-utils": {
-                    "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
-                    "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
-                    "dev": true,
-                    "requires": {
-                        "ajv": "^6.1.0",
-                        "ajv-errors": "^1.0.0",
-                        "ajv-keywords": "^3.1.0"
-                    }
-                }
             }
         },
         "file-saver": {
@@ -7140,7 +6612,7 @@
         "fill-range": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-            "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+            "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
             "requires": {
                 "extend-shallow": "^2.0.1",
                 "is-number": "^3.0.0",
@@ -7151,34 +6623,29 @@
                 "extend-shallow": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-                    "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+                    "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
                     "requires": {
                         "is-extendable": "^0.1.0"
                     }
-                },
-                "is-extendable": {
-                    "version": "0.1.1",
-                    "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
-                    "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
                 }
             }
         },
         "filter-obj": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
-            "integrity": "sha1-mzERErxsYSehbgFsbF1/GeCAXFs="
+            "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ=="
         },
         "finalhandler": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
-            "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
+            "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
             "requires": {
                 "debug": "2.6.9",
                 "encodeurl": "~1.0.2",
                 "escape-html": "~1.0.3",
-                "on-finished": "~2.3.0",
+                "on-finished": "2.4.1",
                 "parseurl": "~1.3.3",
-                "statuses": "~1.5.0",
+                "statuses": "2.0.1",
                 "unpipe": "~1.0.0"
             },
             "dependencies": {
@@ -7193,14 +6660,14 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
                 }
             }
         },
         "find": {
             "version": "0.1.7",
             "resolved": "https://registry.npmjs.org/find/-/find-0.1.7.tgz",
-            "integrity": "sha1-yGyHrxqxjyIrvjjeyGy8dg0Wpvs=",
+            "integrity": "sha512-jPrupTOe/pO//3a9Ty2o4NqQCp0L46UG+swUnfFtdmtQVN8pEltKpAqR7Nuf6vWn0GBXx5w+R1MyZzqwjEIqdA==",
             "requires": {
                 "traverse-chain": "~0.1.0"
             }
@@ -7208,12 +6675,81 @@
         "find-cache-dir": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz",
-            "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=",
+            "integrity": "sha512-46TFiBOzX7xq/PcSWfFwkyjpemdRnMe31UQF+os0y+1W3k95f6R4SEt02Hj4p3X0Mir9gfrkmOtshFidS0VPUg==",
             "dev": true,
             "requires": {
                 "commondir": "^1.0.1",
                 "make-dir": "^1.0.0",
                 "pkg-dir": "^2.0.0"
+            },
+            "dependencies": {
+                "find-up": {
+                    "version": "2.1.0",
+                    "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+                    "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==",
+                    "dev": true,
+                    "requires": {
+                        "locate-path": "^2.0.0"
+                    }
+                },
+                "locate-path": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+                    "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==",
+                    "dev": true,
+                    "requires": {
+                        "p-locate": "^2.0.0",
+                        "path-exists": "^3.0.0"
+                    }
+                },
+                "make-dir": {
+                    "version": "1.3.0",
+                    "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
+                    "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
+                    "dev": true,
+                    "requires": {
+                        "pify": "^3.0.0"
+                    }
+                },
+                "p-limit": {
+                    "version": "1.3.0",
+                    "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+                    "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+                    "dev": true,
+                    "requires": {
+                        "p-try": "^1.0.0"
+                    }
+                },
+                "p-locate": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+                    "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==",
+                    "dev": true,
+                    "requires": {
+                        "p-limit": "^1.1.0"
+                    }
+                },
+                "p-try": {
+                    "version": "1.0.0",
+                    "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+                    "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==",
+                    "dev": true
+                },
+                "pify": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+                    "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
+                    "dev": true
+                },
+                "pkg-dir": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
+                    "integrity": "sha512-ojakdnUgL5pzJYWw2AIDEupaQCX5OPbM688ZevubICjdIX01PRSYKqm33fJoCOJBRseYCTUlQRnBNX+Pchaejw==",
+                    "dev": true,
+                    "requires": {
+                        "find-up": "^2.1.0"
+                    }
+                }
             }
         },
         "find-in-files": {
@@ -7226,9 +6762,9 @@
             }
         },
         "find-parent-dir": {
-            "version": "0.3.0",
-            "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.0.tgz",
-            "integrity": "sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ="
+            "version": "0.3.1",
+            "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.1.tgz",
+            "integrity": "sha512-o4UcykWV/XN9wm+jMEtWLPlV8RXCZnMhQI6F6OdHeSez7iiJWePw8ijOlskJZMsaQoGR/b7dH6lO02HhaTN7+A=="
         },
         "find-root": {
             "version": "1.1.0",
@@ -7249,9 +6785,9 @@
             "integrity": "sha512-Md3b3ReA/qJwwYvKXeHpOV1fhPqwhJ9/29zc9lfi8ijyg00J0S9C7+5XMZDFhlDAKbwOvVgBxVIG1EHoMSC3vw=="
         },
         "flat": {
-            "version": "4.1.0",
-            "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
-            "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
+            "version": "4.1.1",
+            "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz",
+            "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==",
             "requires": {
                 "is-buffer": "~2.0.3"
             }
@@ -7264,7 +6800,7 @@
         "fluent-ffmpeg": {
             "version": "2.1.2",
             "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.2.tgz",
-            "integrity": "sha1-yVLeIkD4EuvaCqgAbXd27irPfXQ=",
+            "integrity": "sha512-IZTB4kq5GK0DPp7sGQ0q/BWurGHffRtQQwVkiqDgeO6wYJLLV5ZhgNOQ65loZxxuPMKZKZcICCUnaGtlxBiR0Q==",
             "requires": {
                 "async": ">=0.2.9",
                 "which": "^1.1.1"
@@ -7298,14 +6834,40 @@
             }
         },
         "follow-redirects": {
-            "version": "1.12.1",
-            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.12.1.tgz",
-            "integrity": "sha512-tmRv0AVuR7ZyouUHLeNSiO6pqulF7dYa3s19c6t+wz9LD69/uSzdMxJ2S91nTI9U3rt/IldxpzMOFejp6f0hjg=="
+            "version": "1.5.10",
+            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
+            "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
+            "requires": {
+                "debug": "=3.1.0"
+            },
+            "dependencies": {
+                "debug": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+                    "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+                    "requires": {
+                        "ms": "2.0.0"
+                    }
+                },
+                "ms": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+                }
+            }
+        },
+        "for-each": {
+            "version": "0.3.3",
+            "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+            "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+            "requires": {
+                "is-callable": "^1.1.3"
+            }
         },
         "for-each-property": {
             "version": "0.0.4",
             "resolved": "https://registry.npmjs.org/for-each-property/-/for-each-property-0.0.4.tgz",
-            "integrity": "sha1-z6hXrsFCLh0Sb/CHhPz2Jim8g/Y=",
+            "integrity": "sha512-xYs28PM0CKXETFzuGC6ZooH0voZlsSDZwidJcy92flQJi3PK7i3gZx23xHXCPOaD4zmet3bDo+wS7E7SujrlCw==",
             "requires": {
                 "get-prototype-chain": "^1.0.1"
             }
@@ -7313,7 +6875,7 @@
         "for-each-property-deep": {
             "version": "0.0.3",
             "resolved": "https://registry.npmjs.org/for-each-property-deep/-/for-each-property-deep-0.0.3.tgz",
-            "integrity": "sha1-MTCaSvw4qcygbxsiP1PWSm0IP60=",
+            "integrity": "sha512-qzP8QkODWVVRPpWiBZacSbBl67cTTWoBfxMG0wE46AsS1yl7qv05sGN+dHvD4s4tnvl/goe6Sp4qBI+rlVBgNg==",
             "requires": {
                 "for-each-property": "0.0.4"
             }
@@ -7321,17 +6883,12 @@
         "for-in": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
-            "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA="
-        },
-        "foreach": {
-            "version": "2.0.5",
-            "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
-            "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k="
+            "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ=="
         },
         "forever-agent": {
             "version": "0.6.1",
             "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
-            "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
+            "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw=="
         },
         "fork-ts-checker-webpack-plugin": {
             "version": "1.6.0",
@@ -7347,13 +6904,20 @@
                 "semver": "^5.6.0",
                 "tapable": "^1.0.0",
                 "worker-rpc": "^0.1.0"
+            },
+            "dependencies": {
+                "tapable": {
+                    "version": "1.1.3",
+                    "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+                    "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+                    "dev": true
+                }
             }
         },
         "form-data": {
-            "version": "2.5.1",
-            "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
-            "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
-            "dev": true,
+            "version": "2.3.3",
+            "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.6",
@@ -7378,7 +6942,7 @@
         "fragment-cache": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
-            "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+            "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==",
             "requires": {
                 "map-cache": "^0.2.2"
             }
@@ -7386,12 +6950,12 @@
         "fresh": {
             "version": "0.5.2",
             "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
-            "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
+            "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="
         },
         "from2": {
             "version": "2.3.0",
             "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
-            "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
+            "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==",
             "dev": true,
             "requires": {
                 "inherits": "^2.0.1",
@@ -7423,7 +6987,7 @@
         "fs-extra": {
             "version": "0.26.7",
             "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz",
-            "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=",
+            "integrity": "sha512-waKu+1KumRhYv8D8gMRCKJGAMI9pRnPuEb1mvgYD0f7wBscg+h6bW4FDTmEZhB9VKxvoTtxW+Y7bnIlB7zja6Q==",
             "requires": {
                 "graceful-fs": "^4.1.2",
                 "jsonfile": "^2.1.0",
@@ -7458,7 +7022,7 @@
         "fs-write-stream-atomic": {
             "version": "1.0.10",
             "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
-            "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
+            "integrity": "sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==",
             "dev": true,
             "requires": {
                 "graceful-fs": "^4.1.2",
@@ -7487,7 +7051,17 @@
         "fs.realpath": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-            "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+            "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
+        },
+        "fsevents": {
+            "version": "1.2.13",
+            "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+            "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+            "optional": true,
+            "requires": {
+                "bindings": "^1.5.0",
+                "nan": "^2.12.1"
+            }
         },
         "fstream": {
             "version": "1.0.12",
@@ -7533,50 +7107,69 @@
                 "interval-arithmetic-eval": "^0.4.7"
             }
         },
+        "function.prototype.name": {
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
+            "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
+            "requires": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.19.0",
+                "functions-have-names": "^1.2.2"
+            }
+        },
+        "functions-have-names": {
+            "version": "1.2.3",
+            "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+            "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="
+        },
         "gauge": {
-            "version": "2.7.4",
-            "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
-            "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
+            "version": "3.0.2",
+            "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
+            "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
             "requires": {
-                "aproba": "^1.0.3",
+                "aproba": "^1.0.3 || ^2.0.0",
+                "color-support": "^1.1.2",
                 "console-control-strings": "^1.0.0",
-                "has-unicode": "^2.0.0",
-                "object-assign": "^4.1.0",
+                "has-unicode": "^2.0.1",
+                "object-assign": "^4.1.1",
                 "signal-exit": "^3.0.0",
-                "string-width": "^1.0.1",
-                "strip-ansi": "^3.0.1",
-                "wide-align": "^1.1.0"
+                "string-width": "^4.2.3",
+                "strip-ansi": "^6.0.1",
+                "wide-align": "^1.1.2"
             },
             "dependencies": {
                 "ansi-regex": {
-                    "version": "2.1.1",
-                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-                    "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+                    "version": "5.0.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                    "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+                },
+                "emoji-regex": {
+                    "version": "8.0.0",
+                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+                    "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
                 },
                 "is-fullwidth-code-point": {
-                    "version": "1.0.0",
-                    "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.0"
-                    }
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+                    "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
                 },
                 "string-width": {
-                    "version": "1.0.2",
-                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-                    "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+                    "version": "4.2.3",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+                    "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
                     "requires": {
-                        "code-point-at": "^1.0.0",
-                        "is-fullwidth-code-point": "^1.0.0",
-                        "strip-ansi": "^3.0.0"
+                        "emoji-regex": "^8.0.0",
+                        "is-fullwidth-code-point": "^3.0.0",
+                        "strip-ansi": "^6.0.1"
                     }
                 },
                 "strip-ansi": {
-                    "version": "3.0.1",
-                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-                    "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+                    "version": "6.0.1",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                    "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
                     "requires": {
-                        "ansi-regex": "^2.0.0"
+                        "ansi-regex": "^5.0.1"
                     }
                 }
             }
@@ -7594,14 +7187,17 @@
             },
             "dependencies": {
                 "is-stream": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz",
-                    "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw=="
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+                    "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="
                 },
                 "node-fetch": {
-                    "version": "2.6.0",
-                    "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
-                    "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
+                    "version": "2.6.7",
+                    "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+                    "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+                    "requires": {
+                        "whatwg-url": "^5.0.0"
+                    }
                 }
             }
         },
@@ -7630,16 +7226,16 @@
         "get-func-name": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
-            "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE="
+            "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig=="
         },
         "get-intrinsic": {
-            "version": "1.1.1",
-            "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
-            "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz",
+            "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==",
             "requires": {
                 "function-bind": "^1.1.1",
                 "has": "^1.0.3",
-                "has-symbols": "^1.0.1"
+                "has-symbols": "^1.0.3"
             }
         },
         "get-node-dimensions": {
@@ -7650,17 +7246,17 @@
         "get-prototype-chain": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/get-prototype-chain/-/get-prototype-chain-1.0.1.tgz",
-            "integrity": "sha1-oXGhFeoeSQbG7ThDofABwYUQQW8="
+            "integrity": "sha512-2m7WZ0jveIg/dAbCbpUxEToaJ8Dmti5EkgDP8YM3UpHUT6SAORjE2odP8XQGNVGXMHi8q8cCCoy3HTByTaTVTw=="
         },
         "get-stdin": {
             "version": "5.0.1",
             "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz",
-            "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g="
+            "integrity": "sha512-jZV7n6jGE3Gt7fgSTJoz91Ak5MuTLwMwkoYdjxuJ/AmjIsE1UC03y/IWkZCQGEvVNS9qoRNwy5BCqxImv0FVeA=="
         },
         "get-stream": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
-            "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
+            "version": "6.0.1",
+            "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+            "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="
         },
         "get-symbol-description": {
             "version": "1.0.0",
@@ -7674,12 +7270,12 @@
         "get-value": {
             "version": "2.0.6",
             "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
-            "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg="
+            "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA=="
         },
         "getpass": {
             "version": "0.1.7",
             "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
-            "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+            "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
             "requires": {
                 "assert-plus": "^1.0.0"
             }
@@ -7687,17 +7283,17 @@
         "github-from-package": {
             "version": "0.0.0",
             "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
-            "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4="
+            "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
         },
         "glob": {
-            "version": "7.1.6",
-            "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
-            "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+            "version": "7.2.3",
+            "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+            "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
             "requires": {
                 "fs.realpath": "^1.0.0",
                 "inflight": "^1.0.4",
                 "inherits": "2",
-                "minimatch": "^3.0.4",
+                "minimatch": "^3.1.1",
                 "once": "^1.3.0",
                 "path-is-absolute": "^1.0.0"
             }
@@ -7705,7 +7301,7 @@
         "glob-parent": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
-            "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+            "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==",
             "requires": {
                 "is-glob": "^3.1.0",
                 "path-dirname": "^1.0.0"
@@ -7714,7 +7310,7 @@
                 "is-glob": {
                     "version": "3.1.0",
                     "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
-                    "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+                    "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==",
                     "requires": {
                         "is-extglob": "^2.1.0"
                     }
@@ -7730,7 +7326,7 @@
         "global-dirs": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
-            "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
+            "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==",
             "requires": {
                 "ini": "^1.3.4"
             }
@@ -7743,7 +7339,7 @@
         "globby": {
             "version": "7.1.1",
             "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz",
-            "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=",
+            "integrity": "sha512-yANWAN2DUcBtuus5Cpd+SKROzXHs2iVXFZt/Ykrfz6SAXqacLX25NZpltE+39ceMexYF4TtEadjuSTw8+3wX4g==",
             "dev": true,
             "requires": {
                 "array-union": "^1.0.1",
@@ -7757,25 +7353,48 @@
                 "pify": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-                    "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+                    "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
                     "dev": true
                 }
             }
         },
         "globule": {
-            "version": "1.3.1",
-            "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz",
-            "integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==",
+            "version": "1.3.3",
+            "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.3.tgz",
+            "integrity": "sha512-mb1aYtDbIjTu4ShMB85m3UzjX9BVKe9WCzsnfMSZk+K5GpIbBOexgg4PPCt5eHDEG5/ZQAUX2Kct02zfiPLsKg==",
             "requires": {
                 "glob": "~7.1.1",
-                "lodash": "~4.17.12",
+                "lodash": "~4.17.10",
                 "minimatch": "~3.0.2"
+            },
+            "dependencies": {
+                "glob": {
+                    "version": "7.1.7",
+                    "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
+                    "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
+                    "requires": {
+                        "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"
+                    }
+                },
+                "minimatch": {
+                    "version": "3.0.8",
+                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz",
+                    "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==",
+                    "requires": {
+                        "brace-expansion": "^1.1.7"
+                    }
+                }
             }
         },
         "golden-layout": {
             "version": "1.5.9",
             "resolved": "https://registry.npmjs.org/golden-layout/-/golden-layout-1.5.9.tgz",
-            "integrity": "sha1-o5vB9qZ+b4hreXwBbdkk6UJrp38=",
+            "integrity": "sha512-iBXDQCXOTgUEQJo96zPbjDoy5bRIk9XW5l+q+pDgLnIyReqaa1aiQctNud4epsskyLt952BG521dew5Z1liSxA==",
             "requires": {
                 "jquery": "*"
             }
@@ -7783,7 +7402,7 @@
         "good-listener": {
             "version": "1.2.2",
             "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
-            "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
+            "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
             "requires": {
                 "delegate": "^3.1.2"
             }
@@ -7801,6 +7420,21 @@
                 "gtoken": "^3.0.0",
                 "jws": "^3.1.5",
                 "lru-cache": "^5.0.0"
+            },
+            "dependencies": {
+                "lru-cache": {
+                    "version": "5.1.1",
+                    "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+                    "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+                    "requires": {
+                        "yallist": "^3.0.2"
+                    }
+                },
+                "yallist": {
+                    "version": "3.1.1",
+                    "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+                    "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
+                }
             }
         },
         "google-maps-react": {
@@ -7809,11 +7443,11 @@
             "integrity": "sha512-M8Eo9WndfQEfxcmm6yRq03qdJgw1x6rQmJ9DN+a+xPQ3K7yNDGkVDbinrf4/8vcox7nELbeopbm4bpefKewWfQ=="
         },
         "google-p12-pem": {
-            "version": "2.0.4",
-            "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.4.tgz",
-            "integrity": "sha512-S4blHBQWZRnEW44OcR7TL9WR+QCqByRvhNDZ/uuQfpxywfupikf/miba8js1jZi6ZOGv5slgSuoshCWh6EMDzg==",
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.5.tgz",
+            "integrity": "sha512-7RLkxwSsMsYh9wQ5Vb2zRtkAHvqPvfoMGag+nugl1noYO7gf0844Yr9TIFA5NEBMAeVt2Z+Imu7CQMp3oNatzQ==",
             "requires": {
-                "node-forge": "^0.9.0"
+                "node-forge": "^0.10.0"
             }
         },
         "googleapis": {
@@ -7839,9 +7473,12 @@
             },
             "dependencies": {
                 "qs": {
-                    "version": "6.9.3",
-                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz",
-                    "integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw=="
+                    "version": "6.10.5",
+                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.5.tgz",
+                    "integrity": "sha512-O5RlPh0VFtR78y79rgcgKK4wbAI0C5zGVLztOIdpWX6ep368q5Hv6XRxDvXuZ9q3C6v+e3n8UfZZJw7IIG27eQ==",
+                    "requires": {
+                        "side-channel": "^1.0.4"
+                    }
                 }
             }
         },
@@ -7856,9 +7493,9 @@
             }
         },
         "got": {
-            "version": "12.0.2",
-            "resolved": "https://registry.npmjs.org/got/-/got-12.0.2.tgz",
-            "integrity": "sha512-Zi4yHiqCgaorUbknr/RHFBsC3XqjSodaw0F3qxlqAqyj+OGYZl37/uy01R0qz++KANKQYdY5FHJ0okXZpEzwWQ==",
+            "version": "12.1.0",
+            "resolved": "https://registry.npmjs.org/got/-/got-12.1.0.tgz",
+            "integrity": "sha512-hBv2ty9QN2RdbJJMK3hesmSkFTjVIHyIDDbssCKnSmq62edGgImJWD10Eb1k77TiV1bxloxqcFAVK8+9pkhOig==",
             "requires": {
                 "@sindresorhus/is": "^4.6.0",
                 "@szmarczak/http-timer": "^5.0.1",
@@ -7883,11 +7520,6 @@
                         "mimic-response": "^3.1.0"
                     }
                 },
-                "get-stream": {
-                    "version": "6.0.1",
-                    "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
-                    "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="
-                },
                 "mimic-response": {
                     "version": "3.1.0",
                     "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
@@ -7896,9 +7528,9 @@
             }
         },
         "graceful-fs": {
-            "version": "4.2.4",
-            "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
-            "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw=="
+            "version": "4.2.10",
+            "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+            "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
         },
         "growl": {
             "version": "1.10.5",
@@ -7917,9 +7549,9 @@
             },
             "dependencies": {
                 "mime": {
-                    "version": "2.4.4",
-                    "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
-                    "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA=="
+                    "version": "2.6.0",
+                    "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+                    "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="
                 }
             }
         },
@@ -7937,14 +7569,14 @@
         "har-schema": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
-            "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
+            "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q=="
         },
         "har-validator": {
-            "version": "5.1.3",
-            "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
-            "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
+            "version": "5.1.5",
+            "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+            "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
             "requires": {
-                "ajv": "^6.5.5",
+                "ajv": "^6.12.3",
                 "har-schema": "^2.0.0"
             }
         },
@@ -7959,7 +7591,7 @@
         "has-ansi": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
-            "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+            "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
             "requires": {
                 "ansi-regex": "^2.0.0"
             },
@@ -7967,14 +7599,14 @@
                 "ansi-regex": {
                     "version": "2.1.1",
                     "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-                    "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+                    "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="
                 }
             }
         },
         "has-bigints": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz",
-            "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA=="
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
+            "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ=="
         },
         "has-binary2": {
             "version": "1.0.3",
@@ -7987,24 +7619,32 @@
                 "isarray": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
-                    "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+                    "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
                 }
             }
         },
         "has-cors": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
-            "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
+            "integrity": "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA=="
         },
         "has-flag": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-            "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
+            "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
+        },
+        "has-property-descriptors": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
+            "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
+            "requires": {
+                "get-intrinsic": "^1.1.1"
+            }
         },
         "has-symbols": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
-            "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+            "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
         },
         "has-tostringtag": {
             "version": "1.0.0",
@@ -8012,24 +7652,17 @@
             "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
             "requires": {
                 "has-symbols": "^1.0.2"
-            },
-            "dependencies": {
-                "has-symbols": {
-                    "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
-                    "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
-                }
             }
         },
         "has-unicode": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
-            "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
+            "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
         },
         "has-value": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
-            "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+            "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==",
             "requires": {
                 "get-value": "^2.0.6",
                 "has-values": "^1.0.0",
@@ -8039,7 +7672,7 @@
         "has-values": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
-            "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+            "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==",
             "requires": {
                 "is-number": "^3.0.0",
                 "kind-of": "^4.0.0"
@@ -8053,7 +7686,7 @@
                 "kind-of": {
                     "version": "4.0.0",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
-                    "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+                    "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -8074,9 +7707,9 @@
             }
         },
         "hosted-git-info": {
-            "version": "2.8.8",
-            "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
-            "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg=="
+            "version": "2.8.9",
+            "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+            "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="
         },
         "howler": {
             "version": "2.2.3",
@@ -8086,7 +7719,7 @@
         "hpack.js": {
             "version": "2.1.6",
             "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
-            "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=",
+            "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==",
             "dev": true,
             "requires": {
                 "inherits": "^2.0.1",
@@ -8115,7 +7748,7 @@
         "html": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/html/-/html-1.0.0.tgz",
-            "integrity": "sha1-pUT6nqVJK/s6LMqCEKEL57WvH2E=",
+            "integrity": "sha512-lw/7YsdKiP3kk5PnR1INY17iJuzdAtJewxr14ozKJWbbR97znovZ0mh+WEMZ8rjc3lgTK+ID/htTjuyGKB52Kw==",
             "requires": {
                 "concat-stream": "^1.4.7"
             }
@@ -8131,79 +7764,22 @@
         },
         "html-entities": {
             "version": "1.4.0",
-            "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz",
-            "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==",
-            "dev": true
-        },
-        "html-minifier-terser": {
-            "version": "6.1.0",
-            "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
-            "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==",
-            "requires": {
-                "camel-case": "^4.1.2",
-                "clean-css": "^5.2.2",
-                "commander": "^8.3.0",
-                "he": "^1.2.0",
-                "param-case": "^3.0.4",
-                "relateurl": "^0.2.7",
-                "terser": "^5.10.0"
-            },
-            "dependencies": {
-                "acorn": {
-                    "version": "8.7.0",
-                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
-                    "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ=="
-                },
-                "clean-css": {
-                    "version": "5.2.4",
-                    "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.2.4.tgz",
-                    "integrity": "sha512-nKseG8wCzEuji/4yrgM/5cthL9oTDc5UOQyFMvW/Q53oP6gLH690o1NbuTh6Y18nujr7BxlsFuS7gXLnLzKJGg==",
-                    "requires": {
-                        "source-map": "~0.6.0"
-                    }
-                },
-                "commander": {
-                    "version": "8.3.0",
-                    "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
-                    "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="
-                },
-                "source-map": {
-                    "version": "0.6.1",
-                    "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-                    "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
-                },
-                "source-map-support": {
-                    "version": "0.5.21",
-                    "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
-                    "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
-                    "requires": {
-                        "buffer-from": "^1.0.0",
-                        "source-map": "^0.6.0"
-                    }
-                },
-                "terser": {
-                    "version": "5.12.1",
-                    "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz",
-                    "integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==",
-                    "requires": {
-                        "acorn": "^8.5.0",
-                        "commander": "^2.20.0",
-                        "source-map": "~0.7.2",
-                        "source-map-support": "~0.5.20"
-                    },
-                    "dependencies": {
-                        "commander": {
-                            "version": "2.20.3",
-                            "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
-                            "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
-                        },
-                        "source-map": {
-                            "version": "0.7.3",
-                            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
-                            "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ=="
-                        }
-                    }
-                }
+            "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.4.0.tgz",
+            "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==",
+            "dev": true
+        },
+        "html-minifier-terser": {
+            "version": "6.1.0",
+            "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
+            "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==",
+            "requires": {
+                "camel-case": "^4.1.2",
+                "clean-css": "^5.2.2",
+                "commander": "^8.3.0",
+                "he": "^1.2.0",
+                "param-case": "^3.0.4",
+                "relateurl": "^0.2.7",
+                "terser": "^5.10.0"
             }
         },
         "html-to-image": {
@@ -8232,13 +7808,6 @@
                 "lodash": "^4.17.21",
                 "pretty-error": "^4.0.0",
                 "tapable": "^2.0.0"
-            },
-            "dependencies": {
-                "tapable": {
-                    "version": "2.2.1",
-                    "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
-                    "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="
-                }
             }
         },
         "htmlparser2": {
@@ -8257,7 +7826,7 @@
         "http-browserify": {
             "version": "1.7.0",
             "resolved": "https://registry.npmjs.org/http-browserify/-/http-browserify-1.7.0.tgz",
-            "integrity": "sha1-M3la3nLfiKz7/TZ3PO/tp2RzWyA=",
+            "integrity": "sha512-Irf/LJXmE3cBzU1eaR4+NEX6bmVLqt1wkmDiA7kBwH7zmb0D8kBAXsDmQ88hhj/qv9iEZKlyGx/hrMcFi8sOHw==",
             "requires": {
                 "Base64": "~0.2.0",
                 "inherits": "~2.0.1"
@@ -8271,18 +7840,18 @@
         "http-deceiver": {
             "version": "1.2.7",
             "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
-            "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=",
+            "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==",
             "dev": true
         },
         "http-errors": {
-            "version": "1.8.1",
-            "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
-            "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+            "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
             "requires": {
-                "depd": "~1.1.2",
+                "depd": "2.0.0",
                 "inherits": "2.0.4",
                 "setprototypeof": "1.2.0",
-                "statuses": ">= 1.5.0 < 2",
+                "statuses": "2.0.1",
                 "toidentifier": "1.0.1"
             }
         },
@@ -8318,7 +7887,7 @@
         "http-signature": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
-            "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+            "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
             "requires": {
                 "assert-plus": "^1.0.0",
                 "jsprim": "^1.2.2",
@@ -8326,9 +7895,9 @@
             }
         },
         "http2-wrapper": {
-            "version": "2.1.10",
-            "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.1.10.tgz",
-            "integrity": "sha512-QHgsdYkieKp+6JbXP25P+tepqiHYd+FVnDwXpxi/BlUcoIB0nsmTOymTNvETuTO+pDuwcSklPE72VR3DqV+Haw==",
+            "version": "2.1.11",
+            "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.1.11.tgz",
+            "integrity": "sha512-aNAk5JzLturWEUiuhAN73Jcbq96R7rTitAoXV54FYMatvihnpD2+6PUgU4ce3D/m5VDbw+F5CsyKSF176ptitQ==",
             "requires": {
                 "quick-lru": "^5.1.1",
                 "resolve-alpn": "^1.2.0"
@@ -8337,29 +7906,34 @@
         "https": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz",
-            "integrity": "sha1-PDfHrhqO65ZpBKKtHpdaGUt+06Q="
+            "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg=="
         },
         "https-browserify": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
-            "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM="
+            "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg=="
         },
         "https-proxy-agent": {
-            "version": "5.0.0",
-            "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
-            "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+            "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
             "requires": {
                 "agent-base": "6",
                 "debug": "4"
             },
             "dependencies": {
                 "debug": {
-                    "version": "4.1.1",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
-                    "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+                    "version": "4.3.4",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
                     "requires": {
-                        "ms": "^2.1.1"
+                        "ms": "2.1.2"
                     }
+                },
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
                 }
             }
         },
@@ -8374,9 +7948,9 @@
             "integrity": "sha512-xjxyDLbVDdLgjPnl4NM+Iu6il3UPmk6PNCBXruQKeuKDc/HtaZx1hk1AtMgw3vsn9YnLZRfoBpPxYMXcoT5KAA=="
         },
         "hyphenate-style-name": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz",
-            "integrity": "sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ=="
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
+            "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
         },
         "i": {
             "version": "0.3.7",
@@ -8384,17 +7958,17 @@
             "integrity": "sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q=="
         },
         "iconv-lite": {
-            "version": "0.4.24",
-            "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
-            "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+            "version": "0.6.3",
+            "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+            "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
             "requires": {
-                "safer-buffer": ">= 2.1.2 < 3"
+                "safer-buffer": ">= 2.1.2 < 3.0.0"
             }
         },
         "icss-replace-symbols": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz",
-            "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=",
+            "integrity": "sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==",
             "dev": true
         },
         "icss-utils": {
@@ -8407,14 +7981,14 @@
             }
         },
         "ieee754": {
-            "version": "1.1.13",
-            "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
-            "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+            "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
         },
         "iferr": {
             "version": "0.1.5",
             "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
-            "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
+            "integrity": "sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==",
             "dev": true
         },
         "ignore": {
@@ -8426,7 +8000,7 @@
         "ignore-by-default": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
-            "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk="
+            "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA=="
         },
         "iink-js": {
             "version": "1.5.4",
@@ -8470,7 +8044,7 @@
         "image-size-stream": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/image-size-stream/-/image-size-stream-1.1.0.tgz",
-            "integrity": "sha1-Ivou2mbG31AQh0bacUkmSy0l+Gs=",
+            "integrity": "sha512-N505B5FSy2Xf5l/Haef+99TwfJqTu40hnU560+rC0Cm6cxtwVz2yRFh9WpOk1YEjfv3dI0PgVYAH0hmXQmjDcw==",
             "requires": {
                 "image-size": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1",
                 "readable-stream": "^1.0.33",
@@ -8484,12 +8058,12 @@
                 "isarray": {
                     "version": "0.0.1",
                     "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
-                    "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+                    "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
                 },
                 "readable-stream": {
                     "version": "1.1.14",
                     "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
-                    "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+                    "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==",
                     "requires": {
                         "core-util-is": "~1.0.0",
                         "inherits": "~2.0.1",
@@ -8507,7 +8081,7 @@
         "immediate": {
             "version": "3.0.6",
             "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
-            "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
+            "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
         },
         "import-fresh": {
             "version": "3.3.0",
@@ -8521,7 +8095,7 @@
         "import-lazy": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
-            "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM="
+            "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A=="
         },
         "import-local": {
             "version": "3.1.0",
@@ -8530,52 +8104,12 @@
             "requires": {
                 "pkg-dir": "^4.2.0",
                 "resolve-cwd": "^3.0.0"
-            },
-            "dependencies": {
-                "find-up": {
-                    "version": "4.1.0",
-                    "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
-                    "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
-                    "requires": {
-                        "locate-path": "^5.0.0",
-                        "path-exists": "^4.0.0"
-                    }
-                },
-                "locate-path": {
-                    "version": "5.0.0",
-                    "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
-                    "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
-                    "requires": {
-                        "p-locate": "^4.1.0"
-                    }
-                },
-                "p-locate": {
-                    "version": "4.1.0",
-                    "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
-                    "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
-                    "requires": {
-                        "p-limit": "^2.2.0"
-                    }
-                },
-                "path-exists": {
-                    "version": "4.0.0",
-                    "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
-                    "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
-                },
-                "pkg-dir": {
-                    "version": "4.2.0",
-                    "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
-                    "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
-                    "requires": {
-                        "find-up": "^4.0.0"
-                    }
-                }
             }
         },
         "imurmurhash": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
-            "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
+            "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="
         },
         "in-publish": {
             "version": "2.0.1",
@@ -8585,26 +8119,20 @@
         "indent-string": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
-            "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
+            "integrity": "sha512-aqwDFWSgSgfRaEwao5lg5KEcVd/2a+D1rvoG7NdilmYz0NwRk6StWpWdz/Hpk34MKPpx7s8XxUqimfcQK6gGlg==",
             "requires": {
                 "repeating": "^2.0.0"
             }
         },
-        "indexes-of": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
-            "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
-            "dev": true
-        },
         "indexof": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
-            "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
+            "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg=="
         },
         "inflight": {
             "version": "1.0.6",
             "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-            "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+            "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
             "requires": {
                 "once": "^1.3.0",
                 "wrappy": "1"
@@ -8621,7 +8149,7 @@
                 "camelcase": {
                     "version": "4.1.0",
                     "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
-                    "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
+                    "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw=="
                 }
             }
         },
@@ -8631,14 +8159,14 @@
             "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
         },
         "ini": {
-            "version": "1.3.5",
-            "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
-            "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
+            "version": "1.3.8",
+            "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+            "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
         },
         "inline-style-prefixer": {
             "version": "3.0.8",
             "resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-3.0.8.tgz",
-            "integrity": "sha1-hVG45bTVcyROZqNLBPfTIHaitTQ=",
+            "integrity": "sha512-ne8XIyyqkRaNJ1JfL1NYzNdCNxq+MCBQhC8NgOQlzNm2vv3XxlP0VSLQUbSRCF6KPEoveCVEpayHoHzcMyZsMQ==",
             "requires": {
                 "bowser": "^1.7.3",
                 "css-in-js-utils": "^2.0.0"
@@ -8647,7 +8175,7 @@
         "inspect-function": {
             "version": "0.2.2",
             "resolved": "https://registry.npmjs.org/inspect-function/-/inspect-function-0.2.2.tgz",
-            "integrity": "sha1-hdoMUli8TDMK4yg7Z0fgdZ2QpjU=",
+            "integrity": "sha512-becs5gzcHwPrlHawscYkyQ/ShiOiosrXPhA5RVZ3qyWH4aWdD52RnMfXq/dwQXciHwiieD8aIPwdIWYv6eL+sQ==",
             "requires": {
                 "split-skip": "0.0.1",
                 "unpack-string": "0.0.2"
@@ -8656,7 +8184,7 @@
                 "split-skip": {
                     "version": "0.0.1",
                     "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.1.tgz",
-                    "integrity": "sha1-gK2ONumOV2RUzDtmfB3SXYZejwA="
+                    "integrity": "sha512-7dkvq+gofI4M8zx4iZnEZ3O1s7FP4Y/iaIDHJh5RyWrs8idcPauFi2OZe3TBi36fLvR2j5z3kSzVtz6IhPdncQ=="
                 }
             }
         },
@@ -8671,10 +8199,15 @@
                 "unpack-string": "0.0.2"
             },
             "dependencies": {
+                "commander": {
+                    "version": "2.20.3",
+                    "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+                    "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+                },
                 "magicli": {
                     "version": "0.0.5",
                     "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz",
-                    "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=",
+                    "integrity": "sha512-wZbMtnl2v1b+Jp3xlqA9FU/O4I6YhGXR8xSY/eU2+gDAvut/F+W3gl4qs61iL4LELC7jqSAE6aAD5668EbmQHA==",
                     "requires": {
                         "commander": "^2.9.0",
                         "get-stdin": "^5.0.1",
@@ -8694,6 +8227,11 @@
                 "inspect-function": "^0.3.1"
             },
             "dependencies": {
+                "commander": {
+                    "version": "2.20.3",
+                    "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+                    "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+                },
                 "inspect-function": {
                     "version": "0.3.4",
                     "resolved": "https://registry.npmjs.org/inspect-function/-/inspect-function-0.3.4.tgz",
@@ -8720,7 +8258,7 @@
                         "inspect-function": {
                             "version": "0.2.2",
                             "resolved": "https://registry.npmjs.org/inspect-function/-/inspect-function-0.2.2.tgz",
-                            "integrity": "sha1-hdoMUli8TDMK4yg7Z0fgdZ2QpjU=",
+                            "integrity": "sha512-becs5gzcHwPrlHawscYkyQ/ShiOiosrXPhA5RVZ3qyWH4aWdD52RnMfXq/dwQXciHwiieD8aIPwdIWYv6eL+sQ==",
                             "requires": {
                                 "split-skip": "0.0.1",
                                 "unpack-string": "0.0.2"
@@ -8729,14 +8267,14 @@
                                 "split-skip": {
                                     "version": "0.0.1",
                                     "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.1.tgz",
-                                    "integrity": "sha1-gK2ONumOV2RUzDtmfB3SXYZejwA="
+                                    "integrity": "sha512-7dkvq+gofI4M8zx4iZnEZ3O1s7FP4Y/iaIDHJh5RyWrs8idcPauFi2OZe3TBi36fLvR2j5z3kSzVtz6IhPdncQ=="
                                 }
                             }
                         },
                         "magicli": {
                             "version": "0.0.5",
                             "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz",
-                            "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=",
+                            "integrity": "sha512-wZbMtnl2v1b+Jp3xlqA9FU/O4I6YhGXR8xSY/eU2+gDAvut/F+W3gl4qs61iL4LELC7jqSAE6aAD5668EbmQHA==",
                             "requires": {
                                 "commander": "^2.9.0",
                                 "get-stdin": "^5.0.1",
@@ -8747,14 +8285,14 @@
                         "split-skip": {
                             "version": "0.0.2",
                             "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.2.tgz",
-                            "integrity": "sha1-2J2Iu9L3Pka1FYqjcKVhIk6A1GE="
+                            "integrity": "sha512-weHOi8BolsDnGIwhhWHbA+wKSuSpvWwjRrdj8SdbIIis2vSwOE37CQP8x3EleuzxanUr3AK8BdUy4MkiOULPZg=="
                         }
                     }
                 },
                 "split-skip": {
                     "version": "0.0.1",
                     "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.1.tgz",
-                    "integrity": "sha1-gK2ONumOV2RUzDtmfB3SXYZejwA="
+                    "integrity": "sha512-7dkvq+gofI4M8zx4iZnEZ3O1s7FP4Y/iaIDHJh5RyWrs8idcPauFi2OZe3TBi36fLvR2j5z3kSzVtz6IhPdncQ=="
                 }
             }
         },
@@ -8817,15 +8355,15 @@
             }
         },
         "ip": {
-            "version": "1.1.5",
-            "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
-            "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
+            "version": "1.1.8",
+            "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz",
+            "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==",
             "dev": true
         },
         "ip-regex": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
-            "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
+            "integrity": "sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==",
             "dev": true
         },
         "ipaddr.js": {
@@ -8842,7 +8380,7 @@
         "is-accessor-descriptor": {
             "version": "0.1.6",
             "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
-            "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+            "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
             "requires": {
                 "kind-of": "^3.0.2"
             },
@@ -8855,7 +8393,7 @@
                 "kind-of": {
                     "version": "3.2.2",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                    "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+                    "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -8874,7 +8412,7 @@
         "is-arrayish": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
-            "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
+            "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
         },
         "is-bigint": {
             "version": "1.0.4",
@@ -8887,7 +8425,7 @@
         "is-binary-path": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
-            "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+            "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==",
             "requires": {
                 "binary-extensions": "^1.0.0"
             }
@@ -8902,14 +8440,14 @@
             }
         },
         "is-buffer": {
-            "version": "2.0.4",
-            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
-            "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A=="
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz",
+            "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ=="
         },
         "is-callable": {
-            "version": "1.1.5",
-            "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
-            "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q=="
+            "version": "1.2.4",
+            "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz",
+            "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w=="
         },
         "is-ci": {
             "version": "1.2.1",
@@ -8919,10 +8457,18 @@
                 "ci-info": "^1.5.0"
             }
         },
+        "is-core-module": {
+            "version": "2.9.0",
+            "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
+            "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
+            "requires": {
+                "has": "^1.0.3"
+            }
+        },
         "is-data-descriptor": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
-            "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+            "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
             "requires": {
                 "kind-of": "^3.0.2"
             },
@@ -8935,7 +8481,7 @@
                 "kind-of": {
                     "version": "3.2.2",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                    "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+                    "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -8943,9 +8489,12 @@
             }
         },
         "is-date-object": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
-            "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g=="
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
+            "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
+            "requires": {
+                "has-tostringtag": "^1.0.0"
+            }
         },
         "is-descriptor": {
             "version": "0.1.6",
@@ -8967,12 +8516,12 @@
         "is-directory": {
             "version": "0.3.1",
             "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
-            "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE="
+            "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw=="
         },
         "is-expression": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-3.0.0.tgz",
-            "integrity": "sha1-Oayqa+f9HzRx3ELHQW5hwkMXrJ8=",
+            "integrity": "sha512-vyMeQMq+AiH5uUnoBfMTwf18tO3bM6k1QXBE9D6ueAAquEfCZe3AJPtud9g6qS0+4X8xA7ndpZiDyeb2l2qOBw==",
             "requires": {
                 "acorn": "~4.0.2",
                 "object-assign": "^4.0.1"
@@ -8981,22 +8530,19 @@
                 "acorn": {
                     "version": "4.0.13",
                     "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
-                    "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c="
+                    "integrity": "sha512-fu2ygVGuMmlzG8ZeRJ0bvR41nsAkxxhbyk8bZ1SS521Z7vmgJFTQQlfz/Mp/nJexGBz+v8sC9bM6+lNgskt4Ug=="
                 }
             }
         },
         "is-extendable": {
-            "version": "1.0.1",
-            "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"
-            }
+            "version": "0.1.1",
+            "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+            "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="
         },
         "is-extglob": {
             "version": "2.1.1",
             "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-            "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
+            "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
         },
         "is-finite": {
             "version": "1.1.0",
@@ -9006,7 +8552,7 @@
         "is-fullwidth-code-point": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-            "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
+            "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w=="
         },
         "is-generator-function": {
             "version": "1.0.10",
@@ -9017,9 +8563,9 @@
             }
         },
         "is-glob": {
-            "version": "4.0.1",
-            "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
-            "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+            "version": "4.0.3",
+            "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+            "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
             "requires": {
                 "is-extglob": "^2.1.1"
             }
@@ -9027,12 +8573,12 @@
         "is-in-browser": {
             "version": "1.1.3",
             "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz",
-            "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU="
+            "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g=="
         },
         "is-installed-globally": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz",
-            "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=",
+            "integrity": "sha512-ERNhMg+i/XgDwPIPF3u24qpajVreaiSuvpb1Uu0jugw7KKcxGyCX8cgp8P5fwTmAuXku6beDHHECdKArjlg7tw==",
             "requires": {
                 "global-dirs": "^0.1.0",
                 "is-path-inside": "^1.0.0"
@@ -9046,12 +8592,12 @@
         "is-npm": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz",
-            "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ="
+            "integrity": "sha512-9r39FIr3d+KD9SbX0sfMsHzb5PP3uimOiwr3YupUaUFG4W0l1U57Rx3utpttV7qz5U3jmrO5auUa04LU9pyHsg=="
         },
         "is-number": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-            "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+            "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
             "requires": {
                 "kind-of": "^3.0.2"
             },
@@ -9064,7 +8610,7 @@
                 "kind-of": {
                     "version": "3.2.2",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                    "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+                    "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -9072,9 +8618,9 @@
             }
         },
         "is-number-object": {
-            "version": "1.0.6",
-            "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz",
-            "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==",
+            "version": "1.0.7",
+            "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
+            "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
             "requires": {
                 "has-tostringtag": "^1.0.0"
             }
@@ -9082,7 +8628,7 @@
         "is-obj": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
-            "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8="
+            "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg=="
         },
         "is-path-cwd": {
             "version": "2.2.0",
@@ -9113,7 +8659,7 @@
         "is-path-inside": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
-            "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
+            "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==",
             "requires": {
                 "path-is-inside": "^1.0.1"
             }
@@ -9134,14 +8680,15 @@
         "is-redirect": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
-            "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ="
+            "integrity": "sha512-cr/SlUEe5zOGmzvj9bUyC4LVvkNVAXu4GytXLNMr1pny+a65MpQ9IJzFHD5vi7FyJgb4qt27+eS3TuQnqB+RQw=="
         },
         "is-regex": {
-            "version": "1.0.5",
-            "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
-            "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+            "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
             "requires": {
-                "has": "^1.0.3"
+                "call-bind": "^1.0.2",
+                "has-tostringtag": "^1.0.0"
             }
         },
         "is-retry-allowed": {
@@ -9158,14 +8705,17 @@
             }
         },
         "is-shared-array-buffer": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz",
-            "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA=="
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
+            "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
+            "requires": {
+                "call-bind": "^1.0.2"
+            }
         },
         "is-stream": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
-            "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
+            "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ=="
         },
         "is-string": {
             "version": "1.0.7",
@@ -9176,116 +8726,34 @@
             }
         },
         "is-symbol": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
-            "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
+            "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
             "requires": {
-                "has-symbols": "^1.0.1"
+                "has-symbols": "^1.0.2"
             }
         },
         "is-typed-array": {
-            "version": "1.1.8",
-            "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz",
-            "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==",
+            "version": "1.1.9",
+            "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz",
+            "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==",
             "requires": {
                 "available-typed-arrays": "^1.0.5",
                 "call-bind": "^1.0.2",
-                "es-abstract": "^1.18.5",
-                "foreach": "^2.0.5",
+                "es-abstract": "^1.20.0",
+                "for-each": "^0.3.3",
                 "has-tostringtag": "^1.0.0"
-            },
-            "dependencies": {
-                "es-abstract": {
-                    "version": "1.19.1",
-                    "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz",
-                    "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==",
-                    "requires": {
-                        "call-bind": "^1.0.2",
-                        "es-to-primitive": "^1.2.1",
-                        "function-bind": "^1.1.1",
-                        "get-intrinsic": "^1.1.1",
-                        "get-symbol-description": "^1.0.0",
-                        "has": "^1.0.3",
-                        "has-symbols": "^1.0.2",
-                        "internal-slot": "^1.0.3",
-                        "is-callable": "^1.2.4",
-                        "is-negative-zero": "^2.0.1",
-                        "is-regex": "^1.1.4",
-                        "is-shared-array-buffer": "^1.0.1",
-                        "is-string": "^1.0.7",
-                        "is-weakref": "^1.0.1",
-                        "object-inspect": "^1.11.0",
-                        "object-keys": "^1.1.1",
-                        "object.assign": "^4.1.2",
-                        "string.prototype.trimend": "^1.0.4",
-                        "string.prototype.trimstart": "^1.0.4",
-                        "unbox-primitive": "^1.0.1"
-                    }
-                },
-                "has-symbols": {
-                    "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
-                    "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
-                },
-                "is-callable": {
-                    "version": "1.2.4",
-                    "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz",
-                    "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w=="
-                },
-                "is-regex": {
-                    "version": "1.1.4",
-                    "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
-                    "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
-                    "requires": {
-                        "call-bind": "^1.0.2",
-                        "has-tostringtag": "^1.0.0"
-                    }
-                },
-                "object-inspect": {
-                    "version": "1.12.0",
-                    "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
-                    "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g=="
-                },
-                "object.assign": {
-                    "version": "4.1.2",
-                    "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
-                    "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
-                    "requires": {
-                        "call-bind": "^1.0.0",
-                        "define-properties": "^1.1.3",
-                        "has-symbols": "^1.0.1",
-                        "object-keys": "^1.1.1"
-                    }
-                },
-                "string.prototype.trimend": {
-                    "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
-                    "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
-                    "requires": {
-                        "call-bind": "^1.0.2",
-                        "define-properties": "^1.1.3"
-                    }
-                },
-                "string.prototype.trimstart": {
-                    "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
-                    "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
-                    "requires": {
-                        "call-bind": "^1.0.2",
-                        "define-properties": "^1.1.3"
-                    }
-                }
             }
         },
         "is-typedarray": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-            "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
+            "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
         },
         "is-utf8": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
-            "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI="
+            "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q=="
         },
         "is-weakref": {
             "version": "1.0.2",
@@ -9296,9 +8764,9 @@
             }
         },
         "is-what": {
-            "version": "3.10.0",
-            "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.10.0.tgz",
-            "integrity": "sha512-U4RYCXNOmATQHlOPlOCHCfXyKEFIPqvyaKDqYRuLbD6EYKcTTfc3YXkAYjzOVxO3zt34L+Wh2feIyWrYiZ7kng=="
+            "version": "3.14.1",
+            "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz",
+            "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA=="
         },
         "is-windows": {
             "version": "1.0.2",
@@ -9308,28 +8776,28 @@
         "is-wsl": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
-            "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
+            "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==",
             "dev": true
         },
         "isarray": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-            "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+            "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
         },
         "isexe": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-            "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
+            "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
         },
         "isobject": {
             "version": "3.0.1",
             "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-            "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
+            "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="
         },
         "isomorphic-fetch": {
             "version": "2.2.1",
             "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
-            "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
+            "integrity": "sha512-9c4TNAKYXM5PRyVcwUZrF3W09nQ+sO7+jydgs4ZGW9dhsLG2VOlISJABombdQqQRXCwuYG3sYV/puGf5rp0qmA==",
             "requires": {
                 "node-fetch": "^1.0.1",
                 "whatwg-fetch": ">=0.10.0"
@@ -9338,7 +8806,7 @@
         "isstream": {
             "version": "0.1.2",
             "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
-            "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+            "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g=="
         },
         "its-set": {
             "version": "1.2.3",
@@ -9378,14 +8846,14 @@
             }
         },
         "jquery": {
-            "version": "3.5.1",
-            "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz",
-            "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg=="
+            "version": "3.6.0",
+            "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
+            "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
         },
         "js-base64": {
-            "version": "2.5.2",
-            "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz",
-            "integrity": "sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ=="
+            "version": "2.6.4",
+            "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz",
+            "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ=="
         },
         "js-datepicker": {
             "version": "4.6.6",
@@ -9395,7 +8863,7 @@
         "js-stringify": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz",
-            "integrity": "sha1-Fzb939lyTyijaCrcYjCufk6Weds="
+            "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g=="
         },
         "js-tokens": {
             "version": "4.0.0",
@@ -9414,7 +8882,7 @@
         "jsbn": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
-            "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
+            "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg=="
         },
         "jsdom": {
             "version": "15.2.1",
@@ -9451,9 +8919,9 @@
             },
             "dependencies": {
                 "acorn": {
-                    "version": "7.1.1",
-                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
-                    "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==",
+                    "version": "7.4.1",
+                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+                    "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
                     "dev": true
                 },
                 "acorn-globals": {
@@ -9467,19 +8935,13 @@
                     },
                     "dependencies": {
                         "acorn": {
-                            "version": "6.4.1",
-                            "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
-                            "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
+                            "version": "6.4.2",
+                            "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
+                            "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
                             "dev": true
                         }
                     }
                 },
-                "parse5": {
-                    "version": "5.1.0",
-                    "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
-                    "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==",
-                    "dev": true
-                },
                 "tough-cookie": {
                     "version": "3.0.1",
                     "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
@@ -9490,6 +8952,32 @@
                         "psl": "^1.1.28",
                         "punycode": "^2.1.1"
                     }
+                },
+                "tr46": {
+                    "version": "1.0.1",
+                    "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
+                    "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+                    "dev": true,
+                    "requires": {
+                        "punycode": "^2.1.0"
+                    }
+                },
+                "webidl-conversions": {
+                    "version": "4.0.2",
+                    "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+                    "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
+                    "dev": true
+                },
+                "whatwg-url": {
+                    "version": "7.1.0",
+                    "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
+                    "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
+                    "dev": true,
+                    "requires": {
+                        "lodash.sortby": "^4.7.0",
+                        "tr46": "^1.0.1",
+                        "webidl-conversions": "^4.0.2"
+                    }
                 }
             }
         },
@@ -9499,11 +8987,11 @@
             "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="
         },
         "json-bigint": {
-            "version": "0.3.0",
-            "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz",
-            "integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=",
+            "version": "0.3.1",
+            "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.1.tgz",
+            "integrity": "sha512-DGWnSzmusIreWlEupsUelHrhwmPPE+FiQvg+drKfk2p+bdEYa5mp4PJ8JsCWqae0M2jQNb0HPvnwvf1qOTThzQ==",
             "requires": {
-                "bignumber.js": "^7.0.0"
+                "bignumber.js": "^9.0.0"
             }
         },
         "json-buffer": {
@@ -9514,7 +9002,7 @@
         "json-css": {
             "version": "1.5.6",
             "resolved": "https://registry.npmjs.org/json-css/-/json-css-1.5.6.tgz",
-            "integrity": "sha1-65ZPg0ouTqobwvaY/12wB6JsfAA="
+            "integrity": "sha512-B/0T0OxZH9tSb93tXV6VOYtXqrPz/Vgz2QrCT/4NXen8HGElYkYr9V+8IrSVTMj/ftxa8cG1kcu7f3iAMlaFlQ=="
         },
         "json-parse-better-errors": {
             "version": "1.0.2",
@@ -9527,9 +9015,9 @@
             "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
         },
         "json-schema": {
-            "version": "0.2.3",
-            "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
-            "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
+            "version": "0.4.0",
+            "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+            "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="
         },
         "json-schema-traverse": {
             "version": "0.4.1",
@@ -9539,7 +9027,15 @@
         "json-stringify-safe": {
             "version": "5.0.1",
             "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
-            "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
+            "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="
+        },
+        "json5": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+            "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+            "requires": {
+                "minimist": "^1.2.0"
+            }
         },
         "jsondiffpatch": {
             "version": "0.4.1",
@@ -9553,24 +9049,24 @@
         "jsonfile": {
             "version": "2.4.0",
             "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
-            "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
+            "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==",
             "requires": {
                 "graceful-fs": "^4.1.6"
             }
         },
         "jsonschema": {
-            "version": "1.4.0",
-            "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.0.tgz",
-            "integrity": "sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw=="
+            "version": "1.4.1",
+            "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz",
+            "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ=="
         },
         "jsprim": {
-            "version": "1.4.1",
-            "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
-            "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+            "version": "1.4.2",
+            "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
+            "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
             "requires": {
                 "assert-plus": "1.0.0",
                 "extsprintf": "1.3.0",
-                "json-schema": "0.2.3",
+                "json-schema": "0.4.0",
                 "verror": "1.10.0"
             }
         },
@@ -9586,9 +9082,9 @@
             },
             "dependencies": {
                 "csstype": {
-                    "version": "3.0.11",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
-                    "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
                 }
             }
         },
@@ -9662,21 +9158,21 @@
         "jstransformer": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz",
-            "integrity": "sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=",
+            "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==",
             "requires": {
                 "is-promise": "^2.0.0",
                 "promise": "^7.0.1"
             }
         },
         "jszip": {
-            "version": "3.7.1",
-            "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.7.1.tgz",
-            "integrity": "sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg==",
+            "version": "3.10.0",
+            "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.0.tgz",
+            "integrity": "sha512-LDfVtOLtOxb9RXkYOwPyNBTQDL4eUbqahtoY6x07GiDJHwSYvn8sHHIw8wINImV3MqbMNve2gSuM1DDqEKk09Q==",
             "requires": {
                 "lie": "~3.3.0",
                 "pako": "~1.0.2",
                 "readable-stream": "~2.3.6",
-                "set-immediate-shim": "~1.0.1"
+                "setimmediate": "^1.0.5"
             },
             "dependencies": {
                 "readable-stream": {
@@ -9725,9 +9221,9 @@
             "integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew=="
         },
         "keycode": {
-            "version": "2.2.0",
-            "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz",
-            "integrity": "sha1-PQr1bce4uOXLqNCpfxByBO7CKwQ="
+            "version": "2.2.1",
+            "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.1.tgz",
+            "integrity": "sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg=="
         },
         "keygrip": {
             "version": "1.1.0",
@@ -9738,10 +9234,11 @@
             }
         },
         "keyv": {
-            "version": "4.1.1",
-            "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz",
-            "integrity": "sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==",
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.3.0.tgz",
+            "integrity": "sha512-C30Un9+63J0CsR7Wka5quXKqYZsT6dcRQ2aOwGcSc3RiQ4HGWpTAHlCA+puNfw2jA/s11EsxA1nCXgZRuRKMQQ==",
             "requires": {
+                "compress-brotli": "^1.3.8",
                 "json-buffer": "3.0.1"
             }
         },
@@ -9759,7 +9256,7 @@
         "klaw": {
             "version": "1.3.1",
             "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
-            "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=",
+            "integrity": "sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==",
             "requires": {
                 "graceful-fs": "^4.1.9"
             }
@@ -9767,7 +9264,7 @@
         "latest-version": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz",
-            "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=",
+            "integrity": "sha512-Be1YRHWWlZaSsrz2U+VInk+tO0EwLIyV+23RhWLINJYwg/UIikxjlj3MhH37/6/EDCAusjajvMkMMUXRaMWl/w==",
             "requires": {
                 "package-json": "^4.0.0"
             }
@@ -9775,12 +9272,12 @@
         "lazy-cache": {
             "version": "1.0.4",
             "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
-            "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4="
+            "integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ=="
         },
         "lazystream": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz",
-            "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=",
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
+            "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
             "requires": {
                 "readable-stream": "^2.0.5"
             },
@@ -9804,7 +9301,7 @@
         "levn": {
             "version": "0.3.0",
             "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
-            "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+            "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==",
             "dev": true,
             "requires": {
                 "prelude-ls": "~1.1.2",
@@ -9827,7 +9324,7 @@
         "load-json-file": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
-            "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
+            "integrity": "sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==",
             "requires": {
                 "graceful-fs": "^4.1.2",
                 "parse-json": "^2.2.0",
@@ -9839,22 +9336,17 @@
                 "parse-json": {
                     "version": "2.2.0",
                     "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
-                    "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+                    "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==",
                     "requires": {
                         "error-ex": "^1.2.0"
                     }
-                },
-                "pify": {
-                    "version": "2.3.0",
-                    "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-                    "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
                 }
             }
         },
         "loader-runner": {
-            "version": "4.2.0",
-            "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz",
-            "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==",
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
+            "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
             "dev": true
         },
         "loader-utils": {
@@ -9865,16 +9357,6 @@
                 "big.js": "^5.2.2",
                 "emojis-list": "^3.0.0",
                 "json5": "^1.0.1"
-            },
-            "dependencies": {
-                "json5": {
-                    "version": "1.0.1",
-                    "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
-                    "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
-                    "requires": {
-                        "minimist": "^1.2.0"
-                    }
-                }
             }
         },
         "locate-path": {
@@ -9899,22 +9381,22 @@
         "lodash._getnative": {
             "version": "3.9.1",
             "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
-            "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U="
+            "integrity": "sha512-RrL9VxMEPyDMHOd9uFbvMe8X55X16/cGM5IgOKgRElQZutpX89iS6vwl64duTV1/16w5JY7tuFNXqoekmh1EmA=="
         },
         "lodash.chunk": {
             "version": "4.2.0",
             "resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz",
-            "integrity": "sha1-ZuXOH3btJ7QwPYxlEujRIW6BBrw="
+            "integrity": "sha512-ZzydJKfUHJwHa+hF5X66zLFCBrWn5GeF28OHEr4WVWtNDXlQ/IjWKPBiikqKo2ne0+v6JgCgJ0GzJp8k8bHC7w=="
         },
         "lodash.curry": {
             "version": "4.1.1",
             "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz",
-            "integrity": "sha1-JI42By7ekGUB11lmIAqG2riyMXA="
+            "integrity": "sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA=="
         },
         "lodash.debounce": {
             "version": "3.1.1",
             "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-3.1.1.tgz",
-            "integrity": "sha1-gSIRw3ipTMKdWqTjNGzwv846ffU=",
+            "integrity": "sha512-lcmJwMpdPAtChA4hfiwxTtgFeNAaow701wWUgVUqeD0XJF7vMXIN+bu/2FJSGxT0NUbZy9g9VFrlOFfPjl+0Ew==",
             "requires": {
                 "lodash._getnative": "^3.0.0"
             }
@@ -9922,42 +9404,42 @@
         "lodash.defaults": {
             "version": "4.2.0",
             "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
-            "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw="
+            "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="
         },
         "lodash.difference": {
             "version": "4.5.0",
             "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
-            "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw="
+            "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA=="
         },
         "lodash.flatten": {
             "version": "4.4.0",
             "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
-            "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
+            "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g=="
         },
         "lodash.flow": {
             "version": "3.5.0",
             "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz",
-            "integrity": "sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o="
+            "integrity": "sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw=="
         },
         "lodash.get": {
             "version": "4.4.2",
             "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
-            "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
+            "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="
         },
         "lodash.isequal": {
             "version": "4.5.0",
             "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
-            "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+            "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
         },
         "lodash.isplainobject": {
             "version": "4.0.6",
             "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
-            "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
+            "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
         },
         "lodash.memoize": {
             "version": "4.1.2",
             "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
-            "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4="
+            "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag=="
         },
         "lodash.merge": {
             "version": "4.6.2",
@@ -9967,23 +9449,23 @@
         "lodash.padend": {
             "version": "4.6.1",
             "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz",
-            "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4="
+            "integrity": "sha512-sOQs2aqGpbl27tmCS1QNZA09Uqp01ZzWfDUoD+xzTii0E7dSQfRKcRetFwa+uXaxaqL+TKm7CgD2JdKP7aZBSw=="
         },
         "lodash.sortby": {
             "version": "4.7.0",
             "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
-            "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
+            "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==",
             "dev": true
         },
         "lodash.throttle": {
             "version": "4.1.1",
             "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
-            "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
+            "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="
         },
         "lodash.union": {
             "version": "4.6.0",
             "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
-            "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg="
+            "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw=="
         },
         "log-symbols": {
             "version": "2.2.0",
@@ -10011,7 +9493,7 @@
         "longest": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
-            "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc="
+            "integrity": "sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg=="
         },
         "loose-envify": {
             "version": "1.4.0",
@@ -10024,7 +9506,7 @@
         "loud-rejection": {
             "version": "1.6.0",
             "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
-            "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
+            "integrity": "sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==",
             "requires": {
                 "currently-unhandled": "^0.4.1",
                 "signal-exit": "^3.0.0"
@@ -10044,13 +9526,6 @@
             "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
             "requires": {
                 "tslib": "^2.0.3"
-            },
-            "dependencies": {
-                "tslib": {
-                    "version": "2.3.1",
-                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
-                    "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
-                }
             }
         },
         "lowercase-keys": {
@@ -10059,11 +9534,11 @@
             "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ=="
         },
         "lru-cache": {
-            "version": "5.1.1",
-            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
-            "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+            "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
             "requires": {
-                "yallist": "^3.0.2"
+                "yallist": "^4.0.0"
             }
         },
         "magicli": {
@@ -10080,7 +9555,7 @@
                 "find-up": {
                     "version": "2.1.0",
                     "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
-                    "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+                    "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==",
                     "requires": {
                         "locate-path": "^2.0.0"
                     }
@@ -10088,7 +9563,7 @@
                 "locate-path": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
-                    "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+                    "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==",
                     "requires": {
                         "p-locate": "^2.0.0",
                         "path-exists": "^3.0.0"
@@ -10105,7 +9580,7 @@
                 "p-locate": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
-                    "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+                    "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==",
                     "requires": {
                         "p-limit": "^1.1.0"
                     }
@@ -10113,22 +9588,22 @@
                 "p-try": {
                     "version": "1.0.0",
                     "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
-                    "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M="
+                    "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww=="
                 }
             }
         },
         "make-dir": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
-            "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+            "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
             "requires": {
-                "pify": "^3.0.0"
+                "semver": "^6.0.0"
             },
             "dependencies": {
-                "pify": {
-                    "version": "3.0.0",
-                    "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-                    "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
+                "semver": {
+                    "version": "6.3.0",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+                    "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
                 }
             }
         },
@@ -10141,17 +9616,17 @@
         "map-cache": {
             "version": "0.2.2",
             "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
-            "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8="
+            "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg=="
         },
         "map-obj": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
-            "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0="
+            "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg=="
         },
         "map-visit": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
-            "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+            "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==",
             "requires": {
                 "object-visit": "^1.0.0"
             }
@@ -10196,7 +9671,7 @@
         "math-codegen": {
             "version": "0.3.5",
             "resolved": "https://registry.npmjs.org/math-codegen/-/math-codegen-0.3.5.tgz",
-            "integrity": "sha1-R5nuRnfe0Ud2bQA8ykt4ee3UDMo=",
+            "integrity": "sha512-SsFYMv33FxMKYxI1PBiaZT+8AeDITK+k/PKhbHNlOPHIz5FIPF4wy78yWqanN6luXdsXENUZgCIC6xH6bfUq1g==",
             "requires": {
                 "extend": "^3.0.0",
                 "mr-parser": "^0.2.1"
@@ -10205,7 +9680,7 @@
         "mathquill": {
             "version": "0.10.1-a",
             "resolved": "https://registry.npmjs.org/mathquill/-/mathquill-0.10.1-a.tgz",
-            "integrity": "sha1-vyylaQEAY6w0vNXVKa3Ag3zVPD8=",
+            "integrity": "sha512-snSAEwAtwdwBFSor+nVBnWWQtTw67kgAgKMyAIxuz4ZPboy0qkWZmd7BL3lfOXp/INihhRlU1PcfaAtDaRhmzA==",
             "requires": {
                 "jquery": "^1.12.3"
             },
@@ -10213,32 +9688,32 @@
                 "jquery": {
                     "version": "1.12.4",
                     "resolved": "https://registry.npmjs.org/jquery/-/jquery-1.12.4.tgz",
-                    "integrity": "sha1-AeHfuikP5z3rp3zurLD5ui/sngw="
+                    "integrity": "sha512-UEVp7PPK9xXYSk8xqXCJrkXnKZtlgWkd2GsAQbMRFK6S/ePU2JN5G2Zum8hIVjzR3CpdfSqdqAzId/xd4TJHeg=="
                 }
             }
         },
         "max-safe-integer": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/max-safe-integer/-/max-safe-integer-1.0.1.tgz",
-            "integrity": "sha1-84BgvixWPYwC5tSK85Ei/YO29BA="
+            "integrity": "sha512-CHZ/Nopqh46UtA0YvLclxj9F95qExLmTnMS5fnYlXvX4zj1RUOC3cPaGEOMhdPE8SSudSQ6wkQUJLA5E/WeL4A=="
         },
         "media-typer": {
             "version": "0.3.0",
             "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
-            "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
+            "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="
         },
         "memfs": {
-            "version": "3.4.1",
-            "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz",
-            "integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==",
+            "version": "3.4.4",
+            "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.4.tgz",
+            "integrity": "sha512-W4gHNUE++1oSJVn8Y68jPXi+mkx3fXR5ITE/Ubz6EQ3xRpCN5k2CQ4AUR8094Z7211F876TyoBACGsIveqgiGA==",
             "requires": {
                 "fs-monkey": "1.0.3"
             }
         },
         "memoize-one": {
-            "version": "5.1.1",
-            "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz",
-            "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA=="
+            "version": "5.2.1",
+            "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
+            "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
         },
         "memory-fs": {
             "version": "0.5.0",
@@ -10276,12 +9751,12 @@
         "memorystream": {
             "version": "0.3.1",
             "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
-            "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI="
+            "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw=="
         },
         "meow": {
             "version": "3.7.0",
             "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
-            "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
+            "integrity": "sha512-TNdwZs0skRlpPpCUK25StC4VH+tP5GgeY1HQOOGP+lQ2xtdkN2VtT/5tiX9k3IWpkBPV9b3LsAWXn4GGi/PrSA==",
             "requires": {
                 "camelcase-keys": "^2.0.0",
                 "decamelize": "^1.1.2",
@@ -10306,7 +9781,7 @@
         "merge-descriptors": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
-            "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
+            "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
         },
         "merge-stream": {
             "version": "2.0.0",
@@ -10316,7 +9791,7 @@
         "methods": {
             "version": "1.1.2",
             "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
-            "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
+            "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="
         },
         "microevent.ts": {
             "version": "0.1.1",
@@ -10350,16 +9825,16 @@
             "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
         },
         "mime-db": {
-            "version": "1.44.0",
-            "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
-            "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
+            "version": "1.52.0",
+            "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+            "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
         },
         "mime-types": {
-            "version": "2.1.27",
-            "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
-            "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
+            "version": "2.1.35",
+            "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+            "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
             "requires": {
-                "mime-db": "1.44.0"
+                "mime-db": "1.52.0"
             }
         },
         "mimic-fn": {
@@ -10379,17 +9854,17 @@
             "dev": true
         },
         "minimatch": {
-            "version": "3.0.4",
-            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
-            "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+            "version": "3.1.2",
+            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+            "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
             "requires": {
                 "brace-expansion": "^1.1.7"
             }
         },
         "minimist": {
-            "version": "1.2.5",
-            "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
-            "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+            "version": "1.2.6",
+            "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
+            "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
         },
         "minipass": {
             "version": "3.1.6",
@@ -10397,13 +9872,6 @@
             "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==",
             "requires": {
                 "yallist": "^4.0.0"
-            },
-            "dependencies": {
-                "yallist": {
-                    "version": "4.0.0",
-                    "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-                    "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
-                }
             }
         },
         "minizlib": {
@@ -10412,14 +9880,7 @@
             "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
             "requires": {
                 "minipass": "^3.0.0",
-                "yallist": "^4.0.0"
-            },
-            "dependencies": {
-                "yallist": {
-                    "version": "4.0.0",
-                    "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-                    "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
-                }
+                "yallist": "^4.0.0"
             }
         },
         "mississippi": {
@@ -10459,6 +9920,16 @@
             "requires": {
                 "for-in": "^1.0.2",
                 "is-extendable": "^1.0.1"
+            },
+            "dependencies": {
+                "is-extendable": {
+                    "version": "1.0.1",
+                    "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"
+                    }
+                }
             }
         },
         "mkdirp": {
@@ -10470,9 +9941,9 @@
             }
         },
         "mkdirp-classic": {
-            "version": "0.5.2",
-            "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz",
-            "integrity": "sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g=="
+            "version": "0.5.3",
+            "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
+            "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
         },
         "mobile-detect": {
             "version": "1.4.5",
@@ -10554,19 +10025,28 @@
                 "he": {
                     "version": "1.1.1",
                     "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
-                    "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
+                    "integrity": "sha512-z/GDPjlRMNOa2XJiB4em8wJpuuBfrFOlYKTZxtpkdr1uPdibHI8rYA3MY0KDObpVyaes0e/aunid/t88ZI2EKA==",
                     "dev": true
                 },
+                "minimatch": {
+                    "version": "3.0.4",
+                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+                    "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+                    "dev": true,
+                    "requires": {
+                        "brace-expansion": "^1.1.7"
+                    }
+                },
                 "minimist": {
                     "version": "0.0.8",
                     "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
-                    "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+                    "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==",
                     "dev": true
                 },
                 "mkdirp": {
                     "version": "0.5.1",
                     "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
-                    "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+                    "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==",
                     "dev": true,
                     "requires": {
                         "minimist": "0.0.8"
@@ -10575,7 +10055,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
                     "dev": true
                 },
                 "supports-color": {
@@ -10669,15 +10149,6 @@
                 "sliced": "1.0.1"
             },
             "dependencies": {
-                "@types/mongodb": {
-                    "version": "3.6.20",
-                    "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz",
-                    "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==",
-                    "requires": {
-                        "@types/bson": "*",
-                        "@types/node": "*"
-                    }
-                },
                 "bson": {
                     "version": "1.1.6",
                     "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz",
@@ -10708,7 +10179,7 @@
         "move-concurrently": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
-            "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
+            "integrity": "sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==",
             "dev": true,
             "requires": {
                 "aproba": "^1.1.1",
@@ -10719,6 +10190,12 @@
                 "run-queue": "^1.0.3"
             },
             "dependencies": {
+                "aproba": {
+                    "version": "1.2.0",
+                    "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+                    "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+                    "dev": true
+                },
                 "rimraf": {
                     "version": "2.7.1",
                     "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@@ -10763,14 +10240,14 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
                 }
             }
         },
         "mr-parser": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/mr-parser/-/mr-parser-0.2.1.tgz",
-            "integrity": "sha1-hhi5ukF+KOn0OaQcaVtVTq/u2Sc="
+            "integrity": "sha512-hug+mpbSSKnH13rFqy3zm+XiG+QTStiDAgMTHK355TIstQE0qBkBtSJsa5YHP94AuarVX9b/4dcebdTRZ9YiEw=="
         },
         "ms": {
             "version": "2.1.1",
@@ -10790,13 +10267,13 @@
         "multicast-dns-service-types": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
-            "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
+            "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==",
             "dev": true
         },
         "nan": {
-            "version": "2.14.1",
-            "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz",
-            "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw=="
+            "version": "2.16.0",
+            "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz",
+            "integrity": "sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA=="
         },
         "nanoid": {
             "version": "2.1.11",
@@ -10829,7 +10306,7 @@
         "native-or-bluebird": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/native-or-bluebird/-/native-or-bluebird-1.2.0.tgz",
-            "integrity": "sha1-OcR7/Xgl0fuf+tMiEK4l2q3xAck="
+            "integrity": "sha512-0SH8UubxDfe382eYiwmd12qxAbiWGzlGZv6CkMA+DPojWa/Y0oH4hE0lRtFfFgJmPQFyKXeB8XxPbZz6TvvKaQ=="
         },
         "negotiator": {
             "version": "0.6.3",
@@ -10837,20 +10314,20 @@
             "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
         },
         "neo-async": {
-            "version": "2.6.1",
-            "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
-            "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==",
+            "version": "2.6.2",
+            "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+            "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
             "dev": true
         },
         "next-tick": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
-            "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
+            "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
         },
         "nextafter": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/nextafter/-/nextafter-1.0.0.tgz",
-            "integrity": "sha1-t9d7U1MQ4+CX5gJauwqQNHfsGjo=",
+            "integrity": "sha512-7PO+A89Tll2rSEfyrjtqO0MaI37+nnxBdnQcPypfbEYYuGaJxWGCqaOwQX4a3GHNTS08l1kazuiLEWZniZjMUQ==",
             "requires": {
                 "double-bits": "^1.1.0"
             }
@@ -10868,19 +10345,12 @@
             "requires": {
                 "lower-case": "^2.0.2",
                 "tslib": "^2.0.3"
-            },
-            "dependencies": {
-                "tslib": {
-                    "version": "2.3.1",
-                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
-                    "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
-                }
             }
         },
         "node-abi": {
-            "version": "2.16.0",
-            "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.16.0.tgz",
-            "integrity": "sha512-+sa0XNlWDA6T+bDLmkCUYn6W5k5W6BPRL6mqzSCs6H/xUgtl4D5x2fORKDzopKiU6wsyn/+wXlRXwXeSp+mtoA==",
+            "version": "2.30.1",
+            "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz",
+            "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==",
             "requires": {
                 "semver": "^5.4.1"
             }
@@ -10888,7 +10358,7 @@
         "node-ensure": {
             "version": "0.0.0",
             "resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz",
-            "integrity": "sha1-7K52QVDemYYexcgQ/V0Jaxg5Mqc="
+            "integrity": "sha512-DRI60hzo2oKN1ma0ckc6nQWlHU69RH6xN0sjQTjMpChPfTYvKZdcQFfdYK2RWbJcKyUizSIy/l8OTGxMAM1QDw=="
         },
         "node-environment-flags": {
             "version": "1.0.5",
@@ -10909,9 +10379,9 @@
             }
         },
         "node-forge": {
-            "version": "0.9.1",
-            "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.1.tgz",
-            "integrity": "sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ=="
+            "version": "0.10.0",
+            "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
+            "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA=="
         },
         "node-gyp": {
             "version": "3.8.0",
@@ -10932,14 +10402,81 @@
                 "which": "1"
             },
             "dependencies": {
+                "ansi-regex": {
+                    "version": "2.1.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+                    "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="
+                },
+                "aproba": {
+                    "version": "1.2.0",
+                    "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+                    "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+                },
+                "are-we-there-yet": {
+                    "version": "1.1.7",
+                    "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
+                    "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
+                    "requires": {
+                        "delegates": "^1.0.0",
+                        "readable-stream": "^2.0.6"
+                    }
+                },
+                "gauge": {
+                    "version": "2.7.4",
+                    "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+                    "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==",
+                    "requires": {
+                        "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"
+                    }
+                },
+                "is-fullwidth-code-point": {
+                    "version": "1.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+                    "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
+                    "requires": {
+                        "number-is-nan": "^1.0.0"
+                    }
+                },
                 "nopt": {
                     "version": "3.0.6",
                     "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
-                    "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
+                    "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==",
                     "requires": {
                         "abbrev": "1"
                     }
                 },
+                "npmlog": {
+                    "version": "4.1.2",
+                    "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.2",
+                        "console-control-strings": "~1.1.0",
+                        "gauge": "~2.7.3",
+                        "set-blocking": "~2.0.0"
+                    }
+                },
+                "readable-stream": {
+                    "version": "2.3.7",
+                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+                    "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+                    "requires": {
+                        "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": {
                     "version": "2.7.1",
                     "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
@@ -10951,7 +10488,25 @@
                 "semver": {
                     "version": "5.3.0",
                     "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
-                    "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8="
+                    "integrity": "sha512-mfmm3/H9+67MCVix1h+IXTpDwL6710LyHuk7+cWC9T1mE0qz4iHhh6r4hU2wrIT9iTsAAC2XQRvfblL028cpLw=="
+                },
+                "string-width": {
+                    "version": "1.0.2",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+                    "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+                    "requires": {
+                        "code-point-at": "^1.0.0",
+                        "is-fullwidth-code-point": "^1.0.0",
+                        "strip-ansi": "^3.0.0"
+                    }
+                },
+                "strip-ansi": {
+                    "version": "3.0.1",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+                    "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+                    "requires": {
+                        "ansi-regex": "^2.0.0"
+                    }
                 },
                 "tar": {
                     "version": "2.2.2",
@@ -10966,9 +10521,9 @@
             }
         },
         "node-releases": {
-            "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz",
-            "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg=="
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz",
+            "integrity": "sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q=="
         },
         "node-sass": {
             "version": "4.14.1",
@@ -10997,17 +10552,31 @@
                 "ansi-regex": {
                     "version": "2.1.1",
                     "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-                    "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+                    "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="
                 },
                 "ansi-styles": {
                     "version": "2.2.1",
                     "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-                    "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
+                    "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA=="
+                },
+                "aproba": {
+                    "version": "1.2.0",
+                    "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+                    "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+                },
+                "are-we-there-yet": {
+                    "version": "1.1.7",
+                    "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
+                    "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
+                    "requires": {
+                        "delegates": "^1.0.0",
+                        "readable-stream": "^2.0.6"
+                    }
                 },
                 "chalk": {
                     "version": "1.1.3",
                     "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-                    "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+                    "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
                     "requires": {
                         "ansi-styles": "^2.2.1",
                         "escape-string-regexp": "^1.0.2",
@@ -11016,10 +10585,68 @@
                         "supports-color": "^2.0.0"
                     }
                 },
+                "gauge": {
+                    "version": "2.7.4",
+                    "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+                    "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==",
+                    "requires": {
+                        "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"
+                    }
+                },
                 "get-stdin": {
                     "version": "4.0.1",
                     "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
-                    "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4="
+                    "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw=="
+                },
+                "is-fullwidth-code-point": {
+                    "version": "1.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+                    "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
+                    "requires": {
+                        "number-is-nan": "^1.0.0"
+                    }
+                },
+                "npmlog": {
+                    "version": "4.1.2",
+                    "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.2",
+                        "console-control-strings": "~1.1.0",
+                        "gauge": "~2.7.3",
+                        "set-blocking": "~2.0.0"
+                    }
+                },
+                "readable-stream": {
+                    "version": "2.3.7",
+                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+                    "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+                    "requires": {
+                        "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-width": {
+                    "version": "1.0.2",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+                    "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+                    "requires": {
+                        "code-point-at": "^1.0.0",
+                        "is-fullwidth-code-point": "^1.0.0",
+                        "strip-ansi": "^3.0.0"
+                    }
                 },
                 "strip-ansi": {
                     "version": "3.0.1",
@@ -11074,7 +10701,7 @@
         "noop-logger": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
-            "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI="
+            "integrity": "sha512-6kM8CLXvuW5crTxsAtva2YLrRrDaiTIkIePWs9moLHqbFWT94WpNFjwS/5dfLfECg5i/lkmw3aoqVidxt23TEQ=="
         },
         "nopt": {
             "version": "5.0.0",
@@ -11111,9 +10738,9 @@
             "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg=="
         },
         "npm": {
-            "version": "6.14.16",
-            "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.16.tgz",
-            "integrity": "sha512-LMiLGYsVNJfVPlQg7v2NYjG7iRIapcLv+oMunlq7fkXVx0BATCjRu7XyWl0G+iuZzHy4CjtM32QB8ox8juTgaw==",
+            "version": "6.14.17",
+            "resolved": "https://registry.npmjs.org/npm/-/npm-6.14.17.tgz",
+            "integrity": "sha512-CxEDn1ydVRPDl4tHrlnq+WevYAhv4GF2AEHzJKQ4prZDZ96IS3Uo6t0Sy6O9kB6XzqkI+J00WfYCqqk0p6IJ1Q==",
             "requires": {
                 "JSONStream": "^1.3.5",
                 "abbrev": "~1.1.1",
@@ -11242,7 +10869,8 @@
             "dependencies": {
                 "JSONStream": {
                     "version": "1.3.5",
-                    "bundled": true,
+                    "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
+                    "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
                     "requires": {
                         "jsonparse": "^1.2.0",
                         "through": ">=2.2.7 <3"
@@ -11250,12 +10878,12 @@
                 },
                 "abbrev": {
                     "version": "1.1.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
                     "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
                 },
                 "agent-base": {
                     "version": "4.3.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
                     "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
                     "requires": {
                         "es6-promisify": "^5.0.0"
@@ -11263,7 +10891,7 @@
                 },
                 "agentkeepalive": {
                     "version": "3.5.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz",
                     "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==",
                     "requires": {
                         "humanize-ms": "^1.2.1"
@@ -11271,7 +10899,7 @@
                 },
                 "ansi-align": {
                     "version": "2.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz",
                     "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=",
                     "requires": {
                         "string-width": "^2.0.0"
@@ -11279,12 +10907,12 @@
                 },
                 "ansi-regex": {
                     "version": "2.1.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
                     "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
                 },
                 "ansi-styles": {
                     "version": "3.2.1",
-                    "resolved": false,
+                    "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.0"
@@ -11292,27 +10920,27 @@
                 },
                 "ansicolors": {
                     "version": "0.3.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz",
                     "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk="
                 },
                 "ansistyles": {
                     "version": "0.1.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/ansistyles/-/ansistyles-0.1.3.tgz",
                     "integrity": "sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk="
                 },
                 "aproba": {
                     "version": "2.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
                     "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
                 },
                 "archy": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
                     "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA="
                 },
                 "are-we-there-yet": {
                     "version": "1.1.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
                     "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
                     "requires": {
                         "delegates": "^1.0.0",
@@ -11321,7 +10949,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": false,
+                            "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.0",
@@ -11335,7 +10963,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": false,
+                            "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.0"
@@ -11345,12 +10973,12 @@
                 },
                 "asap": {
                     "version": "2.0.6",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
                     "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
                 },
                 "asn1": {
                     "version": "0.2.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
                     "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
                     "requires": {
                         "safer-buffer": "~2.1.0"
@@ -11358,32 +10986,32 @@
                 },
                 "assert-plus": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
                     "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
                 },
                 "asynckit": {
                     "version": "0.4.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
                     "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
                 },
                 "aws-sign2": {
                     "version": "0.7.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
                     "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
                 },
                 "aws4": {
                     "version": "1.8.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
                     "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
                 },
                 "balanced-match": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
                     "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
                 },
                 "bcrypt-pbkdf": {
                     "version": "1.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
                     "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
                     "optional": true,
                     "requires": {
@@ -11405,12 +11033,12 @@
                 },
                 "bluebird": {
                     "version": "3.5.5",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz",
                     "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w=="
                 },
                 "boxen": {
                     "version": "1.3.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz",
                     "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==",
                     "requires": {
                         "ansi-align": "^2.0.0",
@@ -11424,7 +11052,7 @@
                 },
                 "brace-expansion": {
                     "version": "1.1.11",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
                     "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
                     "requires": {
                         "balanced-match": "^1.0.0",
@@ -11433,27 +11061,27 @@
                 },
                 "buffer-from": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.0.0.tgz",
                     "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA=="
                 },
                 "builtins": {
                     "version": "1.0.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz",
                     "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og="
                 },
                 "byline": {
                     "version": "5.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz",
                     "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE="
                 },
                 "byte-size": {
                     "version": "5.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/byte-size/-/byte-size-5.0.1.tgz",
                     "integrity": "sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw=="
                 },
                 "cacache": {
                     "version": "12.0.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz",
                     "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==",
                     "requires": {
                         "bluebird": "^3.5.5",
@@ -11475,27 +11103,27 @@
                 },
                 "call-limit": {
                     "version": "1.1.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/call-limit/-/call-limit-1.1.1.tgz",
                     "integrity": "sha512-5twvci5b9eRBw2wCfPtN0GmlR2/gadZqyFpPhOK6CvMFoFgA+USnZ6Jpu1lhG9h85pQ3Ouil3PfXWRD4EUaRiQ=="
                 },
                 "camelcase": {
                     "version": "4.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
                     "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
                 },
                 "capture-stack-trace": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz",
                     "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0="
                 },
                 "caseless": {
                     "version": "0.12.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
                     "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
                 },
                 "chalk": {
                     "version": "2.4.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
                     "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
                     "requires": {
                         "ansi-styles": "^3.2.1",
@@ -11505,17 +11133,17 @@
                 },
                 "chownr": {
                     "version": "1.1.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
                     "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
                 },
                 "ci-info": {
                     "version": "2.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
                     "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="
                 },
                 "cidr-regex": {
                     "version": "2.0.10",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/cidr-regex/-/cidr-regex-2.0.10.tgz",
                     "integrity": "sha512-sB3ogMQXWvreNPbJUZMRApxuRYd+KoIo4RGQ81VatjmMW6WJPo+IJZ2846FGItr9VzKo5w7DXzijPLGtSd0N3Q==",
                     "requires": {
                         "ip-regex": "^2.1.0"
@@ -11523,12 +11151,12 @@
                 },
                 "cli-boxes": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
                     "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM="
                 },
                 "cli-columns": {
                     "version": "3.1.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/cli-columns/-/cli-columns-3.1.2.tgz",
                     "integrity": "sha1-ZzLZcpee/CrkRKHwjgj6E5yWoY4=",
                     "requires": {
                         "string-width": "^2.0.0",
@@ -11537,7 +11165,7 @@
                 },
                 "cli-table3": {
                     "version": "0.5.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz",
                     "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==",
                     "requires": {
                         "colors": "^1.1.2",
@@ -11556,9 +11184,9 @@
                     },
                     "dependencies": {
                         "ansi-regex": {
-                            "version": "4.1.0",
-                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
-                            "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
+                            "version": "4.1.1",
+                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+                            "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
                         },
                         "is-fullwidth-code-point": {
                             "version": "2.0.0",
@@ -11587,12 +11215,12 @@
                 },
                 "clone": {
                     "version": "1.0.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
                     "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4="
                 },
                 "cmd-shim": {
                     "version": "3.0.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-3.0.3.tgz",
                     "integrity": "sha512-DtGg+0xiFhQIntSBRzL2fRQBnmtAVwXIDo4Qq46HPpObYquxMaZS4sb82U9nH91qJrlosC1wa9gwr0QyL/HypA==",
                     "requires": {
                         "graceful-fs": "^4.1.2",
@@ -11601,12 +11229,12 @@
                 },
                 "code-point-at": {
                     "version": "1.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
                     "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
                 },
                 "color-convert": {
                     "version": "1.9.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
                     "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
                     "requires": {
                         "color-name": "^1.1.1"
@@ -11614,18 +11242,18 @@
                 },
                 "color-name": {
                     "version": "1.1.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
                     "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
                 },
                 "colors": {
                     "version": "1.3.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz",
                     "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==",
                     "optional": true
                 },
                 "columnify": {
                     "version": "1.5.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz",
                     "integrity": "sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs=",
                     "requires": {
                         "strip-ansi": "^3.0.0",
@@ -11634,7 +11262,7 @@
                 },
                 "combined-stream": {
                     "version": "1.0.6",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
                     "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
                     "requires": {
                         "delayed-stream": "~1.0.0"
@@ -11642,12 +11270,12 @@
                 },
                 "concat-map": {
                     "version": "0.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
                     "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
                 },
                 "concat-stream": {
                     "version": "1.6.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
                     "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
                     "requires": {
                         "buffer-from": "^1.0.0",
@@ -11658,7 +11286,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": false,
+                            "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.0",
@@ -11672,7 +11300,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": false,
+                            "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.0"
@@ -11682,7 +11310,7 @@
                 },
                 "config-chain": {
                     "version": "1.1.12",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz",
                     "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==",
                     "requires": {
                         "ini": "^1.3.4",
@@ -11704,12 +11332,12 @@
                 },
                 "console-control-strings": {
                     "version": "1.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
                     "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
                 },
                 "copy-concurrently": {
                     "version": "1.0.5",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
                     "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
                     "requires": {
                         "aproba": "^1.1.1",
@@ -11722,24 +11350,24 @@
                     "dependencies": {
                         "aproba": {
                             "version": "1.2.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
                             "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
                         },
                         "iferr": {
                             "version": "0.1.5",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
                             "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE="
                         }
                     }
                 },
                 "core-util-is": {
                     "version": "1.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
                     "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
                 },
                 "create-error-class": {
                     "version": "3.0.2",
-                    "resolved": false,
+                    "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.0"
@@ -11747,7 +11375,7 @@
                 },
                 "cross-spawn": {
                     "version": "5.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
                     "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
                     "requires": {
                         "lru-cache": "^4.0.1",
@@ -11757,7 +11385,7 @@
                     "dependencies": {
                         "lru-cache": {
                             "version": "4.1.5",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
                             "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
                             "requires": {
                                 "pseudomap": "^1.0.2",
@@ -11766,24 +11394,24 @@
                         },
                         "yallist": {
                             "version": "2.1.2",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
                             "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
                         }
                     }
                 },
                 "crypto-random-string": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
                     "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4="
                 },
                 "cyclist": {
                     "version": "0.2.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
                     "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA="
                 },
                 "dashdash": {
                     "version": "1.14.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
                     "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
                     "requires": {
                         "assert-plus": "^1.0.0"
@@ -11791,7 +11419,7 @@
                 },
                 "debug": {
                     "version": "3.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
                     "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
                     "requires": {
                         "ms": "2.0.0"
@@ -11799,34 +11427,34 @@
                     "dependencies": {
                         "ms": {
                             "version": "2.0.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
                             "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
                         }
                     }
                 },
                 "debuglog": {
                     "version": "1.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz",
                     "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI="
                 },
                 "decamelize": {
                     "version": "1.2.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
                     "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
                 },
                 "decode-uri-component": {
                     "version": "0.2.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
                     "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
                 },
                 "deep-extend": {
                     "version": "0.6.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
                     "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
                 },
                 "defaults": {
                     "version": "1.0.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz",
                     "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=",
                     "requires": {
                         "clone": "^1.0.2"
@@ -11834,7 +11462,7 @@
                 },
                 "define-properties": {
                     "version": "1.1.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
                     "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
                     "requires": {
                         "object-keys": "^1.0.12"
@@ -11842,27 +11470,27 @@
                 },
                 "delayed-stream": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
                     "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
                 },
                 "delegates": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
                     "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
                 },
                 "detect-indent": {
                     "version": "5.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz",
                     "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50="
                 },
                 "detect-newline": {
                     "version": "2.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz",
                     "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I="
                 },
                 "dezalgo": {
                     "version": "1.0.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz",
                     "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=",
                     "requires": {
                         "asap": "^2.0.0",
@@ -11879,17 +11507,17 @@
                 },
                 "dotenv": {
                     "version": "5.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-5.0.1.tgz",
                     "integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow=="
                 },
                 "duplexer3": {
                     "version": "0.1.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
                     "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
                 },
                 "duplexify": {
                     "version": "3.6.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz",
                     "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==",
                     "requires": {
                         "end-of-stream": "^1.0.0",
@@ -11900,7 +11528,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": false,
+                            "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.0",
@@ -11914,7 +11542,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": false,
+                            "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.0"
@@ -11924,7 +11552,7 @@
                 },
                 "ecc-jsbn": {
                     "version": "0.1.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
                     "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
                     "optional": true,
                     "requires": {
@@ -11934,7 +11562,7 @@
                 },
                 "editor": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/editor/-/editor-1.0.0.tgz",
                     "integrity": "sha1-YMf4e9YrzGqJT6jM1q+3gjok90I="
                 },
                 "emoji-regex": {
@@ -11944,7 +11572,7 @@
                 },
                 "encoding": {
                     "version": "0.1.12",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
                     "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
                     "requires": {
                         "iconv-lite": "~0.4.13"
@@ -11952,7 +11580,7 @@
                 },
                 "end-of-stream": {
                     "version": "1.4.1",
-                    "resolved": false,
+                    "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"
@@ -11960,17 +11588,17 @@
                 },
                 "env-paths": {
                     "version": "2.2.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz",
                     "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA=="
                 },
                 "err-code": {
                     "version": "1.1.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz",
                     "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA="
                 },
                 "errno": {
                     "version": "0.1.7",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
                     "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
                     "requires": {
                         "prr": "~1.0.1"
@@ -11978,7 +11606,7 @@
                 },
                 "es-abstract": {
                     "version": "1.12.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz",
                     "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==",
                     "requires": {
                         "es-to-primitive": "^1.1.1",
@@ -11990,7 +11618,7 @@
                 },
                 "es-to-primitive": {
                     "version": "1.2.0",
-                    "resolved": false,
+                    "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",
@@ -12000,12 +11628,12 @@
                 },
                 "es6-promise": {
                     "version": "4.2.8",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
                     "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w=="
                 },
                 "es6-promisify": {
                     "version": "5.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
                     "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
                     "requires": {
                         "es6-promise": "^4.0.3"
@@ -12013,12 +11641,12 @@
                 },
                 "escape-string-regexp": {
                     "version": "1.0.5",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
                     "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
                 },
                 "execa": {
                     "version": "0.7.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
                     "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
                     "requires": {
                         "cross-spawn": "^5.0.1",
@@ -12032,39 +11660,39 @@
                     "dependencies": {
                         "get-stream": {
                             "version": "3.0.0",
-                            "resolved": false,
+                            "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
                             "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
                         }
                     }
                 },
                 "extend": {
                     "version": "3.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
                     "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
                 },
                 "extsprintf": {
                     "version": "1.3.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
                     "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
                 },
                 "fast-json-stable-stringify": {
                     "version": "2.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
                     "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
                 },
                 "figgy-pudding": {
                     "version": "3.5.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz",
                     "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w=="
                 },
                 "find-npm-prefix": {
                     "version": "1.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/find-npm-prefix/-/find-npm-prefix-1.0.2.tgz",
                     "integrity": "sha512-KEftzJ+H90x6pcKtdXZEPsQse8/y/UnvzRKrOSQFprnrGaFuJ62fVkP34Iu2IYuMvyauCyoLTNkJZgrrGA2wkA=="
                 },
                 "flush-write-stream": {
                     "version": "1.0.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz",
                     "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==",
                     "requires": {
                         "inherits": "^2.0.1",
@@ -12073,7 +11701,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": false,
+                            "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.0",
@@ -12087,7 +11715,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": false,
+                            "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.0"
@@ -12097,12 +11725,12 @@
                 },
                 "forever-agent": {
                     "version": "0.6.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
                     "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
                 },
                 "form-data": {
                     "version": "2.3.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
                     "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
                     "requires": {
                         "asynckit": "^0.4.0",
@@ -12112,7 +11740,7 @@
                 },
                 "from2": {
                     "version": "2.3.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
                     "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
                     "requires": {
                         "inherits": "^2.0.1",
@@ -12121,7 +11749,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": false,
+                            "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.0",
@@ -12135,7 +11763,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": false,
+                            "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.0"
@@ -12145,7 +11773,7 @@
                 },
                 "fs-minipass": {
                     "version": "1.2.7",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
                     "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
                     "requires": {
                         "minipass": "^2.6.0"
@@ -12153,7 +11781,7 @@
                     "dependencies": {
                         "minipass": {
                             "version": "2.9.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
                             "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
                             "requires": {
                                 "safe-buffer": "^5.1.2",
@@ -12164,7 +11792,7 @@
                 },
                 "fs-vacuum": {
                     "version": "1.2.10",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/fs-vacuum/-/fs-vacuum-1.2.10.tgz",
                     "integrity": "sha1-t2Kb7AekAxolSP35n17PHMizHjY=",
                     "requires": {
                         "graceful-fs": "^4.1.2",
@@ -12174,7 +11802,7 @@
                 },
                 "fs-write-stream-atomic": {
                     "version": "1.0.10",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
                     "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
                     "requires": {
                         "graceful-fs": "^4.1.2",
@@ -12185,12 +11813,12 @@
                     "dependencies": {
                         "iferr": {
                             "version": "0.1.5",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
                             "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE="
                         },
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": false,
+                            "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.0",
@@ -12204,7 +11832,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": false,
+                            "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.0"
@@ -12214,17 +11842,17 @@
                 },
                 "fs.realpath": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
                     "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
                 },
                 "function-bind": {
                     "version": "1.1.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
                     "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
                 },
                 "gauge": {
                     "version": "2.7.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
                     "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
                     "requires": {
                         "aproba": "^1.0.3",
@@ -12239,12 +11867,12 @@
                     "dependencies": {
                         "aproba": {
                             "version": "1.2.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
                             "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
                         },
                         "string-width": {
                             "version": "1.0.2",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
                             "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
                             "requires": {
                                 "code-point-at": "^1.0.0",
@@ -12256,7 +11884,7 @@
                 },
                 "genfun": {
                     "version": "5.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz",
                     "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA=="
                 },
                 "gentle-fs": {
@@ -12279,12 +11907,12 @@
                     "dependencies": {
                         "aproba": {
                             "version": "1.2.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
                             "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
                         },
                         "iferr": {
                             "version": "0.1.5",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
                             "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE="
                         }
                     }
@@ -12296,7 +11924,7 @@
                 },
                 "get-stream": {
                     "version": "4.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
                     "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
                     "requires": {
                         "pump": "^3.0.0"
@@ -12304,7 +11932,7 @@
                 },
                 "getpass": {
                     "version": "0.1.7",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
                     "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
                     "requires": {
                         "assert-plus": "^1.0.0"
@@ -12312,7 +11940,7 @@
                 },
                 "glob": {
                     "version": "7.1.6",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
                     "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
                     "requires": {
                         "fs.realpath": "^1.0.0",
@@ -12325,7 +11953,7 @@
                 },
                 "global-dirs": {
                     "version": "0.1.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz",
                     "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=",
                     "requires": {
                         "ini": "^1.3.4"
@@ -12333,7 +11961,7 @@
                 },
                 "got": {
                     "version": "6.7.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
                     "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
                     "requires": {
                         "create-error-class": "^3.0.0",
@@ -12351,19 +11979,19 @@
                     "dependencies": {
                         "get-stream": {
                             "version": "3.0.0",
-                            "resolved": false,
+                            "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
                             "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
                         }
                     }
                 },
                 "graceful-fs": {
                     "version": "4.2.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
                     "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw=="
                 },
                 "har-schema": {
                     "version": "2.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
                     "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
                 },
                 "har-validator": {
@@ -12400,7 +12028,7 @@
                 },
                 "has": {
                     "version": "1.0.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
                     "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
                     "requires": {
                         "function-bind": "^1.1.1"
@@ -12408,17 +12036,17 @@
                 },
                 "has-flag": {
                     "version": "3.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
                     "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
                 },
                 "has-symbols": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
                     "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q="
                 },
                 "has-unicode": {
                     "version": "2.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
                     "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
                 },
                 "hosted-git-info": {
@@ -12428,12 +12056,12 @@
                 },
                 "http-cache-semantics": {
                     "version": "3.8.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz",
                     "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w=="
                 },
                 "http-proxy-agent": {
                     "version": "2.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz",
                     "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==",
                     "requires": {
                         "agent-base": "4",
@@ -12442,7 +12070,7 @@
                 },
                 "http-signature": {
                     "version": "1.2.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
                     "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
                     "requires": {
                         "assert-plus": "^1.0.0",
@@ -12452,7 +12080,7 @@
                 },
                 "https-proxy-agent": {
                     "version": "2.2.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
                     "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
                     "requires": {
                         "agent-base": "^4.3.0",
@@ -12461,7 +12089,7 @@
                 },
                 "humanize-ms": {
                     "version": "1.2.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
                     "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=",
                     "requires": {
                         "ms": "^2.0.0"
@@ -12469,7 +12097,7 @@
                 },
                 "iconv-lite": {
                     "version": "0.4.23",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
                     "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
                     "requires": {
                         "safer-buffer": ">= 2.1.2 < 3"
@@ -12477,12 +12105,12 @@
                 },
                 "iferr": {
                     "version": "1.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/iferr/-/iferr-1.0.2.tgz",
                     "integrity": "sha512-9AfeLfji44r5TKInjhz3W9DyZI1zR1JAf2hVBMGhddAKPqBsupb89jGfbCTHIGZd6fGZl9WlHdn4AObygyMKwg=="
                 },
                 "ignore-walk": {
                     "version": "3.0.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz",
                     "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
                     "requires": {
                         "minimatch": "^3.0.4"
@@ -12490,22 +12118,22 @@
                 },
                 "import-lazy": {
                     "version": "2.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
                     "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM="
                 },
                 "imurmurhash": {
                     "version": "0.1.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
                     "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
                 },
                 "infer-owner": {
                     "version": "1.0.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
                     "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A=="
                 },
                 "inflight": {
                     "version": "1.0.6",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
                     "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
                     "requires": {
                         "once": "^1.3.0",
@@ -12514,7 +12142,7 @@
                 },
                 "inherits": {
                     "version": "2.0.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
                     "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
                 },
                 "ini": {
@@ -12524,7 +12152,7 @@
                 },
                 "init-package-json": {
                     "version": "1.10.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/init-package-json/-/init-package-json-1.10.3.tgz",
                     "integrity": "sha512-zKSiXKhQveNteyhcj1CoOP8tqp1QuxPIPBl8Bid99DGLFqA1p87M6lNgfjJHSBoWJJlidGOv5rWjyYKEB3g2Jw==",
                     "requires": {
                         "glob": "^7.1.1",
@@ -12539,22 +12167,22 @@
                 },
                 "ip": {
                     "version": "1.1.5",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
                     "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
                 },
                 "ip-regex": {
                     "version": "2.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
                     "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk="
                 },
                 "is-callable": {
                     "version": "1.1.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
                     "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA=="
                 },
                 "is-ci": {
                     "version": "1.2.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz",
                     "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==",
                     "requires": {
                         "ci-info": "^1.5.0"
@@ -12562,14 +12190,14 @@
                     "dependencies": {
                         "ci-info": {
                             "version": "1.6.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz",
                             "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A=="
                         }
                     }
                 },
                 "is-cidr": {
                     "version": "3.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/is-cidr/-/is-cidr-3.0.0.tgz",
                     "integrity": "sha512-8Xnnbjsb0x462VoYiGlhEi+drY8SFwrHiSYuzc/CEwco55vkehTaxAyIjEdpi3EMvLPPJAJi9FlzP+h+03gp0Q==",
                     "requires": {
                         "cidr-regex": "^2.0.10"
@@ -12577,12 +12205,12 @@
                 },
                 "is-date-object": {
                     "version": "1.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
                     "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY="
                 },
                 "is-fullwidth-code-point": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "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.0"
@@ -12590,7 +12218,7 @@
                 },
                 "is-installed-globally": {
                     "version": "0.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz",
                     "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=",
                     "requires": {
                         "global-dirs": "^0.1.0",
@@ -12599,17 +12227,17 @@
                 },
                 "is-npm": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz",
                     "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ="
                 },
                 "is-obj": {
                     "version": "1.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
                     "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8="
                 },
                 "is-path-inside": {
                     "version": "1.0.1",
-                    "resolved": false,
+                    "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.1"
@@ -12617,12 +12245,12 @@
                 },
                 "is-redirect": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
                     "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ="
                 },
                 "is-regex": {
                     "version": "1.0.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
                     "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
                     "requires": {
                         "has": "^1.0.1"
@@ -12630,17 +12258,17 @@
                 },
                 "is-retry-allowed": {
                     "version": "1.2.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz",
                     "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg=="
                 },
                 "is-stream": {
                     "version": "1.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
                     "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
                 },
                 "is-symbol": {
                     "version": "1.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
                     "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
                     "requires": {
                         "has-symbols": "^1.0.0"
@@ -12648,33 +12276,33 @@
                 },
                 "is-typedarray": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
                     "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
                 },
                 "isarray": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
                     "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
                 },
                 "isexe": {
                     "version": "2.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
                     "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
                 },
                 "isstream": {
                     "version": "0.1.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
                     "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
                 },
                 "jsbn": {
                     "version": "0.1.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
                     "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
                     "optional": true
                 },
                 "json-parse-better-errors": {
                     "version": "1.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
                     "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
                 },
                 "json-schema": {
@@ -12684,12 +12312,13 @@
                 },
                 "json-stringify-safe": {
                     "version": "5.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
                     "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
                 },
                 "jsonparse": {
                     "version": "1.3.1",
-                    "bundled": true
+                    "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+                    "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA="
                 },
                 "jsprim": {
                     "version": "1.4.2",
@@ -12704,7 +12333,7 @@
                 },
                 "latest-version": {
                     "version": "3.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz",
                     "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=",
                     "requires": {
                         "package-json": "^4.0.0"
@@ -12712,7 +12341,7 @@
                 },
                 "lazy-property": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/lazy-property/-/lazy-property-1.0.0.tgz",
                     "integrity": "sha1-hN3Es3Bnm6i9TNz6TAa0PVcREUc="
                 },
                 "libcipm": {
@@ -12739,7 +12368,7 @@
                 },
                 "libnpm": {
                     "version": "3.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/libnpm/-/libnpm-3.0.1.tgz",
                     "integrity": "sha512-d7jU5ZcMiTfBqTUJVZ3xid44fE5ERBm9vBnmhp2ECD2Ls+FNXWxHSkO7gtvrnbLO78gwPdNPz1HpsF3W4rjkBQ==",
                     "requires": {
                         "bin-links": "^1.1.2",
@@ -12766,7 +12395,7 @@
                 },
                 "libnpmaccess": {
                     "version": "3.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-3.0.2.tgz",
                     "integrity": "sha512-01512AK7MqByrI2mfC7h5j8N9V4I7MHJuk9buo8Gv+5QgThpOgpjB7sQBDDkeZqRteFb1QM/6YNdHfG7cDvfAQ==",
                     "requires": {
                         "aproba": "^2.0.0",
@@ -12777,7 +12406,7 @@
                 },
                 "libnpmconfig": {
                     "version": "1.2.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/libnpmconfig/-/libnpmconfig-1.2.1.tgz",
                     "integrity": "sha512-9esX8rTQAHqarx6qeZqmGQKBNZR5OIbl/Ayr0qQDy3oXja2iFVQQI81R6GZ2a02bSNZ9p3YOGX1O6HHCb1X7kA==",
                     "requires": {
                         "figgy-pudding": "^3.5.1",
@@ -12787,7 +12416,7 @@
                     "dependencies": {
                         "find-up": {
                             "version": "3.0.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
                             "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
                             "requires": {
                                 "locate-path": "^3.0.0"
@@ -12795,7 +12424,7 @@
                         },
                         "locate-path": {
                             "version": "3.0.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
                             "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
                             "requires": {
                                 "p-locate": "^3.0.0",
@@ -12804,7 +12433,7 @@
                         },
                         "p-limit": {
                             "version": "2.2.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
                             "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
                             "requires": {
                                 "p-try": "^2.0.0"
@@ -12812,7 +12441,7 @@
                         },
                         "p-locate": {
                             "version": "3.0.0",
-                            "resolved": false,
+                            "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.0.0"
@@ -12820,14 +12449,14 @@
                         },
                         "p-try": {
                             "version": "2.2.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
                             "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
                         }
                     }
                 },
                 "libnpmhook": {
                     "version": "5.0.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/libnpmhook/-/libnpmhook-5.0.3.tgz",
                     "integrity": "sha512-UdNLMuefVZra/wbnBXECZPefHMGsVDTq5zaM/LgKNE9Keyl5YXQTnGAzEo+nFOpdRqTWI9LYi4ApqF9uVCCtuA==",
                     "requires": {
                         "aproba": "^2.0.0",
@@ -12838,7 +12467,7 @@
                 },
                 "libnpmorg": {
                     "version": "1.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/libnpmorg/-/libnpmorg-1.0.1.tgz",
                     "integrity": "sha512-0sRUXLh+PLBgZmARvthhYXQAWn0fOsa6T5l3JSe2n9vKG/lCVK4nuG7pDsa7uMq+uTt2epdPK+a2g6btcY11Ww==",
                     "requires": {
                         "aproba": "^2.0.0",
@@ -12849,7 +12478,7 @@
                 },
                 "libnpmpublish": {
                     "version": "1.1.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/libnpmpublish/-/libnpmpublish-1.1.2.tgz",
                     "integrity": "sha512-2yIwaXrhTTcF7bkJKIKmaCV9wZOALf/gsTDxVSu/Gu/6wiG3fA8ce8YKstiWKTxSFNC0R7isPUb6tXTVFZHt2g==",
                     "requires": {
                         "aproba": "^2.0.0",
@@ -12865,7 +12494,7 @@
                 },
                 "libnpmsearch": {
                     "version": "2.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/libnpmsearch/-/libnpmsearch-2.0.2.tgz",
                     "integrity": "sha512-VTBbV55Q6fRzTdzziYCr64+f8AopQ1YZ+BdPOv16UegIEaE8C0Kch01wo4s3kRTFV64P121WZJwgmBwrq68zYg==",
                     "requires": {
                         "figgy-pudding": "^3.5.1",
@@ -12875,7 +12504,7 @@
                 },
                 "libnpmteam": {
                     "version": "1.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/libnpmteam/-/libnpmteam-1.0.2.tgz",
                     "integrity": "sha512-p420vM28Us04NAcg1rzgGW63LMM6rwe+6rtZpfDxCcXxM0zUTLl7nPFEnRF3JfFBF5skF/yuZDUthTsHgde8QA==",
                     "requires": {
                         "aproba": "^2.0.0",
@@ -12901,7 +12530,7 @@
                 },
                 "lock-verify": {
                     "version": "2.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/lock-verify/-/lock-verify-2.1.0.tgz",
                     "integrity": "sha512-vcLpxnGvrqisKvLQ2C2v0/u7LVly17ak2YSgoK4PrdsYBXQIax19vhKiLfvKNFx7FRrpTnitrpzF/uuCMuorIg==",
                     "requires": {
                         "npm-package-arg": "^6.1.0",
@@ -12910,7 +12539,7 @@
                 },
                 "lockfile": {
                     "version": "1.0.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz",
                     "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==",
                     "requires": {
                         "signal-exit": "^3.0.2"
@@ -12918,12 +12547,12 @@
                 },
                 "lodash._baseindexof": {
                     "version": "3.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz",
                     "integrity": "sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw="
                 },
                 "lodash._baseuniq": {
                     "version": "4.6.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz",
                     "integrity": "sha1-DrtE5FaBSveQXGIS+iybLVG4Qeg=",
                     "requires": {
                         "lodash._createset": "~4.0.0",
@@ -12932,17 +12561,17 @@
                 },
                 "lodash._bindcallback": {
                     "version": "3.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz",
                     "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4="
                 },
                 "lodash._cacheindexof": {
                     "version": "3.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz",
                     "integrity": "sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI="
                 },
                 "lodash._createcache": {
                     "version": "3.1.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/lodash._createcache/-/lodash._createcache-3.1.2.tgz",
                     "integrity": "sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=",
                     "requires": {
                         "lodash._getnative": "^3.0.0"
@@ -12950,52 +12579,52 @@
                 },
                 "lodash._createset": {
                     "version": "4.0.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/lodash._createset/-/lodash._createset-4.0.3.tgz",
                     "integrity": "sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY="
                 },
                 "lodash._getnative": {
                     "version": "3.9.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
                     "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U="
                 },
                 "lodash._root": {
                     "version": "3.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz",
                     "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI="
                 },
                 "lodash.clonedeep": {
                     "version": "4.5.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
                     "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
                 },
                 "lodash.restparam": {
                     "version": "3.6.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz",
                     "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU="
                 },
                 "lodash.union": {
                     "version": "4.6.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
                     "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg="
                 },
                 "lodash.uniq": {
                     "version": "4.5.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
                     "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M="
                 },
                 "lodash.without": {
                     "version": "4.4.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/lodash.without/-/lodash.without-4.4.0.tgz",
                     "integrity": "sha1-PNRXSgC2e643OpS3SHcmQFB7eqw="
                 },
                 "lowercase-keys": {
                     "version": "1.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
                     "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA=="
                 },
                 "lru-cache": {
                     "version": "5.1.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
                     "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
                     "requires": {
                         "yallist": "^3.0.2"
@@ -13003,7 +12632,7 @@
                 },
                 "make-dir": {
                     "version": "1.3.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
                     "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
                     "requires": {
                         "pify": "^3.0.0"
@@ -13011,7 +12640,7 @@
                 },
                 "make-fetch-happen": {
                     "version": "5.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz",
                     "integrity": "sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag==",
                     "requires": {
                         "agentkeepalive": "^3.4.1",
@@ -13034,12 +12663,12 @@
                 },
                 "mime-db": {
                     "version": "1.35.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz",
                     "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg=="
                 },
                 "mime-types": {
                     "version": "2.1.19",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz",
                     "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==",
                     "requires": {
                         "mime-db": "~1.35.0"
@@ -13047,20 +12676,20 @@
                 },
                 "minimatch": {
                     "version": "3.0.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
                     "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
                     "requires": {
                         "brace-expansion": "^1.1.7"
                     }
                 },
                 "minimist": {
-                    "version": "1.2.5",
-                    "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
-                    "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+                    "version": "1.2.6",
+                    "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
+                    "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
                 },
                 "minizlib": {
                     "version": "1.3.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
                     "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
                     "requires": {
                         "minipass": "^2.9.0"
@@ -13068,7 +12697,7 @@
                     "dependencies": {
                         "minipass": {
                             "version": "2.9.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
                             "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
                             "requires": {
                                 "safe-buffer": "^5.1.2",
@@ -13079,7 +12708,7 @@
                 },
                 "mississippi": {
                     "version": "3.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
                     "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
                     "requires": {
                         "concat-stream": "^1.5.0",
@@ -13096,22 +12725,22 @@
                 },
                 "mkdirp": {
                     "version": "0.5.5",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
                     "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
                     "requires": {
                         "minimist": "^1.2.5"
                     },
                     "dependencies": {
                         "minimist": {
-                            "version": "1.2.5",
-                            "resolved": false,
-                            "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+                            "version": "1.2.6",
+                            "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
+                            "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
                         }
                     }
                 },
                 "move-concurrently": {
                     "version": "1.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
                     "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
                     "requires": {
                         "aproba": "^1.1.1",
@@ -13124,24 +12753,24 @@
                     "dependencies": {
                         "aproba": {
                             "version": "1.2.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
                             "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
                         }
                     }
                 },
                 "ms": {
                     "version": "2.1.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
                     "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
                 },
                 "mute-stream": {
                     "version": "0.0.7",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
                     "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
                 },
                 "node-fetch-npm": {
                     "version": "2.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz",
                     "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==",
                     "requires": {
                         "encoding": "^0.1.11",
@@ -13151,7 +12780,7 @@
                 },
                 "node-gyp": {
                     "version": "5.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.0.tgz",
                     "integrity": "sha512-OUTryc5bt/P8zVgNUmC6xdXiDJxLMAW8cF5tLQOT9E5sOQj+UeQxnnPy74K3CLCa/SOjjBlbuzDLR8ANwA+wmw==",
                     "requires": {
                         "env-paths": "^2.2.0",
@@ -13169,7 +12798,7 @@
                 },
                 "nopt": {
                     "version": "4.0.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
                     "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
                     "requires": {
                         "abbrev": "1",
@@ -13178,7 +12807,7 @@
                 },
                 "normalize-package-data": {
                     "version": "2.5.0",
-                    "resolved": false,
+                    "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.1.4",
@@ -13189,7 +12818,7 @@
                     "dependencies": {
                         "resolve": {
                             "version": "1.10.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz",
                             "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
                             "requires": {
                                 "path-parse": "^1.0.6"
@@ -13208,7 +12837,7 @@
                 },
                 "npm-bundled": {
                     "version": "1.1.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
                     "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
                     "requires": {
                         "npm-normalize-package-bin": "^1.0.1"
@@ -13216,12 +12845,12 @@
                 },
                 "npm-cache-filename": {
                     "version": "1.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/npm-cache-filename/-/npm-cache-filename-1.0.2.tgz",
                     "integrity": "sha1-3tMGxbC/yHCp6fr4I7xfKD4FrhE="
                 },
                 "npm-install-checks": {
                     "version": "3.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-3.0.2.tgz",
                     "integrity": "sha512-E4kzkyZDIWoin6uT5howP8VDvkM+E8IQDcHAycaAxMbwkqhIg5eEYALnXOl3Hq9MrkdQB/2/g1xwBINXdKSRkg==",
                     "requires": {
                         "semver": "^2.3.0 || 3.x || 4 || 5"
@@ -13244,17 +12873,17 @@
                 },
                 "npm-logical-tree": {
                     "version": "1.2.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/npm-logical-tree/-/npm-logical-tree-1.2.1.tgz",
                     "integrity": "sha512-AJI/qxDB2PWI4LG1CYN579AY1vCiNyWfkiquCsJWqntRu/WwimVrC8yXeILBFHDwxfOejxewlmnvW9XXjMlYIg=="
                 },
                 "npm-normalize-package-bin": {
                     "version": "1.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
                     "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA=="
                 },
                 "npm-package-arg": {
                     "version": "6.1.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz",
                     "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==",
                     "requires": {
                         "hosted-git-info": "^2.7.1",
@@ -13265,7 +12894,7 @@
                 },
                 "npm-packlist": {
                     "version": "1.4.8",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
                     "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
                     "requires": {
                         "ignore-walk": "^3.0.1",
@@ -13275,7 +12904,7 @@
                 },
                 "npm-pick-manifest": {
                     "version": "3.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz",
                     "integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==",
                     "requires": {
                         "figgy-pudding": "^3.5.1",
@@ -13285,7 +12914,7 @@
                 },
                 "npm-profile": {
                     "version": "4.0.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/npm-profile/-/npm-profile-4.0.4.tgz",
                     "integrity": "sha512-Ta8xq8TLMpqssF0H60BXS1A90iMoM6GeKwsmravJ6wYjWwSzcYBTdyWa3DZCYqPutacBMEm7cxiOkiIeCUAHDQ==",
                     "requires": {
                         "aproba": "^1.1.2 || 2",
@@ -13316,7 +12945,7 @@
                 },
                 "npm-run-path": {
                     "version": "2.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
                     "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
                     "requires": {
                         "path-key": "^2.0.0"
@@ -13329,7 +12958,7 @@
                 },
                 "npmlog": {
                     "version": "4.1.2",
-                    "resolved": false,
+                    "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.2",
@@ -13340,27 +12969,27 @@
                 },
                 "number-is-nan": {
                     "version": "1.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
                     "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
                 },
                 "oauth-sign": {
                     "version": "0.9.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
                     "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
                 },
                 "object-assign": {
                     "version": "4.1.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
                     "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
                 },
                 "object-keys": {
                     "version": "1.0.12",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
                     "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag=="
                 },
                 "object.getownpropertydescriptors": {
                     "version": "2.0.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz",
                     "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=",
                     "requires": {
                         "define-properties": "^1.1.2",
@@ -13369,7 +12998,7 @@
                 },
                 "once": {
                     "version": "1.4.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
                     "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
                     "requires": {
                         "wrappy": "1"
@@ -13382,17 +13011,17 @@
                 },
                 "os-homedir": {
                     "version": "1.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
                     "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
                 },
                 "os-tmpdir": {
                     "version": "1.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
                     "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
                 },
                 "osenv": {
                     "version": "0.1.5",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
                     "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
                     "requires": {
                         "os-homedir": "^1.0.0",
@@ -13401,12 +13030,12 @@
                 },
                 "p-finally": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
                     "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
                 },
                 "package-json": {
                     "version": "4.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz",
                     "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=",
                     "requires": {
                         "got": "^6.7.1",
@@ -13417,7 +13046,7 @@
                 },
                 "pacote": {
                     "version": "9.5.12",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.5.12.tgz",
                     "integrity": "sha512-BUIj/4kKbwWg4RtnBncXPJd15piFSVNpTzY0rysSr3VnMowTYgkGKcaHrbReepAkjTr8lH2CVWRi58Spg2CicQ==",
                     "requires": {
                         "bluebird": "^3.5.3",
@@ -13454,7 +13083,7 @@
                     "dependencies": {
                         "minipass": {
                             "version": "2.9.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
                             "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
                             "requires": {
                                 "safe-buffer": "^5.1.2",
@@ -13465,7 +13094,7 @@
                 },
                 "parallel-transform": {
                     "version": "1.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz",
                     "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=",
                     "requires": {
                         "cyclist": "~0.2.2",
@@ -13475,7 +13104,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": false,
+                            "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.0",
@@ -13489,7 +13118,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": false,
+                            "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.0"
@@ -13499,22 +13128,22 @@
                 },
                 "path-exists": {
                     "version": "3.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
                     "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
                 },
                 "path-is-absolute": {
                     "version": "1.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
                     "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
                 },
                 "path-is-inside": {
                     "version": "1.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
                     "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM="
                 },
                 "path-key": {
                     "version": "2.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
                     "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
                 },
                 "path-parse": {
@@ -13524,32 +13153,32 @@
                 },
                 "performance-now": {
                     "version": "2.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
                     "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
                 },
                 "pify": {
                     "version": "3.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
                     "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
                 },
                 "prepend-http": {
                     "version": "1.0.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
                     "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw="
                 },
                 "process-nextick-args": {
                     "version": "2.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
                     "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
                 },
                 "promise-inflight": {
                     "version": "1.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
                     "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM="
                 },
                 "promise-retry": {
                     "version": "1.1.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz",
                     "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=",
                     "requires": {
                         "err-code": "^1.0.0",
@@ -13558,14 +13187,14 @@
                     "dependencies": {
                         "retry": {
                             "version": "0.10.1",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz",
                             "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q="
                         }
                     }
                 },
                 "promzard": {
                     "version": "0.3.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/promzard/-/promzard-0.3.0.tgz",
                     "integrity": "sha1-JqXW7ox97kyxIggwWs+5O6OCqe4=",
                     "requires": {
                         "read": "1"
@@ -13573,12 +13202,12 @@
                 },
                 "proto-list": {
                     "version": "1.2.4",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
                     "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk="
                 },
                 "protoduck": {
                     "version": "5.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz",
                     "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==",
                     "requires": {
                         "genfun": "^5.0.0"
@@ -13586,22 +13215,22 @@
                 },
                 "prr": {
                     "version": "1.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
                     "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY="
                 },
                 "pseudomap": {
                     "version": "1.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
                     "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
                 },
                 "psl": {
                     "version": "1.1.29",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz",
                     "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ=="
                 },
                 "pump": {
                     "version": "3.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
                     "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
                     "requires": {
                         "end-of-stream": "^1.1.0",
@@ -13610,7 +13239,7 @@
                 },
                 "pumpify": {
                     "version": "1.5.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
                     "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
                     "requires": {
                         "duplexify": "^3.6.0",
@@ -13620,7 +13249,7 @@
                     "dependencies": {
                         "pump": {
                             "version": "2.0.1",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
                             "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
                             "requires": {
                                 "end-of-stream": "^1.1.0",
@@ -13631,22 +13260,22 @@
                 },
                 "punycode": {
                     "version": "1.4.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
                     "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
                 },
                 "qrcode-terminal": {
                     "version": "0.12.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz",
                     "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ=="
                 },
                 "qs": {
                     "version": "6.5.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
                     "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
                 },
                 "query-string": {
                     "version": "6.8.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.8.2.tgz",
                     "integrity": "sha512-J3Qi8XZJXh93t2FiKyd/7Ec6GNifsjKXUsVFkSBj/kjLsDylWhnCz4NT1bkPcKotttPW+QbKGqqPH8OoI2pdqw==",
                     "requires": {
                         "decode-uri-component": "^0.2.0",
@@ -13656,12 +13285,12 @@
                 },
                 "qw": {
                     "version": "1.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/qw/-/qw-1.0.1.tgz",
                     "integrity": "sha1-77/cdA+a0FQwRCassYNBLMi5ltQ="
                 },
                 "rc": {
                     "version": "1.2.8",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
                     "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
                     "requires": {
                         "deep-extend": "^0.6.0",
@@ -13672,7 +13301,7 @@
                 },
                 "read": {
                     "version": "1.0.7",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
                     "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=",
                     "requires": {
                         "mute-stream": "~0.0.4"
@@ -13680,7 +13309,7 @@
                 },
                 "read-cmd-shim": {
                     "version": "1.0.5",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-1.0.5.tgz",
                     "integrity": "sha512-v5yCqQ/7okKoZZkBQUAfTsQ3sVJtXdNfbPnI5cceppoxEVLYA3k+VtV2omkeo8MS94JCy4fSiUwlRBAwCVRPUA==",
                     "requires": {
                         "graceful-fs": "^4.1.2"
@@ -13688,7 +13317,7 @@
                 },
                 "read-installed": {
                     "version": "4.0.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz",
                     "integrity": "sha1-/5uLZ/GH0eTCm5/rMfayI6zRkGc=",
                     "requires": {
                         "debuglog": "^1.0.1",
@@ -13702,7 +13331,7 @@
                 },
                 "read-package-json": {
                     "version": "2.1.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.1.tgz",
                     "integrity": "sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A==",
                     "requires": {
                         "glob": "^7.1.1",
@@ -13714,7 +13343,7 @@
                 },
                 "read-package-tree": {
                     "version": "5.3.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz",
                     "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==",
                     "requires": {
                         "read-package-json": "^2.0.0",
@@ -13724,7 +13353,7 @@
                 },
                 "readable-stream": {
                     "version": "3.6.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
                     "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
                     "requires": {
                         "inherits": "^2.0.3",
@@ -13734,7 +13363,7 @@
                 },
                 "readdir-scoped-modules": {
                     "version": "1.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz",
                     "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==",
                     "requires": {
                         "debuglog": "^1.0.1",
@@ -13745,7 +13374,7 @@
                 },
                 "registry-auth-token": {
                     "version": "3.4.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz",
                     "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==",
                     "requires": {
                         "rc": "^1.1.6",
@@ -13754,7 +13383,7 @@
                 },
                 "registry-url": {
                     "version": "3.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
                     "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
                     "requires": {
                         "rc": "^1.0.1"
@@ -13762,7 +13391,7 @@
                 },
                 "request": {
                     "version": "2.88.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
                     "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
                     "requires": {
                         "aws-sign2": "~0.7.0",
@@ -13789,7 +13418,7 @@
                 },
                 "require-directory": {
                     "version": "2.1.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
                     "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
                 },
                 "require-main-filename": {
@@ -13799,17 +13428,17 @@
                 },
                 "resolve-from": {
                     "version": "4.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
                     "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
                 },
                 "retry": {
                     "version": "0.12.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
                     "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs="
                 },
                 "rimraf": {
                     "version": "2.7.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
                     "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
                     "requires": {
                         "glob": "^7.1.3"
@@ -13817,7 +13446,7 @@
                 },
                 "run-queue": {
                     "version": "1.0.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
                     "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
                     "requires": {
                         "aproba": "^1.1.1"
@@ -13825,29 +13454,29 @@
                     "dependencies": {
                         "aproba": {
                             "version": "1.2.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
                             "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
                         }
                     }
                 },
                 "safe-buffer": {
                     "version": "5.1.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
                     "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
                 },
                 "safer-buffer": {
                     "version": "2.1.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
                     "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
                 },
                 "semver": {
                     "version": "5.7.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
                     "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
                 },
                 "semver-diff": {
                     "version": "2.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz",
                     "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=",
                     "requires": {
                         "semver": "^5.0.3"
@@ -13855,12 +13484,12 @@
                 },
                 "set-blocking": {
                     "version": "2.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
                     "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
                 },
                 "sha": {
                     "version": "3.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/sha/-/sha-3.0.0.tgz",
                     "integrity": "sha512-DOYnM37cNsLNSGIG/zZWch5CKIRNoLdYUQTQlcgkRkoYIUwDYjqDyye16YcDZg/OPdcbUgTKMjc4SY6TB7ZAPw==",
                     "requires": {
                         "graceful-fs": "^4.1.2"
@@ -13868,7 +13497,7 @@
                 },
                 "shebang-command": {
                     "version": "1.2.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
                     "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
                     "requires": {
                         "shebang-regex": "^1.0.0"
@@ -13876,27 +13505,27 @@
                 },
                 "shebang-regex": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
                     "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
                 },
                 "signal-exit": {
                     "version": "3.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
                     "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
                 },
                 "slide": {
                     "version": "1.1.6",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
                     "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc="
                 },
                 "smart-buffer": {
                     "version": "4.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz",
                     "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw=="
                 },
                 "socks": {
                     "version": "2.3.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz",
                     "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==",
                     "requires": {
                         "ip": "1.1.5",
@@ -13905,7 +13534,7 @@
                 },
                 "socks-proxy-agent": {
                     "version": "4.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz",
                     "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==",
                     "requires": {
                         "agent-base": "~4.2.1",
@@ -13914,7 +13543,7 @@
                     "dependencies": {
                         "agent-base": {
                             "version": "4.2.1",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
                             "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
                             "requires": {
                                 "es6-promisify": "^5.0.0"
@@ -13924,12 +13553,12 @@
                 },
                 "sorted-object": {
                     "version": "2.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/sorted-object/-/sorted-object-2.0.1.tgz",
                     "integrity": "sha1-fWMfS9OnmKJK8d/8+/6DM3pd9fw="
                 },
                 "sorted-union-stream": {
                     "version": "2.1.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/sorted-union-stream/-/sorted-union-stream-2.1.3.tgz",
                     "integrity": "sha1-x3lMfgd4gAUv9xqNSi27Sppjisc=",
                     "requires": {
                         "from2": "^1.3.0",
@@ -13938,7 +13567,7 @@
                     "dependencies": {
                         "from2": {
                             "version": "1.3.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/from2/-/from2-1.3.0.tgz",
                             "integrity": "sha1-iEE7qqX5pZfP3pIh2GmGzTwGHf0=",
                             "requires": {
                                 "inherits": "~2.0.1",
@@ -13947,12 +13576,12 @@
                         },
                         "isarray": {
                             "version": "0.0.1",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
                             "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
                         },
                         "readable-stream": {
                             "version": "1.1.14",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
                             "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
                             "requires": {
                                 "core-util-is": "~1.0.0",
@@ -13963,14 +13592,14 @@
                         },
                         "string_decoder": {
                             "version": "0.10.31",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
                             "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
                         }
                     }
                 },
                 "spdx-correct": {
                     "version": "3.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz",
                     "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==",
                     "requires": {
                         "spdx-expression-parse": "^3.0.0",
@@ -13979,12 +13608,12 @@
                 },
                 "spdx-exceptions": {
                     "version": "2.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz",
                     "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg=="
                 },
                 "spdx-expression-parse": {
                     "version": "3.0.0",
-                    "resolved": false,
+                    "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.1.0",
@@ -13998,12 +13627,12 @@
                 },
                 "split-on-first": {
                     "version": "1.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
                     "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="
                 },
                 "sshpk": {
                     "version": "1.14.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz",
                     "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=",
                     "requires": {
                         "asn1": "~0.2.3",
@@ -14027,7 +13656,7 @@
                 },
                 "stream-each": {
                     "version": "1.2.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz",
                     "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==",
                     "requires": {
                         "end-of-stream": "^1.1.0",
@@ -14036,7 +13665,7 @@
                 },
                 "stream-iterate": {
                     "version": "1.2.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/stream-iterate/-/stream-iterate-1.2.0.tgz",
                     "integrity": "sha1-K9fHcpbBcCpGSIuK1B95hl7s1OE=",
                     "requires": {
                         "readable-stream": "^2.1.5",
@@ -14045,7 +13674,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": false,
+                            "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.0",
@@ -14059,7 +13688,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": false,
+                            "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.0"
@@ -14069,16 +13698,17 @@
                 },
                 "stream-shift": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
                     "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI="
                 },
                 "strict-uri-encode": {
                     "version": "2.0.0",
-                    "bundled": true
+                    "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
+                    "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
                 },
                 "string-width": {
                     "version": "2.1.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
                     "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
                     "requires": {
                         "is-fullwidth-code-point": "^2.0.0",
@@ -14087,17 +13717,17 @@
                     "dependencies": {
                         "ansi-regex": {
                             "version": "3.0.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
                             "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
                         },
                         "is-fullwidth-code-point": {
                             "version": "2.0.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
                             "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
                         },
                         "strip-ansi": {
                             "version": "4.0.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
                             "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
                             "requires": {
                                 "ansi-regex": "^3.0.0"
@@ -14107,25 +13737,27 @@
                 },
                 "string_decoder": {
                     "version": "1.3.0",
-                    "bundled": true,
+                    "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+                    "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
                     "requires": {
                         "safe-buffer": "~5.2.0"
                     },
                     "dependencies": {
                         "safe-buffer": {
                             "version": "5.2.0",
-                            "bundled": true
+                            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
+                            "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
                         }
                     }
                 },
                 "stringify-package": {
                     "version": "1.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz",
                     "integrity": "sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg=="
                 },
                 "strip-ansi": {
                     "version": "3.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
                     "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
                     "requires": {
                         "ansi-regex": "^2.0.0"
@@ -14133,17 +13765,17 @@
                 },
                 "strip-eof": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
                     "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
                 },
                 "strip-json-comments": {
                     "version": "2.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
                     "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
                 },
                 "supports-color": {
                     "version": "5.4.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
                     "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
                     "requires": {
                         "has-flag": "^3.0.0"
@@ -14165,7 +13797,7 @@
                     "dependencies": {
                         "minipass": {
                             "version": "2.9.0",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
                             "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
                             "requires": {
                                 "safe-buffer": "^5.1.2",
@@ -14186,7 +13818,7 @@
                 },
                 "term-size": {
                     "version": "1.2.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz",
                     "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=",
                     "requires": {
                         "execa": "^0.7.0"
@@ -14194,17 +13826,17 @@
                 },
                 "text-table": {
                     "version": "0.2.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
                     "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ="
                 },
                 "through": {
                     "version": "2.3.8",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
                     "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
                 },
                 "through2": {
                     "version": "2.0.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz",
                     "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=",
                     "requires": {
                         "readable-stream": "^2.1.5",
@@ -14213,7 +13845,7 @@
                     "dependencies": {
                         "readable-stream": {
                             "version": "2.3.6",
-                            "resolved": false,
+                            "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.0",
@@ -14227,7 +13859,7 @@
                         },
                         "string_decoder": {
                             "version": "1.1.1",
-                            "resolved": false,
+                            "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.0"
@@ -14237,17 +13869,17 @@
                 },
                 "timed-out": {
                     "version": "4.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
                     "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8="
                 },
                 "tiny-relative-date": {
                     "version": "1.3.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz",
                     "integrity": "sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A=="
                 },
                 "tough-cookie": {
                     "version": "2.4.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
                     "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
                     "requires": {
                         "psl": "^1.1.24",
@@ -14256,7 +13888,7 @@
                 },
                 "tunnel-agent": {
                     "version": "0.6.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
                     "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
                     "requires": {
                         "safe-buffer": "^5.0.1"
@@ -14264,28 +13896,28 @@
                 },
                 "tweetnacl": {
                     "version": "0.14.5",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
                     "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
                     "optional": true
                 },
                 "typedarray": {
                     "version": "0.0.6",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
                     "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
                 },
                 "uid-number": {
                     "version": "0.0.6",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz",
                     "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE="
                 },
                 "umask": {
                     "version": "1.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/umask/-/umask-1.1.0.tgz",
                     "integrity": "sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0="
                 },
                 "unique-filename": {
                     "version": "1.1.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
                     "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
                     "requires": {
                         "unique-slug": "^2.0.0"
@@ -14293,7 +13925,7 @@
                 },
                 "unique-slug": {
                     "version": "2.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz",
                     "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=",
                     "requires": {
                         "imurmurhash": "^0.1.4"
@@ -14301,7 +13933,7 @@
                 },
                 "unique-string": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz",
                     "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=",
                     "requires": {
                         "crypto-random-string": "^1.0.0"
@@ -14309,17 +13941,17 @@
                 },
                 "unpipe": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
                     "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
                 },
                 "unzip-response": {
                     "version": "2.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz",
                     "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c="
                 },
                 "update-notifier": {
                     "version": "2.5.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz",
                     "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==",
                     "requires": {
                         "boxen": "^1.2.1",
@@ -14351,7 +13983,7 @@
                 },
                 "url-parse-lax": {
                     "version": "1.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz",
                     "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=",
                     "requires": {
                         "prepend-http": "^1.0.1"
@@ -14359,17 +13991,17 @@
                 },
                 "util-deprecate": {
                     "version": "1.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
                     "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
                 },
                 "util-extend": {
                     "version": "1.0.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz",
                     "integrity": "sha1-p8IW0mdUUWljeztu3GypEZ4v+T8="
                 },
                 "util-promisify": {
                     "version": "2.1.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz",
                     "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=",
                     "requires": {
                         "object.getownpropertydescriptors": "^2.0.3"
@@ -14377,12 +14009,12 @@
                 },
                 "uuid": {
                     "version": "3.3.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
                     "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ=="
                 },
                 "validate-npm-package-license": {
                     "version": "3.0.4",
-                    "resolved": false,
+                    "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.0.0",
@@ -14391,7 +14023,7 @@
                 },
                 "validate-npm-package-name": {
                     "version": "3.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz",
                     "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=",
                     "requires": {
                         "builtins": "^1.0.3"
@@ -14399,7 +14031,7 @@
                 },
                 "verror": {
                     "version": "1.10.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
                     "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
                     "requires": {
                         "assert-plus": "^1.0.0",
@@ -14409,7 +14041,7 @@
                 },
                 "wcwidth": {
                     "version": "1.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
                     "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=",
                     "requires": {
                         "defaults": "^1.0.3"
@@ -14417,7 +14049,7 @@
                 },
                 "which": {
                     "version": "1.3.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
                     "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
                     "requires": {
                         "isexe": "^2.0.0"
@@ -14425,12 +14057,12 @@
                 },
                 "which-module": {
                     "version": "2.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
                     "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
                 },
                 "wide-align": {
                     "version": "1.1.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
                     "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
                     "requires": {
                         "string-width": "^1.0.2"
@@ -14438,7 +14070,7 @@
                     "dependencies": {
                         "string-width": {
                             "version": "1.0.2",
-                            "resolved": false,
+                            "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
                             "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
                             "requires": {
                                 "code-point-at": "^1.0.0",
@@ -14450,7 +14082,7 @@
                 },
                 "widest-line": {
                     "version": "2.0.1",
-                    "resolved": false,
+                    "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"
@@ -14458,7 +14090,7 @@
                 },
                 "worker-farm": {
                     "version": "1.7.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
                     "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
                     "requires": {
                         "errno": "~0.1.7"
@@ -14475,9 +14107,9 @@
                     },
                     "dependencies": {
                         "ansi-regex": {
-                            "version": "4.1.0",
-                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
-                            "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
+                            "version": "4.1.1",
+                            "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+                            "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
                         },
                         "is-fullwidth-code-point": {
                             "version": "2.0.0",
@@ -14506,12 +14138,12 @@
                 },
                 "wrappy": {
                     "version": "1.0.2",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
                     "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
                 },
                 "write-file-atomic": {
                     "version": "2.4.3",
-                    "resolved": false,
+                    "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.1.11",
@@ -14521,12 +14153,12 @@
                 },
                 "xdg-basedir": {
                     "version": "3.0.0",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz",
                     "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ="
                 },
                 "xtend": {
                     "version": "4.0.1",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
                     "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
                 },
                 "y18n": {
@@ -14536,7 +14168,7 @@
                 },
                 "yallist": {
                     "version": "3.0.3",
-                    "resolved": false,
+                    "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
                     "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A=="
                 },
                 "yargs": {
@@ -14646,26 +14278,26 @@
         "npm-run-path": {
             "version": "2.0.2",
             "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
-            "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+            "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==",
             "requires": {
                 "path-key": "^2.0.0"
             }
         },
         "npmlog": {
-            "version": "4.1.2",
-            "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
-            "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
+            "version": "5.0.1",
+            "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
+            "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
             "requires": {
-                "are-we-there-yet": "~1.1.2",
-                "console-control-strings": "~1.1.0",
-                "gauge": "~2.7.3",
-                "set-blocking": "~2.0.0"
+                "are-we-there-yet": "^2.0.0",
+                "console-control-strings": "^1.1.0",
+                "gauge": "^3.0.0",
+                "set-blocking": "^2.0.0"
             }
         },
         "nth-check": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz",
-            "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==",
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+            "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
             "requires": {
                 "boolbase": "^1.0.0"
             }
@@ -14673,7 +14305,7 @@
         "number-is-nan": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
-            "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+            "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ=="
         },
         "nwsapi": {
             "version": "2.2.0",
@@ -14684,7 +14316,7 @@
         "oauth": {
             "version": "0.9.15",
             "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
-            "integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE="
+            "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
         },
         "oauth-sign": {
             "version": "0.9.0",
@@ -14694,12 +14326,12 @@
         "object-assign": {
             "version": "4.1.1",
             "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-            "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+            "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
         },
         "object-copy": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
-            "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+            "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==",
             "requires": {
                 "copy-descriptor": "^0.1.0",
                 "define-property": "^0.2.5",
@@ -14709,7 +14341,7 @@
                 "define-property": {
                     "version": "0.2.5",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-                    "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+                    "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
                     "requires": {
                         "is-descriptor": "^0.1.0"
                     }
@@ -14722,7 +14354,7 @@
                 "kind-of": {
                     "version": "3.2.2",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                    "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+                    "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -14730,9 +14362,9 @@
             }
         },
         "object-inspect": {
-            "version": "1.7.0",
-            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
-            "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw=="
+            "version": "1.12.2",
+            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
+            "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ=="
         },
         "object-is": {
             "version": "1.1.5",
@@ -14760,6 +14392,11 @@
                 "unpack-string": "0.0.2"
             },
             "dependencies": {
+                "commander": {
+                    "version": "2.20.3",
+                    "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+                    "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+                },
                 "inspect-parameters-declaration": {
                     "version": "0.0.10",
                     "resolved": "https://registry.npmjs.org/inspect-parameters-declaration/-/inspect-parameters-declaration-0.0.10.tgz",
@@ -14774,7 +14411,7 @@
                 "magicli": {
                     "version": "0.0.5",
                     "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz",
-                    "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=",
+                    "integrity": "sha512-wZbMtnl2v1b+Jp3xlqA9FU/O4I6YhGXR8xSY/eU2+gDAvut/F+W3gl4qs61iL4LELC7jqSAE6aAD5668EbmQHA==",
                     "requires": {
                         "commander": "^2.9.0",
                         "get-stdin": "^5.0.1",
@@ -14787,7 +14424,7 @@
         "object-visit": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
-            "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+            "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==",
             "requires": {
                 "isobject": "^3.0.0"
             }
@@ -14804,18 +14441,20 @@
             }
         },
         "object.getownpropertydescriptors": {
-            "version": "2.1.0",
-            "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz",
-            "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==",
+            "version": "2.1.4",
+            "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz",
+            "integrity": "sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==",
             "requires": {
-                "define-properties": "^1.1.3",
-                "es-abstract": "^1.17.0-next.1"
+                "array.prototype.reduce": "^1.0.4",
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.4",
+                "es-abstract": "^1.20.1"
             }
         },
         "object.pick": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
-            "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+            "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==",
             "requires": {
                 "isobject": "^3.0.1"
             }
@@ -14827,9 +14466,9 @@
             "dev": true
         },
         "on-finished": {
-            "version": "2.3.0",
-            "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
-            "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+            "version": "2.4.1",
+            "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+            "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
             "requires": {
                 "ee-first": "1.1.1"
             }
@@ -14842,7 +14481,7 @@
         "once": {
             "version": "1.4.0",
             "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-            "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+            "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
             "requires": {
                 "wrappy": "1"
             }
@@ -14896,28 +14535,19 @@
             }
         },
         "orderedmap": {
-            "version": "1.1.5",
-            "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-1.1.5.tgz",
-            "integrity": "sha512-/fzlCGKRmfayGoI9UUXvJfc2nMZlJHW30QqEvwPvlg8tsX7jyiUSomYie6mYqx7Z9bOMGoag0H/q1PS/0PjYkg=="
-        },
-        "original": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
-            "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
-            "dev": true,
-            "requires": {
-                "url-parse": "^1.4.3"
-            }
+            "version": "1.1.8",
+            "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-1.1.8.tgz",
+            "integrity": "sha512-eWEYOAggZZpZbJ9CTsqAKOTxlbBHdHZ8pzcfEvNTxGrjQ/m+Q25nSWUiMlT9MTbgpB6FOiBDKqsgJ2FlLDVNaw=="
         },
         "os-homedir": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
-            "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
+            "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ=="
         },
         "os-tmpdir": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
-            "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
+            "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g=="
         },
         "osenv": {
             "version": "0.1.5",
@@ -14936,12 +14566,12 @@
         "p-debounce": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/p-debounce/-/p-debounce-1.0.0.tgz",
-            "integrity": "sha1-y38svu/YegnrqGHhErZ1J+Yh4v0="
+            "integrity": "sha512-ttOxn4Yt0hzIsLLqKi/Ry9QRxW+UQKdoWHz7g99Ci57zPkqUU3kbWKAeHuv+HfRLe109acYLUY6kuVCOOqnt4g=="
         },
         "p-finally": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
-            "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
+            "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow=="
         },
         "p-limit": {
             "version": "2.3.0",
@@ -14982,7 +14612,7 @@
         "package-json": {
             "version": "4.0.1",
             "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz",
-            "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=",
+            "integrity": "sha512-q/R5GrMek0vzgoomq6rm9OX+3PQve8sLwTirmK30YB3Cu0Bbt9OX9M/SIUnroN5BGJkzwGsFwDaRGD9EwBOlCA==",
             "requires": {
                 "got": "^6.7.1",
                 "registry-auth-token": "^3.0.1",
@@ -14990,10 +14620,15 @@
                 "semver": "^5.1.0"
             },
             "dependencies": {
+                "get-stream": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
+                    "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ=="
+                },
                 "got": {
                     "version": "6.7.1",
                     "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
-                    "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
+                    "integrity": "sha512-Y/K3EDuiQN9rTZhBvPRWMLXIKdeD1Rj0nzunfoi0Yyn5WBEbzxXKU9Ub2X41oZBagVWOBU3MuDonFMgPWQFnwg==",
                     "requires": {
                         "create-error-class": "^3.0.0",
                         "duplexer3": "^0.1.4",
@@ -15055,13 +14690,6 @@
             "requires": {
                 "dot-case": "^3.0.4",
                 "tslib": "^2.0.3"
-            },
-            "dependencies": {
-                "tslib": {
-                    "version": "2.3.1",
-                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
-                    "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
-                }
             }
         },
         "parent-module": {
@@ -15083,6 +14711,12 @@
                 "lines-and-columns": "^1.1.6"
             }
         },
+        "parse5": {
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
+            "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==",
+            "dev": true
+        },
         "parseqs": {
             "version": "0.0.6",
             "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
@@ -15105,19 +14739,12 @@
             "requires": {
                 "no-case": "^3.0.4",
                 "tslib": "^2.0.3"
-            },
-            "dependencies": {
-                "tslib": {
-                    "version": "2.3.1",
-                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
-                    "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
-                }
             }
         },
         "pascalcase": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
-            "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ="
+            "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw=="
         },
         "passport": {
             "version": "0.4.1",
@@ -15139,15 +14766,15 @@
         "passport-local": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
-            "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=",
+            "integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==",
             "requires": {
                 "passport-strategy": "1.x.x"
             }
         },
         "passport-oauth2": {
-            "version": "1.5.0",
-            "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.5.0.tgz",
-            "integrity": "sha512-kqBt6vR/5VlCK8iCx1/KpY42kQ+NEHZwsSyt4Y6STiNjU+wWICG1i8ucc1FapXDGO15C5O5VZz7+7vRzrDPXXQ==",
+            "version": "1.6.1",
+            "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.6.1.tgz",
+            "integrity": "sha512-ZbV43Hq9d/SBSYQ22GOiglFsjsD1YY/qdiptA+8ej+9C1dL1TVB+mBE5kDH/D4AJo50+2i8f4bx0vg4/yDDZCQ==",
             "requires": {
                 "base64url": "3.x.x",
                 "oauth": "0.9.x",
@@ -15159,7 +14786,7 @@
         "passport-strategy": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
-            "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ="
+            "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA=="
         },
         "path-browserify": {
             "version": "1.0.1",
@@ -15169,37 +14796,37 @@
         "path-dirname": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
-            "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA="
+            "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q=="
         },
         "path-exists": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-            "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
+            "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="
         },
         "path-is-absolute": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-            "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+            "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="
         },
         "path-is-inside": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
-            "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM="
+            "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w=="
         },
         "path-key": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
-            "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
+            "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="
         },
         "path-parse": {
-            "version": "1.0.6",
-            "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
-            "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
+            "version": "1.0.7",
+            "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+            "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
         },
         "path-to-regexp": {
             "version": "0.1.7",
             "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
-            "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+            "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
         },
         "path-type": {
             "version": "4.0.0",
@@ -15214,7 +14841,7 @@
         "pause": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
-            "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
+            "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
         },
         "pdf-parse": {
             "version": "1.1.1",
@@ -15251,17 +14878,18 @@
             }
         },
         "pdfjs-dist": {
-            "version": "2.13.216",
-            "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.13.216.tgz",
-            "integrity": "sha512-qn/9a/3IHIKZarTK6ajeeFXBkG15Lg1Fx99PxU09PAU2i874X8mTcHJYyDJxu7WDfNhV6hM7bRQBZU384anoqQ==",
+            "version": "2.14.305",
+            "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.14.305.tgz",
+            "integrity": "sha512-5f7i25J1dKIBczhgfxEgNxfYNIxXEdxqo6Qb4ehY7Ja+p6AI4uUmk/OcVGXfRGm2ys5iaJJhJUwBFwv6Jl/Qww==",
             "requires": {
-                "web-streams-polyfill": "^3.2.0"
+                "dommatrix": "^1.0.1",
+                "web-streams-polyfill": "^3.2.1"
             }
         },
         "pend": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
-            "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
+            "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="
         },
         "perfect-scrollbar": {
             "version": "1.5.5",
@@ -15271,7 +14899,7 @@
         "performance-now": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
-            "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+            "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
         },
         "picocolors": {
             "version": "1.0.0",
@@ -15281,24 +14909,22 @@
         "picomatch": {
             "version": "2.3.1",
             "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
-            "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
-            "dev": true
+            "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
         },
         "pify": {
-            "version": "4.0.1",
-            "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
-            "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
-            "dev": true
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+            "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="
         },
         "pinkie": {
             "version": "2.0.4",
             "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
-            "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA="
+            "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg=="
         },
         "pinkie-promise": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
-            "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+            "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==",
             "requires": {
                 "pinkie": "^2.0.0"
             }
@@ -15309,56 +14935,42 @@
             "integrity": "sha512-6Rtbp7criZRwedlvWbUYxqlqJoAlMvYHo2UcRWq79xZ54vZcaNHpVBOcWkX3ErT2aUA69tv+uiv4zKJbhD/Wgg=="
         },
         "pkg-dir": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
-            "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
-            "dev": true,
+            "version": "4.2.0",
+            "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+            "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
             "requires": {
-                "find-up": "^2.1.0"
+                "find-up": "^4.0.0"
             },
             "dependencies": {
-                "find-up": {
-                    "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
-                    "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
-                    "dev": true,
+                "find-up": {
+                    "version": "4.1.0",
+                    "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+                    "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
                     "requires": {
-                        "locate-path": "^2.0.0"
+                        "locate-path": "^5.0.0",
+                        "path-exists": "^4.0.0"
                     }
                 },
                 "locate-path": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
-                    "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
-                    "dev": true,
-                    "requires": {
-                        "p-locate": "^2.0.0",
-                        "path-exists": "^3.0.0"
-                    }
-                },
-                "p-limit": {
-                    "version": "1.3.0",
-                    "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
-                    "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
-                    "dev": true,
+                    "version": "5.0.0",
+                    "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+                    "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
                     "requires": {
-                        "p-try": "^1.0.0"
+                        "p-locate": "^4.1.0"
                     }
                 },
                 "p-locate": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
-                    "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
-                    "dev": true,
+                    "version": "4.1.0",
+                    "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+                    "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
                     "requires": {
-                        "p-limit": "^1.1.0"
+                        "p-limit": "^2.2.0"
                     }
                 },
-                "p-try": {
-                    "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
-                    "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
-                    "dev": true
+                "path-exists": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+                    "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
                 }
             }
         },
@@ -15373,7 +14985,7 @@
         "plist": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/plist/-/plist-1.2.0.tgz",
-            "integrity": "sha1-CEtQk93JJQbiWfh0uNmxr7jHlZM=",
+            "integrity": "sha512-dL9Xc2Aj3YyBnwvCNuHmFl2LWvQacm/HEAsoVwLiuu0POboMChETt5wexpU1P6F6MnibIucXlVsMFFgNUT2IyA==",
             "requires": {
                 "base64-js": "0.0.8",
                 "util-deprecate": "1.0.2",
@@ -15384,7 +14996,7 @@
                 "base64-js": {
                     "version": "0.0.8",
                     "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
-                    "integrity": "sha1-EQHpVE9KdrG8OybUUsqW16NeeXg="
+                    "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw=="
                 }
             }
         },
@@ -15411,12 +15023,12 @@
             },
             "dependencies": {
                 "mkdirp": {
-                    "version": "0.5.5",
-                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
-                    "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+                    "version": "0.5.6",
+                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+                    "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
                     "dev": true,
                     "requires": {
-                        "minimist": "^1.2.5"
+                        "minimist": "^1.2.6"
                     }
                 }
             }
@@ -15424,33 +15036,29 @@
         "posix-character-classes": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
-            "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs="
+            "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg=="
         },
         "postcss": {
-            "version": "7.0.27",
-            "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz",
-            "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==",
+            "version": "7.0.39",
+            "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+            "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
             "dev": true,
             "requires": {
-                "chalk": "^2.4.2",
-                "source-map": "^0.6.1",
-                "supports-color": "^6.1.0"
+                "picocolors": "^0.2.1",
+                "source-map": "^0.6.1"
             },
             "dependencies": {
+                "picocolors": {
+                    "version": "0.2.1",
+                    "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+                    "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+                    "dev": true
+                },
                 "source-map": {
                     "version": "0.6.1",
                     "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
                     "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
                     "dev": true
-                },
-                "supports-color": {
-                    "version": "6.1.0",
-                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
-                    "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
-                    "dev": true,
-                    "requires": {
-                        "has-flag": "^3.0.0"
-                    }
                 }
             }
         },
@@ -15495,14 +15103,13 @@
             }
         },
         "postcss-selector-parser": {
-            "version": "6.0.2",
-            "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
-            "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
+            "version": "6.0.10",
+            "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
+            "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
             "dev": true,
             "requires": {
                 "cssesc": "^3.0.0",
-                "indexes-of": "^1.0.1",
-                "uniq": "^1.0.1"
+                "util-deprecate": "^1.0.2"
             }
         },
         "postcss-value-parser": {
@@ -15511,15 +15118,15 @@
             "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
         },
         "prebuild-install": {
-            "version": "5.3.3",
-            "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.3.tgz",
-            "integrity": "sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g==",
+            "version": "5.3.6",
+            "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.6.tgz",
+            "integrity": "sha512-s8Aai8++QQGi4sSbs/M1Qku62PFK49Jm1CbgXklGz4nmHveDq0wzJkg7Na5QbnO1uNH8K7iqx2EQ/mV0MZEmOg==",
             "requires": {
                 "detect-libc": "^1.0.3",
                 "expand-template": "^2.0.3",
                 "github-from-package": "0.0.0",
-                "minimist": "^1.2.0",
-                "mkdirp": "^0.5.1",
+                "minimist": "^1.2.3",
+                "mkdirp-classic": "^0.5.3",
                 "napi-build-utils": "^1.0.1",
                 "node-abi": "^2.7.0",
                 "noop-logger": "^0.1.1",
@@ -15530,18 +15137,110 @@
                 "tar-fs": "^2.0.0",
                 "tunnel-agent": "^0.6.0",
                 "which-pm-runs": "^1.0.0"
+            },
+            "dependencies": {
+                "ansi-regex": {
+                    "version": "2.1.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+                    "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="
+                },
+                "aproba": {
+                    "version": "1.2.0",
+                    "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+                    "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+                },
+                "are-we-there-yet": {
+                    "version": "1.1.7",
+                    "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
+                    "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
+                    "requires": {
+                        "delegates": "^1.0.0",
+                        "readable-stream": "^2.0.6"
+                    }
+                },
+                "detect-libc": {
+                    "version": "1.0.3",
+                    "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+                    "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="
+                },
+                "gauge": {
+                    "version": "2.7.4",
+                    "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+                    "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==",
+                    "requires": {
+                        "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"
+                    }
+                },
+                "is-fullwidth-code-point": {
+                    "version": "1.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+                    "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
+                    "requires": {
+                        "number-is-nan": "^1.0.0"
+                    }
+                },
+                "npmlog": {
+                    "version": "4.1.2",
+                    "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.2",
+                        "console-control-strings": "~1.1.0",
+                        "gauge": "~2.7.3",
+                        "set-blocking": "~2.0.0"
+                    }
+                },
+                "readable-stream": {
+                    "version": "2.3.7",
+                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+                    "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+                    "requires": {
+                        "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-width": {
+                    "version": "1.0.2",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+                    "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+                    "requires": {
+                        "code-point-at": "^1.0.0",
+                        "is-fullwidth-code-point": "^1.0.0",
+                        "strip-ansi": "^3.0.0"
+                    }
+                },
+                "strip-ansi": {
+                    "version": "3.0.1",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+                    "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+                    "requires": {
+                        "ansi-regex": "^2.0.0"
+                    }
+                }
             }
         },
         "prelude-ls": {
             "version": "1.1.2",
             "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
-            "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+            "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==",
             "dev": true
         },
         "prepend-http": {
             "version": "1.0.4",
             "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
-            "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw="
+            "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg=="
         },
         "pretty-error": {
             "version": "4.0.0",
@@ -15575,7 +15274,7 @@
         "process": {
             "version": "0.11.10",
             "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
-            "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
+            "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="
         },
         "process-nextick-args": {
             "version": "2.0.1",
@@ -15598,23 +15297,23 @@
         "promise-inflight": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
-            "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
+            "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==",
             "dev": true
         },
         "prop-types": {
-            "version": "15.7.2",
-            "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
-            "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
+            "version": "15.8.1",
+            "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+            "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
             "requires": {
                 "loose-envify": "^1.4.0",
                 "object-assign": "^4.1.1",
-                "react-is": "^16.8.1"
+                "react-is": "^16.13.1"
             }
         },
         "prosemirror-commands": {
-            "version": "1.2.2",
-            "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.2.2.tgz",
-            "integrity": "sha512-TX+KpWudMon06frryfpO/u7hsQv2hu8L4VSVbCpi3/7wXHBgl+35mV85qfa3RpT8xD2f3MdeoTqH0vy5JdbXPg==",
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.3.0.tgz",
+            "integrity": "sha512-BwBbZ5OAScPcm0x7H8SPbqjuEJnCU2RJT9LDyOiiIl/3NbL1nJZI4SFNHwU2e/tRr2Xe7JsptpzseqvZvToLBQ==",
             "requires": {
                 "prosemirror-model": "^1.0.0",
                 "prosemirror-state": "^1.0.0",
@@ -15644,12 +15343,12 @@
         "prosemirror-find-replace": {
             "version": "0.9.0",
             "resolved": "https://registry.npmjs.org/prosemirror-find-replace/-/prosemirror-find-replace-0.9.0.tgz",
-            "integrity": "sha1-QgsENNF5xdBJD44hSNhVGpVJY4I="
+            "integrity": "sha512-LfhQ/Zr0PkkJpCsr9vTJ5ZPYh49mSVVG+hHJ6djT+chlCW+t2ilSxBpBG+2IeE/I5nlbcvuLLAbxeI1g3pTCpA=="
         },
         "prosemirror-history": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.2.0.tgz",
-            "integrity": "sha512-B9v9xtf4fYbKxQwIr+3wtTDNLDZcmMMmGiI3TAPShnUzvo+Rmv1GiUrsQChY1meetHl7rhML2cppF3FTs7f7UQ==",
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.3.0.tgz",
+            "integrity": "sha512-qo/9Wn4B/Bq89/YD+eNWFbAytu6dmIM85EhID+fz9Jcl9+DfGEo8TTSrRhP15+fFEoaPqpHSxlvSzSEbmlxlUA==",
             "requires": {
                 "prosemirror-state": "^1.2.2",
                 "prosemirror-transform": "^1.0.0",
@@ -15657,61 +15356,62 @@
             }
         },
         "prosemirror-inputrules": {
-            "version": "1.1.3",
-            "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.1.3.tgz",
-            "integrity": "sha512-ZaHCLyBtvbyIHv0f5p6boQTIJjlD6o2NPZiEaZWT2DA+j591zS29QQEMT4lBqwcLW3qRSf7ZvoKNbf05YrsStw==",
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.2.0.tgz",
+            "integrity": "sha512-eAW/M/NTSSzpCOxfR8Abw6OagdG0MiDAiWHQMQveIsZtoKVYzm0AflSPq/ymqJd56/Su1YPbwy9lM13wgHOFmQ==",
             "requires": {
                 "prosemirror-state": "^1.0.0",
                 "prosemirror-transform": "^1.0.0"
             }
         },
         "prosemirror-keymap": {
-            "version": "1.1.5",
-            "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.1.5.tgz",
-            "integrity": "sha512-8SZgPH3K+GLsHL2wKuwBD9rxhsbnVBTwpHCO4VUO5GmqUQlxd/2GtBVWTsyLq4Dp3N9nGgPd3+lZFKUDuVp+Vw==",
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.0.tgz",
+            "integrity": "sha512-TdSfu+YyLDd54ufN/ZeD1VtBRYpgZnTPnnbY+4R08DDgs84KrIPEPbJL8t1Lm2dkljFx6xeBE26YWH3aIzkPKg==",
             "requires": {
                 "prosemirror-state": "^1.0.0",
                 "w3c-keyname": "^2.2.0"
             }
         },
         "prosemirror-model": {
-            "version": "1.16.1",
-            "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.16.1.tgz",
-            "integrity": "sha512-r1/w0HDU40TtkXp0DyKBnFPYwd8FSlUSJmGCGFv4DeynfeSlyQF2FD0RQbVEMOe6P3PpUSXM6LZBV7W/YNZ4mA==",
+            "version": "1.18.0",
+            "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.18.0.tgz",
+            "integrity": "sha512-EZLl0mvVIixEL1V538GsPP4Iw+D83gBRdLcjL7VouzoPPWlhhv/0Y0V9ttT5lUhF7jieIrNHOa3XNFs1FwgrOw==",
             "requires": {
                 "orderedmap": "^1.1.0"
             }
         },
         "prosemirror-schema-list": {
-            "version": "1.1.6",
-            "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.1.6.tgz",
-            "integrity": "sha512-aFGEdaCWmJzouZ8DwedmvSsL50JpRkqhQ6tcpThwJONVVmCgI36LJHtoQ4VGZbusMavaBhXXr33zyD2IVsTlkw==",
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.2.0.tgz",
+            "integrity": "sha512-8PT/9xOx1HHdC7fDNNfhQ50Z8Mzu7nKyA1KCDltSpcZVZIbB0k7KtsHrnXyuIhbLlScoymBiLZ00c5MH6wdFsA==",
             "requires": {
                 "prosemirror-model": "^1.0.0",
+                "prosemirror-state": "^1.0.0",
                 "prosemirror-transform": "^1.0.0"
             }
         },
         "prosemirror-state": {
-            "version": "1.3.4",
-            "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.3.4.tgz",
-            "integrity": "sha512-Xkkrpd1y/TQ6HKzN3agsQIGRcLckUMA9u3j207L04mt8ToRgpGeyhbVv0HI7omDORIBHjR29b7AwlATFFf2GLA==",
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.0.tgz",
+            "integrity": "sha512-mVDZdjNX/YT5FvypiwbphJe9psA5h+j9apsSszVRFc6oKFoIInvzdujh8QW9f9lwHtSYajLxNiM1hPhd0Sl1XA==",
             "requires": {
                 "prosemirror-model": "^1.0.0",
                 "prosemirror-transform": "^1.0.0"
             }
         },
         "prosemirror-transform": {
-            "version": "1.3.4",
-            "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.3.4.tgz",
-            "integrity": "sha512-gTsg3UIeaFuEY6+YmNPMgTpEkCKPedkFIUnsPpOMbclU701fEVI/e4VOXACXh3BO5rZJaBbEBwrnzB0mLp6eBA==",
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.6.0.tgz",
+            "integrity": "sha512-MAp7AjsjEGEqQY0sSMufNIUuEyB1ZR9Fqlm8dTwwWwpEJRv/plsKjWXBbx52q3Ml8MtaMcd7ic14zAHVB3WaMw==",
             "requires": {
                 "prosemirror-model": "^1.0.0"
             }
         },
         "prosemirror-view": {
-            "version": "1.23.9",
-            "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.23.9.tgz",
-            "integrity": "sha512-4ft8PuzYPLqQlYjkW7+jy3drquLrzB0cXu6bXyEgBEYnbv/uI/zBF1rGxmSZCDKwteKm1Wn24B+BSheSw/9sOA==",
+            "version": "1.26.0",
+            "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.26.0.tgz",
+            "integrity": "sha512-tT8ICUAw6YfOTRIwVQJoCdF65BYxoWFjAqmv44Kn4phYjdLJ8Vc92dhL3LnmwAQuZ9yfPHzRR6Lfb3/5EVJHWg==",
             "requires": {
                 "prosemirror-model": "^1.16.0",
                 "prosemirror-state": "^1.0.0",
@@ -15735,13 +15435,13 @@
         "prr": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
-            "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
+            "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
             "dev": true
         },
         "pseudomap": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
-            "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
+            "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ=="
         },
         "psl": {
             "version": "1.8.0",
@@ -15749,9 +15449,9 @@
             "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
         },
         "pstree.remy": {
-            "version": "1.1.7",
-            "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.7.tgz",
-            "integrity": "sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A=="
+            "version": "1.1.8",
+            "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
+            "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w=="
         },
         "pug": {
             "version": "2.0.4",
@@ -15779,9 +15479,9 @@
             }
         },
         "pug-code-gen": {
-            "version": "2.0.2",
-            "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-2.0.2.tgz",
-            "integrity": "sha512-kROFWv/AHx/9CRgoGJeRSm+4mLWchbgpRzTEn8XCiwwOy6Vh0gAClS8Vh5TEJ9DBjaP8wCjS3J6HKsEsYdvaCw==",
+            "version": "2.0.3",
+            "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-2.0.3.tgz",
+            "integrity": "sha512-r9sezXdDuZJfW9J91TN/2LFbiqDhmltTFmGpHTsGdrNGp3p4SxAjjXEfnuK2e4ywYsRIVP0NeLbSAMHUcaX1EA==",
             "requires": {
                 "constantinople": "^3.1.2",
                 "doctypes": "^1.1.0",
@@ -15810,6 +15510,21 @@
                 "pug-walk": "^1.1.8",
                 "resolve": "^1.1.6",
                 "uglify-js": "^2.6.1"
+            },
+            "dependencies": {
+                "clean-css": {
+                    "version": "4.2.4",
+                    "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz",
+                    "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==",
+                    "requires": {
+                        "source-map": "~0.6.0"
+                    }
+                },
+                "source-map": {
+                    "version": "0.6.1",
+                    "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+                    "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+                }
             }
         },
         "pug-lexer": {
@@ -15927,11 +15642,11 @@
                     "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g=="
                 },
                 "debug": {
-                    "version": "4.1.1",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
-                    "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+                    "version": "4.3.4",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
                     "requires": {
-                        "ms": "^2.1.1"
+                        "ms": "2.1.2"
                     }
                 },
                 "https-proxy-agent": {
@@ -15944,26 +15659,31 @@
                     }
                 },
                 "mime": {
-                    "version": "2.4.6",
-                    "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
-                    "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA=="
+                    "version": "2.6.0",
+                    "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+                    "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="
+                },
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
                 }
             }
         },
         "pure-color": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/pure-color/-/pure-color-1.3.0.tgz",
-            "integrity": "sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4="
+            "integrity": "sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA=="
         },
         "q": {
             "version": "1.5.1",
             "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
-            "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc="
+            "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw=="
         },
         "qs": {
-            "version": "6.5.2",
-            "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
-            "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
+            "version": "6.5.3",
+            "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
+            "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA=="
         },
         "query-string": {
             "version": "6.14.1",
@@ -15979,7 +15699,7 @@
         "querystring": {
             "version": "0.2.0",
             "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
-            "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
+            "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==",
             "dev": true
         },
         "querystringify": {
@@ -16001,7 +15721,7 @@
         "random-bytes": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
-            "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs="
+            "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ=="
         },
         "randombytes": {
             "version": "2.1.0",
@@ -16018,14 +15738,24 @@
             "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
         },
         "raw-body": {
-            "version": "2.4.3",
-            "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz",
-            "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==",
+            "version": "2.5.1",
+            "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
+            "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
             "requires": {
                 "bytes": "3.1.2",
-                "http-errors": "1.8.1",
+                "http-errors": "2.0.0",
                 "iconv-lite": "0.4.24",
                 "unpipe": "1.0.0"
+            },
+            "dependencies": {
+                "iconv-lite": {
+                    "version": "0.4.24",
+                    "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+                    "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+                    "requires": {
+                        "safer-buffer": ">= 2.1.2 < 3"
+                    }
+                }
             }
         },
         "raw-loader": {
@@ -16035,18 +15765,6 @@
             "requires": {
                 "loader-utils": "^1.1.0",
                 "schema-utils": "^1.0.0"
-            },
-            "dependencies": {
-                "schema-utils": {
-                    "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
-                    "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
-                    "requires": {
-                        "ajv": "^6.1.0",
-                        "ajv-errors": "^1.0.0",
-                        "ajv-keywords": "^3.1.0"
-                    }
-                }
             }
         },
         "rc": {
@@ -16108,7 +15826,7 @@
         "react-base16-styling": {
             "version": "0.5.3",
             "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.5.3.tgz",
-            "integrity": "sha1-OFjyTpxN2MvT9wLz901YHKKRcmk=",
+            "integrity": "sha512-EPuchwVvYPSFFIjGpH0k6wM0HQsmJ0vCk7BSl5ryxMVFIWW4hX4Kksu4PNtxfgOxDebTLkJQ8iC7zwAql0eusg==",
             "requires": {
                 "base16": "^1.0.0",
                 "lodash.curry": "^4.0.1",
@@ -16153,6 +15871,13 @@
                 "d3-array": "^1.2.4",
                 "prop-types": "^15.7.2",
                 "warning": "^3.0.0"
+            },
+            "dependencies": {
+                "d3-array": {
+                    "version": "1.2.4",
+                    "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
+                    "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
+                }
             }
         },
         "react-datepicker": {
@@ -16170,7 +15895,7 @@
         "react-dock": {
             "version": "0.2.4",
             "resolved": "https://registry.npmjs.org/react-dock/-/react-dock-0.2.4.tgz",
-            "integrity": "sha1-5yfcdVCztzEWY13LnA4E0Lev4Xw=",
+            "integrity": "sha512-ywUJPC/TIM9PO700skka0fH4aqbrH8RojUXejZFvjtqlc5KZ+xjHqFdo4A3j+dp+0NLFZ3Nai4xzcf3FUJ9BsQ==",
             "requires": {
                 "lodash.debounce": "^3.1.1",
                 "prop-types": "^15.5.8"
@@ -16199,12 +15924,12 @@
             }
         },
         "react-draggable": {
-            "version": "4.4.3",
-            "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.3.tgz",
-            "integrity": "sha512-jV4TE59MBuWm7gb6Ns3Q1mxX8Azffb7oTtDtBgFkxRvhDp38YAARmRplrj0+XGkhOJB5XziArX+4HUUABtyZ0w==",
+            "version": "4.4.5",
+            "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.5.tgz",
+            "integrity": "sha512-OMHzJdyJbYTZo4uQE393fHcqqPYsEtkjfMgvCHr6rejT+Ezn4OZbNyGH50vv+SunC1RMvwOTSWkEODQLzw1M9g==",
             "requires": {
-                "classnames": "^2.2.5",
-                "prop-types": "^15.6.0"
+                "clsx": "^1.1.1",
+                "prop-types": "^15.8.1"
             }
         },
         "react-event-listener": {
@@ -16239,6 +15964,11 @@
                 "react-resizable": "^1.9.0"
             }
         },
+        "react-icons": {
+            "version": "4.4.0",
+            "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.4.0.tgz",
+            "integrity": "sha512-fSbvHeVYo/B5/L4VhB7sBA1i2tS8MkT0Hb9t2H1AVPkwGfVHLJCqyr2Py9dKMxsyM63Eng1GkdZfbWj+Fmv8Rg=="
+        },
         "react-image-lightbox-with-rotate": {
             "version": "5.1.1",
             "resolved": "https://registry.npmjs.org/react-image-lightbox-with-rotate/-/react-image-lightbox-with-rotate-5.1.1.tgz",
@@ -16287,9 +16017,9 @@
             },
             "dependencies": {
                 "@types/react": {
-                    "version": "17.0.40",
-                    "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.40.tgz",
-                    "integrity": "sha512-UrXhD/JyLH+W70nNSufXqMZNuUD2cXHu6UjCllC6pmOQgBX4SGXOH8fjRka0O0Ee0HrFxapDD8Bwn81Kmiz6jQ==",
+                    "version": "17.0.45",
+                    "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.45.tgz",
+                    "integrity": "sha512-YfhQ22Lah2e3CHPsb93tRwIGNiSwkuz1/blk4e6QrWS0jQzCSNbGLtOEYhPg02W0yGTTmpajp7dCTbBAMN3qsg==",
                     "optional": true,
                     "requires": {
                         "@types/prop-types": "*",
@@ -16298,28 +16028,23 @@
                     }
                 },
                 "@types/react-dom": {
-                    "version": "17.0.13",
-                    "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.13.tgz",
-                    "integrity": "sha512-wEP+B8hzvy6ORDv1QBhcQia4j6ea4SFIBttHYpXKPFZRviBvknq0FRh3VrIxeXUmsPkwuXVZrVGG7KUVONmXCQ==",
+                    "version": "17.0.17",
+                    "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.17.tgz",
+                    "integrity": "sha512-VjnqEmqGnasQKV0CWLevqMTXBYG9GbwuE6x3VetERLh0cq2LTptFE73MrQi2S7GkKXCf2GgwItB/melLnxfnsg==",
                     "optional": true,
                     "requires": {
-                        "@types/react": "*"
+                        "@types/react": "^17"
                     }
                 },
-                "acorn": {
-                    "version": "8.7.0",
-                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
-                    "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ=="
-                },
                 "core-js": {
-                    "version": "3.21.1",
-                    "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.21.1.tgz",
-                    "integrity": "sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig=="
+                    "version": "3.22.8",
+                    "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.22.8.tgz",
+                    "integrity": "sha512-UoGQ/cfzGYIuiq6Z7vWL1HfkE9U9IZ4Ub+0XSiJTCzvbZzgPA69oDF2f+lgJ6dFFLEdjW5O6svvoKzXX23xFkA=="
                 },
                 "csstype": {
-                    "version": "3.0.11",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
-                    "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==",
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
                     "optional": true
                 }
             }
@@ -16351,12 +16076,12 @@
             "integrity": "sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ=="
         },
         "react-modal": {
-            "version": "3.11.2",
-            "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.11.2.tgz",
-            "integrity": "sha512-o8gvvCOFaG1T7W6JUvsYjRjMVToLZgLIsi5kdhFIQCtHxDkA47LznX62j+l6YQkpXDbvQegsDyxe/+JJsFQN7w==",
+            "version": "3.15.1",
+            "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.15.1.tgz",
+            "integrity": "sha512-duB9bxOaYg7Zt6TMFldIFxQRtSP+Dg3F1ZX3FXxSUn+3tZZ/9JCgeAQKDg7rhZSAqopq8TFRw3yIbnx77gyFTw==",
             "requires": {
                 "exenv": "^1.2.0",
-                "prop-types": "^15.5.10",
+                "prop-types": "^15.7.2",
                 "react-lifecycles-compat": "^3.0.0",
                 "warning": "^4.0.3"
             },
@@ -16372,9 +16097,9 @@
             }
         },
         "react-onclickoutside": {
-            "version": "6.12.1",
-            "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.12.1.tgz",
-            "integrity": "sha512-a5Q7CkWznBRUWPmocCvE8b6lEYw1s6+opp/60dCunhO+G6E4tDTO2Sd2jKE+leEnnrLAE2Wj5DlDHNqj5wPv1Q=="
+            "version": "6.12.2",
+            "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.12.2.tgz",
+            "integrity": "sha512-NMXGa223OnsrGVp5dJHkuKxQ4czdLmXSp5jSV9OqiCky9LOpPATn3vLldc+q5fK3gKbEHvr7J1u0yhBh/xYkpA=="
         },
         "react-popper": {
             "version": "1.3.11",
@@ -16416,9 +16141,9 @@
             }
         },
         "react-redux": {
-            "version": "7.2.6",
-            "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz",
-            "integrity": "sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==",
+            "version": "7.2.8",
+            "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.8.tgz",
+            "integrity": "sha512-6+uDjhs3PSIclqoCk0kd6iX74gzrGc3W5zcAjbrFgEdIjRSQObdIwfx80unTkVUYvbQ95Y8Av3OvFHq1w5EOUw==",
             "requires": {
                 "@babel/runtime": "^7.15.4",
                 "@types/react-redux": "^7.1.20",
@@ -16428,14 +16153,6 @@
                 "react-is": "^17.0.2"
             },
             "dependencies": {
-                "@babel/runtime": {
-                    "version": "7.17.7",
-                    "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.7.tgz",
-                    "integrity": "sha512-L6rvG9GDxaLgFjg41K+5Yv9OMrU98sWe+Ykmc6FDJW/+vYZMhdOMKkISgzptMaERHvS2Y2lw9MDRm2gHhlQQoA==",
-                    "requires": {
-                        "regenerator-runtime": "^0.13.4"
-                    }
-                },
                 "react-is": {
                     "version": "17.0.2",
                     "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
@@ -16444,9 +16161,9 @@
             }
         },
         "react-refresh-typescript": {
-            "version": "2.0.3",
-            "resolved": "https://registry.npmjs.org/react-refresh-typescript/-/react-refresh-typescript-2.0.3.tgz",
-            "integrity": "sha512-CnR+UkpCFUIxW3u+KPxc6UhSO3M3aQxd9XCvgdry4a22LFwwITnjbANFUKtPGxA/Skw3PbZJrLiZ08UQnpXprQ=="
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/react-refresh-typescript/-/react-refresh-typescript-2.0.5.tgz",
+            "integrity": "sha512-TlcxF9VMBBS8dHvMD+3dklgArXeIhjbtT7R09ycsXvhPPYmv70J67kiGT8mxe6/tYzkql9aCHaZAY6QYdutLdw=="
         },
         "react-resizable": {
             "version": "1.11.1",
@@ -16496,9 +16213,9 @@
                     }
                 },
                 "csstype": {
-                    "version": "3.0.11",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
-                    "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
                 },
                 "dom-helpers": {
                     "version": "5.2.1",
@@ -16535,7 +16252,7 @@
         "react-themeable": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/react-themeable/-/react-themeable-1.1.0.tgz",
-            "integrity": "sha1-fURm3ZsrX6dQWHJ4JenxUro3mg4=",
+            "integrity": "sha512-kl5tQ8K+r9IdQXZd8WLa+xxYN04lLnJXRVhHfdgwsUJr/SlKJxIejoc9z9obEkx1mdqbTw1ry43fxEUwyD9u7w==",
             "requires": {
                 "object-assign": "^3.0.0"
             },
@@ -16543,7 +16260,7 @@
                 "object-assign": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz",
-                    "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I="
+                    "integrity": "sha512-jHP15vXVGeVh1HuaA2wY6lxk+whK/x4KBG88VXeRma7CCun7iGD5qPc4eYykQ9sdQvg8jkwFKsSxHln2ybW3xQ=="
                 }
             }
         },
@@ -16582,7 +16299,7 @@
         "read-pkg": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
-            "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
+            "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==",
             "requires": {
                 "load-json-file": "^1.0.0",
                 "normalize-package-data": "^2.3.2",
@@ -16592,24 +16309,19 @@
                 "path-type": {
                     "version": "1.1.0",
                     "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
-                    "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
+                    "integrity": "sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==",
                     "requires": {
                         "graceful-fs": "^4.1.2",
                         "pify": "^2.0.0",
                         "pinkie-promise": "^2.0.0"
                     }
-                },
-                "pify": {
-                    "version": "2.3.0",
-                    "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-                    "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
                 }
             }
         },
         "read-pkg-up": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
-            "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
+            "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==",
             "requires": {
                 "find-up": "^1.0.0",
                 "read-pkg": "^1.0.0"
@@ -16618,7 +16330,7 @@
                 "find-up": {
                     "version": "1.1.2",
                     "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
-                    "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+                    "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==",
                     "requires": {
                         "path-exists": "^2.0.0",
                         "pinkie-promise": "^2.0.0"
@@ -16627,7 +16339,7 @@
                 "path-exists": {
                     "version": "2.1.0",
                     "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
-                    "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+                    "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==",
                     "requires": {
                         "pinkie-promise": "^2.0.0"
                     }
@@ -16673,12 +16385,12 @@
         "readline": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz",
-            "integrity": "sha1-xYDXfvLPyHUrEySYBg3JeTp6wBw="
+            "integrity": "sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg=="
         },
         "rechoir": {
             "version": "0.6.2",
             "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
-            "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
+            "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==",
             "requires": {
                 "resolve": "^1.1.6"
             }
@@ -16704,7 +16416,7 @@
         "redent": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
-            "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
+            "integrity": "sha512-qtW5hKzGQZqKoh6JNSD+4lfitfPKGz42e6QwiRmPM5mmKtR0N41AbJRYu0xJi7nhOJ4WDgRkKvAk6tw4WIwR4g==",
             "requires": {
                 "indent-string": "^2.1.0",
                 "strip-indent": "^1.0.1"
@@ -16713,20 +16425,20 @@
         "reduce-flatten": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz",
-            "integrity": "sha1-JYx479FT3fk8tWEjf2EYTzaW4yc="
+            "integrity": "sha512-j5WfFJfc9CoXv/WbwVLHq74i/hdTUpy+iNC534LxczMRP67vJeK3V9JOdnL0N1cIRbn9mYhE2yVjvvKXDxvNXQ=="
         },
         "redux": {
-            "version": "4.1.2",
-            "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz",
-            "integrity": "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==",
+            "version": "4.2.0",
+            "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz",
+            "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==",
             "requires": {
                 "@babel/runtime": "^7.9.2"
             }
         },
         "regenerator-runtime": {
-            "version": "0.13.5",
-            "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
-            "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
+            "version": "0.13.9",
+            "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
+            "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
         },
         "regex-not": {
             "version": "1.0.2",
@@ -16743,12 +16455,13 @@
             "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw=="
         },
         "regexp.prototype.flags": {
-            "version": "1.4.1",
-            "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz",
-            "integrity": "sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ==",
+            "version": "1.4.3",
+            "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
+            "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
             "requires": {
                 "call-bind": "^1.0.2",
-                "define-properties": "^1.1.3"
+                "define-properties": "^1.1.3",
+                "functions-have-names": "^1.2.2"
             }
         },
         "registry-auth-token": {
@@ -16763,7 +16476,7 @@
         "registry-url": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
-            "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=",
+            "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==",
             "requires": {
                 "rc": "^1.0.1"
             }
@@ -16771,12 +16484,12 @@
         "relateurl": {
             "version": "0.2.7",
             "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
-            "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk="
+            "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog=="
         },
         "remove-trailing-separator": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
-            "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8="
+            "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw=="
         },
         "renderkid": {
             "version": "3.0.0",
@@ -16796,9 +16509,9 @@
                     "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
                 },
                 "dom-serializer": {
-                    "version": "1.3.2",
-                    "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
-                    "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==",
+                    "version": "1.4.1",
+                    "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
+                    "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
                     "requires": {
                         "domelementtype": "^2.0.1",
                         "domhandler": "^4.2.0",
@@ -16806,14 +16519,14 @@
                     }
                 },
                 "domelementtype": {
-                    "version": "2.2.0",
-                    "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz",
-                    "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
+                    "version": "2.3.0",
+                    "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+                    "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
                 },
                 "domhandler": {
-                    "version": "4.3.0",
-                    "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz",
-                    "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==",
+                    "version": "4.3.1",
+                    "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
+                    "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
                     "requires": {
                         "domelementtype": "^2.2.0"
                     }
@@ -16855,19 +16568,19 @@
             }
         },
         "repeat-element": {
-            "version": "1.1.3",
-            "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
-            "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g=="
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
+            "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ=="
         },
         "repeat-string": {
             "version": "1.6.1",
             "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
-            "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
+            "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w=="
         },
         "repeating": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
-            "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
+            "integrity": "sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==",
             "requires": {
                 "is-finite": "^1.0.0"
             }
@@ -16899,16 +16612,6 @@
                 "uuid": "^3.3.2"
             },
             "dependencies": {
-                "form-data": {
-                    "version": "2.3.3",
-                    "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.6",
-                        "mime-types": "^2.1.12"
-                    }
-                },
                 "tough-cookie": {
                     "version": "2.5.0",
                     "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
@@ -16931,14 +16634,6 @@
                 "tough-cookie": "^2.3.3"
             },
             "dependencies": {
-                "request-promise-core": {
-                    "version": "1.1.4",
-                    "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
-                    "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
-                    "requires": {
-                        "lodash": "^4.17.19"
-                    }
-                },
                 "tough-cookie": {
                     "version": "2.5.0",
                     "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
@@ -16951,21 +16646,20 @@
             }
         },
         "request-promise-core": {
-            "version": "1.1.3",
-            "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz",
-            "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==",
-            "dev": true,
+            "version": "1.1.4",
+            "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
+            "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
             "requires": {
-                "lodash": "^4.17.15"
+                "lodash": "^4.17.19"
             }
         },
         "request-promise-native": {
-            "version": "1.0.8",
-            "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz",
-            "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==",
+            "version": "1.0.9",
+            "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz",
+            "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==",
             "dev": true,
             "requires": {
-                "request-promise-core": "1.1.3",
+                "request-promise-core": "1.1.4",
                 "stealthy-require": "^1.1.1",
                 "tough-cookie": "^2.3.3"
             },
@@ -16990,7 +16684,7 @@
         "require-directory": {
             "version": "2.1.1",
             "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-            "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
+            "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
         },
         "require-from-string": {
             "version": "2.0.2",
@@ -17005,7 +16699,7 @@
         "require-package-name": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/require-package-name/-/require-package-name-2.0.1.tgz",
-            "integrity": "sha1-wR6XJ2tluOKSP3Xav1+y7ww4Qbk="
+            "integrity": "sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q=="
         },
         "require_optional": {
             "version": "1.0.1",
@@ -17019,14 +16713,14 @@
                 "resolve-from": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
-                    "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c="
+                    "integrity": "sha512-qpFcKaXsq8+oRoLilkwyc7zHGF5i9Q2/25NIgLQQ/+VVv9rU4qvr6nXVAw1DsnXJyQkZsR4Ytfbtg5ehfcUssQ=="
                 }
             }
         },
         "requires-port": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
-            "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
+            "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
             "dev": true
         },
         "resize-observer-polyfill": {
@@ -17035,11 +16729,13 @@
             "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
         },
         "resolve": {
-            "version": "1.17.0",
-            "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
-            "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+            "version": "1.22.0",
+            "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
+            "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
             "requires": {
-                "path-parse": "^1.0.6"
+                "is-core-module": "^2.8.1",
+                "path-parse": "^1.0.7",
+                "supports-preserve-symlinks-flag": "^1.0.0"
             }
         },
         "resolve-alpn": {
@@ -17070,7 +16766,7 @@
         "resolve-url": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
-            "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="
+            "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg=="
         },
         "responselike": {
             "version": "2.0.0",
@@ -17095,18 +16791,18 @@
         "retry": {
             "version": "0.12.0",
             "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
-            "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
+            "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
             "dev": true
         },
         "reveal.js": {
-            "version": "4.3.0",
-            "resolved": "https://registry.npmjs.org/reveal.js/-/reveal.js-4.3.0.tgz",
-            "integrity": "sha512-KzZxJjj1gmxVNyplY6g9MiGwtDvZJiYkMvG1Qmaita7vWT/8eoTEK+RuIPLvxDeyxOtTz56u2wrOETVO79qL4A=="
+            "version": "4.3.1",
+            "resolved": "https://registry.npmjs.org/reveal.js/-/reveal.js-4.3.1.tgz",
+            "integrity": "sha512-1kyEnWeUkaCdBdX//XXq9dtBK95ppvIlSwlHelrP8/wrX6LcsYp4HT9WTFoFEOUBfVqkm8C2aHQ367o+UKfcxw=="
         },
         "right-align": {
             "version": "0.1.3",
             "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
-            "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
+            "integrity": "sha512-yqINtL/G7vs2v+dFIZmFUDbnVyFUJFKd6gK22Kgo6R4jfJGFtisKyncWDDULgjfqf4ASQuIQyjJ7XZ+3aWpsAg==",
             "requires": {
                 "align-text": "^0.1.1"
             }
@@ -17120,9 +16816,9 @@
             }
         },
         "rope-sequence": {
-            "version": "1.3.2",
-            "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.2.tgz",
-            "integrity": "sha512-ku6MFrwEVSVmXLvy3dYph3LAMNS0890K7fabn+0YIRQ2T96T9F4gkFf0vf0WW0JUraNWwGRtInEpH7yO4tbQZg=="
+            "version": "1.3.3",
+            "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.3.tgz",
+            "integrity": "sha512-85aZYCxweiD5J8yTEbw+E6A27zSnLPNDL0WfPdw3YYodq7WjnTKo0q4dtyQ2gz23iPT8Q9CUyJtAaUNcTxRf5Q=="
         },
         "rtcpeerconnection-shim": {
             "version": "1.2.15",
@@ -17135,10 +16831,18 @@
         "run-queue": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
-            "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
+            "integrity": "sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==",
             "dev": true,
             "requires": {
                 "aproba": "^1.1.1"
+            },
+            "dependencies": {
+                "aproba": {
+                    "version": "1.2.0",
+                    "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+                    "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+                    "dev": true
+                }
             }
         },
         "safe-buffer": {
@@ -17149,7 +16853,7 @@
         "safe-regex": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
-            "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+            "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==",
             "requires": {
                 "ret": "~0.1.10"
             }
@@ -17192,6 +16896,12 @@
                 "semver": "^6.3.0"
             },
             "dependencies": {
+                "pify": {
+                    "version": "4.0.1",
+                    "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+                    "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+                    "dev": true
+                },
                 "semver": {
                     "version": "6.3.0",
                     "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
@@ -17218,16 +16928,26 @@
                 "object-assign": "^4.1.1"
             }
         },
+        "schema-utils": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+            "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+            "requires": {
+                "ajv": "^6.1.0",
+                "ajv-errors": "^1.0.0",
+                "ajv-keywords": "^3.1.0"
+            }
+        },
         "scss-loader": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/scss-loader/-/scss-loader-0.0.1.tgz",
-            "integrity": "sha1-6uAXueDzjBKlMtslwiC5Avs05nE=",
+            "integrity": "sha512-SbT/smRJjkvvdHSEdAYAplosVkrtaSwwgUlnQCOuDS5sOKNjrS/eYCMvKeV6+YxK5cCOCsOJZd3vltrXatFp+g==",
             "dev": true
         },
         "scss-tokenizer": {
             "version": "0.2.3",
             "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
-            "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=",
+            "integrity": "sha512-dYE8LhncfBUar6POCxMTm0Ln+erjeczqEvCJib5/7XNkdw1FkUGgwMPY360FY0FgPWQxHWCx29Jl3oejyGLM9Q==",
             "requires": {
                 "js-base64": "^2.1.8",
                 "source-map": "^0.4.2"
@@ -17236,7 +16956,7 @@
                 "source-map": {
                     "version": "0.4.4",
                     "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
-                    "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
+                    "integrity": "sha512-Y8nIfcb1s/7DcobUz1yOO1GSp7gyL+D9zLHDehT7iRESqGSxjJ448Sg7rvfgsRJCnKLdSl11uGf0s9X80cH0/A==",
                     "requires": {
                         "amdefine": ">=0.0.4"
                     }
@@ -17251,17 +16971,17 @@
         "section-iterator": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/section-iterator/-/section-iterator-2.0.0.tgz",
-            "integrity": "sha1-v0RNev7rlK1Dw5rS+yYVFifMuio="
+            "integrity": "sha512-xvTNwcbeDayXotnV32zLb3duQsP+4XosHpb/F+tu6VzEZFmIjzPdNk6/O+QOOx5XTh08KL2ufdXeCO33p380pQ=="
         },
         "select": {
             "version": "1.1.2",
             "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
-            "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
+            "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
         },
         "select-hose": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
-            "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=",
+            "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==",
             "dev": true
         },
         "selfsigned": {
@@ -17271,14 +16991,6 @@
             "dev": true,
             "requires": {
                 "node-forge": "^0.10.0"
-            },
-            "dependencies": {
-                "node-forge": {
-                    "version": "0.10.0",
-                    "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
-                    "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==",
-                    "dev": true
-                }
             }
         },
         "semver": {
@@ -17289,34 +17001,34 @@
         "semver-compare": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
-            "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w="
+            "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="
         },
         "semver-diff": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz",
-            "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=",
+            "integrity": "sha512-gL8F8L4ORwsS0+iQ34yCYv///jsOq0ZL7WP55d1HnJ32o7tyFYEFQZQA22mrLIacZdU6xecaBBZ+uEiffGNyXw==",
             "requires": {
                 "semver": "^5.0.3"
             }
         },
         "send": {
-            "version": "0.17.2",
-            "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz",
-            "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==",
+            "version": "0.18.0",
+            "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
+            "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
             "requires": {
                 "debug": "2.6.9",
-                "depd": "~1.1.2",
-                "destroy": "~1.0.4",
+                "depd": "2.0.0",
+                "destroy": "1.2.0",
                 "encodeurl": "~1.0.2",
                 "escape-html": "~1.0.3",
                 "etag": "~1.8.1",
                 "fresh": "0.5.2",
-                "http-errors": "1.8.1",
+                "http-errors": "2.0.0",
                 "mime": "1.6.0",
                 "ms": "2.1.3",
-                "on-finished": "~2.3.0",
+                "on-finished": "2.4.1",
                 "range-parser": "~1.2.1",
-                "statuses": "~1.5.0"
+                "statuses": "2.0.1"
             },
             "dependencies": {
                 "debug": {
@@ -17330,7 +17042,7 @@
                         "ms": {
                             "version": "2.0.0",
                             "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                            "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                            "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
                         }
                     }
                 },
@@ -17342,10 +17054,13 @@
             }
         },
         "serialize-javascript": {
-            "version": "1.9.1",
-            "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz",
-            "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==",
-            "dev": true
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+            "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+            "dev": true,
+            "requires": {
+                "randombytes": "^2.1.0"
+            }
         },
         "serializr": {
             "version": "1.5.4",
@@ -17355,7 +17070,7 @@
         "serve-index": {
             "version": "1.9.1",
             "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
-            "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=",
+            "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==",
             "dev": true,
             "requires": {
                 "accepts": "~1.3.4",
@@ -17376,10 +17091,16 @@
                         "ms": "2.0.0"
                     }
                 },
+                "depd": {
+                    "version": "1.1.2",
+                    "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+                    "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
+                    "dev": true
+                },
                 "http-errors": {
                     "version": "1.6.3",
                     "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
-                    "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
+                    "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==",
                     "dev": true,
                     "requires": {
                         "depd": "~1.1.2",
@@ -17391,13 +17112,13 @@
                 "inherits": {
                     "version": "2.0.3",
                     "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-                    "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+                    "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
                     "dev": true
                 },
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
                     "dev": true
                 },
                 "setprototypeof": {
@@ -17405,29 +17126,30 @@
                     "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
                     "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
                     "dev": true
+                },
+                "statuses": {
+                    "version": "1.5.0",
+                    "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+                    "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
+                    "dev": true
                 }
             }
         },
         "serve-static": {
-            "version": "1.14.2",
-            "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz",
-            "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==",
+            "version": "1.15.0",
+            "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
+            "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
             "requires": {
                 "encodeurl": "~1.0.2",
                 "escape-html": "~1.0.3",
                 "parseurl": "~1.3.3",
-                "send": "0.17.2"
+                "send": "0.18.0"
             }
         },
         "set-blocking": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-            "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
-        },
-        "set-immediate-shim": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
-            "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E="
+            "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
         },
         "set-value": {
             "version": "2.0.1",
@@ -17443,22 +17165,17 @@
                 "extend-shallow": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-                    "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+                    "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
                     "requires": {
                         "is-extendable": "^0.1.0"
                     }
-                },
-                "is-extendable": {
-                    "version": "0.1.1",
-                    "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
-                    "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
                 }
             }
         },
         "setimmediate": {
             "version": "1.0.5",
             "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
-            "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
+            "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
         },
         "setprototypeof": {
             "version": "1.2.0",
@@ -17494,29 +17211,89 @@
                 "tunnel-agent": "^0.6.0"
             },
             "dependencies": {
-                "fs-minipass": {
-                    "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
-                    "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+                "ansi-regex": {
+                    "version": "2.1.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+                    "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="
+                },
+                "aproba": {
+                    "version": "1.2.0",
+                    "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+                    "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
+                },
+                "are-we-there-yet": {
+                    "version": "1.1.7",
+                    "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz",
+                    "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==",
                     "requires": {
-                        "minipass": "^3.0.0"
+                        "delegates": "^1.0.0",
+                        "readable-stream": "^2.0.6"
                     }
                 },
-                "minipass": {
-                    "version": "3.1.1",
-                    "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.1.tgz",
-                    "integrity": "sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w==",
+                "chownr": {
+                    "version": "1.1.4",
+                    "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+                    "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
+                },
+                "detect-libc": {
+                    "version": "1.0.3",
+                    "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+                    "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="
+                },
+                "gauge": {
+                    "version": "2.7.4",
+                    "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
+                    "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==",
                     "requires": {
-                        "yallist": "^4.0.0"
+                        "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"
                     }
                 },
-                "minizlib": {
-                    "version": "2.1.0",
-                    "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.0.tgz",
-                    "integrity": "sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA==",
+                "is-fullwidth-code-point": {
+                    "version": "1.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+                    "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==",
                     "requires": {
-                        "minipass": "^3.0.0",
-                        "yallist": "^4.0.0"
+                        "number-is-nan": "^1.0.0"
+                    }
+                },
+                "mkdirp": {
+                    "version": "0.5.6",
+                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+                    "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+                    "requires": {
+                        "minimist": "^1.2.6"
+                    }
+                },
+                "npmlog": {
+                    "version": "4.1.2",
+                    "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.2",
+                        "console-control-strings": "~1.1.0",
+                        "gauge": "~2.7.3",
+                        "set-blocking": "~2.0.0"
+                    }
+                },
+                "readable-stream": {
+                    "version": "2.3.7",
+                    "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+                    "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+                    "requires": {
+                        "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"
                     }
                 },
                 "semver": {
@@ -17524,30 +17301,43 @@
                     "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
                     "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
                 },
+                "string-width": {
+                    "version": "1.0.2",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+                    "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+                    "requires": {
+                        "code-point-at": "^1.0.0",
+                        "is-fullwidth-code-point": "^1.0.0",
+                        "strip-ansi": "^3.0.0"
+                    }
+                },
+                "strip-ansi": {
+                    "version": "3.0.1",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+                    "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+                    "requires": {
+                        "ansi-regex": "^2.0.0"
+                    }
+                },
                 "tar": {
-                    "version": "5.0.5",
-                    "resolved": "https://registry.npmjs.org/tar/-/tar-5.0.5.tgz",
-                    "integrity": "sha512-MNIgJddrV2TkuwChwcSNds/5E9VijOiw7kAc1y5hTNJoLDSuIyid2QtLYiCYNnICebpuvjhPQZsXwUL0O3l7OQ==",
-                    "requires": {
-                        "chownr": "^1.1.3",
-                        "fs-minipass": "^2.0.0",
-                        "minipass": "^3.0.0",
-                        "minizlib": "^2.1.0",
-                        "mkdirp": "^0.5.0",
+                    "version": "5.0.11",
+                    "resolved": "https://registry.npmjs.org/tar/-/tar-5.0.11.tgz",
+                    "integrity": "sha512-E6q48d5y4XSCD+Xmwc0yc8lXuyDK38E0FB8N4S/drQRtXOMUhfhDxbB0xr2KKDhNfO51CFmoa6Oz00nAkWsjnA==",
+                    "requires": {
+                        "chownr": "^1.1.4",
+                        "fs-minipass": "^2.1.0",
+                        "minipass": "^3.1.3",
+                        "minizlib": "^2.1.2",
+                        "mkdirp": "^0.5.5",
                         "yallist": "^4.0.0"
                     }
-                },
-                "yallist": {
-                    "version": "4.0.0",
-                    "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-                    "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
                 }
             }
         },
         "shebang-command": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
-            "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+            "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
             "requires": {
                 "shebang-regex": "^1.0.0"
             }
@@ -17555,7 +17345,7 @@
         "shebang-regex": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
-            "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
+            "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ=="
         },
         "shelljs": {
             "version": "0.8.5",
@@ -17575,13 +17365,6 @@
                 "call-bind": "^1.0.0",
                 "get-intrinsic": "^1.0.2",
                 "object-inspect": "^1.9.0"
-            },
-            "dependencies": {
-                "object-inspect": {
-                    "version": "1.12.0",
-                    "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
-                    "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g=="
-                }
             }
         },
         "sift": {
@@ -17590,24 +17373,24 @@
             "integrity": "sha512-+gxdEOMA2J+AI+fVsCqeNn7Tgx3M9ZN9jdi95939l1IJ8cZsqS8sqpJyOkic2SJk+1+98Uwryt/gL6XDaV+UZA=="
         },
         "signal-exit": {
-            "version": "3.0.3",
-            "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
-            "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
+            "version": "3.0.7",
+            "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+            "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
         },
         "simple-assign": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/simple-assign/-/simple-assign-0.1.0.tgz",
-            "integrity": "sha1-F/0wZqXz13OPUDIbsPFMooHMS6o="
+            "integrity": "sha512-otdSSQzuVsmDoe5MnSm4ZgHd5sl0ak6A1CTjW1R/DUHQ8xoZuU1NUzf9x6n9Dvp3nxpvW51WNMQ/7rQ9432xDg=="
         },
         "simple-concat": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz",
-            "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY="
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
+            "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
         },
         "simple-get": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
-            "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
+            "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
             "requires": {
                 "decompress-response": "^4.2.0",
                 "once": "^1.3.1",
@@ -17617,7 +17400,7 @@
         "simple-swizzle": {
             "version": "0.2.2",
             "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
-            "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
+            "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
             "requires": {
                 "is-arrayish": "^0.3.1"
             },
@@ -17629,21 +17412,16 @@
                 }
             }
         },
-        "skmeans": {
-            "version": "0.9.7",
-            "resolved": "https://registry.npmjs.org/skmeans/-/skmeans-0.9.7.tgz",
-            "integrity": "sha512-hNj1/oZ7ygsfmPZ7ZfN5MUBRoGg1gtpnImuJBgLO0ljQ67DtJuiQaiYdS4lUA6s0KCwnPhGivtC/WRwIZLkHyg=="
-        },
         "slash": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
-            "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
+            "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==",
             "dev": true
         },
         "sliced": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz",
-            "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E="
+            "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA=="
         },
         "snapdragon": {
             "version": "0.8.2",
@@ -17671,7 +17449,7 @@
                 "define-property": {
                     "version": "0.2.5",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-                    "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+                    "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
                     "requires": {
                         "is-descriptor": "^0.1.0"
                     }
@@ -17679,20 +17457,15 @@
                 "extend-shallow": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-                    "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+                    "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
                     "requires": {
                         "is-extendable": "^0.1.0"
                     }
                 },
-                "is-extendable": {
-                    "version": "0.1.1",
-                    "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
-                    "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
-                },
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
                 }
             }
         },
@@ -17709,7 +17482,7 @@
                 "define-property": {
                     "version": "1.0.0",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-                    "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+                    "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
                     "requires": {
                         "is-descriptor": "^1.0.0"
                     }
@@ -17758,7 +17531,7 @@
                 "kind-of": {
                     "version": "3.2.2",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                    "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+                    "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -17822,12 +17595,12 @@
                 "isarray": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
-                    "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+                    "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
                 },
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
                 },
                 "socket.io-parser": {
                     "version": "3.3.2",
@@ -17854,7 +17627,7 @@
                 "component-emitter": {
                     "version": "1.2.1",
                     "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
-                    "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
+                    "integrity": "sha512-jPatnhd33viNplKjqXKRkGU345p263OIWzDL2wH3LGIGp5Kojo+uXizHmOADRvhGFFTnJqX3jBAKP6vvmSDKcA=="
                 },
                 "debug": {
                     "version": "4.1.1",
@@ -17867,7 +17640,7 @@
                 "isarray": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
-                    "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+                    "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
                 }
             }
         },
@@ -17891,13 +17664,13 @@
             }
         },
         "sockjs-client": {
-            "version": "1.6.0",
-            "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.0.tgz",
-            "integrity": "sha512-qVHJlyfdHFht3eBFZdKEXKTlb7I4IV41xnVNo8yUKA1UHcPJwgW2SvTq9LhnjjCywSkSK7c/e4nghU0GOoMCRQ==",
+            "version": "1.6.1",
+            "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.1.tgz",
+            "integrity": "sha512-2g0tjOR+fRs0amxENLi/q5TiJTqY+WXFOzb5UwXndlK6TO3U/mirZznpx6w34HVMoc3g7cY24yC/ZMIYnDlfkw==",
             "dev": true,
             "requires": {
                 "debug": "^3.2.7",
-                "eventsource": "^1.1.0",
+                "eventsource": "^2.0.2",
                 "faye-websocket": "^0.11.4",
                 "inherits": "^2.0.4",
                 "url-parse": "^1.5.10"
@@ -17925,16 +17698,19 @@
             },
             "dependencies": {
                 "node-fetch": {
-                    "version": "2.6.0",
-                    "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
-                    "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
+                    "version": "2.6.7",
+                    "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+                    "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+                    "requires": {
+                        "whatwg-url": "^5.0.0"
+                    }
                 }
             }
         },
         "source-map": {
             "version": "0.5.7",
             "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-            "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+            "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="
         },
         "source-map-resolve": {
             "version": "0.5.3",
@@ -17949,10 +17725,9 @@
             }
         },
         "source-map-support": {
-            "version": "0.5.19",
-            "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
-            "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
-            "dev": true,
+            "version": "0.5.21",
+            "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+            "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
             "requires": {
                 "buffer-from": "^1.0.0",
                 "source-map": "^0.6.0"
@@ -17961,29 +17736,28 @@
                 "source-map": {
                     "version": "0.6.1",
                     "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-                    "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-                    "dev": true
+                    "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
                 }
             }
         },
         "source-map-url": {
-            "version": "0.4.0",
-            "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
-            "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM="
+            "version": "0.4.1",
+            "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
+            "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw=="
         },
         "sparse-bitfield": {
             "version": "3.0.3",
             "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
-            "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
+            "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
             "optional": true,
             "requires": {
                 "memory-pager": "^1.0.2"
             }
         },
         "spdx-correct": {
-            "version": "3.1.0",
-            "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
-            "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+            "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
             "requires": {
                 "spdx-expression-parse": "^3.0.0",
                 "spdx-license-ids": "^3.0.0"
@@ -17995,18 +17769,18 @@
             "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A=="
         },
         "spdx-expression-parse": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
-            "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+            "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
             "requires": {
                 "spdx-exceptions": "^2.1.0",
                 "spdx-license-ids": "^3.0.0"
             }
         },
         "spdx-license-ids": {
-            "version": "3.0.5",
-            "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
-            "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q=="
+            "version": "3.0.11",
+            "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz",
+            "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g=="
         },
         "spdy": {
             "version": "4.0.2",
@@ -18077,7 +17851,7 @@
         "split-skip": {
             "version": "0.0.2",
             "resolved": "https://registry.npmjs.org/split-skip/-/split-skip-0.0.2.tgz",
-            "integrity": "sha1-2J2Iu9L3Pka1FYqjcKVhIk6A1GE="
+            "integrity": "sha512-weHOi8BolsDnGIwhhWHbA+wKSuSpvWwjRrdj8SdbIIis2vSwOE37CQP8x3EleuzxanUr3AK8BdUy4MkiOULPZg=="
         },
         "split-string": {
             "version": "3.1.0",
@@ -18090,12 +17864,12 @@
         "sprintf-js": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-            "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
+            "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
         },
         "sshpk": {
-            "version": "1.16.1",
-            "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
-            "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+            "version": "1.17.0",
+            "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
+            "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==",
             "requires": {
                 "asn1": "~0.2.3",
                 "assert-plus": "^1.0.0",
@@ -18142,7 +17916,7 @@
                 "define-property": {
                     "version": "0.2.5",
                     "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-                    "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+                    "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
                     "requires": {
                         "is-descriptor": "^0.1.0"
                     }
@@ -18150,9 +17924,9 @@
             }
         },
         "statuses": {
-            "version": "1.5.0",
-            "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
-            "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
+            "version": "2.0.1",
+            "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+            "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
         },
         "stdout-stream": {
             "version": "1.4.1",
@@ -18221,7 +17995,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
                 }
             }
         },
@@ -18251,41 +18025,23 @@
             "integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg=="
         },
         "string.prototype.trimend": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
-            "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
-            "requires": {
-                "define-properties": "^1.1.3",
-                "es-abstract": "^1.17.5"
-            }
-        },
-        "string.prototype.trimleft": {
-            "version": "2.1.2",
-            "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz",
-            "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==",
-            "requires": {
-                "define-properties": "^1.1.3",
-                "es-abstract": "^1.17.5",
-                "string.prototype.trimstart": "^1.0.0"
-            }
-        },
-        "string.prototype.trimright": {
-            "version": "2.1.2",
-            "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz",
-            "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==",
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz",
+            "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==",
             "requires": {
-                "define-properties": "^1.1.3",
-                "es-abstract": "^1.17.5",
-                "string.prototype.trimend": "^1.0.0"
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.4",
+                "es-abstract": "^1.19.5"
             }
         },
         "string.prototype.trimstart": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
-            "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz",
+            "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==",
             "requires": {
-                "define-properties": "^1.1.3",
-                "es-abstract": "^1.17.5"
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.4",
+                "es-abstract": "^1.19.5"
             }
         },
         "string_decoder": {
@@ -18305,10 +18061,15 @@
                 "unpack-string": "0.0.2"
             },
             "dependencies": {
+                "commander": {
+                    "version": "2.20.3",
+                    "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+                    "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+                },
                 "magicli": {
                     "version": "0.0.5",
                     "resolved": "https://registry.npmjs.org/magicli/-/magicli-0.0.5.tgz",
-                    "integrity": "sha1-zufQ+7THBRiqyxHsPrfiX/SaSSE=",
+                    "integrity": "sha512-wZbMtnl2v1b+Jp3xlqA9FU/O4I6YhGXR8xSY/eU2+gDAvut/F+W3gl4qs61iL4LELC7jqSAE6aAD5668EbmQHA==",
                     "requires": {
                         "commander": "^2.9.0",
                         "get-stdin": "^5.0.1",
@@ -18355,7 +18116,7 @@
                 "get-stdin": {
                     "version": "4.0.1",
                     "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
-                    "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4="
+                    "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw=="
                 }
             }
         },
@@ -18372,19 +18133,6 @@
             "requires": {
                 "loader-utils": "^1.1.0",
                 "schema-utils": "^1.0.0"
-            },
-            "dependencies": {
-                "schema-utils": {
-                    "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
-                    "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
-                    "dev": true,
-                    "requires": {
-                        "ajv": "^6.1.0",
-                        "ajv-errors": "^1.0.0",
-                        "ajv-keywords": "^3.1.0"
-                    }
-                }
             }
         },
         "styled-components": {
@@ -18405,12 +18153,27 @@
                 "stylis": "^3.5.0",
                 "stylis-rule-sheet": "^0.0.10",
                 "supports-color": "^5.5.0"
+            },
+            "dependencies": {
+                "@emotion/is-prop-valid": {
+                    "version": "0.8.8",
+                    "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
+                    "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==",
+                    "requires": {
+                        "@emotion/memoize": "0.7.4"
+                    }
+                },
+                "stylis": {
+                    "version": "3.5.4",
+                    "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz",
+                    "integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q=="
+                }
             }
         },
         "stylis": {
-            "version": "3.5.4",
-            "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz",
-            "integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q=="
+            "version": "4.0.13",
+            "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
+            "integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
         },
         "stylis-rule-sheet": {
             "version": "0.0.10",
@@ -18418,9 +18181,9 @@
             "integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw=="
         },
         "supercluster": {
-            "version": "7.1.4",
-            "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.4.tgz",
-            "integrity": "sha512-GhKkRM1jMR6WUwGPw05fs66pOFWhf59lXq+Q3J3SxPvhNcmgOtLRV6aVQPMRsmXdpaeFJGivt+t7QXUPL3ff4g==",
+            "version": "7.1.5",
+            "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.5.tgz",
+            "integrity": "sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==",
             "requires": {
                 "kdbush": "^3.0.0"
             }
@@ -18433,6 +18196,11 @@
                 "has-flag": "^3.0.0"
             }
         },
+        "supports-preserve-symlinks-flag": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+            "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
+        },
         "symbol-observable": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
@@ -18457,10 +18225,9 @@
             }
         },
         "tapable": {
-            "version": "1.1.3",
-            "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
-            "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
-            "dev": true
+            "version": "2.2.1",
+            "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+            "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="
         },
         "tar": {
             "version": "6.1.11",
@@ -18475,40 +18242,37 @@
                 "yallist": "^4.0.0"
             },
             "dependencies": {
-                "chownr": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
-                    "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
-                },
                 "mkdirp": {
                     "version": "1.0.4",
                     "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
                     "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
-                },
-                "yallist": {
-                    "version": "4.0.0",
-                    "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
-                    "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
                 }
             }
         },
         "tar-fs": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz",
-            "integrity": "sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==",
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz",
+            "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==",
             "requires": {
                 "chownr": "^1.1.1",
                 "mkdirp-classic": "^0.5.2",
                 "pump": "^3.0.0",
-                "tar-stream": "^2.0.0"
+                "tar-stream": "^2.1.4"
+            },
+            "dependencies": {
+                "chownr": {
+                    "version": "1.1.4",
+                    "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+                    "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
+                }
             }
         },
         "tar-stream": {
-            "version": "2.1.2",
-            "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz",
-            "integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==",
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
+            "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
             "requires": {
-                "bl": "^4.0.1",
+                "bl": "^4.0.3",
                 "end-of-stream": "^1.4.1",
                 "fs-constants": "^1.0.0",
                 "inherits": "^2.0.3",
@@ -18538,80 +18302,36 @@
             }
         },
         "terser": {
-            "version": "5.12.1",
-            "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz",
-            "integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==",
-            "dev": true,
+            "version": "5.14.0",
+            "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.0.tgz",
+            "integrity": "sha512-JC6qfIEkPBd9j1SMO3Pfn+A6w2kQV54tv+ABQLgZr7dA3k/DL/OBoYSWxzVpZev3J+bUHXfr55L8Mox7AaNo6g==",
             "requires": {
+                "@jridgewell/source-map": "^0.3.2",
                 "acorn": "^8.5.0",
                 "commander": "^2.20.0",
-                "source-map": "~0.7.2",
                 "source-map-support": "~0.5.20"
             },
             "dependencies": {
-                "acorn": {
-                    "version": "8.7.0",
-                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
-                    "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
-                    "dev": true
-                },
-                "source-map": {
-                    "version": "0.7.3",
-                    "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
-                    "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
-                    "dev": true
-                },
-                "source-map-support": {
-                    "version": "0.5.21",
-                    "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
-                    "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
-                    "dev": true,
-                    "requires": {
-                        "buffer-from": "^1.0.0",
-                        "source-map": "^0.6.0"
-                    },
-                    "dependencies": {
-                        "source-map": {
-                            "version": "0.6.1",
-                            "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-                            "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-                            "dev": true
-                        }
-                    }
+                "commander": {
+                    "version": "2.20.3",
+                    "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+                    "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
                 }
             }
         },
         "terser-webpack-plugin": {
-            "version": "5.3.1",
-            "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz",
-            "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==",
+            "version": "5.3.3",
+            "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz",
+            "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==",
             "dev": true,
             "requires": {
+                "@jridgewell/trace-mapping": "^0.3.7",
                 "jest-worker": "^27.4.5",
                 "schema-utils": "^3.1.1",
                 "serialize-javascript": "^6.0.0",
-                "source-map": "^0.6.1",
                 "terser": "^5.7.2"
             },
             "dependencies": {
-                "ajv": {
-                    "version": "6.12.6",
-                    "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
-                    "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
-                    "dev": true,
-                    "requires": {
-                        "fast-deep-equal": "^3.1.1",
-                        "fast-json-stable-stringify": "^2.0.0",
-                        "json-schema-traverse": "^0.4.1",
-                        "uri-js": "^4.2.2"
-                    }
-                },
-                "ajv-keywords": {
-                    "version": "3.5.2",
-                    "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
-                    "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
-                    "dev": true
-                },
                 "schema-utils": {
                     "version": "3.1.1",
                     "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
@@ -18622,21 +18342,6 @@
                         "ajv": "^6.12.5",
                         "ajv-keywords": "^3.5.2"
                     }
-                },
-                "serialize-javascript": {
-                    "version": "6.0.0",
-                    "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
-                    "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
-                    "dev": true,
-                    "requires": {
-                        "randombytes": "^2.1.0"
-                    }
-                },
-                "source-map": {
-                    "version": "0.6.1",
-                    "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-                    "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-                    "dev": true
                 }
             }
         },
@@ -18744,7 +18449,7 @@
                 "kind-of": {
                     "version": "3.2.2",
                     "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-                    "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+                    "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
                     "requires": {
                         "is-buffer": "^1.1.5"
                     }
@@ -18792,7 +18497,7 @@
                 "nopt": {
                     "version": "1.0.10",
                     "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
-                    "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
+                    "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
                     "requires": {
                         "abbrev": "1"
                     }
@@ -18810,13 +18515,9 @@
             }
         },
         "tr46": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
-            "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
-            "dev": true,
-            "requires": {
-                "punycode": "^2.1.0"
-            }
+            "version": "0.0.3",
+            "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+            "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
         },
         "translate-google-api": {
             "version": "1.0.4",
@@ -18833,6 +18534,11 @@
                     "requires": {
                         "follow-redirects": "^1.10.0"
                     }
+                },
+                "follow-redirects": {
+                    "version": "1.15.1",
+                    "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
+                    "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA=="
                 }
             }
         },
@@ -18876,6 +18582,25 @@
                 "loader-utils": "^1.0.2",
                 "micromatch": "^3.1.4",
                 "semver": "^5.0.1"
+            },
+            "dependencies": {
+                "enhanced-resolve": {
+                    "version": "4.5.0",
+                    "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz",
+                    "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==",
+                    "dev": true,
+                    "requires": {
+                        "graceful-fs": "^4.1.2",
+                        "memory-fs": "^0.5.0",
+                        "tapable": "^1.0.0"
+                    }
+                },
+                "tapable": {
+                    "version": "1.1.3",
+                    "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+                    "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+                    "dev": true
+                }
             }
         },
         "ts-node": {
@@ -18897,7 +18622,7 @@
                 "arrify": {
                     "version": "1.0.1",
                     "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
-                    "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
+                    "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==",
                     "dev": true
                 }
             }
@@ -19083,10 +18808,9 @@
             }
         },
         "tslib": {
-            "version": "1.11.1",
-            "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
-            "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==",
-            "dev": true
+            "version": "2.4.0",
+            "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
+            "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
         },
         "tslint": {
             "version": "5.20.1",
@@ -19109,11 +18833,29 @@
                 "tsutils": "^2.29.0"
             },
             "dependencies": {
+                "builtin-modules": {
+                    "version": "1.1.1",
+                    "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+                    "integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==",
+                    "dev": true
+                },
+                "commander": {
+                    "version": "2.20.3",
+                    "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+                    "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+                    "dev": true
+                },
                 "diff": {
                     "version": "4.0.2",
                     "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
                     "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
                     "dev": true
+                },
+                "tslib": {
+                    "version": "1.14.1",
+                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+                    "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+                    "dev": true
                 }
             }
         },
@@ -19153,6 +18895,14 @@
             "dev": true,
             "requires": {
                 "tslib": "^1.8.1"
+            },
+            "dependencies": {
+                "tslib": {
+                    "version": "1.14.1",
+                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+                    "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+                    "dev": true
+                }
             }
         },
         "tunnel-agent": {
@@ -19168,6 +18918,12 @@
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
             "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
         },
+        "type": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+            "dev": true
+        },
         "type-check": {
             "version": "0.3.2",
             "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
@@ -19202,9 +18958,9 @@
             "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
         },
         "typescript": {
-            "version": "4.6.2",
-            "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.2.tgz",
-            "integrity": "sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==",
+            "version": "4.7.3",
+            "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz",
+            "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==",
             "dev": true
         },
         "typescript-collections": {
@@ -19226,6 +18982,11 @@
                 "vscode-uri": "^1.0.5"
             },
             "dependencies": {
+                "commander": {
+                    "version": "2.20.3",
+                    "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+                    "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+                },
                 "fs-extra": {
                     "version": "7.0.1",
                     "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
@@ -19239,7 +19000,7 @@
                 "jsonfile": {
                     "version": "4.0.0",
                     "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
-                    "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+                    "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
                     "requires": {
                         "graceful-fs": "^4.1.6"
                     }
@@ -19252,9 +19013,9 @@
             "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0="
         },
         "ua-parser-js": {
-            "version": "0.7.21",
-            "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz",
-            "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ=="
+            "version": "0.7.31",
+            "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz",
+            "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ=="
         },
         "uglify-js": {
             "version": "2.8.29",
@@ -19269,12 +19030,12 @@
                 "camelcase": {
                     "version": "1.2.1",
                     "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
-                    "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk="
+                    "integrity": "sha512-wzLkDa4K/mzI1OSITC+DUyjgIl/ETNHE9QvYgy6J6Jvqyyz4C0Xfd+lQhb19sX2jMpZV4IssUn0VDVmglV+s4g=="
                 },
                 "cliui": {
                     "version": "2.1.0",
                     "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
-                    "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
+                    "integrity": "sha512-GIOYRizG+TGoc7Wgc1LiOTLare95R3mzKgoln+Q/lE4ceiYH19gUpl0l0Ffq4lJDEf3FxujMe6IBfOCs7pfqNA==",
                     "requires": {
                         "center-align": "^0.1.1",
                         "right-align": "^0.1.1",
@@ -19309,26 +19070,19 @@
             }
         },
         "uid2": {
-            "version": "0.0.3",
-            "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz",
-            "integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I="
+            "version": "0.0.4",
+            "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz",
+            "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA=="
         },
         "unbox-primitive": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
-            "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==",
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
+            "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
             "requires": {
-                "function-bind": "^1.1.1",
-                "has-bigints": "^1.0.1",
-                "has-symbols": "^1.0.2",
+                "call-bind": "^1.0.2",
+                "has-bigints": "^1.0.2",
+                "has-symbols": "^1.0.3",
                 "which-boxed-primitive": "^1.0.2"
-            },
-            "dependencies": {
-                "has-symbols": {
-                    "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
-                    "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
-                }
             }
         },
         "unbzip2-stream": {
@@ -19341,32 +19095,14 @@
             }
         },
         "undefsafe": {
-            "version": "2.0.3",
-            "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz",
-            "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==",
-            "requires": {
-                "debug": "^2.2.0"
-            },
-            "dependencies": {
-                "debug": {
-                    "version": "2.6.9",
-                    "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-                    "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-                    "requires": {
-                        "ms": "2.0.0"
-                    }
-                },
-                "ms": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
-                }
-            }
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
+            "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="
         },
         "underscore": {
-            "version": "1.10.2",
-            "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz",
-            "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg=="
+            "version": "1.13.4",
+            "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz",
+            "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ=="
         },
         "unicode-trie": {
             "version": "0.3.1",
@@ -19380,7 +19116,7 @@
                 "pako": {
                     "version": "0.2.9",
                     "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
-                    "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU="
+                    "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA=="
                 }
             }
         },
@@ -19393,21 +19129,8 @@
                 "get-value": "^2.0.6",
                 "is-extendable": "^0.1.1",
                 "set-value": "^2.0.1"
-            },
-            "dependencies": {
-                "is-extendable": {
-                    "version": "0.1.1",
-                    "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
-                    "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
-                }
             }
         },
-        "uniq": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
-            "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
-            "dev": true
-        },
         "unique-filename": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
@@ -19466,7 +19189,7 @@
                 "has-value": {
                     "version": "0.3.1",
                     "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
-                    "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+                    "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==",
                     "requires": {
                         "get-value": "^2.0.3",
                         "has-values": "^0.1.4",
@@ -19476,7 +19199,7 @@
                         "isobject": {
                             "version": "2.1.0",
                             "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
-                            "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+                            "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
                             "requires": {
                                 "isarray": "1.0.0"
                             }
@@ -19486,7 +19209,7 @@
                 "has-values": {
                     "version": "0.1.4",
                     "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
-                    "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E="
+                    "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ=="
                 }
             }
         },
@@ -19533,9 +19256,9 @@
             }
         },
         "uri-js": {
-            "version": "4.2.2",
-            "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
-            "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+            "version": "4.4.1",
+            "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+            "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
             "requires": {
                 "punycode": "^2.1.0"
             }
@@ -19558,7 +19281,7 @@
                 "punycode": {
                     "version": "1.3.2",
                     "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
-                    "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
+                    "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==",
                     "dev": true
                 }
             }
@@ -19574,19 +19297,9 @@
             },
             "dependencies": {
                 "mime": {
-                    "version": "2.4.4",
-                    "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz",
-                    "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA=="
-                },
-                "schema-utils": {
-                    "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
-                    "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
-                    "requires": {
-                        "ajv": "^6.1.0",
-                        "ajv-errors": "^1.0.0",
-                        "ajv-keywords": "^3.1.0"
-                    }
+                    "version": "2.6.0",
+                    "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+                    "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="
                 }
             }
         },
@@ -19624,13 +19337,6 @@
             "integrity": "sha512-7/hqDrWa0iMnCoET9W1T07EmD4Eg/Wmoj/X8TGBc++ECRK4m5yTsjP4O6s0yagbxfqIOuUkIxe2/sA+VR2GxZA==",
             "requires": {
                 "fast-deep-equal": "^3.1.3"
-            },
-            "dependencies": {
-                "fast-deep-equal": {
-                    "version": "3.1.3",
-                    "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
-                    "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
-                }
             }
         },
         "use-memo-one": {
@@ -19713,6 +19419,13 @@
                 "assert-plus": "^1.0.0",
                 "core-util-is": "1.0.2",
                 "extsprintf": "^1.2.0"
+            },
+            "dependencies": {
+                "core-util-is": {
+                    "version": "1.0.2",
+                    "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+                    "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="
+                }
             }
         },
         "void-elements": {
@@ -19721,9 +19434,9 @@
             "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w="
         },
         "vscode-jsonrpc": {
-            "version": "5.0.1",
-            "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz",
-            "integrity": "sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A=="
+            "version": "8.0.1",
+            "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.1.tgz",
+            "integrity": "sha512-N/WKvghIajmEvXpatSzvTvOIz61ZSmOSa4BRA4pTLi+1+jozquQKP/MkaylP9iB68k73Oua1feLQvH3xQuigiQ=="
         },
         "vscode-languageserver": {
             "version": "5.3.0-next.10",
@@ -19735,18 +19448,18 @@
             }
         },
         "vscode-languageserver-protocol": {
-            "version": "3.15.3",
-            "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz",
-            "integrity": "sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==",
+            "version": "3.17.1",
+            "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.1.tgz",
+            "integrity": "sha512-BNlAYgQoYwlSgDLJhSG+DeA8G1JyECqRzM2YO6tMmMji3Ad9Mw6AW7vnZMti90qlAKb0LqAlJfSVGEdqMMNzKg==",
             "requires": {
-                "vscode-jsonrpc": "^5.0.1",
-                "vscode-languageserver-types": "3.15.1"
+                "vscode-jsonrpc": "8.0.1",
+                "vscode-languageserver-types": "3.17.1"
             }
         },
         "vscode-languageserver-types": {
-            "version": "3.15.1",
-            "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz",
-            "integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ=="
+            "version": "3.17.1",
+            "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz",
+            "integrity": "sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ=="
         },
         "vscode-textbuffer": {
             "version": "1.0.0",
@@ -19759,9 +19472,9 @@
             "integrity": "sha512-obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ=="
         },
         "vue-template-compiler": {
-            "version": "2.6.11",
-            "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz",
-            "integrity": "sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA==",
+            "version": "2.6.14",
+            "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz",
+            "integrity": "sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g==",
             "requires": {
                 "de-indent": "^1.0.2",
                 "he": "^1.1.0"
@@ -19790,6 +19503,14 @@
                 "domexception": "^1.0.1",
                 "webidl-conversions": "^4.0.2",
                 "xml-name-validator": "^3.0.0"
+            },
+            "dependencies": {
+                "webidl-conversions": {
+                    "version": "4.0.2",
+                    "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+                    "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
+                    "dev": true
+                }
             }
         },
         "walkdir": {
@@ -19806,9 +19527,9 @@
             }
         },
         "watchpack": {
-            "version": "2.3.1",
-            "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.3.1.tgz",
-            "integrity": "sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA==",
+            "version": "2.4.0",
+            "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
+            "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
             "dev": true,
             "requires": {
                 "glob-to-regexp": "^0.4.1",
@@ -19833,20 +19554,19 @@
             }
         },
         "web-streams-polyfill": {
-            "version": "3.2.0",
-            "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz",
-            "integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA=="
+            "version": "3.2.1",
+            "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
+            "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q=="
         },
         "webidl-conversions": {
-            "version": "4.0.2",
-            "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
-            "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
-            "dev": true
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+            "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
         },
         "webpack": {
-            "version": "5.69.1",
-            "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.69.1.tgz",
-            "integrity": "sha512-+VyvOSJXZMT2V5vLzOnDuMz5GxEqLk7hKWQ56YxPW/PQRUuKimPqmEIJOx8jHYeyo65pKbapbW464mvsKbaj4A==",
+            "version": "5.73.0",
+            "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz",
+            "integrity": "sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==",
             "dev": true,
             "requires": {
                 "@types/eslint-scope": "^3.7.3",
@@ -19858,13 +19578,13 @@
                 "acorn-import-assertions": "^1.7.6",
                 "browserslist": "^4.14.5",
                 "chrome-trace-event": "^1.0.2",
-                "enhanced-resolve": "^5.8.3",
+                "enhanced-resolve": "^5.9.3",
                 "es-module-lexer": "^0.9.0",
                 "eslint-scope": "5.1.1",
                 "events": "^3.2.0",
                 "glob-to-regexp": "^0.4.1",
                 "graceful-fs": "^4.2.9",
-                "json-parse-better-errors": "^1.0.2",
+                "json-parse-even-better-errors": "^2.3.1",
                 "loader-runner": "^4.2.0",
                 "mime-types": "^2.1.27",
                 "neo-async": "^2.6.2",
@@ -19875,52 +19595,6 @@
                 "webpack-sources": "^3.2.3"
             },
             "dependencies": {
-                "acorn": {
-                    "version": "8.7.0",
-                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
-                    "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
-                    "dev": true
-                },
-                "ajv": {
-                    "version": "6.12.6",
-                    "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
-                    "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
-                    "dev": true,
-                    "requires": {
-                        "fast-deep-equal": "^3.1.1",
-                        "fast-json-stable-stringify": "^2.0.0",
-                        "json-schema-traverse": "^0.4.1",
-                        "uri-js": "^4.2.2"
-                    }
-                },
-                "ajv-keywords": {
-                    "version": "3.5.2",
-                    "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
-                    "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
-                    "dev": true
-                },
-                "enhanced-resolve": {
-                    "version": "5.9.2",
-                    "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz",
-                    "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==",
-                    "dev": true,
-                    "requires": {
-                        "graceful-fs": "^4.2.4",
-                        "tapable": "^2.2.0"
-                    }
-                },
-                "graceful-fs": {
-                    "version": "4.2.9",
-                    "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
-                    "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==",
-                    "dev": true
-                },
-                "neo-async": {
-                    "version": "2.6.2",
-                    "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
-                    "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
-                    "dev": true
-                },
                 "schema-utils": {
                     "version": "3.1.1",
                     "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
@@ -19931,12 +19605,6 @@
                         "ajv": "^6.12.5",
                         "ajv-keywords": "^3.5.2"
                     }
-                },
-                "tapable": {
-                    "version": "2.2.1",
-                    "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
-                    "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
-                    "dev": true
                 }
             }
         },
@@ -19990,11 +19658,6 @@
                         "strip-final-newline": "^2.0.0"
                     }
                 },
-                "get-stream": {
-                    "version": "6.0.1",
-                    "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
-                    "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="
-                },
                 "interpret": {
                     "version": "2.2.0",
                     "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
@@ -20050,21 +19713,21 @@
             }
         },
         "webpack-dev-middleware": {
-            "version": "5.3.1",
-            "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz",
-            "integrity": "sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==",
+            "version": "5.3.3",
+            "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz",
+            "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==",
             "requires": {
                 "colorette": "^2.0.10",
-                "memfs": "^3.4.1",
+                "memfs": "^3.4.3",
                 "mime-types": "^2.1.31",
                 "range-parser": "^1.2.1",
                 "schema-utils": "^4.0.0"
             },
             "dependencies": {
                 "ajv": {
-                    "version": "8.10.0",
-                    "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz",
-                    "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==",
+                    "version": "8.11.0",
+                    "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
+                    "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
                     "requires": {
                         "fast-deep-equal": "^3.1.1",
                         "json-schema-traverse": "^1.0.0",
@@ -20078,13 +19741,6 @@
                     "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
                     "requires": {
                         "fast-deep-equal": "^3.1.3"
-                    },
-                    "dependencies": {
-                        "fast-deep-equal": {
-                            "version": "3.1.3",
-                            "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
-                            "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
-                        }
                     }
                 },
                 "json-schema-traverse": {
@@ -20092,19 +19748,6 @@
                     "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
                     "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
                 },
-                "mime-db": {
-                    "version": "1.52.0",
-                    "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
-                    "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
-                },
-                "mime-types": {
-                    "version": "2.1.35",
-                    "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
-                    "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
-                    "requires": {
-                        "mime-db": "1.52.0"
-                    }
-                },
                 "schema-utils": {
                     "version": "4.0.0",
                     "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
@@ -20162,7 +19805,7 @@
                 "ansi-regex": {
                     "version": "2.1.1",
                     "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-                    "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+                    "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
                     "dev": true
                 },
                 "debug": {
@@ -20187,7 +19830,7 @@
                 "memory-fs": {
                     "version": "0.4.1",
                     "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
-                    "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
+                    "integrity": "sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==",
                     "dev": true,
                     "requires": {
                         "errno": "^0.1.3",
@@ -20233,7 +19876,7 @@
                 "resolve-cwd": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
-                    "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
+                    "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==",
                     "dev": true,
                     "requires": {
                         "resolve-from": "^3.0.0"
@@ -20242,20 +19885,9 @@
                 "resolve-from": {
                     "version": "3.0.0",
                     "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
-                    "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
+                    "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==",
                     "dev": true
                 },
-                "schema-utils": {
-                    "version": "1.0.0",
-                    "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
-                    "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
-                    "dev": true,
-                    "requires": {
-                        "ajv": "^6.1.0",
-                        "ajv-errors": "^1.0.0",
-                        "ajv-keywords": "^3.1.0"
-                    }
-                },
                 "semver": {
                     "version": "6.3.0",
                     "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
@@ -20333,9 +19965,9 @@
                     "dev": true
                 },
                 "html-entities": {
-                    "version": "2.3.2",
-                    "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz",
-                    "integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==",
+                    "version": "2.3.3",
+                    "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz",
+                    "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==",
                     "dev": true
                 },
                 "strip-ansi": {
@@ -20409,12 +20041,23 @@
             "dev": true,
             "requires": {
                 "iconv-lite": "0.4.24"
+            },
+            "dependencies": {
+                "iconv-lite": {
+                    "version": "0.4.24",
+                    "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+                    "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+                    "dev": true,
+                    "requires": {
+                        "safer-buffer": ">= 2.1.2 < 3"
+                    }
+                }
             }
         },
         "whatwg-fetch": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
-            "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q=="
+            "version": "3.6.2",
+            "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz",
+            "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA=="
         },
         "whatwg-mimetype": {
             "version": "2.3.0",
@@ -20423,16 +20066,19 @@
             "dev": true
         },
         "whatwg-url": {
-            "version": "7.1.0",
-            "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
-            "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
-            "dev": true,
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+            "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
             "requires": {
-                "lodash.sortby": "^4.7.0",
-                "tr46": "^1.0.1",
-                "webidl-conversions": "^4.0.2"
+                "tr46": "~0.0.3",
+                "webidl-conversions": "^3.0.0"
             }
         },
+        "when": {
+            "version": "3.7.8",
+            "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz",
+            "integrity": "sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I="
+        },
         "which": {
             "version": "1.3.1",
             "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
@@ -20459,103 +20105,21 @@
             "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
         },
         "which-pm-runs": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
-            "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
+            "version": "1.1.0",
+            "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz",
+            "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="
         },
         "which-typed-array": {
-            "version": "1.1.7",
-            "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz",
-            "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==",
+            "version": "1.1.8",
+            "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz",
+            "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==",
             "requires": {
                 "available-typed-arrays": "^1.0.5",
                 "call-bind": "^1.0.2",
-                "es-abstract": "^1.18.5",
-                "foreach": "^2.0.5",
+                "es-abstract": "^1.20.0",
+                "for-each": "^0.3.3",
                 "has-tostringtag": "^1.0.0",
-                "is-typed-array": "^1.1.7"
-            },
-            "dependencies": {
-                "es-abstract": {
-                    "version": "1.19.1",
-                    "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz",
-                    "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==",
-                    "requires": {
-                        "call-bind": "^1.0.2",
-                        "es-to-primitive": "^1.2.1",
-                        "function-bind": "^1.1.1",
-                        "get-intrinsic": "^1.1.1",
-                        "get-symbol-description": "^1.0.0",
-                        "has": "^1.0.3",
-                        "has-symbols": "^1.0.2",
-                        "internal-slot": "^1.0.3",
-                        "is-callable": "^1.2.4",
-                        "is-negative-zero": "^2.0.1",
-                        "is-regex": "^1.1.4",
-                        "is-shared-array-buffer": "^1.0.1",
-                        "is-string": "^1.0.7",
-                        "is-weakref": "^1.0.1",
-                        "object-inspect": "^1.11.0",
-                        "object-keys": "^1.1.1",
-                        "object.assign": "^4.1.2",
-                        "string.prototype.trimend": "^1.0.4",
-                        "string.prototype.trimstart": "^1.0.4",
-                        "unbox-primitive": "^1.0.1"
-                    }
-                },
-                "has-symbols": {
-                    "version": "1.0.3",
-                    "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
-                    "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
-                },
-                "is-callable": {
-                    "version": "1.2.4",
-                    "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz",
-                    "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w=="
-                },
-                "is-regex": {
-                    "version": "1.1.4",
-                    "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
-                    "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
-                    "requires": {
-                        "call-bind": "^1.0.2",
-                        "has-tostringtag": "^1.0.0"
-                    }
-                },
-                "object-inspect": {
-                    "version": "1.12.0",
-                    "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
-                    "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g=="
-                },
-                "object.assign": {
-                    "version": "4.1.2",
-                    "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
-                    "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
-                    "requires": {
-                        "call-bind": "^1.0.0",
-                        "define-properties": "^1.1.3",
-                        "has-symbols": "^1.0.1",
-                        "object-keys": "^1.1.1"
-                    }
-                },
-                "string.prototype.trimend": {
-                    "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz",
-                    "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==",
-                    "requires": {
-                        "call-bind": "^1.0.2",
-                        "define-properties": "^1.1.3"
-                    }
-                },
-                "string.prototype.trimstart": {
-                    "version": "1.0.4",
-                    "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz",
-                    "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==",
-                    "requires": {
-                        "call-bind": "^1.0.2",
-                        "define-properties": "^1.1.3"
-                    }
-                }
+                "is-typed-array": "^1.1.9"
             }
         },
         "wide-align": {
@@ -20601,6 +20165,13 @@
             "requires": {
                 "acorn": "^3.1.0",
                 "acorn-globals": "^3.0.0"
+            },
+            "dependencies": {
+                "acorn": {
+                    "version": "3.3.0",
+                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
+                    "integrity": "sha512-OLUyIIZ7mF5oaAUT1w0TFqQS81q3saT46x8t7ukpPjMNk+nbs4ZHhs7ToV8EWnLYLepjETXd4XaCE4uxkMeqUw=="
+                }
             }
         },
         "word-wrap": {
@@ -20653,9 +20224,9 @@
             },
             "dependencies": {
                 "ansi-regex": {
-                    "version": "4.1.0",
-                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
-                    "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
+                    "version": "4.1.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+                    "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
                 },
                 "string-width": {
                     "version": "3.1.0",
@@ -20693,9 +20264,9 @@
             }
         },
         "ws": {
-            "version": "7.2.5",
-            "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz",
-            "integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA=="
+            "version": "7.5.8",
+            "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.8.tgz",
+            "integrity": "sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw=="
         },
         "xdg-basedir": {
             "version": "3.0.0",
@@ -20719,7 +20290,7 @@
                 "lodash": {
                     "version": "3.10.1",
                     "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
-                    "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y="
+                    "integrity": "sha512-9mDDwqVIma6OZX79ZlDACZl8sBm0TEnkf99zV3iMA4GzkIT/9hiqP5mY0HoT1iNLCrKc/R1HByV+yJfRWVJryQ=="
                 }
             }
         },
@@ -20759,14 +20330,14 @@
             "dev": true
         },
         "y18n": {
-            "version": "4.0.0",
-            "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
-            "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
+            "version": "4.0.3",
+            "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
+            "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
         },
         "yallist": {
-            "version": "3.1.1",
-            "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
-            "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+            "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
         },
         "yaml": {
             "version": "1.10.2",
@@ -20791,9 +20362,9 @@
             },
             "dependencies": {
                 "ansi-regex": {
-                    "version": "4.1.0",
-                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
-                    "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
+                    "version": "4.1.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+                    "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
                 },
                 "string-width": {
                     "version": "3.1.0",
@@ -20865,9 +20436,9 @@
             }
         },
         "zustand": {
-            "version": "3.7.1",
-            "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.1.tgz",
-            "integrity": "sha512-wHBCZlKj+bg03/hP+Tzv24YhnqqP8MCeN9ECPDXoF01062SIbnfl3j9O0znkDw1lNTY0a8WN3F///a0UhhaEqg=="
+            "version": "3.7.2",
+            "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz",
+            "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA=="
         }
     }
 }
diff --git a/package.json b/package.json
index c8455c8a3..d5b442bea 100644
--- a/package.json
+++ b/package.json
@@ -132,6 +132,7 @@
         "@types/dom-speech-recognition": "0.0.1",
         "@types/formidable": "1.0.31",
         "@types/google-maps": "^3.2.3",
+        "@types/react-icons": "^3.0.0",
         "@types/react-reconciler": "^0.26.4",
         "@types/reveal": "^3.3.33",
         "@types/supercluster": "^7.1.0",
@@ -174,6 +175,7 @@
         "express-session": "^1.17.2",
         "express-validator": "^5.3.1",
         "expressjs": "^1.0.1",
+        "ffmpeg": "0.0.4",
         "file-saver": "^2.0.5",
         "find-in-files": "^0.5.0",
         "fit-curve": "^0.1.7",
@@ -254,6 +256,7 @@
         "react-datepicker": "^3.8.0",
         "react-dom": "^16.14.0",
         "react-grid-layout": "^0.18.3",
+        "react-icons": "^4.3.1",
         "react-image-lightbox-with-rotate": "^5.1.1",
         "react-jsx-parser": "^1.29.0",
         "react-loading": "^2.0.3",
diff --git a/src/Utils.ts b/src/Utils.ts
index 4dd9979ea..b87980397 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -677,6 +677,8 @@ export function StopEvent(e: React.PointerEvent | React.MouseEvent) {
     e.preventDefault();
 }
 
+
+
 export function setupMoveUpEvents(
     target: object,
     e: React.PointerEvent,
@@ -692,6 +694,7 @@ export function setupMoveUpEvents(
     (target as any)._lastTap = Date.now();
     (target as any)._downX = (target as any)._lastX = e.clientX;
     (target as any)._downY = (target as any)._lastY = e.clientY;
+    (target as any)._noClick = false;
 
     const _moveEvent = (e: PointerEvent): void => {
         if (Math.abs(e.clientX - (target as any)._downX) > Utils.DRAG_THRESHOLD || Math.abs(e.clientY - (target as any)._downY) > Utils.DRAG_THRESHOLD) {
@@ -723,17 +726,20 @@ export function setupMoveUpEvents(
                 clearTimeout((target as any)._doubleTime);
                 (target as any)._doubleTime = undefined;
             }
-            clickEvent(e, (target as any)._doubleTap);
+            (target as any)._noClick = clickEvent(e, (target as any)._doubleTap);
         }
         document.removeEventListener("pointermove", _moveEvent);
         document.removeEventListener("pointerup", _upEvent);
     };
+    const _clickEvent = (e: MouseEvent): void => {
+        if ((target as any)._noClick) e.stopPropagation();
+        document.removeEventListener("click", _clickEvent, true);
+    }
     if (stopPropagation) {
         e.stopPropagation();
         e.preventDefault();
     }
-    document.removeEventListener("pointermove", _moveEvent);
-    document.removeEventListener("pointerup", _upEvent);
     document.addEventListener("pointermove", _moveEvent);
     document.addEventListener("pointerup", _upEvent);
+    document.addEventListener("click", _clickEvent, true);
 }
\ No newline at end of file
diff --git a/src/client/Network.ts b/src/client/Network.ts
index bf2918734..3597e7b2b 100644
--- a/src/client/Network.ts
+++ b/src/client/Network.ts
@@ -4,46 +4,46 @@ import { Upload } from "../server/SharedMediaTypes";
 
 export namespace Networking {
 
-    export async function FetchFromServer(relativeRoute: string) {
-        return (await fetch(relativeRoute)).text();
-    }
+      export async function FetchFromServer(relativeRoute: string) {
+            return (await fetch(relativeRoute)).text();
+      }
 
-    export async function PostToServer(relativeRoute: string, body?: any) {
-        const options = {
-            uri: Utils.prepend(relativeRoute),
-            method: "POST",
-            body,
-            json: true
-        };
-        return requestPromise.post(options);
-    }
+      export async function PostToServer(relativeRoute: string, body?: any) {
+            const options = {
+                  uri: Utils.prepend(relativeRoute),
+                  method: "POST",
+                  body,
+                  json: true
+            };
+            return requestPromise.post(options);
+      }
 
-    export async function UploadFilesToServer<T extends Upload.FileInformation = Upload.FileInformation>(files: File | File[]): Promise<Upload.FileResponse<T>[]> {
-        const formData = new FormData();
-        if (Array.isArray(files)) {
-            if (!files.length) {
-                return [];
+      export async function UploadFilesToServer<T extends Upload.FileInformation = Upload.FileInformation>(files: File | File[]): Promise<Upload.FileResponse<T>[]> {
+            const formData = new FormData();
+            if (Array.isArray(files)) {
+                  if (!files.length) {
+                        return [];
+                  }
+                  files.forEach(file => formData.append(Utils.GenerateGuid(), file));
+            } else {
+                  formData.append(Utils.GenerateGuid(), files);
             }
-            files.forEach(file => formData.append(Utils.GenerateGuid(), file));
-        } else {
-            formData.append(Utils.GenerateGuid(), files);
-        }
-        const parameters = {
-            method: 'POST',
-            body: formData
-        };
-        const response = await fetch("/uploadFormData", parameters);
-        return response.json();
-    }
+            const parameters = {
+                  method: 'POST',
+                  body: formData
+            };
+            const response = await fetch("/uploadFormData", parameters);
+            return response.json();
+      }
 
-    export async function UploadYoutubeToServer<T extends Upload.FileInformation = Upload.FileInformation>(videoId: string): Promise<Upload.FileResponse<T>[]> {
-        const parameters = {
-            method: 'POST',
-            body: JSON.stringify({ videoId }),
-            json: true
-        };
-        const response = await fetch("/uploadYoutubeVideo", parameters);
-        return response.json();
-    }
+      export async function UploadYoutubeToServer<T extends Upload.FileInformation = Upload.FileInformation>(videoId: string): Promise<Upload.FileResponse<T>[]> {
+            const parameters = {
+                  method: 'POST',
+                  body: JSON.stringify({ videoId }),
+                  json: true
+            };
+            const response = await fetch("/uploadYoutubeVideo", parameters);
+            return response.json();
+      }
 
 }
\ No newline at end of file
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index 161dff6e0..ca942a38a 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -9,6 +9,7 @@ export enum DocumentType {
     KVP = "kvp",
     VID = "video",
     AUDIO = "audio",
+    REC = "recording",
     PDF = "pdf",
     INK = "inks",
     SCREENSHOT = "screenshot",
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 2ca9cdb71..5f009573e 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -11,7 +11,7 @@ import { RichTextField } from "../../fields/RichTextField";
 import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
 import { ComputedField, ScriptField } from "../../fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../fields/Types";
-import { AudioField, ImageField, MapField, PdfField, VideoField, WebField, YoutubeField } from "../../fields/URLField";
+import { AudioField, ImageField, MapField, PdfField, RecordingField, VideoField, WebField, YoutubeField } from "../../fields/URLField";
 import { SharingPermissions } from "../../fields/util";
 import { Upload } from "../../server/SharedMediaTypes";
 import { OmitKeys, Utils, aggregateBounds } from "../../Utils";
@@ -61,6 +61,7 @@ import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo";
 import { DocumentType } from "./DocumentTypes";
 import { IconProp } from "@fortawesome/fontawesome-svg-core";
 import { MapBox } from "../views/nodes/MapBox/MapBox";
+import { RecordingBox } from "../views/nodes/RecordingBox/RecordingBox";
 
 const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", ""));
 class EmptyBox {
@@ -97,6 +98,7 @@ export class DocumentOptions {
     allowOverlayDrop?: BOOLt = new BoolInfo("can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?");
     childDropAction?: DROPt = new DAInfo("what should happen to the source document when it's dropped onto a child of a collection ");
     targetDropAction?: DROPt = new DAInfo("what should happen to the source document when ??? ");
+    userColor?: string; // color associated with a Dash user (seen in header fields of shared documents)
     color?: string; // foreground color data doc
     backgroundColor?: STRt = new StrInfo("background color for data doc");
     _backgroundColor?: STRt = new StrInfo("background color for each template layout doc (overrides backgroundColor)", true);
@@ -113,7 +115,8 @@ export class DocumentOptions {
     _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height", true);
     _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units", true);
     _fitWidth?: BOOLt = new BoolInfo("whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)", true);
-    _fitToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents
+    _fitContentsToBox?: boolean; // whether a freeformview should zoom/scale to create a shrinkwrapped view of its contents
+    _contentBounds?: List<number>; // the (forced) bounds of the document to display. format is: [left, top, right, bottom]
     _lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged
     _lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed
     _isPushpin?: boolean; // whether document, when clicked, toggles display of its link target
@@ -188,11 +191,18 @@ export class DocumentOptions {
     childLayoutString?: string; // template string for collection to use to render its children
     childDontRegisterViews?: boolean;
     childHideLinkButton?: boolean; // hide link buttons on all children
+    childContextMenuFilters?: List<ScriptField>;
+    childContextMenuScripts?: List<ScriptField>;
+    childContextMenuLabels?: List<string>;
+    childContextMenuIcons?: List<string>;
     hideLinkButton?: boolean; // whether the blue link counter button should be hidden
+    hideDecorationTitle?: boolean;
+    hideOpenButton?: boolean;
+    hideResizeHandles?: boolean;
+    hideDocumentButtonBar?: boolean;
     hideAllLinks?: boolean; // whether all individual blue anchor dots should be hidden
     isTemplateForField?: string; // the field key for which the containing document is a rendering template
     isTemplateDoc?: boolean;
-    watchedDocuments?: Doc; // list of documents an icon doc monitors in order to display a badge count
     targetScriptKey?: string; // where to write a template script (used by collections with click templates which need to target onClick, onDoubleClick, etc)
     templates?: List<string>;
     hero?: ImageField; // primary image that best represents a compound document (e.g., for a buxton device document that has multiple images)
@@ -244,7 +254,8 @@ export class DocumentOptions {
     numBtnType?: string;
     numBtnMax?: number;
     numBtnMin?: number;
-    switchToggle?: boolean;
+    switchToggle?: boolean;    
+    badgeValue?: ScriptField;
 
     //LINEAR VIEW
     linearViewIsExpanded?: boolean; // is linear view expanded
@@ -288,6 +299,7 @@ export class DocumentOptions {
     treeViewHideHeader?: boolean; // whether to hide the header for a document in a tree view
     treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items.
     treeViewGrowsHorizontally?: boolean; // whether an embedded tree view of the document can grow horizontally without growing vertically
+    treeViewChildDoubleClick?: ScriptField; // 
 
     // Action Button
     buttonMenu?: boolean; // whether a action button should be displayed 
@@ -409,6 +421,10 @@ export namespace Docs {
                 layout: { view: AudioBox, dataField: defaultDataKey },
                 options: { _height: 100, backgroundColor: "lightGray", links: "@links(self)" }
             }],
+            [DocumentType.REC, {
+                layout: { view: VideoBox, dataField: defaultDataKey },
+                options: { _height: 100, backgroundColor: "pink", links: "@links(self)" }
+            }],
             [DocumentType.PDF, {
                 layout: { view: PDFBox, dataField: defaultDataKey },
                 options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: "@links(self)" }
@@ -476,7 +492,7 @@ export namespace Docs {
                 options: { hideLinkButton: true, _width: 40, _height: 40, borderRounding: "100%", links: "@links(self)" },
             }],
             [DocumentType.WEBCAM, {
-                layout: { view: DashWebRTCVideo, dataField: defaultDataKey },
+                layout: { view: RecordingBox, dataField: defaultDataKey },
                 options: { links: "@links(self)" }
             }],
             [DocumentType.PRESELEMENT, {
@@ -653,6 +669,7 @@ export namespace Docs {
             // so that the list of annotations is already initialised, prevents issues in addonly.
             // without this, if a doc has no annotations but the user has AddOnly privileges, they won't be able to add an annotation because they would have needed to create the field's list which they don't have permissions to do.
             dataProps[fieldKey + "-annotations"] = new List<Doc>();
+            dataProps[fieldKey + "-sidebar"] = new List<Doc>();
             const dataDoc = Doc.assign(Doc.MakeDelegate(proto, protoId), dataProps, undefined, true);
 
             const viewFirstProps: { [id: string]: any } = {};
@@ -671,9 +688,9 @@ export namespace Docs {
             return viewDoc;
         }
 
-        export function ImageDocument(url: string, options: DocumentOptions = {}) {
-            const imgField = new ImageField(url);
-            return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(url), ...options });
+        export function ImageDocument(url: string|ImageField, options: DocumentOptions = {}) {
+            const imgField =  url instanceof ImageField ? url : new ImageField(url);
+            return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField.url.href), ...options });
         }
 
         export function PresDocument(options: DocumentOptions = {}) {
@@ -710,6 +727,10 @@ export namespace Docs {
                 { ...options, backgroundColor: ComputedField.MakeFunction("this._mediaState === 'playing' ? 'green':'gray'") as any });
         }
 
+        export function RecordingDocument(url: string, options: DocumentOptions = {}) {
+            return InstanceFromProto(Prototypes.get(DocumentType.REC), "", options);
+        }
+
         export function SearchDocument(options: DocumentOptions = {}) {
             return InstanceFromProto(Prototypes.get(DocumentType.SEARCH), new List<Doc>([]), options);
         }
@@ -1097,8 +1118,11 @@ export namespace DocUtils {
 
     export function MakeLinkToActiveAudio(getSourceDoc: () => Doc, broadcastEvent = true) {
         broadcastEvent && runInAction(() => DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1);
-        return DocUtils.ActiveRecordings.map(audio =>
-            DocUtils.MakeLink({ doc: getSourceDoc() }, { doc: audio.getAnchor() || audio.props.Document }, "recording annotation:linked recording", "recording timeline"));
+        return DocUtils.ActiveRecordings.map(audio => {
+            const link = DocUtils.MakeLink({ doc: getSourceDoc() }, { doc: audio.getAnchor() || audio.props.Document }, "recording annotation:linked recording", "recording timeline");
+            link && (link.followLinkLocation = "add:right");
+            return link;
+        });
     }
 
     export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", description: string = "", id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) {
@@ -1170,6 +1194,9 @@ export namespace DocUtils {
         } else if (field instanceof AudioField) {
             created = Docs.Create.AudioDocument((field).url.href, resolved);
             layout = AudioBox.LayoutString;
+        } else if (field instanceof RecordingField) {
+            created = Docs.Create.RecordingDocument((field).url.href, resolved);
+            layout = RecordingBox.LayoutString;
         } else if (field instanceof InkField) {
             created = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), (field).inkData, resolved);
             layout = InkingStroke.LayoutString;
diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js
index 82b10608d..012ee163c 100644
--- a/src/client/goldenLayout.js
+++ b/src/client/goldenLayout.js
@@ -458,8 +458,9 @@
                 if (this._bDragging === true) {
                     this._bDragging = false;
                     this.emit('dragStop', oEvent, this._nOriginalX + this._nX);
-                } else if (oEvent.target) { // make title receive pointer events to allow setting insertion position or selecting texst range
-                    if (oEvent.target.className.includes("lm_title_wrap")) {
+                } else  { // make title receive pointer events to allow setting insertion position or selecting texst range
+                    const classname = typeof oEvent.target?.className === "string" ? oEvent.target.className : "";
+                    if (classname.includes("lm_title_wrap")) {
                         oEvent.target.children[0].style.pointerEvents = "all";
                         oEvent.target.children[0].focus();
                     }
@@ -1431,7 +1432,7 @@
             this.root.callDownwards('_$init');
 
             if (config.maximisedItemId === '__glMaximised') {
-                this.root.getItemsById(config.maximisedItemId)[0].toggleMaximise();
+                this.root.getItemsById(config.maximisedItemId)[0]?.toggleMaximise();
             }
         },
 
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 51bfdbbd2..b4e18a8bb 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -7,8 +7,8 @@ import { List } from "../../fields/List";
 import { PrefetchProxy } from "../../fields/Proxy";
 import { RichTextField } from "../../fields/RichTextField";
 import { ComputedField, ScriptField } from "../../fields/ScriptField";
-import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types";
-import { nullAudio } from "../../fields/URLField";
+import { BoolCast, Cast, DateCast, DocCast, NumCast, PromiseValue, StrCast } from "../../fields/Types";
+import { ImageField, nullAudio } from "../../fields/URLField";
 import { SharingPermissions } from "../../fields/util";
 import { Utils } from "../../Utils";
 import { DocServer } from "../DocServer";
@@ -22,11 +22,10 @@ import { TreeView } from "../views/collections/TreeView";
 import { Colors } from "../views/global/globalEnums";
 import { MainView } from "../views/MainView";
 import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox";
-import { LabelBox } from "../views/nodes/LabelBox";
 import { CollectionFreeFormDocumentView } from "../views/nodes/CollectionFreeFormDocumentView";
 import { OverlayView } from "../views/OverlayView";
 import { DocumentManager } from "./DocumentManager";
-import { DragManager, dropActionType } from "./DragManager";
+import { DragManager } from "./DragManager";
 import { makeTemplate, MakeTemplate } from "./DropConverter";
 import { HistoryUtil } from "./History";
 import { LinkManager } from "./LinkManager";
@@ -272,8 +271,8 @@ export class CurrentUserUtils {
             doc.emptyScreenshot = Docs.Create.ScreenshotDocument({ ...standardOps(), title: "empty screenshot", _width: 400, _height: 200 });
         }
         if (doc.emptyWall === undefined) {
-            doc.emptyWall = Docs.Create.ScreenshotDocument({ ...standardOps(), title: "screen snapshot", _width: 400, _height: 200, });
-            (doc.emptyWall as Doc).videoWall = true;
+            doc.emptyWall = Docs.Create.WebCamDocument("", { _width: 400, _height: 200, title: "recording", system: true, cloneFieldFilter: new List<string>(["system"]) });
+            (doc.emptyWall as Doc).recording = true;
         }
         if (doc.emptyAudio === undefined) {
             doc.emptyAudio = Docs.Create.AudioDocument(nullAudio, { ...standardOps(), title: "audio recording", x: 200, y: 200, _width: 200, _height: 100, });
@@ -355,6 +354,7 @@ export class CurrentUserUtils {
     }
 
     static menuBtnDescriptions(doc: Doc) {
+        const badgeValue = ScriptField.MakeFunction("((len) => len ? len: undefined)(docList(self.target.data).filter(doc => !docList(self.target.viewed).includes(doc)).length)")
         return [
             { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' },
             { title: "Search", target: Cast(doc.mySearchPanel, Doc, null), icon: "search", click: 'selectMainMenu(self)' },
@@ -362,7 +362,7 @@ export class CurrentUserUtils {
             { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" },
             { title: "Imports", target: Cast(doc.myImportDocs, Doc, null), icon: "upload", click: 'selectMainMenu(self)' },
             { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' },
-            { title: "Shared  with me", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc },
+            { title: "Shared  with me", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', badgeValue},
             { title: "Trails", target: Cast(doc.myTrails, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' },
             { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)', hidden: "IsNoviceMode()" },
         ];
@@ -370,8 +370,9 @@ export class CurrentUserUtils {
 
     static async setupMenuPanel(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
         if (doc.menuStack === undefined) {
-            await this.setupSharingSidebar(doc, sharingDocumentId, linkDatabaseId);  // sets up the right sidebar collection for mobile upload documents and sharing
-            const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, watchedDocuments, hidden }) =>
+            await this.setupLinkDocs(doc, linkDatabaseId);
+            await this.setupSharedDocs(doc, sharingDocumentId);  // sets up the right sidebar collection for mobile upload documents and sharing
+            const menuBtns = CurrentUserUtils.menuBtnDescriptions(doc).map(({ title, target, icon, click, badgeValue, hidden }) =>
                 Docs.Create.FontIconDocument({
                     icon,
                     btnType: ButtonType.MenuButton,
@@ -382,12 +383,13 @@ export class CurrentUserUtils {
                     dontUndo: true,
                     title,
                     target,
+                    dontRegisterView: true,
                     hidden: hidden ? ComputedField.MakeFunction("IsNoviceMode()") as any : undefined,
                     _dropAction: "alias",
                     _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
                     _width: 60,
                     _height: 60,
-                    watchedDocuments,
+                    badgeValue,
                     onClick: ScriptField.MakeScript(click, { scriptContext: "any" })
                 })
             );
@@ -400,6 +402,7 @@ export class CurrentUserUtils {
                 _chromeHidden: true,
                 backgroundColor: Colors.DARK_GRAY,
                 boxShadow: "rgba(0,0,0,0)",
+                dontRegisterView: true,
                 dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }),
                 ignoreClick: true,
                 _gridGap: 0,
@@ -720,7 +723,7 @@ export class CurrentUserUtils {
             ];
             doc.dockedBtns = CurrentUserUtils.linearButtonList({
                 title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true,
-                linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true
+                childDontRegisterViews: true, linearViewIsExpanded: true, linearViewExpandable: true, ignoreClick: true
             }, btnDescs.map(desc => doc[`dockedBtn-${desc.title}`] as Doc ?? (doc[`dockedBtn-${desc.title}`] = dockBtn({ title: desc.title, ...desc.opts() }))));
         }
     }
@@ -834,12 +837,13 @@ export class CurrentUserUtils {
         });
         if (doc.contextMenuBtns === undefined) {
             doc.contextMenuBtns = CurrentUserUtils.linearButtonList(
-                { title: "menu buttons", flexGap: 0, linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 },
+                { title: "menu buttons", flexGap: 0, childDontRegisterViews: true,  linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 },
                 CurrentUserUtils.contextMenuTools(doc).map(params =>
                     !params.subMenu ?
                         btnFunc(params) :
                         CurrentUserUtils.linearButtonList({
                             title: params.title,
+                            childDontRegisterViews: true,
                             linearViewSubMenu: true, flexGap: 0, ignoreClick: true,
                             linearViewExpandable: true, icon: params.title, _height: 30,
                             linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined,
@@ -855,6 +859,7 @@ export class CurrentUserUtils {
                         btnFunc(params) :
                         CurrentUserUtils.linearButtonList({
                             title: params.title,
+                            childDontRegisterViews: true,
                             linearViewSubMenu: true, flexGap: 0, ignoreClick: true,
                             linearViewExpandable: true, icon: params.title, _height: 30,
                             linearViewIsExpanded: params.expanded ? !(ComputedField.MakeFunction(params.expanded) as any) : undefined,
@@ -897,10 +902,7 @@ export class CurrentUserUtils {
     }
 
     // Sharing sidebar is where shared documents are contained
-    static async setupSharingSidebar(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
-        if (doc.myPublishedDocs === undefined) {
-            doc.myPublishedDocs = new List<Doc>();
-        }
+    static async setupLinkDocs(doc: Doc, linkDatabaseId: string) {
         if (doc.myLinkDatabase === undefined) {
             let linkDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(linkDatabaseId);
             if (!linkDocs) {
@@ -912,29 +914,41 @@ export class CurrentUserUtils {
             }
             doc.myLinkDatabase = new PrefetchProxy(linkDocs);
         }
-        // TODO:glr NOTE: treeViewHideTitle & _showTitle may be confusing, treeViewHideTitle is for the editable title (just for tree view), _showTitle is to show the Document title for any document
-        if (doc.mySharedDocs === undefined) {
-            let sharedDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(sharingDocumentId + "outer");
-            if (!sharedDocs) {
-                sharedDocs = Docs.Create.TreeDocument([], {
-                    title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15,
-                    _showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment,
-                    _chromeHidden: true, boxShadow: "0 0",
-                    explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
-                }, sharingDocumentId + "outer", sharingDocumentId);
-                (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Augment;
-            }
-            if (sharedDocs instanceof Doc) {
-                Doc.GetProto(sharedDocs).userColor = sharedDocs.userColor || "rgb(202, 202, 202)";
-                const addToDashboards = ScriptField.MakeScript(`addToDashboards(self)`);
-                const dashboardFilter = ScriptField.MakeFunction(`doc._viewType === '${CollectionViewType.Docking}'`, { doc: Doc.name });
-                sharedDocs.childContextMenuFilters = new List<ScriptField>([dashboardFilter!,]);
-                sharedDocs.childContextMenuScripts = new List<ScriptField>([addToDashboards!,]);
-                sharedDocs.childContextMenuLabels = new List<string>(["Add to Dashboards",]);
-                sharedDocs.childContextMenuIcons = new List<string>(["user-plus",]);
-
-            }
-            doc.mySharedDocs = new PrefetchProxy(sharedDocs as Doc);
+    }
+    // A user's sharing document is where all documents that are shared to that user are placed.  
+    // When the user views one of these documents, it will be added to the sharing documents 'viewed' list field
+    // The sharing document also stores the user's color value which helps distinguish shared documents from personal documents
+    static async setupSharedDocs(doc: Doc, sharingDocumentId: string) {
+        const addToDashboards = ScriptField.MakeScript(`addToDashboards(self)`);
+        const dashboardFilter = ScriptField.MakeFunction(`doc._viewType === '${CollectionViewType.Docking}'`, { doc: Doc.name });
+        const dblClkScript = ScriptField.MakeScript("{scriptContext.openLevel(documentView); addDocToList(scriptContext.props.treeView.props.Document, 'viewed', documentView.rootDoc);}", {scriptContext:"any", documentView:Doc.name})
+           
+        const sharedDocOpts:DocumentOptions = {
+            title: "My Shared Docs",
+            userColor: "rgb(202, 202, 202)",
+            childContextMenuFilters: new List<ScriptField>([dashboardFilter!,]),
+            childContextMenuScripts: new List<ScriptField>([addToDashboards!,]),
+            childContextMenuLabels: new List<string>(["Add to Dashboards",]),
+            childContextMenuIcons: new List<string>(["user-plus",]),
+            treeViewChildDoubleClick: dblClkScript, 
+         };
+        const sharedRequiredDocOpts:DocumentOptions = { 
+            "acl-Public": SharingPermissions.Augment, "_acl-Public": SharingPermissions.Augment,
+            childDropAction: "alias", system: true, contentPointerEvents: "all", childLimitHeight: 0, _yMargin: 50, _gridGap: 15,
+            // NOTE: treeViewHideTitle & _showTitle is for a TreeView's editable title, _showTitle is for DocumentViews title bar
+            _showTitle: "title", treeViewHideTitle: true, ignoreClick: true, _lockedPosition: true, boxShadow: "0 0", _chromeHidden: true, dontRegisterView: true, 
+            explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
+        };
+        
+        const sharedDocs = Docs.newAccount ? undefined : DocCast(doc.mySharedDocs) ?? DocCast(await DocServer.GetRefField(sharingDocumentId + "outer"));
+        if (!(sharedDocs instanceof Doc)) {
+            doc.mySharedDocs = new PrefetchProxy(
+                Docs.Create.TreeDocument([], {...sharedDocOpts, ...sharedRequiredDocOpts}, sharingDocumentId + "outer", sharingDocumentId));
+        } else {
+            Object.entries(sharedRequiredDocOpts).forEach(pair => {
+                const targetDoc = pair[0].startsWith("_") ? sharedDocs as Doc : Doc.GetProto(sharedDocs as Doc);
+                targetDoc[pair[0]] = pair[1];
+            });
         }
     }
 
@@ -945,7 +959,7 @@ export class CurrentUserUtils {
             doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([], {
                 title: "My Imports", _forceActive: true, buttonMenu: true, buttonMenuDoc: newImportButton, ignoreClick: true, _showTitle: "title", _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0,
                 childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true,
-                explainer: "This is where documents that are Imported into Dash will go."
+                dontRegisterView: true, explainer: "This is where documents that are Imported into Dash will go."
             }));
         }
     }
@@ -954,7 +968,7 @@ export class CurrentUserUtils {
     static setupSearchSidebar(doc: Doc) {
         if (doc.mySearchPanel === undefined) {
             doc.mySearchPanel = new PrefetchProxy(Docs.Create.SearchDocument({
-                backgroundColor: "dimgray", ignoreClick: true, _searchDoc: true,
+                dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, _searchDoc: true,
                 childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "Search Panel", system: true
             })) as any as Doc;
         }
@@ -1042,6 +1056,7 @@ export class CurrentUserUtils {
         doc.savedFilters = new List<Doc>();
         doc.filterDocCount = 0;
         doc.freezeChildren = "remove|add";
+        doc.myPublishedDocs = doc.myPublishedDocs ?? new List<Doc>();
         doc.myHeaderBarDoc = doc.myHeaderBarDoc ?? Docs.Create.MulticolumnDocument([], { title: "header bar", system: true });
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
         this.setupDocTemplates(doc); // sets up the template menu of templates
@@ -1094,6 +1109,7 @@ export class CurrentUserUtils {
                     Docs.newAccount = !(field instanceof Doc);
                     await Docs.Prototypes.initialize();
                     const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc;
+                    Docs.newAccount &&(userDoc.activePage = "home");
                     const updated = this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId);
                     (await DocListCastAsync(Cast(Doc.UserDoc().myLinkDatabase, Doc, null)?.data))?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager
                         const a1 = await Cast(link?.anchor1, Doc, null);
@@ -1111,6 +1127,9 @@ export class CurrentUserUtils {
 
     public static openDashboard = (userDoc: Doc, doc: Doc, fromHistory = false) => {
         CurrentUserUtils.MainDocId = doc[Id];
+        if (!DocListCast(CurrentUserUtils.MyDashboards.data).includes(doc)) {
+            Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", doc);
+        }
 
         if (doc) {  // this has the side-effect of setting the main container since we're assigning the active/guest dashboard
             !("presentationView" in doc) && (doc.presentationView = new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })]));
@@ -1184,18 +1203,42 @@ export class CurrentUserUtils {
         input.click();
     }
 
+    public static CaptureDashboardThumbnail() {
+        const docView = CollectionDockingView.Instance.props.DocumentView?.();
+        const content = docView?.ContentDiv;
+        if (docView && content) {
+            const _width = Number(getComputedStyle(content).width.replace("px",""));
+            const _height = Number(getComputedStyle(content).height.replace("px",""));
+            return CollectionFreeFormView.UpdateIcon(
+                docView.layoutDoc[Id] + "-icon" + (new Date()).getTime(),
+                content,
+                _width, _height,
+                _width, _height, 0, 1, true, docView.layoutDoc[Id] + "-icon", 
+                (iconFile, _nativeWidth, _nativeHeight) => {
+                    const img = Docs.Create.ImageDocument(new ImageField(iconFile), { title: docView.rootDoc.title+"-icon", _width, _height, _nativeWidth, _nativeHeight});
+                    const proto = Cast(img.proto, Doc, null)!;
+                    proto["data-nativeWidth"] = _width;
+                    proto["data-nativeHeight"] = _height;
+                    Doc.GetProto(CurrentUserUtils.ActiveDashboard).thumb = img;
+                });
+        }
+
+    }
+
     public static async snapshotDashboard(userDoc: Doc) {
         const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard);
         Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy);
         CurrentUserUtils.openDashboard(userDoc, copy);
     }
 
+    public static closeActiveDashboard = () => {
+          Doc.UserDoc().activeDashboard = undefined;
+    }
+
     public static createNewDashboard = async (userDoc: Doc, id?: string) => {
         const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true);
         const dashboards = await Cast(userDoc.myDashboards, Doc) as Doc;
         const dashboardCount = DocListCast(dashboards.data).length + 1;
-        const emptyPane = Cast(userDoc.emptyPane, Doc, null);
-        emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
         const freeformOptions: DocumentOptions = {
             x: 0,
             y: 400,
@@ -1203,7 +1246,7 @@ export class CurrentUserUtils {
             _height: 1000,
             _fitWidth: true,
             _backgroundGridShow: true,
-            title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`,
+            title: `Untitled Tab 1`,
         };
         const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, "row");
@@ -1212,11 +1255,12 @@ export class CurrentUserUtils {
         // switching the tabs from the datadoc to the regular doc
         const dashboardTabs = DocListCast(dashboardDoc[DataSym].data);
         dashboardDoc.data = new List<Doc>(dashboardTabs);
+        dashboardDoc["pane-count"] = 1;
 
         userDoc.activePresentation = presentation;
 
         Doc.AddDocToList(dashboards, "data", dashboardDoc);
-        CurrentUserUtils.openDashboard(userDoc, dashboardDoc);
+      //   CurrentUserUtils.openDashboard(userDoc, dashboardDoc);
     }
 
     public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) {
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index 9f8c49081..dbfba8992 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -455,7 +455,7 @@ export namespace DragManager {
             if (dragData instanceof DocumentDragData) {
                 dragData.userDropAction = e.ctrlKey && e.altKey ? "copy" : e.ctrlKey ? "alias" : dragData.defaultDropAction;
             }
-            if (((e.target as any)?.className === "lm_tabs" || e?.shiftKey) && dragData.draggedDocuments.length === 1) {
+            if (((e.target as any)?.className === "lm_tabs" || (e.target as any)?.className === "lm_header" || e?.shiftKey) && dragData.draggedDocuments.length === 1) {
                 if (!startWindowDragTimer) {
                     startWindowDragTimer = setTimeout(async () => {
                         startWindowDragTimer = undefined;
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index e6f75a7db..632348306 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -58,8 +58,10 @@ export namespace HistoryUtil {
 
     export function getState(): ParsedUrl {
         const state = copyState(history.state);
-        state.initializers = state.initializers || {};
-        return state;
+        if (state) {
+            state.initializers = state.initializers || {};
+        }
+        return state ?? {initializers:{}};
     }
 
     // export function addHandler(handler: (state: ParsedUrl | null) => void) {
diff --git a/src/client/util/RecordingApi.ts b/src/client/util/RecordingApi.ts
new file mode 100644
index 000000000..021feee9a
--- /dev/null
+++ b/src/client/util/RecordingApi.ts
@@ -0,0 +1,269 @@
+import { CollectionFreeFormView } from "../views/collections/collectionFreeForm";
+import { IReactionDisposer, observable, reaction } from "mobx";
+import { NumCast } from "../../fields/Types";
+import { Doc } from "../../fields/Doc";
+import { VideoBox } from "../views/nodes/VideoBox";
+import { scaleDiverging } from "d3-scale";
+import { Transform } from "./Transform";
+
+type Movement = {
+    time: number,
+    panX: number,
+    panY: number,
+    scale: number,
+}
+
+type Presentation = {
+    movements: Array<Movement> | null
+    meta: Object,
+}
+
+export class RecordingApi {
+
+    private static NULL_PRESENTATION: Presentation = {
+        movements: null,
+        meta: {},
+    }
+
+    // instance variables
+    private currentPresentation: Presentation;
+    private isRecording: boolean;
+    private absoluteStart: number;
+
+
+    // create static instance and getter for global use
+    @observable static _instance: RecordingApi;
+    public static get Instance(): RecordingApi { return RecordingApi._instance }
+    public constructor() {
+        // init the global instance
+        RecordingApi._instance = this;
+
+        // init the instance variables
+        this.currentPresentation = RecordingApi.NULL_PRESENTATION
+        this.isRecording = false;
+        this.absoluteStart = -1;
+
+        // used for tracking movements in the view frame
+        this.disposeFunc = null;
+        this.recordingFFView = null;
+
+        // for now, set playFFView
+        this.playFFView = null;
+        this.timers = null;
+    }
+
+    // little helper :)
+    private get isInitPresenation(): boolean {
+        return this.currentPresentation.movements === null
+    }
+
+    public start = (meta?: Object): Error | undefined => {
+        // check if already init a presentation
+        if (!this.isInitPresenation) {
+            console.error('[recordingApi.ts] start() failed: current presentation data exists. please call clear() first.')
+            return new Error('[recordingApi.ts] start()')
+        }
+
+        // update the presentation mode
+        Doc.UserDoc().presentationMode = 'recording'
+
+        // (1a) get start date for presenation
+        const startDate = new Date()
+        // (1b) set start timestamp to absolute timestamp
+        this.absoluteStart = startDate.getTime()
+
+        // (2) assign meta content if it exists
+        this.currentPresentation.meta = meta || {}
+        // (3) assign start date to currentPresenation
+        this.currentPresentation.movements = []
+        // (4) set isRecording true to allow trackMovements
+        this.isRecording = true
+    }
+
+    public clear = (): Error | Presentation => {
+        // TODO: maybe archive the data?
+        if (this.isRecording) {
+            console.error('[recordingApi.ts] clear() failed: currently recording presentation. call pause() first')
+            return new Error('[recordingApi.ts] clear()')
+        }
+
+        // update the presentation mode
+        Doc.UserDoc().presentationMode = 'none'
+        // set the previus recording view to the play view
+        this.playFFView = this.recordingFFView
+
+        const presCopy = { ...this.currentPresentation }
+
+        // clear presenation data
+        this.currentPresentation = RecordingApi.NULL_PRESENTATION
+        // clear isRecording
+        this.isRecording = false
+        // clear absoluteStart
+        this.absoluteStart = -1
+        // clear the disposeFunc
+        this.removeRecordingFFView()
+
+        return presCopy;
+    }
+
+    public pause = (): Error | undefined => {
+        if (this.isInitPresenation) {
+            console.error('[recordingApi.ts] pause() failed: no presentation started. try calling init() first')
+            return new Error('[recordingApi.ts] pause(): no presentation')
+        }
+        // don't allow track movments
+        this.isRecording = false
+
+        // set adjust absoluteStart to add the time difference
+        const timestamp = new Date().getTime()
+        this.absoluteStart = timestamp - this.absoluteStart
+    }
+
+    public resume = () => {
+        this.isRecording = true
+        // set absoluteStart to the difference in time
+        this.absoluteStart = new Date().getTime() - this.absoluteStart
+    }
+
+    private trackMovements = (panX: number, panY: number, scale: number = 0): Error | undefined => {
+        // ensure we are recording
+        if (!this.isRecording) {
+            return new Error('[recordingApi.ts] trackMovements()')
+        }
+        // check to see if the presetation is init
+        if (this.isInitPresenation) {
+            return new Error('[recordingApi.ts] trackMovements(): no presentation')
+        }
+
+        // get the time
+        const time = new Date().getTime() - this.absoluteStart
+        // make new movement object
+        const movement: Movement = { time, panX, panY, scale }
+        
+        // add that movement to the current presentation data's movement array
+        this.currentPresentation.movements && this.currentPresentation.movements.push(movement)
+    }
+
+    // instance variable for the FFView
+    private disposeFunc: IReactionDisposer | null;
+    private recordingFFView: CollectionFreeFormView | null;
+    
+    // set the FFView that will be used in a reaction to track the movements
+    public setRecordingFFView = (view: CollectionFreeFormView): void => {
+        // set the view to the current view
+        if (view === this.recordingFFView || view == null) return;
+        
+        // this.recordingFFView = view;
+        // set the reaction to track the movements
+        this.disposeFunc = reaction(
+            () => ({ x: NumCast(view.Document.panX, -1), y: NumCast(view.Document.panY, -1), scale: NumCast(view.Document.viewScale, -1) }),
+            (res) => (res.x !== -1 && res.y !== -1 && this.isRecording) && this.trackMovements(res.x, res.y, res.scale)
+        )
+
+        // for now, set the most recent recordingFFView to the playFFView
+        this.recordingFFView = view;
+    }
+
+    // call on dispose function to stop tracking movements
+    public removeRecordingFFView = (): void => {
+        this.disposeFunc?.();
+        this.disposeFunc = null;
+    }
+
+    // TODO: extract this into different class with pause and resume recording
+    // TODO: store the FFview with the movements
+    private playFFView: CollectionFreeFormView | null;
+    private timers: NodeJS.Timeout[] | null;
+
+    public setPlayFFView = (view: CollectionFreeFormView): void => {
+        this.playFFView = view
+    }
+
+    // pausing movements will dispose all timers that are planned to replay the movements
+    // play movemvents will recreate them when the user resumes the presentation
+    public pauseMovements = (): undefined | Error => {
+        if (this.playFFView === null) {
+            return new Error('[recordingApi.ts] pauseMovements() failed: no view')
+        }
+
+        if (!this._isPlaying) {
+            //return new Error('[recordingApi.ts] pauseMovements() failed: not playing')
+            return
+        }
+        this._isPlaying = false
+        // TODO: set userdoc presentMode to browsing
+        this.timers?.map(timer => clearTimeout(timer))
+
+        // this.videoBox = null;
+    }
+    
+    private videoBox: VideoBox | null = null;
+
+    // by calling pause on the VideoBox, the pauseMovements will be called
+    public pauseVideoAndMovements = (): boolean => {
+        this.videoBox?.Pause()
+
+        this.pauseMovements()
+        return this.videoBox == null
+    }
+
+    public _isPlaying = false;
+
+    public playMovements = (presentation: Presentation, timeViewed: number = 0, videoBox?: VideoBox): undefined | Error => {
+        if (presentation.movements === null || this.playFFView === null) {
+            return new Error('[recordingApi.ts] followMovements() failed: no presentation data or no view')
+        }
+        if(this._isPlaying) return;
+
+        this._isPlaying = true;
+        Doc.UserDoc().presentationMode = 'watching';
+
+        // TODO: consider this bug at the end of the clip on seek
+        this.videoBox = videoBox || null;
+
+        // only get the movements that are remaining in the video time left
+        const filteredMovements = presentation.movements.filter(movement => movement.time > timeViewed * 1000)
+
+        // helper to replay a movement
+        const document = this.playFFView
+        let preScale = -1;
+        const zoomAndPan = (movement: Movement) => {
+            const { panX, panY, scale } = movement;
+            (scale !== -1 && preScale !== scale) && document.zoomSmoothlyAboutPt([panX, panY], scale, 0);
+            document.Document._panX = panX;
+            document.Document._panY = panY;
+
+            preScale = scale;
+        }
+
+        // set the first frame to be at the start of the pres
+        zoomAndPan(filteredMovements[0]);
+
+        // make timers that will execute each movement at the correct replay time
+        this.timers = filteredMovements.map(movement => {
+            const timeDiff = movement.time - timeViewed*1000
+            return setTimeout(() => {
+                // replay the movement
+                zoomAndPan(movement)
+                // if last movement, presentation is done -> set the instance var
+                if (movement === filteredMovements[filteredMovements.length - 1]) RecordingApi.Instance._isPlaying = false;
+            }, timeDiff)
+        })
+    }
+
+    // Unfinished code for tracing multiple free form views
+    // export let pres: Map<CollectionFreeFormView, IReactionDisposer> = new Map()
+
+    // export function AddRecordingFFView(ffView: CollectionFreeFormView): void {
+    //     pres.set(ffView,
+    //          reaction(() => ({ x: ffView.panX, y: ffView.panY }),
+    //     (pt) => RecordingApi.trackMovements(ffView, pt.x, pt.y)))
+    //     )
+    // }
+
+    // export function RemoveRecordingFFView(ffView: CollectionFreeFormView): void {
+    //     const disposer = pres.get(ffView);
+    //     disposer?.();
+    //     pres.delete(ffView)
+    // }
+}
diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts
index 3b0a47b54..223b0268c 100644
--- a/src/client/util/Scripting.ts
+++ b/src/client/util/Scripting.ts
@@ -223,7 +223,7 @@ export function CompileScript(script: string, options: ScriptOptions = {}): Comp
         paramList.push(`${key}: ${typeof val === "object" ? Object.getPrototypeOf(val).constructor.name : typeof val}`);
     }
     const paramString = paramList.join(", ");
-    const body = addReturn ? `return ${script};` : `return ${script};`;
+    const body = addReturn ? `return ${script};` : script;
     const reqTypes = requiredType ? `: ${requiredType}` : '';
     const funcScript = `(function(${paramString})${reqTypes} { ${body} })`;
     host.writeFile("file.ts", funcScript);
diff --git a/src/client/views/DashboardView.scss b/src/client/views/DashboardView.scss
new file mode 100644
index 000000000..67587dd2b
--- /dev/null
+++ b/src/client/views/DashboardView.scss
@@ -0,0 +1,48 @@
+.dashboard-view {
+      padding: 50px;
+      display: flex;
+      flex-direction: row;
+
+      .left-menu {
+            display: flex;
+            flex-direction: column;
+            width: 300px;
+      }
+
+      .all-dashboards {
+            display: flex;
+            flex-direction: row;
+            flex-wrap: wrap;
+            overflow-y: scroll;
+      }
+}
+
+.text-button {
+      padding: 10px 0;
+      &:hover {
+            font-weight: 500;
+      }
+
+      &.selected {
+            font-weight: 700;
+      }
+}
+
+.dashboard-container {
+      border-radius: 10px;
+      width: 250px;
+      border: solid .5px grey;
+      display: flex;
+      flex-direction: column;
+      margin: 0 30px 30px 30px;
+      overflow: hidden;
+
+      &:hover {
+            border: solid 1.5px grey;  
+      }
+
+      .title {
+            margin: 10px;
+            font-weight: 500;
+      }
+}
\ No newline at end of file
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
new file mode 100644
index 000000000..efc1644fe
--- /dev/null
+++ b/src/client/views/DashboardView.tsx
@@ -0,0 +1,69 @@
+import { action, observable } from "mobx";
+import { extname } from 'path';
+import { observer } from "mobx-react";
+import * as React from 'react';
+import { Doc, DocListCast } from "../../fields/Doc";
+import { Id } from "../../fields/FieldSymbols";
+import { Cast, ImageCast, StrCast } from "../../fields/Types";
+import { CurrentUserUtils } from "../util/CurrentUserUtils";
+import { UndoManager } from "../util/UndoManager";
+import "./DashboardView.scss"
+
+enum DashboardGroup {
+      MyDashboards, SharedDashboards
+}
+
+@observer
+export class DashboardView extends React.Component {
+
+      //TODO: delete dashboard, share dashboard, etc.
+
+      @observable
+      private selectedDashboardGroup = DashboardGroup.MyDashboards;
+
+      @action
+      selectDashboardGroup = (group: DashboardGroup) => {
+            this.selectedDashboardGroup = group
+      }
+
+      newDashboard = async () => {
+            const batch = UndoManager.StartBatch("new dash");
+            await CurrentUserUtils.createNewDashboard(Doc.UserDoc());
+            batch.end();
+      }
+
+      clickDashboard = async (e: React.MouseEvent, dashboard: Doc) => {
+            if (e.detail === 2) {
+                  Doc.UserDoc().activeDashboard = dashboard;
+                  Doc.UserDoc().activePage = "dashboard";
+            }
+      }
+
+      getDashboards = () => {
+            const allDashbaords = DocListCast(CurrentUserUtils.MyDashboards.data);
+            // TODO: filter the dashboards
+            // return allDashbaords.filter(...)
+            return allDashbaords
+      }
+
+      render() {
+            return <div className="dashboard-view">
+                  <div className="left-menu">
+                        <div className="text-button" onClick={this.newDashboard}>New</div>
+                        <div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.MyDashboards && 'selected'}`}onClick={() => this.selectDashboardGroup(DashboardGroup.MyDashboards) }>My Dashboards</div>
+                        <div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.SharedDashboards && 'selected'}`} onClick={() => this.selectDashboardGroup(DashboardGroup.SharedDashboards) }>Shared Dashboards</div>
+                  </div>
+                  <div className="all-dashboards">
+                        {this.getDashboards().map((dashboard) => {
+                              const href = ImageCast((dashboard.thumb as Doc)?.data)?.url.href;
+                              return <div className="dashboard-container" key={dashboard[Id]} onClick={e => this.clickDashboard(e, dashboard)}>
+                                    <img src={href ?? "https://media.istockphoto.com/photos/hot-air-balloons-flying-over-the-botan-canyon-in-turkey-picture-id1297349747?b=1&k=20&m=1297349747&s=170667a&w=0&h=oH31fJty_4xWl_JQ4OIQWZKP8C6ji9Mz7L4XmEnbqRU="}></img>
+                                    <div className="title"> {StrCast(dashboard.title)} </div>
+                              </div>
+
+                        })}
+
+                  </div>
+            </div>
+      }
+}
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 9c176a4fd..8486b3d62 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -60,12 +60,9 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() {
         @computed get layoutDoc() { return Doc.Layout(this.props.Document); }
         // This is the data part of a document -- ie, the data that is constant across all views of the document
         @computed get dataDoc() { return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym]; }
-
         // key where data is stored
         @computed get fieldKey() { return this.props.fieldKey; }
 
-        lookupField = (field: string) => ScriptCast(this.layoutDoc.lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field, container: this.props.DocumentView?.().props.treeViewDoc ?? this.props.ContainingCollectionDoc }).result;
-
         isContentActive = (outsideReaction?: boolean) => (
             this.props.isContentActive?.() === false ? false :
                 (CurrentUserUtils.SelectedTool !== InkTool.None ||
@@ -95,7 +92,6 @@ export interface ViewBoxAnnotatableProps {
 export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>() {
     class Component extends Touchable<P> {
         @observable _annotationKeySuffix = () => "annotations";
-
         @observable _isAnyChildContentActive = false;
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
         @computed get Document() { return this.props.Document; }
@@ -187,7 +183,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             const targetDataDoc = this.props.Document[DataSym];
             const docList = DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]);
             const added = docs.filter(d => !docList.includes(d));
-            const effectiveAcl = GetEffectiveAcl(this.dataDoc);
+            const effectiveAcl = GetEffectiveAcl(targetDataDoc);
 
             if (added.length) {
                 if (effectiveAcl === AclPrivate || effectiveAcl === AclReadonly) {
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 25297e167..0bbe473d7 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -204,58 +204,13 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <div className="dash-tooltip">{SelectionManager.Views().length > 1 ? "Pin multiple documents to presentation" : "Pin to presentation"}</div>}>
             <div className="documentButtonBar-icon"
                 style={{ color: "white" }}
-                onClick={undoBatch(e => this.props.views().map(view => view && this.pinWithView(view.props.Document)))}>
+                onClick={(e => TabDocView.PinDoc(this.props.views().filter(v => v).map(dv => dv!.rootDoc), {pinDocView: true}))}
+             >
                 <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
             </div>
         </Tooltip>;
     }
 
-    @undoBatch
-    @action
-    pinWithView = (targetDoc: Doc) => {
-        if (targetDoc) {
-            TabDocView.PinDoc(targetDoc);
-            setTimeout(() => {
-                const activeDoc = PresBox.Instance.childDocs[PresBox.Instance.childDocs.length - 1];
-                const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(targetDoc.type as any) || targetDoc._viewType === CollectionViewType.Stacking;
-                const pannable: boolean = ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG);
-                if (scrollable) {
-                    const scroll = targetDoc._scrollTop;
-                    activeDoc.presPinView = true;
-                    activeDoc.presPinViewScroll = scroll;
-                } else if (targetDoc.type === DocumentType.VID) {
-                    activeDoc.presPinView = true;
-                    activeDoc.presStartTime = targetDoc._currentTimecode;
-                    activeDoc.presEndTime = NumCast(targetDoc._currentTimecode) + 0.1;
-                    activeDoc.presPinTimecode = targetDoc._currentTimecode;
-                } else if (pannable) {
-                    const x = targetDoc._panX;
-                    const y = targetDoc._panY;
-                    const scale = targetDoc._viewScale;
-                    activeDoc.presPinView = true;
-                    activeDoc.presPinViewX = x;
-                    activeDoc.presPinViewY = y;
-                    activeDoc.presPinViewScale = scale;
-                } else if (targetDoc.type === DocumentType.COMPARISON) {
-                    const width = targetDoc._clipWidth;
-                    activeDoc.presPinClipWidth = width;
-                    activeDoc.presPinView = true;
-                }
-            });
-        }
-    }
-
-    @computed
-    get pinWithViewButton() {
-        const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: "auto", width: 17, transform: 'translate(0, 1px)' }} />;
-        const targetDoc = this.view0?.props.Document;
-        return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Pin with current view"}</div></>}>
-            <div className="documentButtonBar-icon" onClick={() => this.pinWithView(targetDoc)}>
-                {presPinWithViewIcon}
-            </div>
-        </Tooltip>;
-    }
-
     @computed
     get shareButton() {
         const targetDoc = this.view0?.props.Document;
@@ -380,9 +335,6 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <div className="documentButtonBar-button">
                 {this.pinButton}
             </div>
-            {/* <div className="documentButtonBar-button">
-                {this.pinWithViewButton}
-            </div> */}
             {!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button">
                 {this.shareButton}
             </div>}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 29e088143..b666b5977 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -33,548 +33,549 @@ import React = require("react");
 
 @observer
 export class DocumentDecorations extends React.Component<{ PanelWidth: number, PanelHeight: number, boundsLeft: number, boundsTop: number }, { value: string }> {
-    static Instance: DocumentDecorations;
-    private _resizeHdlId = "";
-    private _keyinput = React.createRef<HTMLInputElement>();
-    private _resizeBorderWidth = 16;
-    private _linkBoxHeight = 20 + 3; // link button height + margin
-    private _titleHeight = 20;
-    private _resizeUndo?: UndoManager.Batch;
-    private _offX = 0; _offY = 0;  // offset from click pt to inner edge of resize border
-    private _snapX = 0; _snapY = 0; // last snapped location of resize border
-    private _dragHeights = new Map<Doc, { start: number, lowest: number }>();
-    private _inkDragDocs: { doc: Doc, x: number, y: number, width: number, height: number }[] = [];
-
-    @observable private _accumulatedTitle = "";
-    @observable private _titleControlString: string = "#title";
-    @observable private _edtingTitle = false;
-    @observable private _hidden = false;
-    @observable public AddToSelection = false; // if Shift is pressed, then this should be set so that clicking on the selection background is ignored so overlapped documents can be added to the selection set.
-    @observable public Interacting = false;
-    @observable public pushIcon: IconProp = "arrow-alt-circle-up";
-    @observable public pullIcon: IconProp = "arrow-alt-circle-down";
-    @observable public pullColor: string = "white";
-
-    constructor(props: any) {
-        super(props);
-        DocumentDecorations.Instance = this;
-        reaction(() => SelectionManager.Views().slice(), action(docs => this._edtingTitle = false));
-    }
-
-    @computed
-    get Bounds() {
-        const views = SelectionManager.Views();
-        return views.filter(dv => dv.props.renderDepth > 0).map(dv => dv.getBounds()).reduce((bounds, rect) =>
-            !rect ? bounds :
-                {
-                    x: Math.min(rect.left, bounds.x),
-                    y: Math.min(rect.top, bounds.y),
-                    r: Math.max(rect.right, bounds.r),
-                    b: Math.max(rect.bottom, bounds.b),
-                    c: views.length === 1 ? rect.center : undefined
-                },
-            { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE, c: undefined as ({ X: number, Y: number } | undefined) });
-    }
-
-    @action
-    titleBlur = () => {
-        this._edtingTitle = false;
-        if (this._accumulatedTitle.startsWith("#") || this._accumulatedTitle.startsWith("=")) {
-            this._titleControlString = this._accumulatedTitle;
-        } else if (this._titleControlString.startsWith("#")) {
-            const titleFieldKey = this._titleControlString.substring(1);
-            UndoManager.RunInBatch(() => titleFieldKey && SelectionManager.Views().forEach(d => {
-                if (titleFieldKey === "title") {
-                    d.dataDoc["title-custom"] = !this._accumulatedTitle.startsWith("-");
-                    if (StrCast(d.rootDoc.title).startsWith("@") && !this._accumulatedTitle.startsWith("@")) {
-                        Doc.RemoveDocFromList(Doc.UserDoc(), "myPublishedDocs", d.rootDoc);
-                    }
-                    if (!StrCast(d.rootDoc.title).startsWith("@") && this._accumulatedTitle.startsWith("@")) {
-                        Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", d.rootDoc);
-                    }
-                }
-                //@ts-ignore
-                const titleField = (+this._accumulatedTitle === this._accumulatedTitle ? +this._accumulatedTitle : this._accumulatedTitle);
-                Doc.SetInPlace(d.rootDoc, titleFieldKey, titleField, true);
-
-                if (d.rootDoc.syncLayoutFieldWithTitle) {
-                    const title = titleField.toString();
-                    const curKey = Doc.LayoutFieldKey(d.rootDoc);
-                    if (curKey !== title && d.dataDoc[title] === undefined) {
-                        d.rootDoc.layout = FormattedTextBox.LayoutString(title);
-                        setTimeout(() => {
-                            const val = d.dataDoc[curKey];
-                            d.dataDoc[curKey] = undefined;
-                            d.dataDoc[title] = val;
-                        });
-                    }
-                }
-            }), "title blur");
-        }
-    }
-
-    titleEntered = (e: React.KeyboardEvent) => {
-        if (e.key === "Enter") {
-            e.stopPropagation();
-            (e.target as any).blur();
-        }
-    }
-
-    @action onTitleDown = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(this, e, e => this.onBackgroundMove(true, e), (e) => { }, action((e) => {
-            !this._edtingTitle && (this._accumulatedTitle = this._titleControlString.startsWith("#") ? this.selectionTitle : this._titleControlString);
-            this._edtingTitle = true;
-            this._keyinput.current && setTimeout(this._keyinput.current.focus);
-        }));
-    }
-
-    onBackgroundDown = (e: React.PointerEvent) => setupMoveUpEvents(this, e, e => this.onBackgroundMove(false, e), emptyFunction, emptyFunction);
-
-    @action
-    onBackgroundMove = (dragTitle: boolean, e: PointerEvent): boolean => {
-        const dragDocView = SelectionManager.Views()[0];
-        const { left, top } = dragDocView.getBounds() || { left: 0, top: 0 };
-        const dragData = new DragManager.DocumentDragData(SelectionManager.Views().map(dv => dv.props.Document), dragDocView.props.dropAction);
-        dragData.offset = dragDocView.props.ScreenToLocalTransform().transformDirection(e.x - left, e.y - top);
-        dragData.moveDocument = dragDocView.props.moveDocument;
-        dragData.isDocDecorationMove = true;
-        dragData.canEmbed = dragTitle;
-        this._hidden = this.Interacting = true;
-        DragManager.StartDocumentDrag(SelectionManager.Views().map(dv => dv.ContentDiv!), dragData, e.x, e.y, {
-            dragComplete: action(e => {
-                dragData.canEmbed && SelectionManager.DeselectAll();
-                this._hidden = this.Interacting = false;
-            }),
-            hideSource: true
-        });
-        return true;
-    }
-
-    _deleteAfterIconify = false;
-    _iconifyBatch: UndoManager.Batch | undefined;
-    onCloseClick = (forceDeleteOrIconify: boolean | undefined) => {
-        if (this.canDelete) {
+      static Instance: DocumentDecorations;
+      private _resizeHdlId = "";
+      private _keyinput = React.createRef<HTMLInputElement>();
+      private _resizeBorderWidth = 16;
+      private _linkBoxHeight = 20 + 3; // link button height + margin
+      private _titleHeight = 20;
+      private _resizeUndo?: UndoManager.Batch;
+      private _offX = 0; _offY = 0;  // offset from click pt to inner edge of resize border
+      private _snapX = 0; _snapY = 0; // last snapped location of resize border
+      private _dragHeights = new Map<Doc, { start: number, lowest: number }>();
+      private _inkDragDocs: { doc: Doc, x: number, y: number, width: number, height: number }[] = [];
+
+      @observable private _accumulatedTitle = "";
+      @observable private _titleControlString: string = "#title";
+      @observable private _edtingTitle = false;
+      @observable private _hidden = false;
+      @observable public AddToSelection = false; // if Shift is pressed, then this should be set so that clicking on the selection background is ignored so overlapped documents can be added to the selection set.
+      @observable public Interacting = false;
+      @observable public pushIcon: IconProp = "arrow-alt-circle-up";
+      @observable public pullIcon: IconProp = "arrow-alt-circle-down";
+      @observable public pullColor: string = "white";
+
+      constructor(props: any) {
+            super(props);
+            DocumentDecorations.Instance = this;
+            reaction(() => SelectionManager.Views().slice(), action(docs => this._edtingTitle = false));
+      }
+
+      @computed
+      get Bounds() {
+            const views = SelectionManager.Views();
+            return views.filter(dv => dv.props.renderDepth > 0).map(dv => dv.getBounds()).reduce((bounds, rect) =>
+                  !rect ? bounds :
+                        {
+                              x: Math.min(rect.left, bounds.x),
+                              y: Math.min(rect.top, bounds.y),
+                              r: Math.max(rect.right, bounds.r),
+                              b: Math.max(rect.bottom, bounds.b),
+                              c: views.length === 1 ? rect.center : undefined
+                        },
+                  { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: Number.MIN_VALUE, b: Number.MIN_VALUE, c: undefined as ({ X: number, Y: number } | undefined) });
+      }
+
+      @action
+      titleBlur = () => {
+            this._edtingTitle = false;
+            if (this._accumulatedTitle.startsWith("#") || this._accumulatedTitle.startsWith("=")) {
+                  this._titleControlString = this._accumulatedTitle;
+            } else if (this._titleControlString.startsWith("#")) {
+                  const titleFieldKey = this._titleControlString.substring(1);
+                  UndoManager.RunInBatch(() => titleFieldKey && SelectionManager.Views().forEach(d => {
+                        if (titleFieldKey === "title") {
+                              d.dataDoc["title-custom"] = !this._accumulatedTitle.startsWith("-");
+                              if (StrCast(d.rootDoc.title).startsWith("@") && !this._accumulatedTitle.startsWith("@")) {
+                                    Doc.RemoveDocFromList(Doc.UserDoc(), "myPublishedDocs", d.rootDoc);
+                              }
+                              if (!StrCast(d.rootDoc.title).startsWith("@") && this._accumulatedTitle.startsWith("@")) {
+                                    Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", d.rootDoc);
+                              }
+                        }
+                        //@ts-ignore
+                        const titleField = (+this._accumulatedTitle === this._accumulatedTitle ? +this._accumulatedTitle : this._accumulatedTitle);
+                        Doc.SetInPlace(d.rootDoc, titleFieldKey, titleField, true);
+
+                        if (d.rootDoc.syncLayoutFieldWithTitle) {
+                              const title = titleField.toString();
+                              const curKey = Doc.LayoutFieldKey(d.rootDoc);
+                              if (curKey !== title && d.dataDoc[title] === undefined) {
+                                    d.rootDoc.layout = FormattedTextBox.LayoutString(title);
+                                    setTimeout(() => {
+                                          const val = d.dataDoc[curKey];
+                                          d.dataDoc[curKey] = undefined;
+                                          d.dataDoc[title] = val;
+                                    });
+                              }
+                        }
+                  }), "title blur");
+            }
+      }
+
+      titleEntered = (e: React.KeyboardEvent) => {
+            if (e.key === "Enter") {
+                  e.stopPropagation();
+                  (e.target as any).blur();
+            }
+      }
+
+      @action onTitleDown = (e: React.PointerEvent): void => {
+            setupMoveUpEvents(this, e, e => this.onBackgroundMove(true, e), (e) => { }, action((e) => {
+                  !this._edtingTitle && (this._accumulatedTitle = this._titleControlString.startsWith("#") ? this.selectionTitle : this._titleControlString);
+                  this._edtingTitle = true;
+                  this._keyinput.current && setTimeout(this._keyinput.current.focus);
+            }));
+      }
+
+      onBackgroundDown = (e: React.PointerEvent) => setupMoveUpEvents(this, e, e => this.onBackgroundMove(false, e), emptyFunction, emptyFunction);
+
+      @action
+      onBackgroundMove = (dragTitle: boolean, e: PointerEvent): boolean => {
+            const dragDocView = SelectionManager.Views()[0];
+            const { left, top } = dragDocView.getBounds() || { left: 0, top: 0 };
+            const dragData = new DragManager.DocumentDragData(SelectionManager.Views().map(dv => dv.props.Document), dragDocView.props.dropAction);
+            dragData.offset = dragDocView.props.ScreenToLocalTransform().transformDirection(e.x - left, e.y - top);
+            dragData.moveDocument = dragDocView.props.moveDocument;
+            dragData.isDocDecorationMove = true;
+            dragData.canEmbed = dragTitle;
+            this._hidden = this.Interacting = true;
+            DragManager.StartDocumentDrag(SelectionManager.Views().map(dv => dv.ContentDiv!), dragData, e.x, e.y, {
+                  dragComplete: action(e => {
+                        dragData.canEmbed && SelectionManager.DeselectAll();
+                        this._hidden = this.Interacting = false;
+                  }),
+                  hideSource: true
+            });
+            return true;
+      }
+
+      _deleteAfterIconify = false;
+      _iconifyBatch: UndoManager.Batch | undefined;
+      onCloseClick = (forceDeleteOrIconify: boolean | undefined) => {
+
             const views = SelectionManager.Views().slice().filter(v => v);
             if (forceDeleteOrIconify === false && this._iconifyBatch) return;
             this._deleteAfterIconify = forceDeleteOrIconify || this._iconifyBatch ? true : false;
             if (!this._iconifyBatch) {
-                this._iconifyBatch = UndoManager.StartBatch("iconifying");
+                  this._iconifyBatch = UndoManager.StartBatch("iconifying");
             } else {
-                forceDeleteOrIconify = false; // can't force immediate close in the middle of iconifying -- have to wait until iconifying completes
+                  forceDeleteOrIconify = false; // can't force immediate close in the middle of iconifying -- have to wait until iconifying completes
             }
             var iconifyingCount = views.length;
             const finished = action((force?: boolean) => {
-                if ((force || --iconifyingCount === 0) && this._iconifyBatch) {
-                    if (this._deleteAfterIconify) {
-                        views.forEach(iconView => iconView.props.removeDocument?.(iconView.props.Document));
-                        SelectionManager.DeselectAll();
-                    }
-                    this._iconifyBatch?.end();
-                    this._iconifyBatch = undefined;
-                }
+                  if ((force || --iconifyingCount === 0) && this._iconifyBatch) {
+                        if (this._deleteAfterIconify) {
+                              views.forEach(iconView => iconView.props.removeDocument?.(iconView.props.Document));
+                              SelectionManager.DeselectAll();
+                        }
+                        this._iconifyBatch?.end();
+                        this._iconifyBatch = undefined;
+                  }
             });
             if (forceDeleteOrIconify) finished(forceDeleteOrIconify);
             else if (!this._deleteAfterIconify) views.forEach(dv => dv.iconify(finished));
-        }
-    }
-    onMaximizeDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, () => {
-            DragManager.StartWindowDrag?.(e, [SelectionManager.Views().slice(-1)[0].rootDoc]);
-            return true;
-        }, emptyFunction, this.onMaximizeClick, false, false);
-    }
-
-    onMaximizeClick = (e: any): void => {
-        const selectedDocs = SelectionManager.Views();
-        if (selectedDocs.length) {
-            if (e.ctrlKey) {    // open an alias in a new tab with Ctrl Key
-                const bestAlias = DocListCast(selectedDocs[0].props.Document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
-                CollectionDockingView.AddSplit(bestAlias ?? Doc.MakeAlias(selectedDocs[0].props.Document), "right");
-            } else if (e.shiftKey) {   // open centered in a new workspace with Shift Key
-                const alias = Doc.MakeAlias(selectedDocs[0].props.Document);
-                alias.context = undefined;
-                alias.x = -alias[WidthSym]() / 2;
-                alias.y = -alias[HeightSym]() / 2;
-                CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([alias], { title: "Tab for " + alias.title }), "right");
-            } else if (e.altKey) {    // open same document in new tab
-                CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, "right");
-            } else {
-                var openDoc = selectedDocs[0].props.Document;
-                if (openDoc.layoutKey === "layout_icon") {
-                    openDoc = DocListCast(openDoc.aliases).find(alias => !alias.context) ?? Doc.MakeAlias(openDoc);
-                    Doc.deiconifyView(openDoc);
-                }
-                LightboxView.SetLightboxDoc(openDoc, undefined, selectedDocs.slice(1).map(view => view.props.Document));
-            }
-        }
-        SelectionManager.DeselectAll();
-    }
-
-    onIconifyClick = (): void => {
-        SelectionManager.Views().forEach(dv => dv?.iconify());
-        SelectionManager.DeselectAll();
-    }
-
-    onSelectorClick = () => SelectionManager.Views()?.[0]?.props.ContainingCollectionView?.props.select(false);
-
-    onRadiusDown = (e: React.PointerEvent): void => {
-        this._resizeUndo = UndoManager.StartBatch("DocDecs set radius");
-        setupMoveUpEvents(this, e, (e, down) => {
-            const dist = Math.sqrt((e.clientX - down[0]) * (e.clientX - down[0]) + (e.clientY - down[1]) * (e.clientY - down[1]));
-            SelectionManager.Views().map(dv => dv.props.Document).map(doc => doc.layout instanceof Doc ? doc.layout : doc.isTemplateForField ? doc : Doc.GetProto(doc)).
-                map(d => d.borderRounding = `${Math.max(0, dist < 3 ? 0 : dist)}px`);
-            return false;
-        }, (e) => this._resizeUndo?.end(), (e) => { });
-    }
-
-    @action
-    onRotateDown = (e: React.PointerEvent): void => {
-        const rotateUndo = UndoManager.StartBatch("rotatedown");
-        const selectedInk = SelectionManager.Views().filter(i => i.ComponentView instanceof InkingStroke);
-        const centerPoint = !selectedInk.length ? { X: this.Bounds.x, Y: this.Bounds.y } : { X: this.Bounds.c?.X ?? (this.Bounds.x + this.Bounds.r) / 2, Y: this.Bounds.c?.Y ?? (this.Bounds.y + this.Bounds.b) / 2 };
-        setupMoveUpEvents(this, e,
-            (e: PointerEvent, down: number[], delta: number[]) => {
-                const previousPoint = { X: e.clientX, Y: e.clientY };
-                const movedPoint = { X: e.clientX - delta[0], Y: e.clientY - delta[1] };
-                const angle = InkStrokeProperties.angleChange(previousPoint, movedPoint, centerPoint);
-                if (selectedInk.length) {
-                    angle && InkStrokeProperties.Instance.rotateInk(selectedInk, -angle, centerPoint);
-                } else {
-                    SelectionManager.Views().forEach(dv => dv.rootDoc._jitterRotation = NumCast(dv.rootDoc._jitterRotation) - angle * 180 / Math.PI);
-                }
-                return false;
-            },
-            () => {
-                rotateUndo?.end();
-                UndoManager.FilterBatches(["data", "x", "y", "width", "height"]);
-            },
-            emptyFunction);
-    }
-
-    @action
-    onPointerDown = (e: React.PointerEvent): void => {
-        DragManager.docsBeingDragged = SelectionManager.Views().map(dv => dv.rootDoc);
-        this._inkDragDocs = DragManager.docsBeingDragged
-            .filter(doc => doc.type === DocumentType.INK)
-            .map(doc => {
-                if (InkStrokeProperties.Instance._lock) {
-                    Doc.SetNativeHeight(doc, NumCast(doc._height));
-                    Doc.SetNativeWidth(doc, NumCast(doc._width));
-                }
-                return ({ doc, x: NumCast(doc.x), y: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) });
-            });
 
-        setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
-        this.Interacting = true; // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them
-        this._resizeHdlId = e.currentTarget.className;
-        const bounds = e.currentTarget.getBoundingClientRect();
-        this._offX = this._resizeHdlId.toLowerCase().includes("left") ? bounds.right - e.clientX : bounds.left - e.clientX;
-        this._offY = this._resizeHdlId.toLowerCase().includes("top") ? bounds.bottom - e.clientY : bounds.top - e.clientY;
-        this._resizeUndo = UndoManager.StartBatch("DocDecs resize");
-        this._snapX = e.pageX;
-        this._snapY = e.pageY;
-        const ffviewSet = new Set<CollectionFreeFormView>();
-        SelectionManager.Views().forEach(docView => {
-            const ffview = docView.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
-            ffview && ffviewSet.add(ffview);
-            this._dragHeights.set(docView.layoutDoc, { start: NumCast(docView.rootDoc._height), lowest: NumCast(docView.rootDoc._height) });
-        });
-        Array.from(ffviewSet).map(ffview => ffview.setupDragLines(false));
-    }
-
-    onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
-        const first = SelectionManager.Views()[0];
-        let thisPt = { x: e.clientX - this._offX, y: e.clientY - this._offY };
-        var fixedAspect = Doc.NativeAspect(first.layoutDoc);
-        InkStrokeProperties.Instance._lock && SelectionManager.Views().filter(dv => dv.rootDoc.type === DocumentType.INK)
-            .forEach(dv => fixedAspect = Doc.NativeAspect(dv.rootDoc));
-
-        const resizeHdl = this._resizeHdlId.split(" ")[0];
-        if (fixedAspect && (resizeHdl === "documentDecorations-bottomRightResizer" || resizeHdl === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles
-            const project = (p: number[], a: number[], b: number[]) => {
-                const atob = [b[0] - a[0], b[1] - a[1]];
-                const atop = [p[0] - a[0], p[1] - a[1]];
-                const len = atob[0] * atob[0] + atob[1] * atob[1];
-                let dot = atop[0] * atob[0] + atop[1] * atob[1];
-                const t = dot / len;
-                dot = (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]);
-                return [a[0] + atob[0] * t, a[1] + atob[1] * t];
-            };
-            const tl = first.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
-            const drag = project([e.clientX + this._offX, e.clientY + this._offY], tl, [tl[0] + fixedAspect, tl[1] + 1]);
-            thisPt = DragManager.snapDragAspect(drag, fixedAspect);
-        } else {
-            thisPt = DragManager.snapDrag(e, -this._offX, -this._offY, this._offX, this._offY);
-        }
-
-        move[0] = thisPt.x - this._snapX;
-        move[1] = thisPt.y - this._snapY;
-        this._snapX = thisPt.x;
-        this._snapY = thisPt.y;
-        let dragBottom = false, dragRight = false, dragBotRight = false;
-        let dX = 0, dY = 0, dW = 0, dH = 0;
-        switch (this._resizeHdlId.split(" ")[0]) {
-            case "": break;
-            case "documentDecorations-topLeftResizer":
-                dX = -1;
-                dY = -1;
-                dW = -move[0];
-                dH = -move[1];
-                break;
-            case "documentDecorations-topRightResizer":
-                dW = move[0];
-                dY = -1;
-                dH = -move[1];
-                break;
-            case "documentDecorations-topResizer":
-                dY = -1;
-                dH = -move[1];
-                dragBottom = true;
-                break;
-            case "documentDecorations-bottomLeftResizer":
-                dX = -1;
-                dW = -move[0];
-                dH = move[1];
-                break;
-            case "documentDecorations-bottomRightResizer":
-                dW = move[0];
-                dH = move[1];
-                dragBotRight = true;
-                break;
-            case "documentDecorations-bottomResizer":
-                dH = move[1];
-                dragBottom = true;
-                break;
-            case "documentDecorations-leftResizer":
-                dX = -1;
-                dW = -move[0];
-                break;
-            case "documentDecorations-rightResizer":
-                dW = move[0];
-                dragRight = true;
-                break;
-        }
-
-        SelectionManager.Views().forEach(action((docView: DocumentView) => {
-            if (e.ctrlKey && !Doc.NativeHeight(docView.props.Document)) docView.toggleNativeDimensions();
-            if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
-                const doc = Document(docView.rootDoc);
-                const nwidth = docView.nativeWidth;
-                const nheight = docView.nativeHeight;
-                const docheight = doc._height || 0;
-                const docwidth = doc._width || 0;
-                const width = docwidth;
-                let height = (docheight || (nheight / nwidth * width));
-                height = !height || isNaN(height) ? 20 : height;
-                const scale = docView.props.ScreenToLocalTransform().Scale;
-                const modifyNativeDim = (e.ctrlKey || doc.forceReflow) && doc.nativeDimModifiable;
-                if (nwidth && nheight) {
-                    if (nwidth / nheight !== width / height && !dragBottom) {
-                        height = nheight / nwidth * width;
-                    }
-                    if (modifyNativeDim && !dragBottom) { // ctrl key enables modification of the nativeWidth or nativeHeight durin the interaction
-                        if (Math.abs(dW) > Math.abs(dH)) dH = dW * nheight / nwidth;
-                        else dW = dH * nwidth / nheight;
-                    }
-                }
-                let actualdW = Math.max(width + (dW * scale), 20);
-                let actualdH = Math.max(height + (dH * scale), 20);
-                const fixedAspect = (nwidth && nheight && !doc._fitWidth);
-                if (fixedAspect) {
-                    if ((Math.abs(dW) > Math.abs(dH) && (!dragBottom || !modifyNativeDim)) || dragRight) {
-                        if (dragRight && modifyNativeDim) {
-                            doc._nativeWidth = actualdW / (doc._width || 1) * Doc.NativeWidth(doc);
-                        } else {
-                            if (!doc._fitWidth) {
-                                actualdH = nheight / nwidth * actualdW;
-                                doc._height = actualdH;
-                            }
-                            else if (!modifyNativeDim || dragBotRight) doc._height = actualdH;
+      }
+      onMaximizeDown = (e: React.PointerEvent) => {
+            setupMoveUpEvents(this, e, () => {
+                  DragManager.StartWindowDrag?.(e, [SelectionManager.Views().slice(-1)[0].rootDoc]);
+                  return true;
+            }, emptyFunction, this.onMaximizeClick, false, false);
+      }
+
+      onMaximizeClick = (e: any): void => {
+            const selectedDocs = SelectionManager.Views();
+            if (selectedDocs.length) {
+                  if (e.ctrlKey) {    // open an alias in a new tab with Ctrl Key
+                        const bestAlias = DocListCast(selectedDocs[0].props.Document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
+                        CollectionDockingView.AddSplit(bestAlias ?? Doc.MakeAlias(selectedDocs[0].props.Document), "right");
+                  } else if (e.shiftKey) {   // open centered in a new workspace with Shift Key
+                        const alias = Doc.MakeAlias(selectedDocs[0].props.Document);
+                        alias.context = undefined;
+                        alias.x = -alias[WidthSym]() / 2;
+                        alias.y = -alias[HeightSym]() / 2;
+                        CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([alias], { title: "Tab for " + alias.title }), "right");
+                  } else if (e.altKey) {    // open same document in new tab
+                        CollectionDockingView.ToggleSplit(selectedDocs[0].props.Document, "right");
+                  } else {
+                        var openDoc = selectedDocs[0].props.Document;
+                        if (openDoc.layoutKey === "layout_icon") {
+                              openDoc = DocListCast(openDoc.aliases).find(alias => !alias.context) ?? Doc.MakeAlias(openDoc);
+                              Doc.deiconifyView(openDoc);
                         }
-                        doc._width = actualdW;
-                    }
-                    else {
-                        if (dragBottom && (modifyNativeDim ||
-                            (docView.layoutDoc.nativeHeightUnfrozen && docView.layoutDoc._fitWidth))) { // frozen web pages, PDFs, and some RTFS have frozen nativewidth/height.  But they are marked to allow their nativeHeight to be explicitly modified with fitWidth and vertical resizing. (ie, with fitWidth they can't grow horizontally to match a vertical resize so it makes more sense to change their nativeheight even if the ctrl key isn't used)
-                            doc._nativeHeight = actualdH / (doc._height || 1) * Doc.NativeHeight(doc);
-                            doc._autoHeight = false;
+                        LightboxView.SetLightboxDoc(openDoc, undefined, selectedDocs.slice(1).map(view => view.props.Document));
+                  }
+            }
+            SelectionManager.DeselectAll();
+      }
+
+      onIconifyClick = (): void => {
+            SelectionManager.Views().forEach(dv => dv?.iconify());
+            SelectionManager.DeselectAll();
+      }
+
+      onSelectorClick = () => SelectionManager.Views()?.[0]?.props.ContainingCollectionView?.props.select(false);
+
+      onRadiusDown = (e: React.PointerEvent): void => {
+            this._resizeUndo = UndoManager.StartBatch("DocDecs set radius");
+            setupMoveUpEvents(this, e, (e, down) => {
+                  const dist = Math.sqrt((e.clientX - down[0]) * (e.clientX - down[0]) + (e.clientY - down[1]) * (e.clientY - down[1]));
+                  SelectionManager.Views().map(dv => dv.props.Document).map(doc => doc.layout instanceof Doc ? doc.layout : doc.isTemplateForField ? doc : Doc.GetProto(doc)).
+                        map(d => d.borderRounding = `${Math.max(0, dist < 3 ? 0 : dist)}px`);
+                  return false;
+            }, (e) => this._resizeUndo?.end(), (e) => { });
+      }
+
+      @action
+      onRotateDown = (e: React.PointerEvent): void => {
+            const rotateUndo = UndoManager.StartBatch("rotatedown");
+            const selectedInk = SelectionManager.Views().filter(i => i.ComponentView instanceof InkingStroke);
+            const centerPoint = !selectedInk.length ? { X: this.Bounds.x, Y: this.Bounds.y } : { X: this.Bounds.c?.X ?? (this.Bounds.x + this.Bounds.r) / 2, Y: this.Bounds.c?.Y ?? (this.Bounds.y + this.Bounds.b) / 2 };
+            setupMoveUpEvents(this, e,
+                  (e: PointerEvent, down: number[], delta: number[]) => {
+                        const previousPoint = { X: e.clientX, Y: e.clientY };
+                        const movedPoint = { X: e.clientX - delta[0], Y: e.clientY - delta[1] };
+                        const angle = InkStrokeProperties.angleChange(previousPoint, movedPoint, centerPoint);
+                        if (selectedInk.length) {
+                              angle && InkStrokeProperties.Instance.rotateInk(selectedInk, -angle, centerPoint);
                         } else {
-                            if (!doc._fitWidth) {
-                                actualdW = nwidth / nheight * actualdH;
-                                doc._width = actualdW;
-                            }
-                            else if (!modifyNativeDim || dragBotRight) doc._width = actualdW;
+                              SelectionManager.Views().forEach(dv => dv.rootDoc._jitterRotation = NumCast(dv.rootDoc._jitterRotation) - angle * 180 / Math.PI);
                         }
-                        if (!modifyNativeDim) {
-                            actualdH = Math.min(nheight / nwidth * NumCast(doc._width), actualdH);
-                            doc._height = actualdH;
+                        return false;
+                  },
+                  () => {
+                        rotateUndo?.end();
+                        UndoManager.FilterBatches(["data", "x", "y", "width", "height"]);
+                  },
+                  emptyFunction);
+      }
+
+      @action
+      onPointerDown = (e: React.PointerEvent): void => {
+            DragManager.docsBeingDragged = SelectionManager.Views().map(dv => dv.rootDoc);
+            this._inkDragDocs = DragManager.docsBeingDragged
+                  .filter(doc => doc.type === DocumentType.INK)
+                  .map(doc => {
+                        if (InkStrokeProperties.Instance._lock) {
+                              Doc.SetNativeHeight(doc, NumCast(doc._height));
+                              Doc.SetNativeWidth(doc, NumCast(doc._width));
                         }
-                        else doc._height = actualdH;
-                    }
-                } else {
-                    dH && (doc._height = actualdH);
-                    dW && (doc._width = actualdW);
-                    dH && (doc._autoHeight = false);
-                }
-                doc.x = (doc.x || 0) + dX * (actualdW - docwidth);
-                doc.y = (doc.y || 0) + dY * (actualdH - docheight);
-                doc._lastModified = new DateField();
-            }
-            const val = this._dragHeights.get(docView.layoutDoc);
-            if (val) this._dragHeights.set(docView.layoutDoc, { start: val.start, lowest: Math.min(val.lowest, NumCast(docView.layoutDoc._height)) });
-        }));
-        return false;
-    }
-
-    @action
-    onPointerUp = (e: PointerEvent): void => {
-        this._resizeHdlId = "";
-        this.Interacting = false;
-        this._resizeUndo?.end();
-        SnappingManager.clearSnapLines();
-
-        // detect autoHeight gesture and apply
-        SelectionManager.Views().map(docView => ({ doc: docView.layoutDoc, hgts: this._dragHeights.get(docView.layoutDoc) }))
-            .filter(pair => pair.hgts && pair.hgts.lowest < pair.hgts.start && pair.hgts.lowest <= 20)
-            .forEach(pair => pair.doc._autoHeight = true);
-        //need to change points for resize, or else rotation/control points will fail.
-        this._inkDragDocs.map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] }))
-            .forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => {
-                Doc.GetProto(doc).data = new InkField(inkPts.map(ipt =>  // (new x — oldx) + newWidth * (oldxpoint /oldWidth)
-                ({
-                    X: (NumCast(doc.x) - x) + NumCast(doc.width) * ipt.X / width,
-                    Y: (NumCast(doc.y) - y) + NumCast(doc.height) * ipt.Y / height
-                })));
-                Doc.SetNativeWidth(doc, undefined);
-                Doc.SetNativeHeight(doc, undefined);
+                        return ({ doc, x: NumCast(doc.x), y: NumCast(doc.y), width: NumCast(doc._width), height: NumCast(doc._height) });
+                  });
+
+            setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
+            this.Interacting = true; // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them
+            this._resizeHdlId = e.currentTarget.className;
+            const bounds = e.currentTarget.getBoundingClientRect();
+            this._offX = this._resizeHdlId.toLowerCase().includes("left") ? bounds.right - e.clientX : bounds.left - e.clientX;
+            this._offY = this._resizeHdlId.toLowerCase().includes("top") ? bounds.bottom - e.clientY : bounds.top - e.clientY;
+            this._resizeUndo = UndoManager.StartBatch("DocDecs resize");
+            this._snapX = e.pageX;
+            this._snapY = e.pageY;
+            const ffviewSet = new Set<CollectionFreeFormView>();
+            SelectionManager.Views().forEach(docView => {
+                  const ffview = docView.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+                  ffview && ffviewSet.add(ffview);
+                  this._dragHeights.set(docView.layoutDoc, { start: NumCast(docView.rootDoc._height), lowest: NumCast(docView.rootDoc._height) });
             });
-    }
-
-    @computed
-    get selectionTitle(): string {
-        if (SelectionManager.Views().length === 1) {
-            const selected = SelectionManager.Views()[0];
-            if (selected.ComponentView?.getTitle?.()) {
-                return selected.ComponentView.getTitle();
+            Array.from(ffviewSet).map(ffview => ffview.setupDragLines(false));
+      }
+
+      onPointerMove = (e: PointerEvent, down: number[], move: number[]): boolean => {
+            const first = SelectionManager.Views()[0];
+            let thisPt = { x: e.clientX - this._offX, y: e.clientY - this._offY };
+            var fixedAspect = Doc.NativeAspect(first.layoutDoc);
+            InkStrokeProperties.Instance._lock && SelectionManager.Views().filter(dv => dv.rootDoc.type === DocumentType.INK)
+                  .forEach(dv => fixedAspect = Doc.NativeAspect(dv.rootDoc));
+
+            const resizeHdl = this._resizeHdlId.split(" ")[0];
+            if (fixedAspect && (resizeHdl === "documentDecorations-bottomRightResizer" || resizeHdl === "documentDecorations-topLeftResizer")) { // need to generalize for bl and tr drag handles
+                  const project = (p: number[], a: number[], b: number[]) => {
+                        const atob = [b[0] - a[0], b[1] - a[1]];
+                        const atop = [p[0] - a[0], p[1] - a[1]];
+                        const len = atob[0] * atob[0] + atob[1] * atob[1];
+                        let dot = atop[0] * atob[0] + atop[1] * atob[1];
+                        const t = dot / len;
+                        dot = (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]);
+                        return [a[0] + atob[0] * t, a[1] + atob[1] * t];
+                  };
+                  const tl = first.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
+                  const drag = project([e.clientX + this._offX, e.clientY + this._offY], tl, [tl[0] + fixedAspect, tl[1] + 1]);
+                  thisPt = DragManager.snapDragAspect(drag, fixedAspect);
+            } else {
+                  thisPt = DragManager.snapDrag(e, -this._offX, -this._offY, this._offX, this._offY);
             }
-            if (this._titleControlString.startsWith("=")) {
-                return ScriptField.MakeFunction(this._titleControlString.substring(1), { doc: Doc.name })!.script.run({ self: selected.rootDoc, this: selected.layoutDoc }, console.log).result?.toString() || "";
+
+            move[0] = thisPt.x - this._snapX;
+            move[1] = thisPt.y - this._snapY;
+            this._snapX = thisPt.x;
+            this._snapY = thisPt.y;
+            let dragBottom = false, dragRight = false, dragBotRight = false;
+            let dX = 0, dY = 0, dW = 0, dH = 0;
+            switch (this._resizeHdlId.split(" ")[0]) {
+                  case "": break;
+                  case "documentDecorations-topLeftResizer":
+                        dX = -1;
+                        dY = -1;
+                        dW = -move[0];
+                        dH = -move[1];
+                        break;
+                  case "documentDecorations-topRightResizer":
+                        dW = move[0];
+                        dY = -1;
+                        dH = -move[1];
+                        break;
+                  case "documentDecorations-topResizer":
+                        dY = -1;
+                        dH = -move[1];
+                        dragBottom = true;
+                        break;
+                  case "documentDecorations-bottomLeftResizer":
+                        dX = -1;
+                        dW = -move[0];
+                        dH = move[1];
+                        break;
+                  case "documentDecorations-bottomRightResizer":
+                        dW = move[0];
+                        dH = move[1];
+                        dragBotRight = true;
+                        break;
+                  case "documentDecorations-bottomResizer":
+                        dH = move[1];
+                        dragBottom = true;
+                        break;
+                  case "documentDecorations-leftResizer":
+                        dX = -1;
+                        dW = -move[0];
+                        break;
+                  case "documentDecorations-rightResizer":
+                        dW = move[0];
+                        dragRight = true;
+                        break;
+            }
+
+            SelectionManager.Views().forEach(action((docView: DocumentView) => {
+                  if (e.ctrlKey && !Doc.NativeHeight(docView.props.Document)) docView.toggleNativeDimensions();
+                  if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) {
+                        const doc = Document(docView.rootDoc);
+                        const nwidth = docView.nativeWidth;
+                        const nheight = docView.nativeHeight;
+                        const docheight = doc._height || 0;
+                        const docwidth = doc._width || 0;
+                        const width = docwidth;
+                        let height = (docheight || (nheight / nwidth * width));
+                        height = !height || isNaN(height) ? 20 : height;
+                        const scale = docView.props.ScreenToLocalTransform().Scale;
+                        const modifyNativeDim = (e.ctrlKey || doc.forceReflow) && doc.nativeDimModifiable;
+                        if (nwidth && nheight) {
+                              if (nwidth / nheight !== width / height && !dragBottom) {
+                                    height = nheight / nwidth * width;
+                              }
+                              if (modifyNativeDim && !dragBottom) { // ctrl key enables modification of the nativeWidth or nativeHeight durin the interaction
+                                    if (Math.abs(dW) > Math.abs(dH)) dH = dW * nheight / nwidth;
+                                    else dW = dH * nwidth / nheight;
+                              }
+                        }
+                        let actualdW = Math.max(width + (dW * scale), 20);
+                        let actualdH = Math.max(height + (dH * scale), 20);
+                        const fixedAspect = (nwidth && nheight && !doc._fitWidth);
+                        if (fixedAspect) {
+                              if ((Math.abs(dW) > Math.abs(dH) && (!dragBottom || !modifyNativeDim)) || dragRight) {
+                                    if (dragRight && modifyNativeDim) {
+                                          doc._nativeWidth = actualdW / (doc._width || 1) * Doc.NativeWidth(doc);
+                                    } else {
+                                          if (!doc._fitWidth) {
+                                                actualdH = nheight / nwidth * actualdW;
+                                                doc._height = actualdH;
+                                          }
+                                          else if (!modifyNativeDim || dragBotRight) doc._height = actualdH;
+                                    }
+                                    doc._width = actualdW;
+                              }
+                              else {
+                                    if (dragBottom && (modifyNativeDim ||
+                                          (docView.layoutDoc.nativeHeightUnfrozen && docView.layoutDoc._fitWidth))) { // frozen web pages, PDFs, and some RTFS have frozen nativewidth/height.  But they are marked to allow their nativeHeight to be explicitly modified with fitWidth and vertical resizing. (ie, with fitWidth they can't grow horizontally to match a vertical resize so it makes more sense to change their nativeheight even if the ctrl key isn't used)
+                                          doc._nativeHeight = actualdH / (doc._height || 1) * Doc.NativeHeight(doc);
+                                          doc._autoHeight = false;
+                                    } else {
+                                          if (!doc._fitWidth) {
+                                                actualdW = nwidth / nheight * actualdH;
+                                                doc._width = actualdW;
+                                          }
+                                          else if (!modifyNativeDim || dragBotRight) doc._width = actualdW;
+                                    }
+                                    if (!modifyNativeDim) {
+                                          actualdH = Math.min(nheight / nwidth * NumCast(doc._width), actualdH);
+                                          doc._height = actualdH;
+                                    }
+                                    else doc._height = actualdH;
+                              }
+                        } else {
+                              dH && (doc._height = actualdH);
+                              dW && (doc._width = actualdW);
+                              dH && (doc._autoHeight = false);
+                        }
+                        doc.x = (doc.x || 0) + dX * (actualdW - docwidth);
+                        doc.y = (doc.y || 0) + dY * (actualdH - docheight);
+                        doc._lastModified = new DateField();
+                  }
+                  const val = this._dragHeights.get(docView.layoutDoc);
+                  if (val) this._dragHeights.set(docView.layoutDoc, { start: val.start, lowest: Math.min(val.lowest, NumCast(docView.layoutDoc._height)) });
+            }));
+            return false;
+      }
+
+      @action
+      onPointerUp = (e: PointerEvent): void => {
+            this._resizeHdlId = "";
+            this.Interacting = false;
+            this._resizeUndo?.end();
+            SnappingManager.clearSnapLines();
+
+            // detect autoHeight gesture and apply
+            SelectionManager.Views().map(docView => ({ doc: docView.layoutDoc, hgts: this._dragHeights.get(docView.layoutDoc) }))
+                  .filter(pair => pair.hgts && pair.hgts.lowest < pair.hgts.start && pair.hgts.lowest <= 20)
+                  .forEach(pair => pair.doc._autoHeight = true);
+            //need to change points for resize, or else rotation/control points will fail.
+            this._inkDragDocs.map(oldbds => ({ oldbds, inkPts: Cast(oldbds.doc.data, InkField)?.inkData || [] }))
+                  .forEach(({ oldbds: { doc, x, y, width, height }, inkPts }) => {
+                        Doc.GetProto(doc).data = new InkField(inkPts.map(ipt =>  // (new x — oldx) + newWidth * (oldxpoint /oldWidth)
+                        ({
+                              X: (NumCast(doc.x) - x) + NumCast(doc.width) * ipt.X / width,
+                              Y: (NumCast(doc.y) - y) + NumCast(doc.height) * ipt.Y / height
+                        })));
+                        Doc.SetNativeWidth(doc, undefined);
+                        Doc.SetNativeHeight(doc, undefined);
+                  });
+      }
+
+      @computed
+      get selectionTitle(): string {
+            if (SelectionManager.Views().length === 1) {
+                  const selected = SelectionManager.Views()[0];
+                  if (selected.ComponentView?.getTitle?.()) {
+                        return selected.ComponentView.getTitle();
+                  }
+                  if (this._titleControlString.startsWith("=")) {
+                        return ScriptField.MakeFunction(this._titleControlString.substring(1), { doc: Doc.name })!.script.run({ self: selected.rootDoc, this: selected.layoutDoc }, console.log).result?.toString() || "";
+                  }
+                  if (this._titleControlString.startsWith("#")) {
+                        return Field.toString(selected.props.Document[this._titleControlString.substring(1)] as Field) || "-unset-";
+                  }
+                  return this._accumulatedTitle;
             }
-            if (this._titleControlString.startsWith("#")) {
-                return Field.toString(selected.props.Document[this._titleControlString.substring(1)] as Field) || "-unset-";
+            return SelectionManager.Views().length > 1 ? "-multiple-" : "-unset-";
+      }
+
+      @computed get hasIcons() {
+            return SelectionManager.Views().some(docView => docView.rootDoc.layoutKey === "layout_icon");
+      }
+
+      render() {
+            const bounds = this.Bounds;
+            const seldoc = SelectionManager.Views().slice(-1)[0];
+            if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
+                  return (null);
             }
-            return this._accumulatedTitle;
-        }
-        return SelectionManager.Views().length > 1 ? "-multiple-" : "-unset-";
-    }
-
-    @computed get canDelete() {
-        return SelectionManager.Views().some(docView => {
-            if (docView.rootDoc.stayInCollection) return false;
-            const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit;
-            //return (!docView.rootDoc._stayInCollection || docView.rootDoc.isInkMask) &&
-            return (collectionAcl === AclAdmin || collectionAcl === AclEdit || GetEffectiveAcl(docView.rootDoc) === AclAdmin);
-        });
-    }
-    @computed get hasIcons() {
-        return SelectionManager.Views().some(docView => docView.rootDoc.layoutKey === "layout_icon");
-    }
-
-    render() {
-        const bounds = this.Bounds;
-        const seldoc = SelectionManager.Views().slice(-1)[0];
-        if (SnappingManager.GetIsDragging() || bounds.r - bounds.x < 1 || bounds.x === Number.MAX_VALUE || !seldoc || this._hidden || isNaN(bounds.r) || isNaN(bounds.b) || isNaN(bounds.x) || isNaN(bounds.y)) {
-            return (null);
-        }
-        const hideResizers = seldoc.props.hideResizeHandles || seldoc.rootDoc.hideResizeHandles || seldoc.rootDoc._isGroup;
-        const hideTitle = seldoc.props.hideDecorationTitle || seldoc.rootDoc.hideDecorationTitle;
-        const canOpen = SelectionManager.Views().some(docView => !docView.props.Document._stayInCollection && !docView.props.Document.isGroup && !docView.props.Document.hideOpenButton);
-        const canDelete = this.canDelete;
-        const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
-            <Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top">
-                <div className={`documentDecorations-${key}Button`} onContextMenu={e => e.preventDefault()}
-                    onPointerDown={pointerDown ?? (e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(e => click!(e))))} >
-                    <FontAwesomeIcon icon={icon as any} />
-                </div>
-            </Tooltip>);
-
-        const colorScheme = StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme);
-        const titleArea = hideTitle ? <div className="documentDecorations-title" onPointerDown={this.onTitleDown} key="title" /> :
-            this._edtingTitle ?
-                <input ref={this._keyinput} className={`documentDecorations-title${colorScheme}`}
-                    type="text" name="dynbox" autoComplete="on"
-                    value={this._accumulatedTitle}
-                    onBlur={e => this.titleBlur()}
-                    onChange={action(e => this._accumulatedTitle = e.target.value)}
-                    onKeyDown={this.titleEntered} /> :
-                <div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown} >
-                    <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${this.selectionTitle}`}</span>
-                </div>;
-
-
-        const leftBounds = this.props.boundsLeft;
-        const topBounds = LightboxView.LightboxDoc ? 0 : this.props.boundsTop;
-        bounds.x = Math.max(leftBounds, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2;
-        bounds.y = Math.max(topBounds, bounds.y - this._resizeBorderWidth / 2 - this._titleHeight) + this._resizeBorderWidth / 2 + this._titleHeight;
-        const borderRadiusDraggerWidth = 15;
-        bounds.r = Math.max(bounds.x, Math.max(leftBounds, Math.min(window.innerWidth, bounds.r + borderRadiusDraggerWidth + this._resizeBorderWidth / 2) - this._resizeBorderWidth / 2 - borderRadiusDraggerWidth));
-        bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight));
-
-        const useRotation = seldoc.ComponentView instanceof InkingStroke || seldoc.ComponentView instanceof ImageBox;
-        const resizerScheme = colorScheme ? "documentDecorations-resizer" + colorScheme : "";
-
-        const rotation = NumCast(seldoc.rootDoc._jitterRotation);
-
-        return (<div className={`documentDecorations${colorScheme}`}>
-            <div className="documentDecorations-background" style={{
-                transform: `rotate(${rotation}deg)`,
-                transformOrigin: "top  left",
-                width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
-                height: (bounds.b - bounds.y + this._resizeBorderWidth) + "px",
-                left: bounds.x - this._resizeBorderWidth / 2,
-                top: bounds.y - this._resizeBorderWidth / 2,
-                pointerEvents: DocumentDecorations.Instance.AddToSelection || this.Interacting ? "none" : "all",
-                display: SelectionManager.Views().length <= 1 ? "none" : undefined
-            }} onPointerDown={this.onBackgroundDown} onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} />
-            {bounds.r - bounds.x < 15 && bounds.b - bounds.y < 15 ? (null) : <>
-                <div className="documentDecorations-container" key="container" style={{
-                    transform: ` translate(${bounds.x - this._resizeBorderWidth / 2}px, ${bounds.y - this._resizeBorderWidth / 2 - this._titleHeight}px) rotate(${rotation}deg)`,
-                    transformOrigin: `8px 26px`,
-                    width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
-                    height: (bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight) + "px",
-                }}>
-                    {!canDelete ? <div /> : topBtn("close", this.hasIcons ? "times" : "window-maximize", undefined, e => this.onCloseClick(this.hasIcons ? true : undefined), "Close")}
-                    {titleArea}
-                    {!canOpen ? (null) : topBtn("open", "external-link-alt", this.onMaximizeDown, undefined, "Open in Tab (ctrl: as alias, shift: in new collection)")}
-                    {hideResizers ? (null) :
-                        <>
-                            <div key="tl" className={`documentDecorations-topLeftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                            <div key="t" className={`documentDecorations-topResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                            <div key="tr" className={`documentDecorations-topRightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                            <div key="l" className={`documentDecorations-leftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                            <div key="c" className={`documentDecorations-centerCont ${resizerScheme}`}></div>
-                            <div key="r" className={`documentDecorations-rightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                            <div key="bl" className={`documentDecorations-bottomLeftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                            <div key="b" className={`documentDecorations-bottomResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-                            <div key="br" className={`documentDecorations-bottomRightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
-
-                            {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) :
-                                topBtn("selector", "arrow-alt-circle-up", undefined, this.onSelectorClick, "tap to select containing document")}
-                            <div key="rot" className={`documentDecorations-${useRotation ? "rotation" : "borderRadius"}`}
-                                onPointerDown={useRotation ? this.onRotateDown : this.onRadiusDown}
-                                onContextMenu={e => e.preventDefault()}>{useRotation && "⟲"}</div>
-                        </>
-                    }
-                    {seldoc?.Document.type === DocumentType.FONTICON ? (null) :
-                        <div className="link-button-container" key="links"
-                            style={{
-                                transform: ` translate(${- this._resizeBorderWidth / 2 + 10}px, ${this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `,
-                            }}>
-                            <DocumentButtonBar views={SelectionManager.Views} />
-                        </div>}
-                </div >
-            </>}
-        </div >
-        );
-    }
+            // hide the decorations if the parent chooses to hide it or if the document itself hides it
+            const hideResizers = seldoc.props.hideResizeHandles || seldoc.rootDoc.hideResizeHandles || seldoc.rootDoc._isGroup;
+            const hideTitle = seldoc.props.hideDecorationTitle || seldoc.rootDoc.hideDecorationTitle;
+            const hideDocumentButtonBar = seldoc.props.hideDocumentButtonBar || seldoc.rootDoc.hideDocumentButtonBar;
+            // if multiple documents have been opened at the same time, then don't show open button
+            const hideOpenButton = seldoc.props.hideOpenButton || seldoc.rootDoc.hideOpenButton ||
+                  SelectionManager.Views().some(docView => docView.props.Document._stayInCollection || docView.props.Document.isGroup || docView.props.Document.hideOpenButton);
+            const hideDeleteButton = seldoc.props.hideDeleteButton || seldoc.rootDoc.hideDeleteButton ||
+                  SelectionManager.Views().some(docView => {
+                        const collectionAcl = docView.props.ContainingCollectionView ? GetEffectiveAcl(docView.props.ContainingCollectionDoc?.[DataSym]) : AclEdit;
+                        return docView.rootDoc.stayInCollection || (collectionAcl !== AclAdmin && collectionAcl !== AclEdit && GetEffectiveAcl(docView.rootDoc) !== AclAdmin);
+                  });
+            const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
+                  <Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top">
+                        <div className={`documentDecorations-${key}Button`} onContextMenu={e => e.preventDefault()}
+                              onPointerDown={pointerDown ?? (e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(e => click!(e))))} >
+                              <FontAwesomeIcon icon={icon as any} />
+                        </div>
+                  </Tooltip>);
+
+            const colorScheme = StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme);
+            const titleArea = hideTitle ? (null) :
+                  this._edtingTitle ?
+                        <input ref={this._keyinput} className={`documentDecorations-title${colorScheme}`}
+                              type="text" name="dynbox" autoComplete="on"
+                              value={this._accumulatedTitle}
+                              onBlur={e => this.titleBlur()}
+                              onChange={action(e => this._accumulatedTitle = e.target.value)}
+                              onKeyDown={this.titleEntered} /> :
+                        <div className="documentDecorations-title" key="title" onPointerDown={this.onTitleDown} >
+                              <span className={`documentDecorations-titleSpan${colorScheme}`}>{`${this.selectionTitle}`}</span>
+                        </div>;
+
+
+            const leftBounds = this.props.boundsLeft;
+            const topBounds = LightboxView.LightboxDoc ? 0 : this.props.boundsTop;
+            bounds.x = Math.max(leftBounds, bounds.x - this._resizeBorderWidth / 2) + this._resizeBorderWidth / 2;
+            bounds.y = Math.max(topBounds, bounds.y - this._resizeBorderWidth / 2 - this._titleHeight) + this._resizeBorderWidth / 2 + this._titleHeight;
+            const borderRadiusDraggerWidth = 15;
+            bounds.r = Math.max(bounds.x, Math.max(leftBounds, Math.min(window.innerWidth, bounds.r + borderRadiusDraggerWidth + this._resizeBorderWidth / 2) - this._resizeBorderWidth / 2 - borderRadiusDraggerWidth));
+            bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight));
+
+            const useRotation = seldoc.ComponentView instanceof InkingStroke || seldoc.ComponentView instanceof ImageBox;
+            const resizerScheme = colorScheme ? "documentDecorations-resizer" + colorScheme : "";
+
+            const rotation = NumCast(seldoc.rootDoc._jitterRotation);
+
+            return (<div className={`documentDecorations${colorScheme}`}>
+                  <div className="documentDecorations-background" style={{
+                        transform: `rotate(${rotation}deg)`,
+                        transformOrigin: "top  left",
+                        width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
+                        height: (bounds.b - bounds.y + this._resizeBorderWidth) + "px",
+                        left: bounds.x - this._resizeBorderWidth / 2,
+                        top: bounds.y - this._resizeBorderWidth / 2,
+                        pointerEvents: DocumentDecorations.Instance.AddToSelection || this.Interacting ? "none" : "all",
+                        display: SelectionManager.Views().length <= 1 ? "none" : undefined
+                  }} onPointerDown={this.onBackgroundDown} onContextMenu={e => { e.preventDefault(); e.stopPropagation(); }} />
+                  {bounds.r - bounds.x < 15 && bounds.b - bounds.y < 15 ? (null) : <>
+                        <div className="documentDecorations-container" key="container" style={{
+                              transform: ` translate(${bounds.x - this._resizeBorderWidth / 2}px, ${bounds.y - this._resizeBorderWidth / 2 - this._titleHeight}px) rotate(${rotation}deg)`,
+                              transformOrigin: `8px 26px`,
+                              width: (bounds.r - bounds.x + this._resizeBorderWidth) + "px",
+                              height: (bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight) + "px",
+                        }}>
+                              {hideDeleteButton ? <div /> : topBtn("close", this.hasIcons ? "times" : "window-maximize", undefined, e => this.onCloseClick(this.hasIcons ? true : undefined), "Close")}
+                              {titleArea}
+                              {hideOpenButton ? (null) : topBtn("open", "external-link-alt", this.onMaximizeDown, undefined, "Open in Tab (ctrl: as alias, shift: in new collection)")}
+                              {hideResizers ? (null) :
+                                    <>
+                                          <div key="tl" className={`documentDecorations-topLeftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                                          <div key="t" className={`documentDecorations-topResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                                          <div key="tr" className={`documentDecorations-topRightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                                          <div key="l" className={`documentDecorations-leftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                                          <div key="c" className={`documentDecorations-centerCont ${resizerScheme}`}></div>
+                                          <div key="r" className={`documentDecorations-rightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                                          <div key="bl" className={`documentDecorations-bottomLeftResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                                          <div key="b" className={`documentDecorations-bottomResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+                                          <div key="br" className={`documentDecorations-bottomRightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={(e) => e.preventDefault()} />
+
+                                          {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? (null) :
+                                                topBtn("selector", "arrow-alt-circle-up", undefined, this.onSelectorClick, "tap to select containing document")}
+                                          <div key="rot" className={`documentDecorations-${useRotation ? "rotation" : "borderRadius"}`}
+                                                onPointerDown={useRotation ? this.onRotateDown : this.onRadiusDown}
+                                                onContextMenu={e => e.preventDefault()}>{useRotation && "⟲"}</div>
+                                    </>
+                              }
+
+                              {hideDocumentButtonBar ? (null) :
+                                    <div className="link-button-container" key="links"
+                                          style={{
+                                                transform: ` translate(${- this._resizeBorderWidth / 2 + 10}px, ${this._resizeBorderWidth + bounds.b - bounds.y + this._titleHeight}px) `,
+                                          }}>
+                                          <DocumentButtonBar views={SelectionManager.Views} />
+                                    </div>}
+                        </div >
+                  </>}
+            </div >
+            );
+      }
 }
\ No newline at end of file
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index a118cc144..6afe64868 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -25,6 +25,7 @@ import { RadialMenu } from "./nodes/RadialMenu";
 import HorizontalPalette from "./Palette";
 import { Touchable } from "./Touchable";
 import TouchScrollableMenu, { TouchScrollableMenuItem } from "./TouchScrollableMenu";
+import { InkTranscription } from "./InkTranscription";
 
 @observer
 export class GestureOverlay extends Touchable {
@@ -127,7 +128,7 @@ export class GestureOverlay extends Touchable {
             // pen is also a touch, but with a radius of 0.5 (at least with the surface pens)
             // and this seems to be the only way of differentiating pen and touch on touch events
             if (pt.radiusX > 1 && pt.radiusY > 1) {
-                createInkGroup();
+                InkTranscription.Instance.createInkGroup();
                 Doc.UserDoc().activeInkTool = InkTool.None;
                 this.prevPoints.set(pt.identifier, pt);
             }
@@ -496,7 +497,7 @@ export class GestureOverlay extends Touchable {
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
             setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => {
                 if (doubleTap) {
-                    createInkGroup();
+                    InkTranscription.Instance.createInkGroup();
                     CurrentUserUtils.SelectedTool = InkTool.None;
                     return;
                 }
@@ -644,7 +645,7 @@ export class GestureOverlay extends Touchable {
                         (controlPoints[0].Y - controlPoints.lastElement().Y) * (controlPoints[0].Y - controlPoints.lastElement().Y));
                     if (controlPoints.length > 4 && dist < 10) controlPoints[controlPoints.length - 1] = controlPoints[0];
                     this._points = controlPoints;
-                    this.dispatchGesture(GestureUtils.Gestures.Stroke);
+                    this.dispatchGesture(GestureUtils.Gestures.Stroke);                    
                     // TODO: nda - check inks to group here
                     checkInksToGroup();
                 }
@@ -846,7 +847,7 @@ export class GestureOverlay extends Touchable {
 
     @computed get elements() {
         const selView = SelectionManager.Views().lastElement();
-        const width = Number(ActiveInkWidth()) * NumCast(selView?.rootDoc.viewScale, 1) / (selView?.props.ScreenToLocalTransform().Scale || 1);
+        const width = Number(ActiveInkWidth()) * NumCast(selView?.rootDoc._viewScale, 1) / (selView?.props.ScreenToLocalTransform().Scale || 1);
         const rect = this._overlayRef.current?.getBoundingClientRect();
         const B = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 }; //this.getBounds(this._points, true);
         B.left = B.left - width / 2;
diff --git a/src/client/views/InkTangentHandles.tsx b/src/client/views/InkTangentHandles.tsx
index b15e4260d..ae35bc980 100644
--- a/src/client/views/InkTangentHandles.tsx
+++ b/src/client/views/InkTangentHandles.tsx
@@ -13,7 +13,6 @@ import { Colors } from "./global/globalEnums";
 import { InkingStroke } from "./InkingStroke";
 import { InkStrokeProperties } from "./InkStrokeProperties";
 import { DocumentView } from "./nodes/DocumentView";
-
 export interface InkHandlesProps {
     inkDoc: Doc;
     inkView: DocumentView;
@@ -103,7 +102,7 @@ export class InkTangentHandles extends React.Component<InkHandlesProps> {
                             r={screenSpaceLineWidth * 2}
                             fill={Colors.MEDIUM_BLUE}
                             strokeWidth={1}
-                            stroke={Colors.MEDIUM_BLUE}
+                            stroke={Colors.BLACK}
                             onPointerDown={e => this.onHandleDown(e, pts.I)}
                             pointerEvents="all"
                             cursor="default"
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index 0ac1770cc..8ab54918c 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -2,16 +2,20 @@ import * as iink from 'iink-js';
 import { action, observable } from 'mobx';
 import * as React from 'react';
 import { Doc, DocListCast, HeightSym, WidthSym } from '../../fields/Doc';
-import { InkData, InkField } from "../../fields/InkField";
+import { InkData, InkField, InkTool } from "../../fields/InkField";
 import { Cast, DateCast, NumCast } from '../../fields/Types';
 import { aggregateBounds } from '../../Utils';
 import { DocumentType } from "../documents/DocumentTypes";
 import { DocumentManager } from "../util/DocumentManager";
 import { CollectionFreeFormView } from './collections/collectionFreeForm';
 import { InkingStroke } from './InkingStroke';
-import './InkTranscription.scss';
+import { CurrentUserUtils } from '../util/CurrentUserUtils';
+import "./InkTranscription.scss";
 
 
+/**
+ * Class component that handles inking in writing mode
+ */
 export class InkTranscription extends React.Component {
     static Instance: InkTranscription;
 
@@ -42,8 +46,8 @@ export class InkTranscription extends React.Component {
                     protocol: 'WEBSOCKET',
                     server: {
                         host: 'cloud.myscript.com',
-                        applicationKey: '7277ec34-0c2e-4ee1-9757-ccb657e3f89f',
-                        hmacKey: 'f5cb18f2-1f95-4ddb-96ac-3f7c888dffc1',
+                        applicationKey: process.env.IINKJS_APP,
+                        hmacKey: process.env.IINKJS_HMAC,
                         websocket: {
                             pingEnabled: false,
                             autoReconnect: true
@@ -103,7 +107,15 @@ export class InkTranscription extends React.Component {
         return this._textRef = r;
     }
 
-    transcribeInk = (groupDoc: Doc | undefined, inkDocs: Doc[], math: boolean, ffView?: CollectionFreeFormView) => {
+    /**
+     * Handles processing Dash Doc data for ink transcription.
+     * 
+     * @param groupDoc the group which contains the ink strokes we want to transcribe 
+     * @param containingLayout the layout which contains the group
+     * @param inkDocs the ink docs contained within the selected group
+     * @param math boolean whether to do math transcription or not
+     */
+    transcribeInk = (groupDoc: Doc | undefined, containingLayout: Doc, inkDocs: Doc[], math: boolean) => {
         if (!groupDoc) return;
         const validInks = inkDocs.filter(s => s.type === DocumentType.INK);
 
@@ -129,6 +141,13 @@ export class InkTranscription extends React.Component {
         }
     }
 
+    /**
+     * Converts the Dash Ink Data to JSON.
+     * 
+     * @param stroke The dash ink data
+     * @param time the time of the stroke
+     * @returns json object representation of ink data
+     */
     inkJSON = (stroke: InkData, time: number) => {
         return {
             "pointerType": "PEN",
@@ -140,28 +159,17 @@ export class InkTranscription extends React.Component {
         };
     }
 
-    mmToPixel = (mm: number) => {
-        return ((96 * mm) / 25.4);
-    }
-
-    calcBounds = (coords: any) => {
-        // find max and min x values and subtract
-        const max = Math.max(...coords);
-        const min = Math.min(...coords);
-        return max - min;
-    }
-
+    /**
+     * Creates subgroups for each word for the whole text transcription
+     * @param wordInkDocMap the mapping of words to ink strokes (Ink Docs)
+     */
     subgroupsTranscriptions = (wordInkDocMap: Map<string, Doc[]>) => {
-        // loop through the words in wordInkDocMap
-        // for each word, get the inkDocs
-
         // iterate through the keys of wordInkDocMap
         wordInkDocMap.forEach(async (inkDocs: Doc[], word: string) => {
             const selected = inkDocs.slice();
             if (!selected) {
                 return;
             }
-            // TODO: nda - probably have to cast this to an actual Doc
             const ctx = await Cast(selected[0].context, Doc);
             if (!ctx) {
                 return;
@@ -171,56 +179,16 @@ export class InkTranscription extends React.Component {
             if (!docView) return;
             const marqViewRef = docView._marqueeViewRef.current;
             if (!marqViewRef) return;
-            //  loop through selected an get the bound
-            const bounds: { x: number, y: number, width?: number, height?: number }[] = []
-
-            selected.map(action(d => {
-                const x = NumCast(d.x);
-                const y = NumCast(d.y);
-                const width = d[WidthSym]();
-                const height = d[HeightSym]();
-                bounds.push({ x, y, width, height });
-            }))
-
-            const aggregBounds = aggregateBounds(bounds, 0, 0);
-
-            if (marqViewRef) {
-                marqViewRef._downX = aggregBounds.x;
-                marqViewRef._downY = aggregBounds.y;
-                marqViewRef._lastX = aggregBounds.r;
-                marqViewRef._lastY = aggregBounds.b;
-            }
-
-            // set the vals for bounds in marqueeView
-
-            selected.map(action(d => {
-                const dx = NumCast(d.x);
-                const dy = NumCast(d.y);
-                delete d.x;
-                delete d.y;
-                delete d.activeFrame;
-                delete d._timecodeToShow;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-                delete d._timecodeToHide;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-                // calculate pos based on bounds
-                if (marqViewRef?.Bounds) {
-                    d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2;
-                    d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
-                }
-                return d;
-            }));
-
-            docView.props.removeDocument?.(selected);
-            const newCollection = marqViewRef?.getCollection(selected, undefined, true);
-            if (newCollection) {
-                newCollection.height = newCollection[HeightSym]();
-                newCollection.width = newCollection[WidthSym]();
-                newCollection.title = word;
-            }
-            // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
-            newCollection && docView.props.addDocument?.(newCollection);
+            this.groupInkDocs(selected, docView, word);
         });
     }
 
+    /**
+     * Event listener function for when the 'exported' event is heard.
+     * 
+     * @param e the event objects
+     * @param ref the ref to the editor
+     */
     exportInk = (e: any, ref: any) => {
         const exports = e.detail.exports;
         if (exports) {
@@ -280,6 +248,88 @@ export class InkTranscription extends React.Component {
             }
         }
     }
+    
+/**
+ * Creates the ink grouping once the user leaves the writing mode.
+ */
+createInkGroup() {
+    // TODO nda - if document being added to is a inkGrouping then we can just add to that group
+    if (CurrentUserUtils.SelectedTool === InkTool.Write) {
+        CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
+            // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
+            const selected = ffView.unprocessedDocs;
+            const newCollection = this.groupInkDocs(selected, ffView);
+            ffView.unprocessedDocs = [];
+
+            InkTranscription.Instance.transcribeInk(newCollection, ffView.layoutDoc, selected, false);
+        });
+    }
+    CollectionFreeFormView.collectionsWithUnprocessedInk.clear();
+}
+
+/**
+ * Creates the groupings for a given list of ink docs on a specific doc view
+ * @param selected: the list of ink docs to create a grouping of
+ * @param docView: the view in which we want the grouping to be created
+ * @param word: optional param if the group we are creating is a word (subgrouping individual words)
+ * @returns a new collection Doc or undefined if the grouping fails
+ */
+  groupInkDocs(selected: Doc[], docView: CollectionFreeFormView, word?: string): Doc | undefined {
+    const bounds: { x: number, y: number, width?: number, height?: number }[] = []
+
+    // calculate the necessary bounds from the selected ink docs
+    selected.map(action(d => {
+        const x = NumCast(d.x);
+        const y = NumCast(d.y);
+        const width = d[WidthSym]();
+        const height = d[HeightSym]();
+        bounds.push({ x, y, width, height });
+    }))
+
+    // calculate the aggregated bounds
+    const aggregBounds = aggregateBounds(bounds, 0, 0);
+    const marqViewRef = docView._marqueeViewRef.current;
+
+    // set the vals for bounds in marqueeView
+    if (marqViewRef) {
+        marqViewRef._downX = aggregBounds.x;
+        marqViewRef._downY = aggregBounds.y;
+        marqViewRef._lastX = aggregBounds.r;
+        marqViewRef._lastY = aggregBounds.b;
+    }
+
+    // map through all the selected ink strokes and create the groupings
+    selected.map(action(d => {
+        const dx = NumCast(d.x);
+        const dy = NumCast(d.y);
+        delete d.x;
+        delete d.y;
+        delete d.activeFrame;
+        delete d._timecodeToShow;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+        delete d._timecodeToHide;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+        // calculate pos based on bounds
+        if (marqViewRef?.Bounds) {
+            d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2;
+            d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2;
+        }
+        return d;
+    }));
+    docView.props.removeDocument?.(selected);
+    // Gets a collection based on the selected nodes using a marquee view ref
+    const newCollection = marqViewRef?.getCollection(selected, undefined, true);
+    if (newCollection) {
+        newCollection.height = newCollection[HeightSym]();
+        newCollection.width = newCollection[WidthSym]();
+        // if the grouping we are creating is an individual word
+        if (word) {
+            newCollection.title = word;
+        }
+    }
+
+    // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs
+    newCollection && docView.props.addDocument?.(newCollection);
+    return newCollection;
+}
 
     render() {
         return (
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index f67f37bfb..fcc4aea13 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -207,7 +207,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
 
     future = () => LightboxView._future;
     tourMap = () => LightboxView._tourMap;
-    fitToBox = () => LightboxView._docTarget === LightboxView.LightboxDoc;
     render() {
         let downx = 0, downy = 0;
         return !LightboxView.LightboxDoc ? (null) :
@@ -241,7 +240,6 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                         DataDoc={undefined}
                         LayoutTemplate={LightboxView.LightboxDocTemplate}
                         addDocument={undefined}
-                        // fitContentsToDoc={this.fitToBox} // bcz: why do we want this?  when we initially open a colletion, we shrinkwrap it which allows for user navigation.  if we later encounter a collection, it's not clear to me that we want to make it either shrinkwrap or fitContents...
                         isDocumentActive={returnFalse}
                         isContentActive={returnTrue}
                         addDocTab={this.addDocTab}
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 8560ccb29..49c2dcf34 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -8,7 +8,9 @@ import { AssignAllExtensions } from "../../extensions/General/Extensions";
 import { Docs } from "../documents/Documents";
 import { CurrentUserUtils } from "../util/CurrentUserUtils";
 import { LinkManager } from "../util/LinkManager";
+import { RecordingApi } from "../util/RecordingApi";
 import { CollectionView } from "./collections/CollectionView";
+import { DashboardView } from './DashboardView';
 import { MainView } from "./MainView";
 
 AssignAllExtensions();
@@ -36,5 +38,6 @@ AssignAllExtensions();
     const expires = "expires=" + d.toUTCString();
     document.cookie = `loadtime=${loading};${expires};path=/`;
     new LinkManager();
+    new RecordingApi;
     ReactDOM.render(<MainView />, document.getElementById('root'));
 })();
\ No newline at end of file
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index 15cd2c144..a695577d0 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -20,6 +20,8 @@
     position: relative;
     width: 100%;
     height: 100%;
+    display: flex;
+    flex-direction: column;
 }
 
 // add nodes menu. Note that the + button is actually an input label, not an actual button.
@@ -29,6 +31,11 @@
     left: calc(100% + 5px);
     z-index: 1;
     pointer-events: none;
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    justify-content: space-between;
+    gap: 10px;
 }
 
 .mainView-snapLines {
@@ -148,7 +155,8 @@
     }
 }
 
-.mainView-innerContent, .mainView-innerContent-Dark {
+.mainView-innerContent,
+.mainView-innerContent-Dark {
     display: contents;
     flex-direction: row;
     position: relative;
@@ -181,8 +189,8 @@
 .mainView-libraryHandle {
     background-color: $light-gray;
 }
-.mainView-innerContent-Dark 
-{
+
+.mainView-innerContent-Dark {
     .propertiesView {
         background-color: #252525;
 
@@ -208,6 +216,7 @@
         background: #353535;
     }
 }
+
 .mainView-container-Dark {
     .contextMenu-cont {
         background: $medium-gray;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index f0011d65f..0f7e3188a 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -37,6 +37,7 @@ import { CollectionView, CollectionViewType } from './collections/CollectionView
 import "./collections/TreeView.scss";
 import { ComponentDecorations } from './ComponentDecorations';
 import { ContextMenu } from './ContextMenu';
+import { DashboardView } from './DashboardView';
 import { DictationOverlay } from './DictationOverlay';
 import { DocumentDecorations } from './DocumentDecorations';
 import { GestureOverlay } from './GestureOverlay';
@@ -83,7 +84,7 @@ export class MainView extends React.Component {
 
     @computed private get dashboardTabHeight() { return 27; } //  27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js
     @computed private get topOfDashUI() { return Number(DASHBOARD_SELECTOR_HEIGHT.replace("px", "")); }
-    @computed private get topOfHeaderBarDoc() { return this.topOfDashUI + this.topMenuHeight(); }
+    @computed private get topOfHeaderBarDoc() { return this.topOfDashUI; }
     @computed private get topOfSidebarDoc() { return this.topOfDashUI + this.topMenuHeight(); }
     @computed private get topOfMainDoc() { return this.topOfDashUI + this.topMenuHeight() + this.headerBarDocHeight(); }
     @computed private get topOfMainDocContent() { return this.topOfMainDoc + this.dashboardTabHeight; }
@@ -179,7 +180,7 @@ export class MainView extends React.Component {
         fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt,
         fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer,
         fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV,
-        fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes, fa.faFlag,
+        fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideoSlash, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes, fa.faFlag,
         fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
         fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
         fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any,
@@ -226,17 +227,7 @@ export class MainView extends React.Component {
         if (received && !this.userDoc) {
             reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()), { fireImmediately: true });
         } else {
-            if (received && CurrentUserUtils._urlState.sharing) {
-                reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized,
-                    initialized => initialized && received && DocServer.GetRefField(received).then(docField => {
-                        if (docField instanceof Doc && docField._viewType !== CollectionViewType.Docking) {
-                            CollectionDockingView.AddSplit(docField, "right");
-                        }
-                    }),
-                );
-            }
-            const activeDash = PromiseValue(this.userDoc.activeDashboard);
-            activeDash.then(dash => {
+            PromiseValue(this.userDoc.activeDashboard).then(dash => {
                 if (dash instanceof Doc) CurrentUserUtils.openDashboard(this.userDoc, dash);
                 else CurrentUserUtils.createNewDashboard(this.userDoc);
             });
@@ -286,7 +277,7 @@ export class MainView extends React.Component {
     headerBarScreenXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.headerBarDocHeight(), 1);
 
     @computed get headerBarDocView() {
-        return <div style={{ height: this.headerBarDocHeight() }}>
+        return <div className="mainView-headerBar" style={{ height: this.headerBarDocHeight() }}>
             <DocumentView key="headerBarDoc"
                 Document={this.headerBarDoc}
                 DataDoc={undefined}
@@ -297,11 +288,12 @@ export class MainView extends React.Component {
                 styleProvider={DefaultStyleProvider}
                 rootSelected={returnTrue}
                 removeDocument={returnFalse}
-                fitContentsToDoc={returnTrue}
+                fitContentsToBox={returnTrue}
                 isDocumentActive={returnTrue} // headerBar is always documentActive (ie, the docView gets pointer events)
                 isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive
                 ScreenToLocalTransform={this.headerBarScreenXf}
                 childHideResizeHandles={returnTrue}
+                dontRegisterView={true}
                 hideResizeHandles={true}
                 PanelWidth={this.headerBarDocWidth}
                 PanelHeight={this.headerBarDocHeight}
@@ -502,15 +494,6 @@ export class MainView extends React.Component {
         </>;
     }
 
-    @computed get headerBar() {
-        return !this.userDoc ? (null) :
-            <div className="mainView-dashboardArea" style={{
-                height: this.headerBarDocHeight(),
-                width: "100%",
-            }} >
-                {this.headerBarDocView}
-            </div>;
-    }
 
     @computed get mainDashboardArea() {
         return !this.userDoc ? (null) :
@@ -592,6 +575,7 @@ export class MainView extends React.Component {
                     searchFilterDocs={returnEmptyDoclist}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined} />
+                {['watching', 'recording'].includes(String(this.userDoc?.presentationMode) ?? '') ? <div style={{ border: '.5rem solid green', padding: '5px' }}>{this.userDoc?.presentationMode}</div> : <></>}
             </div>;
     }
     @computed get snapLines() {
@@ -677,12 +661,20 @@ export class MainView extends React.Component {
             {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
             {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => DocumentLinksButton.LinkEditorDocView = undefined)} docView={DocumentLinksButton.LinkEditorDocView} /> : (null)}
             {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)}
-            <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 2001 }} >
-                <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
-            </div>
-            <GestureOverlay >
-                {this.mainDashboardArea}
-            </GestureOverlay>
+            
+            {((page:string) => {
+                switch (page) {
+                    case "dashboard":
+                    default:return <>
+                            <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 1999 }} >
+                                    <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
+                            </div> 
+                            <GestureOverlay> {this.mainDashboardArea}   </GestureOverlay>
+                        </>;
+                    case "home":  return <DashboardView/>;
+                } })(StrCast(Doc.UserDoc().activePage))
+            }
+            
             <PreviewCursor />
             <TaskCompletionBox />
             <ContextMenu />
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index e15624e23..20b99788c 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -132,14 +132,14 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         if (savedAnnoMap.size === 0) return undefined;
         const savedAnnos = Array.from(savedAnnoMap.values())[0];
         if (savedAnnos.length && (savedAnnos[0] as any).marqueeing) {
-            const scale = this.props.scaling?.() || 1;
+            const scale = (this.props.scaling?.() || 1);
             const anno = savedAnnos[0];
             const containerOffset = this.props.containerOffset?.() || [0, 0];
             const marqueeAnno = Docs.Create.FreeformDocument([], { _isLinkButton: isLinkButton, backgroundColor: color, annotationOn: this.props.rootDoc, title: "Annotation on " + this.props.rootDoc.title });
-            marqueeAnno.x = (parseInt(anno.style.left || "0") - containerOffset[0]) / scale;
-            marqueeAnno.y = (parseInt(anno.style.top || "0") - containerOffset[1]) / scale + NumCast(this.props.scrollTop);
-            marqueeAnno._height = parseInt(anno.style.height || "0") / scale;
-            marqueeAnno._width = parseInt(anno.style.width || "0") / scale;
+            marqueeAnno.x = NumCast(this.props.docView.props.Document.panXMin) + (parseInt(anno.style.left || "0") - containerOffset[0]) / scale/ NumCast(this.props.docView.props.Document._viewScale,1);
+            marqueeAnno.y = NumCast(this.props.docView.props.Document.panYMin) + (parseInt(anno.style.top || "0") - containerOffset[1]) / scale/ NumCast(this.props.docView.props.Document._viewScale,1) + NumCast(this.props.scrollTop);
+            marqueeAnno._height = parseInt(anno.style.height || "0") / scale/ NumCast(this.props.docView.props.Document._viewScale,1);
+            marqueeAnno._width = parseInt(anno.style.width || "0") / scale/ NumCast(this.props.docView.props.Document._viewScale,1);
             anno.remove();
             savedAnnoMap.clear();
             return marqueeAnno;
diff --git a/src/client/views/OverlayView.scss b/src/client/views/OverlayView.scss
index 555f4298d..302e7a5e3 100644
--- a/src/client/views/OverlayView.scss
+++ b/src/client/views/OverlayView.scss
@@ -1,3 +1,13 @@
+.overlayView {
+    position: absolute;
+    pointer-events: none;
+    top: 0;
+    width: 100vw;
+    height: 100vh;
+    /* background-color: pink; */
+    z-index: 100000;
+}
+
 .overlayWindow-outerDiv {
     border-radius: 5px;
     overflow: hidden;
@@ -46,4 +56,6 @@
 .overlayView-doc {
     z-index: 9002; //so that it appears above chroma 
     position: absolute;
+    top: 0;
+    left: 0;
 }
\ No newline at end of file
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 95e146df4..ab5dc74c9 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -1,8 +1,10 @@
+import { docs } from "googleapis/build/src/apis/docs";
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
+import { computedFn } from "mobx-utils";
 import * as React from "react";
 import ReactLoading from 'react-loading';
-import { Doc, HeightSym, WidthSym } from "../../fields/Doc";
+import { Doc, WidthSym, HeightSym } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
 import { Cast, NumCast } from "../../fields/Types";
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils } from "../../Utils";
@@ -150,6 +152,10 @@ export class OverlayView extends React.Component {
         (doc instanceof Doc ? [doc] : doc).forEach(doc => Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc));
         return true;
     }
+    
+    docScreenToLocalXf = computedFn(function docScreenToLocalXf(this: any, doc: Doc) {
+        return () => new Transform(-NumCast(doc.x), -NumCast(doc.y), 1);
+    }.bind(this));
 
     @computed get overlayDocs() {
         return CurrentUserUtils.OverlayDocs?.map(d => {
@@ -183,7 +189,7 @@ export class OverlayView extends React.Component {
                 offsetx = NumCast(d.x) - e.clientX;
                 offsety = NumCast(d.y) - e.clientY;
             };
-            return <div className="overlayView-doc" ref={dref} key={d[Id]} onPointerDown={onPointerDown} style={{ top: 0, width: NumCast(d._width), height: NumCast(d._height), transform: `translate(${d.x}px, ${d.y}px)` }}>
+            return <div className="overlayView-doc" ref={dref} key={d[Id]} onPointerDown={onPointerDown} style={{ top: d.type === 'presentation' ? 0 : undefined, width: NumCast(d._width), height: NumCast(d._height), transform: `translate(${d.x}px, ${d.y}px)` }}>
                 <DocumentView
                     Document={d}
                     rootSelected={returnTrue}
@@ -192,9 +198,9 @@ export class OverlayView extends React.Component {
                     removeDocument={this.removeOverlayDoc}
                     PanelWidth={d[WidthSym]}
                     PanelHeight={d[HeightSym]}
-                    ScreenToLocalTransform={() => new Transform(-NumCast(d.x), -NumCast(d.y), 1)}
+                    ScreenToLocalTransform={this.docScreenToLocalXf(d)}
                     renderDepth={1}
-                    isDocumentActive={returnFalse}
+                    isDocumentActive={returnTrue}
                     isContentActive={returnTrue}
                     whenChildContentsActiveChanged={emptyFunction}
                     focus={DocUtils.DefaultFocus}
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 24857d8ee..74055f057 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -3,25 +3,23 @@ import { Tooltip } from '@material-ui/core';
 import { action, computed, observable } from "mobx";
 import { observer } from "mobx-react";
 import { Doc, Opt } from "../../fields/Doc";
+import { Id } from "../../fields/FieldSymbols";
 import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
 import { BoolCast, StrCast } from "../../fields/Types";
+import { ImageField } from "../../fields/URLField";
 import { DocUtils } from '../documents/Documents';
 import { DocumentType } from '../documents/DocumentTypes';
 import { SelectionManager } from '../util/SelectionManager';
 import { undoBatch } from '../util/UndoManager';
 import { CollectionViewType } from './collections/CollectionView';
+import { Colors } from "./global/globalEnums";
 import { InkingStroke } from './InkingStroke';
 import { DocumentView } from './nodes/DocumentView';
+import { VideoBox } from "./nodes/VideoBox";
+import { pasteImageBitmap } from "./nodes/WebBoxRenderer";
 import './PropertiesButtons.scss';
 import React = require("react");
-import { Colors } from "./global/globalEnums";
-import { CollectionFreeFormView } from "./collections/collectionFreeForm";
-import { DocumentManager } from "../util/DocumentManager";
-import { pasteImageBitmap } from "./nodes/WebBoxRenderer";
-import { VideoBox } from "./nodes/VideoBox";
-import { Id } from "../../fields/FieldSymbols";
-import { ImageField } from "../../fields/URLField";
 const higflyout = require("@hig/flyout");
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
@@ -73,7 +71,7 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         return this.propertyToggleBtn("Lock\xA0View", "_lockedTransform", on => `${on ? "Unlock" : "Lock"} panning of view`, on => "lock");
     }
     @computed get fitContentButton() {
-        return this.propertyToggleBtn("View All", "_fitToBox", on => `${on ? "Don't" : "Do"} fit content to container visible area`, on => "eye");
+        return this.propertyToggleBtn("View All", "_fitContentsToBox", on => `${on ? "Don't" : "Do"} fit content to container visible area`, on => "eye");
     }
     @computed get fitWidthButton() {
         return this.propertyToggleBtn("Fit\xA0Width", "_fitWidth", on => `${on ? "Don't" : "Do"} fit content to width of container`, on => "arrows-alt-h");
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index bcfd2dd56..16eb95cf4 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -300,7 +300,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     Document={layoutDoc}
                     DataDoc={this.dataDoc}
                     renderDepth={1}
-                    fitContentsToDoc={returnTrue}
+                    fitContentsToBox={returnTrue}
                     rootSelected={returnFalse}
                     styleProvider={DefaultStyleProvider}
                     docViewPath={returnEmptyDoclist}
diff --git a/src/client/views/ScriptingRepl.tsx b/src/client/views/ScriptingRepl.tsx
index 2b045aa6c..4fecfa4d9 100644
--- a/src/client/views/ScriptingRepl.tsx
+++ b/src/client/views/ScriptingRepl.tsx
@@ -123,8 +123,9 @@ export class ScriptingRepl extends React.Component {
         let stopProp = true;
         switch (e.key) {
             case "Enter": {
+                e.stopPropagation();
                 const docGlobals: { [name: string]: any } = {};
-                DocumentManager.Instance.DocumentViews.forEach((dv, i) => docGlobals[`d${i}`] = dv.props.Document);
+                Array.from(DocumentManager.Instance.DocumentViews).forEach((dv, i) => docGlobals[`d${i}`] = dv.props.Document);
                 const globals = ScriptingGlobals.makeMutableGlobalsCopy(docGlobals);
                 const script = CompileScript(this.commandString, { typecheck: false, addReturn: true, editable: true, params: { args: "any" }, transformer: this.getTransformer(), globals });
                 if (!script.compiled) {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index de2106e4a..d2687df17 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -26,6 +26,7 @@ import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView";
 import { CollectionViewType } from './CollectionView';
 import { TabDocView } from './TabDocView';
 import React = require("react");
+import { SelectionManager } from '../../util/SelectionManager';
 const _global = (window /* browser */ || global /* node */) as any;
 
 @observer
@@ -50,9 +51,8 @@ export class CollectionDockingView extends CollectionSubView() {
     public _flush: UndoManager.Batch | undefined;
     private _ignoreStateChange = "";
     public tabMap: Set<any> = new Set();
-    public get initialized() { return this._goldenLayout !== null; }
     public get HasFullScreen() { return this._goldenLayout._maximisedItem !== null; }
-    @observable private _goldenLayout: any = null;
+    private _goldenLayout: any = null;
 
     constructor(props: SubCollectionViewProps) {
         super(props);
@@ -118,6 +118,7 @@ export class CollectionDockingView extends CollectionSubView() {
 
     @undoBatch
     public static OpenFullScreen(doc: Doc) {
+        SelectionManager.DeselectAll();
         const instance = CollectionDockingView.Instance;
         if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") {
             return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
@@ -171,12 +172,6 @@ export class CollectionDockingView extends CollectionSubView() {
     @undoBatch
     @action
     public static AddSplit(document: Doc, pullSide: string, stack?: any, panelName?: string) {
-        if (document.type === DocumentType.PRES) {
-            const docs = Cast(Cast(Doc.UserDoc().myOverlayDocs, Doc, null).data, listSpec(Doc), []);
-            if (docs.includes(document)) {
-                docs.splice(docs.indexOf(document), 1);
-            }
-        }
         if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), document);
 
         const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document);
@@ -185,6 +180,7 @@ export class CollectionDockingView extends CollectionSubView() {
             return true;
         }
         const instance = CollectionDockingView.Instance;
+        const glayRoot = instance._goldenLayout.root;
         if (!instance) return false;
         const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName);
 
@@ -192,71 +188,75 @@ export class CollectionDockingView extends CollectionSubView() {
             stack.addChild(docContentConfig, undefined);
             stack.setActiveContentItem(stack.contentItems[stack.contentItems.length - 1]);
         } else {
-            const newItemStackConfig = { type: 'stack', content: [docContentConfig] };
-            const newContentItem = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
-            if (instance._goldenLayout.root.contentItems.length === 0) { // if no rows / columns
-                instance._goldenLayout.root.addChild(newContentItem);
-            } else if (instance._goldenLayout.root.contentItems[0].isStack) {
-                instance._goldenLayout.root.contentItems[0].addChild(docContentConfig);
+            const newContentItem = () => {
+                const newItem = glayRoot.layoutManager.createContentItem({ type: 'stack', content: [docContentConfig] }, instance._goldenLayout);
+                newItem.callDownwards('_$init');
+                return newItem;
+            }
+            if (glayRoot.contentItems.length === 0) { // if no rows / columns
+                glayRoot.addChild(newContentItem());
+            } else if (glayRoot.contentItems[0].isStack) {
+                glayRoot.contentItems[0].addChild(docContentConfig);
             } else if (
-                instance._goldenLayout.root.contentItems.length === 1 &&
-                instance._goldenLayout.root.contentItems[0].contentItems.length === 1 &&
-                instance._goldenLayout.root.contentItems[0].contentItems[0].contentItems.length === 0) {
-                instance._goldenLayout.root.contentItems[0].contentItems[0].addChild(docContentConfig);
+                glayRoot.contentItems.length === 1 &&
+                glayRoot.contentItems[0].contentItems.length === 1 &&
+                glayRoot.contentItems[0].contentItems[0].contentItems.length === 0) {
+                glayRoot.contentItems[0].contentItems[0].addChild(docContentConfig);
             }
             else if (instance._goldenLayout.root.contentItems[0].isRow) { // if row
                 switch (pullSide) {
                     default:
-                    case "right": instance._goldenLayout.root.contentItems[0].addChild(newContentItem); break;
-                    case "left": instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0); break;
+                    case "right": glayRoot.contentItems[0].addChild(newContentItem()); break;
+                    case "left": glayRoot.contentItems[0].addChild(newContentItem(), 0); break;
                     case "top":
                     case "bottom":
                         // if not going in a row layout, must add already existing content into column
-                        const rowlayout = instance._goldenLayout.root.contentItems[0];
+                        const rowlayout = glayRoot.contentItems[0];
                         const newColumn = rowlayout.layoutManager.createContentItem({ type: "column" }, instance._goldenLayout);
 
-                        CollectionDockingView.Instance._goldenLayout.saveScrollTops(rowlayout.element);
+                        const newItem = newContentItem();
+                        instance._goldenLayout.saveScrollTops(rowlayout.element);
                         rowlayout.parent.replaceChild(rowlayout, newColumn);
                         if (pullSide === "top") {
                             newColumn.addChild(rowlayout, undefined, true);
-                            newColumn.addChild(newContentItem, 0, true);
+                            newColumn.addChild(newItem, 0, true);
                         } else if (pullSide === "bottom") {
-                            newColumn.addChild(newContentItem, undefined, true);
+                            newColumn.addChild(newItem, undefined, true);
                             newColumn.addChild(rowlayout, 0, true);
                         }
-                        CollectionDockingView.Instance._goldenLayout.restoreScrollTops(rowlayout.element);
+                        instance._goldenLayout.restoreScrollTops(rowlayout.element);
 
                         rowlayout.config.height = 50;
-                        newContentItem.config.height = 50;
+                        newItem.config.height = 50;
                 }
             } else {// if (instance._goldenLayout.root.contentItems[0].isColumn) { // if column
                 switch (pullSide) {
-                    case "top": instance._goldenLayout.root.contentItems[0].addChild(newContentItem, 0); break;
-                    case "bottom": instance._goldenLayout.root.contentItems[0].addChild(newContentItem); break;
+                    case "top": glayRoot.contentItems[0].addChild(newContentItem(), 0); break;
+                    case "bottom": glayRoot.contentItems[0].addChild(newContentItem()); break;
                     case "left":
                     case "right":
                     default:
                         // if not going in a row layout, must add already existing content into column
-                        const collayout = instance._goldenLayout.root.contentItems[0];
+                        const collayout = glayRoot.contentItems[0];
                         const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout);
 
-                        CollectionDockingView.Instance._goldenLayout.saveScrollTops(collayout.element);
+                        const newItem = newContentItem();
+                        instance._goldenLayout.saveScrollTops(collayout.element);
                         collayout.parent.replaceChild(collayout, newRow);
                         if (pullSide === "left") {
                             newRow.addChild(collayout, undefined, true);
-                            newRow.addChild(newContentItem, 0, true);
+                            newRow.addChild(newItem, 0, true);
                         } else {
-                            newRow.addChild(newContentItem, undefined, true);
+                            newRow.addChild(newItem, undefined, true);
                             newRow.addChild(collayout, 0, true);
                         }
-                        CollectionDockingView.Instance._goldenLayout.restoreScrollTops(collayout.element);
+                        instance._goldenLayout.restoreScrollTops(collayout.element);
 
                         collayout.config.width = 50;
-                        newContentItem.config.width = 50;
+                        newItem.config.width = 50;
                 }
             }
             instance._ignoreStateChange = JSON.stringify(instance._goldenLayout.toConfig());
-            newContentItem.callDownwards('_$init');
         }
 
         return instance.layoutChanged();
@@ -271,7 +271,7 @@ export class CollectionDockingView extends CollectionSubView() {
         return true;
     }
 
-    async setupGoldenLayout() {
+    setupGoldenLayout = async () => {
         const config = StrCast(this.props.Document.dockingConfig);
         if (config) {
             const matches = config.match(/\"documentId\":\"[a-z0-9-]+\"/g);
@@ -288,26 +288,19 @@ export class CollectionDockingView extends CollectionSubView() {
                         this._goldenLayout.unbind('stackCreated', this.stackCreated);
                     } catch (e) { }
                 }
+                this.tabMap.clear();
+                this._goldenLayout.destroy();
             }
-            this.tabMap.clear();
-            this._goldenLayout?.destroy();
-            runInAction(() => this._goldenLayout = new GoldenLayout(JSON.parse(config)));
-            this._goldenLayout.on('tabCreated', this.tabCreated);
-            this._goldenLayout.on('tabDestroyed', this.tabDestroyed);
-            this._goldenLayout.on('stackCreated', this.stackCreated);
-            this._goldenLayout.registerComponent('DocumentFrameRenderer', TabDocView);
-            this._goldenLayout.container = this._containerRef.current;
-            if (this._goldenLayout.config.maximisedItemId === '__glMaximised') {
-                try {
-                    this._goldenLayout.config.root.getItemsById(this._goldenLayout.config.maximisedItemId)[0].toggleMaximise();
-                } catch (e) {
-                    this._goldenLayout.config.maximisedItemId = null;
-                }
-            }
-            this._goldenLayout.init();
-            this._goldenLayout.root.layoutManager.on('itemDropped', this.tabItemDropped);
-            this._goldenLayout.root.layoutManager.on('dragStart', this.tabDragStart);
-            this._goldenLayout.root.layoutManager.on('activeContentItemChanged', this.stateChanged);
+            const glay = this._goldenLayout = new GoldenLayout(JSON.parse(config));
+            glay.on('tabCreated', this.tabCreated);
+            glay.on('tabDestroyed', this.tabDestroyed);
+            glay.on('stackCreated', this.stackCreated);
+            glay.registerComponent('DocumentFrameRenderer', TabDocView);
+            glay.container = this._containerRef.current;
+            glay.init();
+            glay.root.layoutManager.on('itemDropped', this.tabItemDropped);
+            glay.root.layoutManager.on('dragStart', this.tabDragStart);
+            glay.root.layoutManager.on('activeContentItemChanged', this.stateChanged);
         }
     }
 
@@ -322,7 +315,7 @@ export class CollectionDockingView extends CollectionSubView() {
                     }
                     this._ignoreStateChange = "";
                 });
-            setTimeout(() => this.setupGoldenLayout(), 0);
+            setTimeout(this.setupGoldenLayout);
             //window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window
         }
     }
@@ -368,7 +361,8 @@ export class CollectionDockingView extends CollectionSubView() {
             const htmlTarget = e.target as HTMLElement;
             window.addEventListener("mouseup", this.onPointerUp);
             if (!htmlTarget.closest("*.lm_content") && (htmlTarget.closest("*.lm_tab") || htmlTarget.closest("*.lm_stack"))) {
-                if (htmlTarget.className !== "lm_close_tab") {
+                const className = typeof htmlTarget.className === "string" ? htmlTarget.className : "";
+                if (!className.includes("lm_close") && !className.includes("lm_maximise")) {
                     this._flush = UndoManager.StartBatch("golden layout edit");
                 }
             }
@@ -413,29 +407,37 @@ export class CollectionDockingView extends CollectionSubView() {
         const docs = !docids ? [] : docids.map(id => DocServer.GetCachedRefField(id)).filter(f => f).map(f => f as Doc);
         const changesMade = this.props.Document.dockcingConfig !== json;
         if (changesMade && !this._flush) {
-            this.props.Document.dockingConfig = json;
-            this.props.Document.data = new List<Doc>(docs);
+            UndoManager.RunInBatch(() => {
+                this.props.Document.dockingConfig = json;
+                this.props.Document.data = new List<Doc>(docs);
+            }, "state changed");
         }
         return changesMade;
     }
 
     tabDestroyed = (tab: any) => {
+        Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", tab.DashDoc);
+        Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true);
+        const dview = CollectionDockingView.Instance.props.Document;
+        const fieldKey = CollectionDockingView.Instance.props.fieldKey;
+        Doc.RemoveDocFromList(dview, fieldKey, tab.DashDoc);
         this.tabMap.delete(tab);
         tab._disposers && Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
         tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele));
         this.stateChanged();
     }
     tabCreated = (tab: any) => {
+        this.tabMap.add(tab);
         tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab?.(tab);  // have to explicitly initialize tabs that reuse contents from previous tabs (ie, when dragging a tab around a new tab is created for the old content)
     }
 
     stackCreated = (stack: any) => {
         stack.header?.element.on('mousedown', (e: any) => {
             if (e.target === stack.header?.element[0] && e.button === 2) {
-                const emptyPane = CurrentUserUtils.EmptyPane;
-                emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
+                const dashboard= CurrentUserUtils.ActiveDashboard;
+                dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1;
                 const docToAdd = Docs.Create.FreeformDocument([], {
-                    _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _backgroundGridShow: true, _fitWidth: true, title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`,
+                    _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _backgroundGridShow: true, _fitWidth: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}`,
                 });
                 this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
                 CollectionDockingView.AddSplit(docToAdd, "", stack);
@@ -452,14 +454,17 @@ export class CollectionDockingView extends CollectionSubView() {
                     alert('cant delete the last stack');
                 }
             }));
+
+        stack.header?.controlsContainer.find('.lm_maximise') //get the close icon
+            .click(() => setTimeout(this.stateChanged));
         stack.header?.controlsContainer.find('.lm_popout') //get the popout icon
             .off('click') //unbind the current click handler
             .click(action(() => {
                 // stack.config.fixed = !stack.config.fixed;  // force the stack to have a fixed size
-                const emptyPane = CurrentUserUtils.EmptyPane;
-                emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
+                const dashboard = CurrentUserUtils.ActiveDashboard;
+                dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1;
                 const docToAdd = Docs.Create.FreeformDocument([], {
-                    _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, _backgroundGridShow: true, title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`
+                    _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, _backgroundGridShow: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}`
                 });
                 this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
                 CollectionDockingView.AddSplit(docToAdd, "", stack);
diff --git a/src/client/views/collections/CollectionDockingViewMenu.scss b/src/client/views/collections/CollectionDockingViewMenu.scss
deleted file mode 100644
index 4157f0f7e..000000000
--- a/src/client/views/collections/CollectionDockingViewMenu.scss
+++ /dev/null
@@ -1,34 +0,0 @@
-
-.dockingViewButtonSelector {
-    div {
-        overflow: visible !important;
-    }
-
-    display: inline-block;
-    width:100%;
-    height:100%;
-}
-.dockingViewButtonSelector-flyout {
-    position: relative;
-    z-index: 9999;
-    background-color: #eeeeee;
-    box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
-    color: black;
-
-    padding: 10px;
-    border-radius: 3px;
-    display: inline-block;
-    height: 100%;
-    width: 100%;
-    border-radius: 3px;
-
-    hr {
-        height: 1px;
-        margin: 0px;
-        background-color: gray;
-        border-top: 0px;
-        border-bottom: 0px;
-        border-right: 0px;
-        border-left: 0px;
-    }
-}
\ No newline at end of file
diff --git a/src/client/views/collections/CollectionDockingViewMenu.tsx b/src/client/views/collections/CollectionDockingViewMenu.tsx
deleted file mode 100644
index 1cab293a8..000000000
--- a/src/client/views/collections/CollectionDockingViewMenu.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Tooltip } from "@material-ui/core";
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import { DocumentButtonBar } from "../DocumentButtonBar";
-import { DocumentView } from "../nodes/DocumentView";
-const higflyout = require("@hig/flyout");
-export const { anchorPoints } = higflyout;
-export const Flyout = higflyout.default;
-
-@observer
-export class CollectionDockingViewMenu extends React.Component<{ views: () => DocumentView[], Stack: any }> {
-    customStylesheet(styles: any) {
-        return {
-            ...styles,
-            panel: {
-                ...styles.panel,
-                minWidth: "100px"
-            },
-        };
-    }
-    _ref = React.createRef<HTMLDivElement>();
-
-    @computed get flyout() {
-        return (
-            <div className="dockingViewButtonSelector-flyout" title=" " ref={this._ref}>
-                <DocumentButtonBar views={this.props.views} stack={this.props.Stack} />
-            </div>
-        );
-    }
-
-    @observable _tooltipOpen: boolean = false;
-    render() {
-        return <Tooltip open={this._tooltipOpen} onClose={action(() => this._tooltipOpen = false)} title={<><div className="dash-tooltip">Tap for toolbar, drag to create alias in another pane</div></>} placement="bottom">
-            <span className="dockingViewButtonSelector"
-                onPointerEnter={action(() => !this._ref.current?.getBoundingClientRect().width && (this._tooltipOpen = true))}
-                onPointerDown={action(e => {
-                    this.props.views()[0]?.select(false);
-                    this._tooltipOpen = false;
-                })} >
-                <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.flyout} stylesheet={this.customStylesheet}>
-                    <FontAwesomeIcon icon={"arrows-alt"} size={"sm"} />
-                </Flyout>
-            </span>
-        </Tooltip >;
-    }
-}
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 23fd4206c..39f6466d6 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -252,8 +252,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     };
     _clusterCommand = {
         params: ["target"], title: "fit content",
-        script: "self.target._fitToBox = !self.target._fitToBox;",
-        immediate: undoBatch((source: Doc[]) => this.target._fitToBox = !this.target._fitToBox),
+        script: "self.target._fitContentsToBox = !self.target._fitContentsToBox;",
+        immediate: undoBatch((source: Doc[]) => this.target._fitContentsToBox = !this.target._fitContentsToBox),
         initialize: emptyFunction
     };
     _fitContentCommand = {
@@ -457,7 +457,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
         const isPinned = targetDoc && Doc.isDocPinned(targetDoc);
         return !targetDoc ? (null) : <Tooltip key="pin" title={<div className="dash-tooltip">{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}</div>} placement="top">
             <button className="antimodeMenu-button" style={{ backgroundColor: isPinned ? "121212" : undefined, borderLeft: "1px solid gray" }}
-                onClick={e => TabDocView.PinDoc(targetDoc, { unpin: isPinned })}>
+                onClick={e => TabDocView.PinDoc(targetDoc, { /* unpin: isPinned*/ })}>
                 <FontAwesomeIcon className="colMenu-icon" size="lg" icon="map-pin" />
             </button>
         </Tooltip>;
@@ -507,7 +507,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
                 activeDoc.presPinViewY = y;
                 activeDoc.presPinViewScale = scale;
             } else if (targetDoc.type === DocumentType.VID) {
-                activeDoc.presPinTimecode = targetDoc._currentTimecode;
                 activeDoc.presPinView = true;
             } else if (targetDoc.type === DocumentType.COMPARISON) {
                 const width = targetDoc._clipWidth;
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index 8bdcfeea9..372629ddf 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -465,6 +465,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             _stayInCollection: true,
             useLinkSmallAnchor: true,
             hideLinkButton: true,
+            _isLinkButton: true,
             annotationOn: rootDoc,
             _timelineLabel: true,
             borderRounding: anchorEndTime === undefined ? "100%" : undefined
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 2f002736d..73572299a 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -30,11 +30,10 @@
         width: 90%;
         margin: 5px;
         font-size: 11px;
-        background-color: $light-blue;
         color: $medium-blue;
         padding: 10px;
-        border-radius: 10px;
-        border: solid 2px $medium-blue;
+        border-radius: 5px;
+        border: solid .5px $medium-blue;
     }
 }
 
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index 4f6f45d2f..7573b938a 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -135,7 +135,7 @@ export class CollectionTimeView extends CollectionSubView() {
             onClick={this.contentsDown}>
             <CollectionFreeFormView {...this.props}
                 engineProps={{ pivotField: this.pivotField, docFilters: this.childDocFilters, docRangeFilters: this.childDocRangeFilters }}
-                fitContentsToDoc={returnTrue}
+                fitContentsToBox={returnTrue}
                 childClickScript={this._childClickedScript}
                 viewDefDivClick={this._viewDefDivClick}
                 childFreezeDimensions={true}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index dab3d490d..022b9fa24 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -4,15 +4,15 @@ import { Tooltip } from '@material-ui/core';
 import 'golden-layout/src/css/goldenlayout-base.css';
 import 'golden-layout/src/css/goldenlayout-dark-theme.css';
 import { clamp } from 'lodash';
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
 import { observer } from "mobx-react";
 import * as ReactDOM from 'react-dom';
-import { DataSym, Doc, DocListCast, DocListCastAsync, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+import { DataSym, Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { FieldId } from "../../../fields/RefField";
 import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
+import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from "../../../Utils";
 import { DocServer } from "../../DocServer";
 import { DocUtils } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -27,14 +27,16 @@ import { Colors, Shadows } from '../global/globalEnums';
 import { LightboxView } from '../LightboxView';
 import { MainView } from '../MainView';
 import { DocFocusOptions, DocumentView, DocumentViewProps } from "../nodes/DocumentView";
+import { DashFieldView } from '../nodes/formattedText/DashFieldView';
 import { PinProps, PresBox, PresMovement } from '../nodes/trails';
 import { DefaultStyleProvider, StyleProp } from '../StyleProvider';
 import { CollectionDockingView } from './CollectionDockingView';
-import { CollectionDockingViewMenu } from './CollectionDockingViewMenu';
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
 import { CollectionView, CollectionViewType } from './CollectionView';
 import "./TabDocView.scss";
 import React = require("react");
+import { listSpec } from '../../../fields/Schema';
+import { ScriptField } from '../../../fields/ScriptField';
 const _global = (window /* browser */ || global /* node */) as any;
 
 interface TabDocViewProps {
@@ -82,7 +84,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             const iconWrap = document.createElement("div");
             const closeWrap = document.createElement("div");
 
-
             titleEle.size = StrCast(doc.title).length + 3;
             titleEle.value = doc.title;
             titleEle.onkeydown = (e: KeyboardEvent) => {
@@ -93,26 +94,33 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 Doc.GetProto(doc).title = e.currentTarget.value;
             }));
 
-            const dragBtnDown = (e: React.PointerEvent) => {
-                setupMoveUpEvents(this, e, e => !e.defaultPrevented && DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY), returnFalse, emptyFunction);
-            };
-
-
             if (tab.element[0].children[1].children.length === 1) {
-                iconWrap.className = "lm_iconWrap";
-                iconWrap.id = "lm_iconWrap";
-                closeWrap.className = "lm_iconWrap";
-                closeWrap.id = "lm_closeWrap";
-                closeWrap.onclick = (e: MouseEvent) => {
-                    tab.header.parent.contentItem.remove();
-                    Doc.AddDocToList(CurrentUserUtils.MyHeaderBarDoc, "data", tab.DashDoc);
-                    Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true);
+                iconWrap.className = "lm_iconWrap lm_moreInfo";
+                const dragBtnDown = (e: React.PointerEvent) => {
+                    setupMoveUpEvents(this, e, e => !e.defaultPrevented && DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY), returnFalse, action(e => {
+                        if (this.view) {
+                            SelectionManager.SelectView(this.view, false);
+                            let child = this.view.ContentDiv!.children[0];
+                            while (child.children.length) {
+                                const next = Array.from(child.children).find(c => c.className?.toString().includes("SVGAnimatedString") || typeof (c.className) === "string");
+                                if (next?.className?.toString().includes(DocumentView.ROOT_DIV)) break;
+                                if (next?.className?.toString().includes(DashFieldView.name)) break;
+                                if (next) child = next;
+                                else break;
+                            }
+                            simulateMouseClick(child, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
+                        }
+                        else { this._activated = true;
+                              setTimeout(() =>this.view && SelectionManager.SelectView(this.view, false));
+                        }
+                    }));
                 };
+                
                 const docIcon = <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} />;
-                const closeIcon = <FontAwesomeIcon icon={"times"} />;
+                const closeIcon = <FontAwesomeIcon icon={"eye"} />;
                 ReactDOM.render(docIcon, iconWrap);
                 ReactDOM.render(closeIcon, closeWrap);
-                // tab.element[0].append(closeWrap);
+                tab.reactComponents = [iconWrap, closeWrap];
                 tab.element[0].prepend(iconWrap);
                 tab._disposers.layerDisposer = reaction(() => ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }),
                     ({ layer, color }) => {
@@ -123,7 +131,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                         titleEle.style.backgroundColor = "transparent";
                         iconWrap.style.color = textColor;
                         closeWrap.style.color = textColor;
-                        moreInfoDrag.style.backgroundColor = textColor;
                         tab.element[0].style.background = !layer ? color : "dimgrey";
                     }, { fireImmediately: true });
             }
@@ -156,19 +163,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     // toggle.style.textTransform = selected ? "uppercase" : "";
                 }));
 
-            //attach the selection doc buttons menu to the drag handle
-            const stack: HTMLDivElement = tab.contentItem.parent;
-            const header: HTMLDivElement = tab;
-            stack.onscroll = action((e: any) => {
-                console.log('scrolling...');
-            });
-            const moreInfoDrag = document.createElement("div");
-            moreInfoDrag.className = "lm_iconWrap";
-            tab._disposers.buttonDisposer = reaction(() => this.view, view =>
-                view && [ReactDOM.render(<span><CollectionDockingViewMenu views={() => [view]} Stack={stack} /></span>, moreInfoDrag), tab._disposers.buttonDisposer?.()],
-                { fireImmediately: true });
-            // tab.reactComponents = [moreInfoDrag];
-            // tab.element[0].children[3].before(moreInfoDrag);
 
             // highlight the tab when the tab document is brushed in any part of the UI
             tab._disposers.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => {
@@ -185,20 +179,21 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     UndoManager.RunInBatch(() => tab.contentItem.remove(), "delete tab");
                 });
         }
-        CollectionDockingView.Instance.tabMap.add(tab);
     }
 
     /**
      * Adds a document to the presentation view
      **/
     @action
-    public static async PinDoc(doc: Doc, pinProps?: PinProps) {
-        if (pinProps?.unpin) console.log('TODO: Remove UNPIN from this location');
-        //add this new doc to props.Document
+    public static PinDoc(docs: Doc|Doc[], pinProps?: PinProps) {
+        const docList = ((docs instanceof Doc) ? [docs]: docs);
+        const batch = UndoManager.StartBatch("pinning doc");
+
+        // all docs will be added to the ActivePresentation as stored on CurrentUserUtils
         const curPres = CurrentUserUtils.ActivePresentation;
-        if (curPres) {
+        curPres && docList.forEach(doc => {
+            // Edge Case 1: Cannot pin document to itself
             if (doc === curPres) { alert("Cannot pin presentation document to itself"); return; }
-            const batch = UndoManager.StartBatch("pinning doc");
             const pinDoc = Doc.MakeAlias(doc);
             pinDoc.presentationTargetDoc = doc;
             pinDoc.title = doc.title + " - Slide";
@@ -218,6 +213,48 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             const size: number = PresBox.Instance?._selectedArray.size;
             const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined;
             const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null);
+			// If pinWithView option set then update scale and x / y props of slide
+			if (pinProps?.pinWithView) {
+				const viewProps = pinProps.pinWithView;
+				pinDoc.presPinView = true;
+            	pinDoc.presPinViewX = viewProps.bounds.left + viewProps.bounds.width / 2;
+            	pinDoc.presPinViewY = viewProps.bounds.top + viewProps.bounds.height / 2;
+            	pinDoc.presPinViewScale = viewProps.scale;
+                pinDoc.contentBounds = new List<number>([viewProps.bounds.left, viewProps.bounds.top, viewProps.bounds.left+viewProps.bounds.width, viewProps.bounds.top+viewProps.bounds.height]);
+            }
+            if (pinProps?.pinDocView) {
+                const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(pinDoc.type as any) || pinDoc._viewType === CollectionViewType.Stacking;
+                const pannable: boolean = ((pinDoc.type === DocumentType.COL && doc._viewType === CollectionViewType.Freeform) || doc.type === DocumentType.IMG);
+                if (scrollable) {
+                    const scroll = doc._scrollTop;
+                    pinDoc.presPinView = true;
+                    pinDoc.presPinViewScroll = scroll;
+                } else if ([DocumentType.AUDIO, DocumentType.VID].includes(doc.type as any)) {
+                    pinDoc.presPinView = true;
+                    pinDoc.presStartTime = doc._currentTimecode;
+                    pinDoc.presEndTime = NumCast(doc._currentTimecode) + 0.1;
+                } else if (pannable) {
+                    pinDoc.presPinView = true;
+                    pinDoc.presPinViewX = pinDoc._panX;
+                    pinDoc.presPinViewY = pinDoc._panY;
+                    pinDoc.presPinViewScale = pinDoc._viewScale;
+                    const pw = NumCast(pinProps.panelWidth);
+                    const ph = NumCast(pinProps.panelHeight);
+                    const ps = NumCast(pinDoc._viewScale);
+                    if (pw && ph && ps) {
+                        pinDoc.contentBounds = new List<number>([
+                            NumCast(pinDoc.panX)-pw/2/ps,
+                            NumCast(pinDoc.panY)-ph/2/ps,
+                            NumCast(pinDoc.panX)+pw/2/ps,
+                            NumCast(pinDoc.panY)+ph/2/ps]);
+                    }
+                } else if (doc.type === DocumentType.COMPARISON) {
+                    const width = doc._clipWidth;
+                    pinDoc.presPinClipWidth = width;
+                    pinDoc.presPinView = true;
+                }
+			}
+            pinDoc.onClick = ScriptField.MakeFunction("navigateToDoc(self.presentationTargetDoc, self)");
             Doc.AddDocToList(curPres, "data", pinDoc, presSelected);
             if (!pinProps?.audioRange && duration !== undefined) {
                 pinDoc.mediaStart = "manual";
@@ -236,20 +273,16 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 pinDoc.presMovement = PresMovement.None;
             }
             if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
-            const dview = CollectionDockingView.Instance.props.Document;
-            const fieldKey = CollectionDockingView.Instance.props.fieldKey;
-            const tabdocs = await DocListCastAsync(dview[fieldKey]);
-            runInAction(() => {
-                if (!pinProps?.hidePresBox && !tabdocs?.includes(curPres)) {
-                    tabdocs?.push(curPres);  // bcz: Argh! this is annoying.  if multiple documents are pinned, this will get called multiple times before the presentation view is drawn.  Thus it won't be in the tabdocs list and it will get created multple times.  so need to explicilty add the presbox to the list of open tabs
-                    CollectionDockingView.AddSplit(curPres, "right");
-                }
-                PresBox.Instance?._selectedArray.clear();
-                pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
-                DocumentManager.Instance.jumpToDocument(doc, false, undefined, []);
-                batch.end();
-            });
+            PresBox.Instance?._selectedArray.clear();
+            pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
+        });
+        if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) {
+            const docs = Cast(Cast(Doc.UserDoc().myOverlayDocs, Doc, null).data, listSpec(Doc), []);
+            if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1);
+            CollectionDockingView.AddSplit(curPres, "right");
+            setTimeout(() => DocumentManager.Instance.jumpToDocument(docList.lastElement(), false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things
         }
+        setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs
     }
 
     componentDidMount() {
@@ -272,7 +305,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     componentWillUnmount() {
         this._tabReaction?.();
         this._view && DocumentManager.Instance.RemoveView(this._view);
-        this.tab && CollectionDockingView.Instance.tabMap.delete(this.tab);
 
         this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged);
     }
@@ -420,9 +452,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
             }} ref={ref => {
                 if (this._mainCont = ref) {
                     if (this._lastTab) {
-                        console.log("DUP tab")
                         this._view && DocumentManager.Instance.RemoveView(this._view);
-                        CollectionDockingView.Instance.tabMap.delete(this._lastTab);
                     }
                     this._lastTab = this.tab;
                     (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document);
@@ -535,11 +565,11 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                     docFilters={CollectionDockingView.Instance.childDocFilters}
                     docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
                     searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
-                    fitContentsToDoc={returnTrue}
+                    fitContentsToBox={returnTrue}
                 />
                 <div className="miniOverlay" onPointerDown={this.miniDown} >
                     <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% `, }} />
                 </div>
             </div>;
     }
-}
+}
\ No newline at end of file
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 23b6a7f72..59dc5671b 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -216,8 +216,11 @@ export class TreeView extends React.Component<TreeViewProps> {
         this.props.hierarchyIndex !== undefined && this.props.RemFromMap?.(this.doc, this.props.hierarchyIndex);
     }
 
+    componentDidUpdate() {
+        this.props.hierarchyIndex !== undefined && this.props.AddToMap?.(this.doc, this.props.hierarchyIndex);
+    }
+
     componentDidMount() {
-        // TODO: [AL] add these
         this.props.hierarchyIndex !== undefined && this.props.AddToMap?.(this.doc, this.props.hierarchyIndex);
     }
 
@@ -468,7 +471,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             const docs = expandKey === "aliases" ? this.childAliases : expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs;
             let downX = 0, downY = 0;
             return <>
-                {!docs?.length ? (null) : <div className={'treeView-sorting'} style={{ background: sortings[sorting]?.color }} >
+                {!docs?.length || this.props.AddToMap /* hack to identify pres box trees */ ? (null) : <div className={'treeView-sorting'} style={{ background: sortings[sorting]?.color }} >
                     {sortings[sorting]?.label}
                 </div>}
                 <ul key={expandKey + "more"} title="click to change sort order" className={this.doc.treeViewHideTitle ? "no-indent" : ""}
@@ -606,13 +609,11 @@ export class TreeView extends React.Component<TreeViewProps> {
         return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
     }
 
-    // TODO: currently doc focus works, but can't seem to edit title
-    // onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
     onChildClick = () => {
-        return this.props.onChildClick?.() ?? (ScriptField.MakeFunction(`DocFocusOrOpen(self)`)! || this._editTitleScript?.());
+        return this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!);
     }
 
-    onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick);
+    onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeViewChildDoubleClick,(!this.props.treeView.outlineMode ? this._openScript?.():null));
 
     refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document);
     ignoreEvent = (e: any) => {
@@ -795,7 +796,7 @@ export class TreeView extends React.Component<TreeViewProps> {
             isDocumentActive={isActive}
             styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
             hideTitle={asText}
-            fitContentsToDoc={returnTrue}
+            fitContentsToBox={returnTrue}
             hideDecorationTitle={this.props.treeView.outlineMode}
             hideResizeHandles={this.props.treeView.outlineMode}
             onClick={this.onChildClick}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 12ad6b02b..52e99f26b 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -9,7 +9,7 @@ import { InkData, InkField, InkTool, PointData, Segment } from "../../../../fiel
 import { List } from "../../../../fields/List";
 import { ObjectField } from "../../../../fields/ObjectField";
 import { RichTextField } from "../../../../fields/RichTextField";
-import { createSchema, listSpec } from "../../../../fields/Schema";
+import { listSpec } from "../../../../fields/Schema";
 import { ScriptField } from "../../../../fields/ScriptField";
 import { BoolCast, Cast, FieldValue, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
 import { ImageField } from "../../../../fields/URLField";
@@ -26,6 +26,7 @@ import { DragManager, dropActionType } from "../../../util/DragManager";
 import { HistoryUtil } from "../../../util/History";
 import { InteractionUtils } from "../../../util/InteractionUtils";
 import { LinkManager } from "../../../util/LinkManager";
+import { RecordingApi } from "../../../util/RecordingApi";
 import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
 import { SearchUtil } from "../../../util/SearchUtil";
 import { SelectionManager } from "../../../util/SelectionManager";
@@ -41,6 +42,7 @@ import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveIn
 import { LightboxView } from "../../LightboxView";
 import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
 import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from "../../nodes/DocumentView";
+import { FieldViewProps } from "../../nodes/FieldView";
 import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
 import { PresBox } from "../../nodes/trails/PresBox";
 import { VideoBox } from "../../nodes/VideoBox";
@@ -50,26 +52,14 @@ import { CollectionDockingView } from "../CollectionDockingView";
 import { CollectionSubView } from "../CollectionSubView";
 import { TreeViewType } from "../CollectionTreeView";
 import { CollectionViewType } from "../CollectionView";
+import { TabDocView } from "../TabDocView";
 import { computePivotLayout, computerPassLayout, computerStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from "./CollectionFreeFormLayoutEngines";
 import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCursors";
 import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
-import { FieldView, FieldViewProps } from "../../nodes/FieldView";
-
-export const panZoomSchema = createSchema({
-    _panX: "number",
-    _panY: "number",
-    _currentTimecode: "number",
-    _timecodeToShow: "number",
-    _currentFrame: "number",
-    _useClusters: "boolean",
-    _viewTransition: "string",
-    _xPadding: "number",         // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
-    _yPadding: "number",         // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
-    _fitToBox: "boolean",
-    scrollHeight: "number"    // this will be set when the collection is an annotation overlay for a PDF/Webpage
-});
+import e = require("connect-flash");
+
 
 export type collectionFreeformViewProps = {
     annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox)
@@ -137,16 +127,20 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     this.props.PanelWidth() / (this.contentBounds.r - this.contentBounds.x))
         };
     }
-    @computed get fitToContent() { return (this.props.fitContentsToDoc?.() || this.Document._fitToBox) && !this.isAnnotationOverlay; }
-    @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.fitToContent ? 0 : Doc.NativeWidth(this.Document); }
-    @computed get nativeHeight() { return this.fitToContent ? 0 : Doc.NativeHeight(this.Document); }
+    @computed get fitContentsToBox() { return (this.props.fitContentsToBox?.() || this.Document._fitContentsToBox) && !this.isAnnotationOverlay; }
+    @computed get contentBounds() { 
+        const cb = Cast(this.rootDoc.contentBounds, listSpec("number"));
+        return cb ? {x:cb[0], y:cb[1], r:cb[2], b: cb[3]} : 
+            this.props.contentBounds?.() ?? 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.fitContentsToBox ? 0 : Doc.NativeWidth(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)); }
+    @computed get nativeHeight() { return this.fitContentsToBox ? 0 : Doc.NativeHeight(this.Document, Cast(this.Document.resolvedDataDoc, Doc, null)); }
     @computed get cachedCenteringShiftX(): number {
-        const scaling = this.fitToContent || !this.contentScaling ? 1 : this.contentScaling;
+        const scaling = this.fitContentsToBox || !this.contentScaling ? 1 : this.contentScaling;
         return this.props.isAnnotationOverlay ? 0 : this.props.PanelWidth() / 2 / scaling;  // shift so pan position is at center of window for non-overlay collections
     }
     @computed get cachedCenteringShiftY(): number {
-        const scaling = this.fitToContent || !this.contentScaling ? 1 : this.contentScaling;
+        const scaling = this.fitContentsToBox || !this.contentScaling ? 1 : this.contentScaling;
         return this.props.isAnnotationOverlay ? 0 : this.props.PanelHeight() / 2 / scaling;// shift so pan position is at center of window for non-overlay collections
     }
     @computed get cachedGetLocalTransform(): Transform {
@@ -171,11 +165,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         this.layoutDoc._panY = vals.bounds.cy;
         this.layoutDoc._viewScale = vals.scale;
     }
-    freeformData = (force?: boolean) => !this._firstRender && (this.fitToContent || force) ? this.fitToContentVals : undefined;
-    reverseNativeScaling = () => this.fitToContent ? true : false;
-    panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document._panX);
-    panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY);
-    zoomScaling = () => (this.freeformData()?.scale ?? NumCast(this.Document[this.scaleFieldKey], 1));
+    freeformData = (force?: boolean) => !this._firstRender && (this.fitContentsToBox || force) ? this.fitToContentVals : undefined;
+    reverseNativeScaling = () => this.fitContentsToBox ? true : false;
+    // panx, pany, zoomscale all attempt to get values first from the layout controller, then from the layout/dataDoc (or template layout doc), and finally from the resolved template data document.
+    // this search order, for example, allows icons of cropped images to find the panx/pany/zoom on the cropped image's data doc instead of the usual layout doc because the zoom/panX/panY define the cropped image 
+    panX = () => this.freeformData()?.bounds.cx ?? NumCast(this.Document._panX, NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panX, 1));
+    panY = () => this.freeformData()?.bounds.cy ?? NumCast(this.Document._panY, NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.panY, 1));
+    zoomScaling = () => this.freeformData()?.scale ?? NumCast(Doc.Layout(this.Document)[this.scaleFieldKey], NumCast(Cast(this.Document.resolvedDataDoc, Doc, null)?.[this.scaleFieldKey], 1));
     contentTransform = () => !this.cachedCenteringShiftX && !this.cachedCenteringShiftY && this.zoomScaling() === 1 ? "" : `translate(${this.cachedCenteringShiftX}px, ${this.cachedCenteringShiftY}px) scale(${this.zoomScaling()}) translate(${-this.panX()}px, ${-this.panY()}px)`;
     getTransform = () => this.cachedGetTransform.copy();
     getLocalTransform = () => this.cachedGetLocalTransform.copy();
@@ -1002,6 +998,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     setPan(panX: number, panY: number, panTime: number = 0, clamp: boolean = false) {
+        // set the current respective FFview to the tab being panned.
+        (Doc.UserDoc()?.presentationMode === 'recording') && RecordingApi.Instance.setRecordingFFView(this);
+        // TODO: make this based off the specific recording FFView
+        (Doc.UserDoc()?.presentationMode === 'none') && RecordingApi.Instance.setPlayFFView(this);
+        if (Doc.UserDoc()?.presentationMode === 'watching') {
+            RecordingApi.Instance.pauseVideoAndMovements();
+            Doc.UserDoc().presentationMode = 'none';
+            // RecordingApi.Instance.pauseMovements()
+        }
+
         if (!this.isAnnotationOverlay && clamp) {
             // this section wraps the pan position, horizontally and/or vertically whenever the content is panned out of the viewing bounds
             const docs = this.childLayoutPairs.map(pair => pair.layout).filter(doc => doc instanceof Doc);
@@ -1228,7 +1234,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             Document={childLayout}
             renderDepth={this.props.renderDepth + 1}
             replica={entry.replica}
-            dataTransition={entry.transition}
             suppressSetHeight={this.layoutEngine ? true : false}
             renderCutoffProvider={this.renderCutoffProvider}
             ContainingCollectionView={this.props.CollectionView}
@@ -1268,7 +1273,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
             pointerEvents={this.pointerEvents}
             jitterRotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
-        //fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
+        //fitContentsToBox={this.props.fitContentsToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this
         />;
     }
     addDocTab = action((doc: Doc, where: string) => {
@@ -1411,9 +1416,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 bounds: this.childDataProvider(entry[1].pair.layout, entry[1].replica)
             }));
 
-        if (this.props.isAnnotationOverlay) {   // don't zoom out farther than 1-1 if it's a bounded item (image, video, pdf), otherwise don't allow zooming in closer than 1-1 if it's a text sidebar
-            if (this.props.scaleField) this.props.Document[this.scaleFieldKey] = Math.min(1, NumCast(this.props.Document[this.scaleFieldKey], 1));
-            else this.props.Document[this.scaleFieldKey] = Math.max(1, NumCast(this.props.Document[this.scaleFieldKey]));
+        if (this.props.isAnnotationOverlay && this.props.Document[this.scaleFieldKey]) {   // don't zoom out farther than 1-1 if it's a bounded item (image, video, pdf), otherwise don't allow zooming in closer than 1-1 if it's a text sidebar
+            if (this.props.scaleField) this.props.Document[this.scaleFieldKey] = Math.min(1, this.zoomScaling());
+            else this.props.Document[this.scaleFieldKey] = Math.max(1,this.zoomScaling()); // NumCast(this.props.Document[this.scaleFieldKey]));
         }
 
         this.Document._useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
@@ -1490,51 +1495,71 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         }));
     }
 
-    replaceCanvases = (oldDiv: HTMLElement, newDiv: HTMLElement) => {
+    static replaceCanvases(oldDiv: HTMLElement, newDiv: HTMLElement) {
         if (oldDiv.childNodes && newDiv) {
             for (let i = 0; i < oldDiv.childNodes.length; i++) {
                 this.replaceCanvases(oldDiv.childNodes[i] as HTMLElement, newDiv.childNodes[i] as HTMLElement);
             }
         }
         if (oldDiv instanceof HTMLCanvasElement) {
-            const canvas = oldDiv;
-            const img = document.createElement('img'); // create a Image Element
-            img.src = canvas.toDataURL();   //image source
-            img.style.width = canvas.style.width;
-            img.style.height = canvas.style.height;
-            const newCan = newDiv as HTMLCanvasElement;
-            const parEle = newCan.parentElement as HTMLElement;
-            parEle.removeChild(newCan);
-            parEle.appendChild(img);
+            if (oldDiv.className === "collectionFreeFormView-grid") {
+                const newCan = newDiv as HTMLCanvasElement;
+                const parEle = newCan.parentElement as HTMLElement;
+                parEle.removeChild(newCan);
+                parEle.appendChild(document.createElement('div'))
+            } else {
+                const canvas = oldDiv;
+                const img = document.createElement('img'); // create a Image Element
+                img.src = canvas.toDataURL();   //image source
+                img.style.width = canvas.style.width;
+                img.style.height = canvas.style.height;
+                const newCan = newDiv as HTMLCanvasElement;
+                const parEle = newCan.parentElement as HTMLElement;
+                parEle.removeChild(newCan);
+                parEle.appendChild(img);
+            }
         }
     }
 
-    updateIcon = () => {
-        const docViewContent = this.props.docViewPath().lastElement().ContentDiv!;
+    updateIcon = () => CollectionFreeFormView.UpdateIcon(
+        this.layoutDoc[Id] + "-icon" + (new Date()).getTime(),
+        this.props.docViewPath().lastElement().ContentDiv!,
+        this.layoutDoc[WidthSym](), this.layoutDoc[HeightSym](), 
+        this.props.PanelWidth(), this.props.PanelHeight(), 0, 1, false, "",
+        (iconFile, nativeWidth, nativeHeight) => {
+            this.dataDoc.icon = new ImageField(iconFile);
+            this.dataDoc["icon-nativeWidth"] = nativeWidth;
+            this.dataDoc["icon-nativeHeight"] = nativeHeight;
+        });
+
+    public static UpdateIcon(
+        filename:string, docViewContent:HTMLElement, 
+        width: number, height: number, 
+        panelWidth:number, panelHeight: number, 
+        scrollTop:number, 
+        realNativeHeight: number,
+        noSuffix: boolean,
+        replaceRootFilename: string| undefined,
+        cb:(iconFile:string, nativeWidth:number, nativeHeight:number) => any) 
+    {
         const newDiv = docViewContent.cloneNode(true) as HTMLDivElement;
-        newDiv.style.width = (this.layoutDoc[WidthSym]()).toString();
-        newDiv.style.height = (this.layoutDoc[HeightSym]()).toString();
+        newDiv.style.width = width.toString();
+        newDiv.style.height = height.toString();
         this.replaceCanvases(docViewContent, newDiv);
-        const htmlString = this._mainCont && new XMLSerializer().serializeToString(newDiv);
-        const nativeWidth = this.layoutDoc[WidthSym]();
-        const nativeHeight = this.layoutDoc[HeightSym]();
-
-        CreateImage(
-            "",
+        const htmlString = new XMLSerializer().serializeToString(newDiv);
+        const nativeWidth = width;
+        const nativeHeight = height;
+        return CreateImage(
+            Utils.prepend(""),
             document.styleSheets,
             htmlString,
             nativeWidth,
-            nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(),
-            NumCast(this.layoutDoc._scrollTop)
+            nativeWidth * panelHeight / panelWidth,
+            scrollTop * panelHeight  / realNativeHeight
         ).then
-            ((data_url: any) => {
-                VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true, this.layoutDoc[Id] + "-icon").then(
-                    returnedfilename => setTimeout(action(() => {
-
-                        this.dataDoc.icon = new ImageField(returnedfilename);
-                        this.dataDoc["icon-nativeWidth"] = nativeWidth;
-                        this.dataDoc["icon-nativeHeight"] = nativeHeight;
-                    }), 500));
+            (async (data_url: any) => {
+                const returnedFilename = await VideoBox.convertDataUri(data_url, filename, noSuffix, replaceRootFilename);
+                cb(returnedFilename as string, nativeWidth, nativeHeight);
             })
             .catch(function (error: any) {
                 console.error('oops, something went wrong!', error);
@@ -1605,8 +1630,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const appearanceItems = appearance && "subitems" in appearance ? appearance.subitems : [];
         appearanceItems.push({ description: "Reset View", event: () => { this.props.Document._panX = this.props.Document._panY = 0; this.props.Document[this.scaleFieldKey] = 1; }, icon: "compress-arrows-alt" });
         !Doc.UserDoc().noviceMode && Doc.UserDoc().defaultTextLayout && appearanceItems.push({ description: "Reset default note style", event: () => Doc.UserDoc().defaultTextLayout = undefined, icon: "eye" });
-        appearanceItems.push({ description: `${this.fitToContent ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitToBox = !this.fitToContent, icon: !this.fitToContent ? "expand-arrows-alt" : "compress-arrows-alt" });
-        appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" });
+        appearanceItems.push({ description: `${this.fitContentsToBox ? "Make Zoomable" : "Scale to Window"}`, event: () => this.Document._fitContentsToBox = !this.fitContentsToBox, icon: !this.fitContentsToBox ? "expand-arrows-alt" : "compress-arrows-alt" });
+        appearanceItems.push({ description: `Pin View`, event: () => TabDocView.PinDoc(this.rootDoc, {pinDocView:true, panelWidth: this.props.PanelWidth(), panelHeight:this.props.PanelHeight()}), icon: "map-pin" });
+        //appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: "compress-arrows-alt" });
         this.props.ContainingCollectionView &&
             appearanceItems.push({ description: "Ungroup collection", event: this.promoteCollection, icon: "table" });
 
@@ -1762,7 +1788,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             isAnnotationOverlay={this.isAnnotationOverlay}>
             <div className="marqueeView-div" ref={this._marqueeRef} style={{ opacity: this.props.dontRenderDocuments ? 0 : undefined }}>
                 {this.layoutDoc._backgroundGridShow ?
-                    <CollectionFreeFormBackgroundGrid
+                    <div><CollectionFreeFormBackgroundGrid  // bcz : UGHH  don't know why, but if we don't wrap in a div, then PDF's don't render whenn taking snapshot of a dashboard and the background grid is on!!?
                         PanelWidth={this.props.PanelWidth}
                         PanelHeight={this.props.PanelHeight}
                         panX={this.panX}
@@ -1772,7 +1798,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                         isAnnotationOverlay={this.isAnnotationOverlay}
                         cachedCenteringShiftX={this.cachedCenteringShiftX}
                         cachedCenteringShiftY={this.cachedCenteringShiftY}
-                    /> : (null)}
+                    /></div> : (null)}
                 <CollectionFreeFormViewPannableContents
                     isAnnotationOverlay={this.isAnnotationOverlay}
                     isAnnotationOverlayScrollable={this.props.isAnnotationOverlayScrollable}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 7865f2381..051da795f 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -19,7 +19,7 @@ import { Transform } from "../../../util/Transform";
 import { undoBatch, UndoManager } from "../../../util/UndoManager";
 import { ContextMenu } from "../../ContextMenu";
 import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
-import { PresBox } from "../../nodes/trails/PresBox";
+import { PinViewProps, PresBox } from "../../nodes/trails/PresBox";
 import { PresMovement } from "../../nodes/trails/PresEnums";
 import { VideoBox } from "../../nodes/VideoBox";
 import { pasteImageBitmap } from "../../nodes/WebBoxRenderer";
@@ -30,6 +30,7 @@ import { TreeView } from "../TreeView";
 import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
 import "./MarqueeView.scss";
 import React = require("react");
+import { TabDocView } from "../TabDocView";
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -43,6 +44,14 @@ interface MarqueeViewProps {
     ungroup?: () => void;
     setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void;
 }
+
+export interface MarqueeViewBounds {
+	left: number;
+	top: number;
+	width: number;
+	height: number;
+}
+
 @observer
 export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps>
 {
@@ -61,7 +70,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         const topLeft = this.Transform.transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
         // nda - args to transformDirection is just x and y diff btw downX/Y and lastX/Y
         const size = this.Transform.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]) };
+		const bounds:MarqueeViewBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) }
+        return bounds;
     }
     get inkDoc() { return this.props.Document; }
     get ink() { return Cast(this.props.Document.ink, InkField); }
@@ -264,7 +274,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             document.removeEventListener("wheel", hideMarquee);
         };
         if (PresBox.startMarquee) {
-            this.pinMarqueeView();
+            this.pinWithView();
             PresBox.startMarquee = false;
         }
         if (!this._commandExecuted && (Math.abs(this.Bounds.height * this.Bounds.width) > 100) && !PresBox.startMarquee) {
@@ -385,98 +395,23 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         this.hideMarquee();
     }
 
+	/**
+	 * This triggers the TabDocView.PinDoc method which is the universal method
+	 * used to pin documents to the currently active presentation trail. 
+	 * 
+	 * This one is unique in that it includes the bounds associated with marquee view. 
+	 */
     @undoBatch
     @action
-    pinMarqueeView = async () => {
-        const doc = this.props.Document;
-        const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
-        if (curPres) {
-            console.log(curPres);
-            if (doc === curPres) { alert("Cannot pin presentation document to itself"); return; }
-            const pinDoc = Doc.MakeAlias(doc);
-            pinDoc.presentationTargetDoc = doc;
-            pinDoc.presMovement = PresMovement.Zoom;
-            pinDoc.groupWithUp = false;
-            pinDoc.context = curPres;
-            pinDoc.title = doc.title + " - Slide";// these should potentially all be props passed down by the CollectionTreeView to the TreeView elements.  That way the PresBox could configure all of its children at render time
-            pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
-            pinDoc.treeViewHeaderWidth = "100%"; // forces the header to grow to be the same size as its largest sibling.
-            pinDoc.treeViewChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc.
-            pinDoc.treeViewFieldKey = "data"; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
-            pinDoc.treeViewExpandedView = "data";// in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
-            pinDoc.treeViewGrowsHorizontally = true;// the document expands horizontally when displayed as a tree view header  
-            pinDoc.treeViewHideHeaderIfTemplate = true;  // this will force the document to render itself as the tree view header
-
-            const presArray = PresBox.Instance?.sortArray();
-            const size = PresBox.Instance?._selectedArray.size;
-            const presSelected = presArray && size ? presArray[size - 1] : undefined;
-            Doc.AddDocToList(curPres, "data", pinDoc, presSelected);
-            if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true;
-            const dview = CollectionDockingView.Instance.props.Document;
-            const fieldKey = CollectionDockingView.Instance.props.fieldKey;
-            const tabdocs = await DocListCastAsync(dview[fieldKey]);
-            if (!tabdocs?.includes(curPres)) {
-                tabdocs?.push(curPres);  // bcz: Argh! this is annoying.  if multiple documents are pinned, this will get called multiple times before the presentation view is drawn.  Thus it won't be in the tabdocs list and it will get created multple times.  so need to explicilty add the presbox to the list of open tabs
-                CollectionDockingView.AddSplit(curPres, "right");
-            }
-            PresBox.Instance?._selectedArray.clear();
-            pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Updates selected array
-            const index = PresBox.Instance?.childDocs.indexOf(pinDoc);
-            index && (curPres._itemIndex = index);
-            const scale = Math.min(this.props.PanelWidth() / this.Bounds.width, this.props.PanelHeight() / this.Bounds.height);
-            pinDoc.presPinView = true;
-            pinDoc.presPinViewX = this.Bounds.left + this.Bounds.width / 2;
-            pinDoc.presPinViewY = this.Bounds.top + this.Bounds.height / 2;
-            pinDoc.presPinViewScale = scale;
-        }
-        this.hideMarquee();
-    }
-
-    @undoBatch
-    @action
-    pinWithView = async (e: KeyboardEvent | React.PointerEvent | undefined) => {
-        const doc = this.props.Document;
-        const curPres = Cast(Doc.UserDoc().activePresentation, Doc) as Doc;
-        if (curPres) {
-            if (doc === curPres) { alert("Cannot pin presentation document to itself"); return; }
-            const pinDoc = Doc.MakeAlias(doc);
-            pinDoc.presentationTargetDoc = doc;
-            pinDoc.presMovement = PresMovement.Zoom;
-            pinDoc.groupWithUp = false;
-            pinDoc.context = curPres;
-            pinDoc.title = doc.title + " - Slide";
-            pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area
-            pinDoc.treeViewHeaderWidth = "100%"; // forces the header to grow to be the same size as its largest sibling.
-            pinDoc.treeViewChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc.
-            pinDoc.treeViewFieldKey = "data"; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field
-            pinDoc.treeViewExpandedView = "data";// in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view
-            pinDoc.treeViewGrowsHorizontally = true;// the document expands horizontally when displayed as a tree view header  
-            pinDoc.treeViewHideHeaderIfTemplate = true;  // this will force the document to render itself as the tree view header
-
-            const presArray = PresBox.Instance?.sortArray();
-            const size = PresBox.Instance?._selectedArray.size;
-            const presSelected = presArray && size ? presArray[size - 1] : undefined;
-            Doc.AddDocToList(curPres, "data", pinDoc, presSelected);
-            if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true; const dview = CollectionDockingView.Instance.props.Document;
-            const fieldKey = CollectionDockingView.Instance.props.fieldKey;
-            const tabdocs = await DocListCastAsync(dview[fieldKey]);
-            if (!tabdocs?.includes(curPres)) {
-                tabdocs?.push(curPres);  // bcz: Argh! this is annoying.  if multiple documents are pinned, this will get called multiple times before the presentation view is drawn.  Thus it won't be in the tabdocs list and it will get created multple times.  so need to explicilty add the presbox to the list of open tabs
-                CollectionDockingView.AddSplit(curPres, "right");
-            }
-            PresBox.Instance?._selectedArray.clear();
-            pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Updates selected array
-            const index = PresBox.Instance?.childDocs.indexOf(pinDoc);
-            index && (curPres._itemIndex = index);
-            if (e instanceof KeyboardEvent ? e.key === "c" : true) {
-                const scale = Math.min(this.props.PanelWidth() / this.Bounds.width, this.props.PanelHeight() / this.Bounds.height);
-                pinDoc.presPinView = true;
-                pinDoc.presPinViewX = this.Bounds.left + this.Bounds.width / 2;
-                pinDoc.presPinViewY = this.Bounds.top + this.Bounds.height / 2;
-                pinDoc.presPinViewScale = scale;
-            }
-        }
-        MarqueeOptionsMenu.Instance.fadeOut(true);
+    pinWithView = async () => {
+        const scale = Math.min(this.props.PanelWidth() / this.Bounds.width, this.props.PanelHeight() / this.Bounds.height);
+		const doc = this.props.Document;
+		const viewOptions:PinViewProps = {
+			bounds: this.Bounds,
+			scale: scale
+		};
+		TabDocView.PinDoc(doc, {pinWithView: viewOptions});
+		MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
     }
 
@@ -499,6 +434,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             }));
             this.props.removeDocument?.(selected);
         }
+        // TODO: nda - this is the code to actually get a new grouped collection
         const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined, group);
         this.props.addDocument?.(newCollection);
         this.props.selectDocuments([newCollection]);
@@ -581,7 +517,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             d.y = NumCast(d.y) - this.Bounds.top;
             return d;
         });
-        const summary = Docs.Create.TextDocument("", { _backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitToBox: true, _showSidebar: true, title: "overview" });
+        const summary = Docs.Create.TextDocument("", { _backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitContentsToBox: true, _showSidebar: true, title: "overview" });
         const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: "transparent" });
         DocUtils.MakeLink({ doc: summary }, { doc: portal }, "summary of:summarized by", "");
 
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index c0a33a5e0..9b1cb1601 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -153,6 +153,7 @@ export class CollectionLinearView extends CollectionSubView() {
                 PanelWidth={nested ? doc[WidthSym] : this.dimension}
                 PanelHeight={nested || doc._height ? doc[HeightSym] : this.dimension}
                 renderDepth={this.props.renderDepth + 1}
+                dontRegisterView={BoolCast(this.rootDoc.childDontRegisterViews)}
                 focus={emptyFunction}
                 styleProvider={this.props.styleProvider}
                 docViewPath={returnEmptyDoclist}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index e2dfb25e2..b7ba94940 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -257,7 +257,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
             hideResizeHandles={this.props.childHideResizeHandles?.()}
             hideDecorationTitle={this.props.childHideDecorationTitle?.()}
-            fitContentsToDoc={this.props.fitContentsToDoc}
+            fitContentsToBox={this.props.fitContentsToBox}
             PanelWidth={width}
             PanelHeight={height}
             rootSelected={this.rootSelected}
@@ -272,6 +272,7 @@ export class CollectionMulticolumnView extends CollectionSubView() {
             searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
+            dontRegisterView={this.props.dontRegisterView}
             addDocument={this.props.addDocument}
             moveDocument={this.props.moveDocument}
             removeDocument={this.props.removeDocument}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
index 3010e36aa..338639a83 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx
@@ -264,13 +264,14 @@ export class CollectionMultirowView extends CollectionSubView() {
             isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.isContentActive}
             hideResizeHandles={this.props.childHideResizeHandles?.()}
             hideDecorationTitle={this.props.childHideDecorationTitle?.()}
-            fitContentsToDoc={this.props.fitContentsToDoc}
+            fitContentsToBox={this.props.fitContentsToBox}
             focus={this.props.focus}
             docFilters={this.childDocFilters}
             docRangeFilters={this.childDocRangeFilters}
             searchFilterDocs={this.searchFilterDocs}
             ContainingCollectionDoc={this.props.CollectionView?.props.Document}
             ContainingCollectionView={this.props.CollectionView}
+            dontRegisterView={this.props.dontRegisterView}
             addDocument={this.props.addDocument}
             moveDocument={this.props.moveDocument}
             removeDocument={this.props.removeDocument}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index b731479a5..f45068b6a 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -402,7 +402,7 @@ export class CollectionSchemaView extends CollectionSubView() {
                 <DocumentView
                     Document={this.previewDocument}
                     DataDoc={undefined}
-                    fitContentsToDoc={returnTrue}
+                    fitContentsToBox={returnTrue}
                     freezeDimensions={true}
                     dontCenter={"y"}
                     focus={DocUtils.DefaultFocus}
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 3ddcf803d..a75e7a0c4 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -102,6 +102,10 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
 
                     this.props.itemHandler?.(this.props.linkDoc);
                 } else {
+                    const focusDoc = Cast(this.props.linkDoc.anchor1, Doc, null)?.annotationOn === this.props.sourceDoc ? Cast(this.props.linkDoc.anchor1, Doc, null) :
+                        Cast(this.props.linkDoc.anchor2, Doc, null)?.annotationOn === this.props.sourceDoc ? Cast(this.props.linkDoc.anchor12, Doc, null) : undefined;
+
+                    if (focusDoc) this.props.docView.ComponentView?.scrollFocus?.(focusDoc, true);
                     LinkManager.FollowLink(this.props.linkDoc, this.props.sourceDoc, this.props.docView.props, false);
                 }
             });
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index 1d06f368f..145b6d5a6 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -4,8 +4,6 @@ import { action, computed, IReactionDisposer, observable, runInAction } from "mo
 import { observer } from "mobx-react";
 import { DateField } from "../../../fields/DateField";
 import { Doc, DocListCast } from "../../../fields/Doc";
-import { documentSchema } from "../../../fields/documentSchemas";
-import { makeInterface } from "../../../fields/Schema";
 import { ComputedField } from "../../../fields/ScriptField";
 import { Cast, DateCast, NumCast } from "../../../fields/Types";
 import { AudioField, nullAudio } from "../../../fields/URLField";
@@ -296,11 +294,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
 
     // button for starting and stopping the recording
-    Record = (e: React.MouseEvent) => {
-        if (e.button === 0 && !e.ctrlKey) {
+    Record = (e: React.PointerEvent) => {
+        e.button === 0 && !e.ctrlKey && setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => {
             this._recorder ? this.stopRecording() : this.recordAudioAnnotation();
-            e.stopPropagation();
-        }
+        }), false);
     }
 
     // for play button
@@ -338,27 +335,28 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     // for dictation button, creates a text document for dictation
     onFile = (e: any) => {
-        const newDoc = CurrentUserUtils.GetNewTextDoc(
-            "",
-            NumCast(this.rootDoc.x),
-            NumCast(this.rootDoc.y) +
-            NumCast(this.layoutDoc._height) +
-            10,
-            NumCast(this.layoutDoc._width),
-            2 * NumCast(this.layoutDoc._height)
-        );
-        Doc.GetProto(newDoc).recordingSource = this.dataDoc;
-        Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.fieldKey}-recordingStart"]`);
-        Doc.GetProto(newDoc).mediaState = ComputedField.MakeFunction("self.recordingSource.mediaState");
-        const overlayDoc = Doc.UserDoc().myOverlayDocs as Doc;
-        if (DocListCast(overlayDoc[Doc.LayoutFieldKey(overlayDoc)]).includes(this.rootDoc)) {
-            newDoc.x = this.rootDoc.x;
-            newDoc.y = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
-            Doc.AddDocToList(overlayDoc, undefined, newDoc);
-        } else {
-            this.props.addDocument?.(newDoc);
-        }
-        e.stopPropagation();
+        setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => {
+            const newDoc = CurrentUserUtils.GetNewTextDoc(
+                "",
+                NumCast(this.rootDoc.x),
+                NumCast(this.rootDoc.y) +
+                NumCast(this.layoutDoc._height) +
+                10,
+                NumCast(this.layoutDoc._width),
+                2 * NumCast(this.layoutDoc._height)
+            );
+            Doc.GetProto(newDoc).recordingSource = this.dataDoc;
+            Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.fieldKey}-recordingStart"]`);
+            Doc.GetProto(newDoc).mediaState = ComputedField.MakeFunction("self.recordingSource.mediaState");
+            const overlayDoc = Doc.UserDoc().myOverlayDocs as Doc;
+            if (DocListCast(overlayDoc[Doc.LayoutFieldKey(overlayDoc)]).includes(this.rootDoc)) {
+                newDoc.x = this.rootDoc.x;
+                newDoc.y = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
+                Doc.AddDocToList(overlayDoc, undefined, newDoc);
+            } else {
+                this.props.addDocument?.(newDoc);
+            }
+        }), false);
     }
 
 
@@ -371,21 +369,21 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
 
     // pause the time during recording phase
-    @action
-    recordPause = (e: React.MouseEvent) => {
-        this._pauseStart = new Date().getTime();
-        this._paused = true;
-        this._recorder.pause();
-        e.stopPropagation();
+    recordPause = (e: React.PointerEvent) => {
+        setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => {
+            this._pauseStart = new Date().getTime();
+            this._paused = true;
+            this._recorder.pause();
+        }), false);
     }
 
     // continue the recording
-    @action
-    recordPlay = (e: React.MouseEvent) => {
-        this._pauseEnd = new Date().getTime();
-        this._paused = false;
-        this._recorder.resume();
-        e.stopPropagation();
+    recordPlay = (e: React.PointerEvent) => {
+        setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => {
+            this._pauseEnd = new Date().getTime();
+            this._paused = false;
+            this._recorder.resume();
+        }), false);
     }
 
 
@@ -504,19 +502,19 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     // UI for recording, initially displayed when new audio created in Dash
     @computed get recordingControls() {
         return <div className="audiobox-recorder">
-            <div className="audiobox-dictation" onClick={this.onFile}>
+            <div className="audiobox-dictation" onPointerDown={this.onFile}>
                 <FontAwesomeIcon
                     size="2x"
                     icon="file-alt" />
             </div>
             {[media_state.Recording, media_state.Playing].includes(this.mediaState) ?
                 <div className="recording-controls" onClick={e => e.stopPropagation()}>
-                    <div className="record-button" onClick={this.Record}>
+                    <div className="record-button" onPointerDown={this.Record}>
                         <FontAwesomeIcon
                             size="2x"
                             icon="stop" />
                     </div>
-                    <div className="record-button" onClick={this._paused ? this.recordPlay : this.recordPause}>
+                    <div className="record-button" onPointerDown={this._paused ? this.recordPlay : this.recordPause}>
                         <FontAwesomeIcon
                             size="2x"
                             icon={this._paused ? "play" : "pause"} />
@@ -526,7 +524,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     </div>
                 </div>
                 :
-                <div className="audiobox-start-record">
+                <div className="audiobox-start-record" onPointerDown={this.Record}>
                     <FontAwesomeIcon icon="microphone" />
                     RECORD
                 </div>}
@@ -655,7 +653,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             ref={this.setupTimelineDrop}
             className="audiobox-container"
             onContextMenu={this.specificContextMenu}
-            onClick={!this.path && !this._recorder ? this.recordAudioAnnotation : undefined}
             style={{ pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined }}
         >
             {!this.path ? this.recordingControls : this.playbackControls}
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 5a0ab9110..bedc97575 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -6,7 +6,7 @@ import { listSpec } from "../../../fields/Schema";
 import { ComputedField } from "../../../fields/ScriptField";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
 import { TraceMobx } from "../../../fields/util";
-import { DashColor, numberRange } from "../../../Utils";
+import { DashColor, numberRange, OmitKeys } from "../../../Utils";
 import { DocumentManager } from "../../util/DocumentManager";
 import { SelectionManager } from "../../util/SelectionManager";
 import { Transform } from "../../util/Transform";
@@ -170,7 +170,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
                 width: this.panelWidth(),
                 height: this.panelHeight(),
                 transform: this.transform,
-                transition: this.props.dataTransition ? this.props.dataTransition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.dataTransition),
+                transition: this.dataProvider?.transition ?? (this.props.dataTransition ? this.props.dataTransition : this.dataProvider ? this.dataProvider.transition : StrCast(this.layoutDoc.dataTransition)),
                 zIndex: this.ZInd,
                 mixBlendMode: mixBlendMode,
                 display: this.ZInd === -99 ? "none" : undefined
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index d4e8ffc7f..70732e74c 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -41,6 +41,7 @@ import { WebBox } from "./WebBox";
 import React = require("react");
 import XRegExp = require("xregexp");
 import { MapBox } from "./MapBox/MapBox";
+import { RecordingBox } from "./RecordingBox";
 
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
@@ -225,7 +226,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
                 components={{
                     FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, LabelBox, EquationBox, SliderBox, FieldView,
                     CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
-                    PDFBox, VideoBox, AudioBox, PresBox, YoutubeBox, PresElementBox, SearchBox, FilterBox, FunctionPlotBox,
+                    PDFBox, VideoBox, AudioBox, RecordingBox, PresBox, YoutubeBox, PresElementBox, SearchBox, FilterBox, FunctionPlotBox,
                     ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, LinkBox, ScriptingBox, MapBox,
                     ScreenshotBox,
                     HTMLtag, ComparisonBox
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 073f734c0..60d16f044 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -85,7 +85,7 @@ export interface DocComponentView {
     getAnchor?: () => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
     scrollFocus?: (doc: Doc, smooth: boolean) => Opt<number>; // returns the duration of the focus
     setViewSpec?: (anchor: Doc, preview: boolean) => void;  // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
-    reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height.  However, some content views (e.g., FreeFormView w/ fitToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
+    reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height.  However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
     shrinkWrap?: () => void;  // requests a document to display all of its contents with no white space.  currently only implemented (needed?) for freeform views
     menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected.
     isAnyChildContentActive?: () => boolean; // is any child content of the document active
@@ -112,7 +112,8 @@ export interface DocumentViewSharedProps {
     renderDepth: number;
     Document: Doc;
     DataDoc?: Doc;
-    fitContentsToDoc?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitToBox property on a Document
+    contentBounds?: () => (undefined|{x:number, y:number, r:number, b:number});
+    fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitContentsToBox property on a Document
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
     suppressSetHeight?: boolean;
@@ -163,6 +164,9 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
     hideResizeHandles?: boolean; // whether to suppress DocumentDecorations when this document is selected
     hideTitle?: boolean;  // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
     hideDecorationTitle?: boolean;  // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
+    hideDocumentButtonBar?: boolean;
+    hideOpenButton?: boolean;
+    hideDeleteButton?: boolean;
     treeViewDoc?: Doc;
     isDocumentActive?: () => boolean | undefined; // whether a document should handle pointer events
     isContentActive: () => boolean | undefined; // whether document contents should handle pointer events
@@ -760,7 +764,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             DocListCast(this.Document.links).length && appearanceItems.splice(0, 0, { description: `${this.layoutDoc.hideLinkButton ? "Show" : "Hide"} Link Button`, event: action(() => this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton), icon: "eye" });
             !appearance && cm.addItem({ description: "UI Controls...", subitems: appearanceItems, icon: "compass" });
 
-            if (!Doc.IsSystem(this.rootDoc) && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
+            if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
                 !Doc.UserDoc().noviceMode && appearanceItems.splice(0, 0, { description: `${!this.layoutDoc._showAudio ? "Show" : "Hide"} Audio Button`, event: action(() => this.layoutDoc._showAudio = !this.layoutDoc._showAudio), icon: "microphone" });
                 const existingOnClick = cm.findByDescription("OnClick...");
                 const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
@@ -830,10 +834,10 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
             const help = cm.findByDescription("Help...");
             const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
-            !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
-            helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
-            !Doc.UserDoc().novice && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
-            !Doc.UserDoc().novice && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" });
+            helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
+            !Doc.UserDoc().noviceMode && helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
+            !Doc.UserDoc().noviceMode && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
+            !Doc.UserDoc().noviceMode && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" });
             cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
         }
 
@@ -956,7 +960,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     @computed get allLinkEndpoints() {  // the small blue dots that mark the endpoints of links
         TraceMobx();
         if (this.layoutDoc.unrendered || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return null;
-        if (this.layoutDoc.presBox || this.rootDoc.type === DocumentType.LINK || this.props.dontRegisterView) return (null);
+        if (this.rootDoc.type=== DocumentType.PRES || this.rootDoc.type === DocumentType.LINK || this.props.dontRegisterView) return (null);
         const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => !d.hidden);
         return filtered.map((link, i) =>
             <div className="documentView-anchorCont" key={link[Id]}>
@@ -1200,6 +1204,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
 
     @observable public docView: DocumentViewInternal | undefined | null;
 
+    showContextMenu(pageX:number, pageY:number) { return this.docView?.onContextMenu(undefined, pageX, pageY); }
     get Document() { return this.props.Document; }
     get topMost() { return this.props.renderDepth === 0; }
     get rootDoc() { return this.docView?.rootDoc || this.Document; }
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index 79c1f1c40..67cf18d8b 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -7,6 +7,7 @@ import { List } from "../../../fields/List";
 import { WebField } from "../../../fields/URLField";
 import { DocumentView, DocumentViewSharedProps } from "./DocumentView";
 import { ScriptField } from "../../../fields/ScriptField";
+import { RecordingBox } from "./RecordingBox";
 
 //
 // these properties get assigned through the render() method of the DocumentView when it creates this node.
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 28834a202..e3708d7b9 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -206,7 +206,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
             facetValues.strings.map(val => {
                 const num = val ? Number(val) : Number.NaN;
                 if (Number.isNaN(num)) {
-                    num && nonNumbers++;
+                    val && nonNumbers++;
                 } else {
                     minVal = Math.min(num, minVal);
                     maxVal = Math.max(num, maxVal);
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index ab4ed6b33..8c27b3508 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -357,7 +357,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return <div className="imageBox-annotationLayer" style={{ height: this.props.PanelHeight() }} ref={this._annotationLayer} />;
     }
     marqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
+        if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._viewScale,1) <= NumCast(this.rootDoc.viewScaleMin,1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.SelectedTool)) {
             setupMoveUpEvents(this, e, action(e => {
                 MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                 this._marqueeing = [e.clientX, e.clientY];
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 152288d8b..6c7e174f7 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -121,7 +121,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
             LinkDocPreview.Clear();
             LinkManager.FollowLink(this._linkDoc, this._linkSrc, this.props.docProps, false);
         } else if (this.props.hrefs?.length) {
-            this.props.docProps?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _width: 200, _height: 400, useCors: true }), "add:right");
+            this.props.docProps?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true }), "add:right");
         }
     }
     width = () => {
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 9f1c019fe..5f4c17ee6 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -259,7 +259,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
             if (this._loadPending && this._map.getBounds()) {
                 this._loadPending = false;
-                this.layoutDoc.fitToBox && this.fitBounds(this._map);
+                this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
             }
         }, 250);
         // listener to addmarker event
@@ -274,7 +274,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     centered = () => {
         if (this._loadPending && this._map.getBounds()) {
             this._loadPending = false;
-            this.layoutDoc.fitToBox && this.fitBounds(this._map);
+            this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
         }
         this.dataDoc.mapLat = this._map.getCenter()?.lat();
         this.dataDoc.mapLng = this._map.getCenter()?.lng();
@@ -284,7 +284,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     zoomChanged = () => {
         if (this._loadPending && this._map.getBounds()) {
             this._loadPending = false;
-            this.layoutDoc.fitToBox && this.fitBounds(this._map);
+            this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
         }
         this.dataDoc.mapZoom = this._map.getZoom();
     }
diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
index db7dcb09d..bdf0f9d64 100644
--- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
+++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
@@ -45,11 +45,9 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
 
 
     _stack: CollectionStackingView | null | undefined;
-
-    // Collection stacking view for documents in the infowindow of a map marker
-    @computed get renderChildDocs() {
-        return;
-    }
+    childFitWidth = (doc:Doc) => doc.type === DocumentType.RTF;
+    addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.AddDocToList(this.props.place, "data", d), true as boolean);
+    removeDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, "data", d), true as boolean);
     render() {
         return <InfoWindow anchor={this.props.markerMap[this.props.place[Id]]} onCloseClick={this.handleInfoWindowClose} >
             <div className="mapbox-infowindow">
@@ -73,10 +71,10 @@ export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & Vi
                         rootSelected={returnFalse}
                         childHideResizeHandles={returnTrue}
                         childHideDecorationTitle={returnTrue}
-                        childFitWidth={doc => doc.type === DocumentType.RTF}
+                        childFitWidth={this.childFitWidth}
                         // childDocumentsActive={returnFalse}
-                        removeDocument={(doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, "data", d), true as boolean)}
-                        addDocument={(doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.AddDocToList(this.props.place, "data", d), true as boolean)}
+                        removeDocument={this.removeDoc}
+                        addDocument={this.addDoc}
                         renderDepth={this.props.renderDepth + 1}
                         viewType={CollectionViewType.Stacking}
                         pointerEvents={returnAll}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index f2ca6c96e..4eb07fd8d 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -26,6 +26,7 @@ import { ImageBox } from './ImageBox';
 import "./PDFBox.scss";
 import { VideoBox } from './VideoBox';
 import React = require("react");
+import { CollectionFreeFormView } from '../collections/collectionFreeForm';
 
 @observer
 export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
@@ -147,34 +148,23 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     }
 
     updateIcon = () => {
-        return; // currently we render pdf icons as text labels
+        // currently we render pdf icons as text labels
         const docViewContent = this.props.docViewPath().lastElement().ContentDiv!;
-        const newDiv = docViewContent.cloneNode(true) as HTMLDivElement;
-        newDiv.style.width = (this.layoutDoc[WidthSym]()).toString();
-        newDiv.style.height = (this.layoutDoc[HeightSym]()).toString();
-        this.replaceCanvases(docViewContent, newDiv);
-        const htmlString = this._pdfViewer?._mainCont.current && new XMLSerializer().serializeToString(newDiv);
-        const nativeWidth = this.layoutDoc[WidthSym]();
-        const nativeHeight = this.layoutDoc[HeightSym]();
-
-        CreateImage(
-            "",
-            document.styleSheets,
-            htmlString,
-            nativeWidth,
-            nativeWidth * this.props.PanelHeight() / this.props.PanelWidth(),
-            NumCast(this.layoutDoc._scrollTop) * this.props.PanelHeight() / NumCast(this.rootDoc[this.fieldKey + "-nativeHeight"])
-        ).then
-            ((data_url: any) => {
-                VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true, this.layoutDoc[Id] + "-icon").then(
-                    returnedfilename => setTimeout(action(() => {
-                        this.dataDoc.icon = new ImageField(returnedfilename);
-                        this.dataDoc["icon-nativeWidth"] = nativeWidth;
-                        this.dataDoc["icon-nativeHeight"] = nativeHeight;
-                    }), 500));
-            })
-            .catch(function (error: any) {
-                console.error('oops, something went wrong!', error);
+        const filename = this.layoutDoc[Id] + "-icon" + (new Date()).getTime();
+        this._pdfViewer?._mainCont.current && CollectionFreeFormView.UpdateIcon(
+            filename, docViewContent, 
+            this.layoutDoc[WidthSym](), this.layoutDoc[HeightSym](), 
+            this.props.PanelWidth(), this.props.PanelHeight(),  
+            NumCast(this.layoutDoc._scrollTop),
+            NumCast(this.rootDoc[this.fieldKey + "-nativeHeight"], 1), 
+            true,  
+            this.layoutDoc[Id] + "-icon",
+            (iconFile:string, nativeWidth:number, nativeHeight:number) => {
+                setTimeout(() => {
+                    this.dataDoc.icon = new ImageField(iconFile);
+                    this.dataDoc["icon-nativeWidth"] = nativeWidth;
+                    this.dataDoc["icon-nativeHeight"] = nativeHeight;
+                }, 500);
             });
     }
 
diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.scss b/src/client/views/nodes/RecordingBox/ProgressBar.scss
new file mode 100644
index 000000000..a493b0b89
--- /dev/null
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.scss
@@ -0,0 +1,26 @@
+
+.progressbar {
+      position: absolute;
+      display: flex;
+      justify-content: flex-start;
+      bottom: 10px;
+      width: 80%;
+      height: 5px;
+      background-color: gray;
+
+      &.done {
+            top: 0;
+            width: 0px;
+            height: 5px;
+            background-color: red;
+            z-index: 2;
+      }
+
+      &.mark {
+            top: 0;
+            background-color: transparent;
+            border-right: 2px solid white;
+            z-index: 3;
+            pointer-events: none; 
+      }
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
new file mode 100644
index 000000000..82d5e1f04
--- /dev/null
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -0,0 +1,45 @@
+import * as React from 'react';
+import { useEffect } from "react"
+import "./ProgressBar.scss"
+
+interface ProgressBarProps {
+      progress: number,
+      marks: number[],
+}
+
+export function ProgressBar(props: ProgressBarProps) {
+
+      // const handleClick = (e: React.MouseEvent) => {
+      //       let progressbar = document.getElementById('progressbar')!
+      //       let bounds = progressbar!.getBoundingClientRect();
+      //       let x = e.clientX - bounds.left;
+      //       let percent = x / progressbar.clientWidth * 100
+
+      //       for (let i = 0; i < props.marks.length; i++) {
+      //             let start = i == 0 ? 0 : props.marks[i-1];
+      //             if (percent > start && percent < props.marks[i]) {
+      //                   props.playSegment(i)
+      //                   // console.log(i)
+      //                   // console.log(percent)
+      //                   // console.log(props.marks[i])
+      //                   break
+      //             }
+      //       }
+      // }
+
+      return (
+            <div className="progressbar" id="progressbar">
+                  <div
+                        className="progressbar done"
+                        style={{ width: `${props.progress}%` }}
+                  // onClick={handleClick}
+                  ></div>
+                  {props.marks.map((mark) => {
+                        return <div
+                              className="progressbar mark"
+                              style={{ width: `${mark}%` }}
+                        ></div>
+                  })}
+            </div>
+      )
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
new file mode 100644
index 000000000..10393624b
--- /dev/null
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -0,0 +1,61 @@
+import { action, observable } from "mobx";
+import { observer } from "mobx-react";
+import * as React from "react";
+import { VideoField } from "../../../../fields/URLField";
+import { Upload } from "../../../../server/SharedMediaTypes";
+import { ViewBoxBaseComponent } from "../../DocComponent";
+import { FieldView } from "../FieldView";
+import { VideoBox } from "../VideoBox";
+import { RecordingView } from './RecordingView';
+import { DocumentType } from "../../../documents/DocumentTypes";
+import { RecordingApi } from "../../../util/RecordingApi";
+import { Doc, FieldsSym } from "../../../../fields/Doc";
+import { Id } from "../../../../fields/FieldSymbols";
+
+
+@observer
+export class RecordingBox extends ViewBoxBaseComponent() {
+
+    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(RecordingBox, fieldKey); }
+
+    private _ref: React.RefObject<HTMLDivElement> = React.createRef();
+
+    constructor(props: any) {
+        super(props);
+    }
+
+    componentDidMount() {
+        console.log("set native width and height")
+        Doc.SetNativeWidth(this.dataDoc, 1280);
+        Doc.SetNativeHeight(this.dataDoc, 720);
+    }
+
+    @observable result: Upload.FileInformation | undefined = undefined
+    @observable videoDuration: number | undefined = undefined
+
+    @action
+    setVideoDuration = (duration: number) => {
+        this.videoDuration = duration
+    }
+
+    @action
+    setResult = (info: Upload.FileInformation, trackScreen: boolean) => {
+        this.result = info
+        this.dataDoc.type = DocumentType.VID;
+        this.dataDoc[this.fieldKey + "-duration"] = this.videoDuration;
+
+        this.dataDoc.layout = VideoBox.LayoutString(this.fieldKey);
+        this.dataDoc[this.props.fieldKey] = new VideoField(this.result.accessPaths.agnostic.client);
+        this.dataDoc[this.fieldKey + "-recorded"] = true;
+        // stringify the presenation and store it
+        if (trackScreen) {
+            this.dataDoc[this.fieldKey + "-presentation"] = JSON.stringify(RecordingApi.Instance.clear());
+        }
+    }
+
+    render() {
+        return <div className="recordingBox" ref={this._ref}>
+            {!this.result && <RecordingView setResult={this.setResult} setDuration={this.setVideoDuration} id={Doc.GetProto(this.rootDoc)[Id]} />}
+        </div>;
+    }
+}
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.scss b/src/client/views/nodes/RecordingBox/RecordingView.scss
new file mode 100644
index 000000000..9b2f6d070
--- /dev/null
+++ b/src/client/views/nodes/RecordingBox/RecordingView.scss
@@ -0,0 +1,207 @@
+video {
+      // flex: 100%;
+      width: 100%;
+      // min-height: 400px;
+      //height: auto;
+      height: 100%;
+      //display: block;
+      object-fit: cover;
+      background-color: black;
+}
+
+button {
+      margin: 0 .5rem
+}
+
+.recording-container {
+      height: 100%;
+      width: 100%;
+      // display: flex;
+      pointer-events: all;
+      background-color: grey;
+}
+
+.video-wrapper {
+      //   max-width: 600px;
+      //   max-width: 700px;
+      position: relative;
+      display: flex;
+      justify-content: center;
+      //   overflow: hidden;
+      border-radius: 10px;
+      margin: 0;
+}
+
+.video-wrapper:hover .controls {
+      bottom: 30px;
+      transform: translateY(0%);
+      opacity: 100%;
+}
+
+.controls {
+      display: flex;
+      align-items: center;
+      justify-content: space-evenly;
+      position: absolute;
+      padding: 14px;
+      width: 100%;
+      max-width: 500px;
+      // max-height: 20%;
+      flex-wrap: wrap;
+      background: rgba(255, 255, 255, 0.25);
+      box-shadow: 0 8px 32px 0 rgba(255, 255, 255, 0.1);
+      backdrop-filter: blur(4px);
+      border-radius: 10px;
+      border: 1px solid rgba(255, 255, 255, 0.18);
+      // transform: translateY(150%);
+      transition: all 0.3s ease-in-out;
+      // opacity: 0%;
+      bottom: 30px;
+      // bottom: -150px;
+}
+
+.actions button {
+      background: none;
+      border: none;
+      outline: none;
+      cursor: pointer;
+}
+
+.actions button i {
+      background-color: none;
+      color: white;
+      font-size: 30px;
+}
+
+
+.velocity {
+      appearance: none;
+      background: none;
+      color: white;
+      outline: none;
+      border: none;
+      text-align: center;
+      font-size: 16px;
+}
+
+.mute-btn {
+      background: none;
+      border: none;
+      outline: none;
+      cursor: pointer;
+}
+
+.mute-btn i {
+      background-color: none;
+      color: white;
+      font-size: 20px;
+}
+
+.recording-sign {
+      height: 20px;
+      width: auto;
+      display: flex;
+      flex-direction: row;
+      position: absolute;
+      top: 10px;
+      right: 15px;
+      align-items: center;
+      justify-content: center;
+
+      .timer {
+            font-size: 15px;
+            color: white;
+            margin: 0;
+      }
+
+      .dot {
+            height: 15px;
+            width: 15px;
+            margin: 5px;
+            background-color: red;
+            border-radius: 50%;
+            display: inline-block;
+      }
+}
+
+.controls-inner-container {
+      display: flex;
+      flex-direction: row;
+      justify-content: center;
+      width: 100%;
+
+}
+
+.record-button-wrapper {
+      width: 35px;
+      height: 35px;
+      font-size: 0;
+      background-color: grey;
+      border: 0px;
+      border-radius: 35px;
+      margin: 10px;
+      display: flex;
+      justify-content: center;
+
+      .record-button {
+            background-color: red;
+            border: 0px;
+            border-radius: 50%;
+            height: 80%;
+            width: 80%;
+            align-self: center;
+            margin: 0;
+
+            &:hover {
+                  height: 85%;
+                  width: 85%;
+            }
+      }
+
+      .stop-button {
+            background-color: red;
+            border: 0px;
+            border-radius: 10%;
+            height: 70%;
+            width: 70%;
+            align-self: center;
+            margin: 0;
+
+
+            // &:hover {
+            //   width: 40px;
+            //   height: 40px
+            // }
+      }
+
+}
+
+.options-wrapper {
+      height: 100%;
+      display: flex;
+      flex-direction: row;
+      align-items: center;
+      position: absolute;
+      top: 0;
+      bottom: 0;
+
+      &.video-edit-wrapper {
+
+            right: 50% - 15;
+
+            .track-screen {
+                  font-weight: 200;
+            }
+
+      }
+
+      &.track-screen-wrapper {
+
+            right: 50% - 30;
+
+            .track-screen {
+                  font-weight: 200;
+            }
+
+      }
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
new file mode 100644
index 000000000..b95335792
--- /dev/null
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -0,0 +1,282 @@
+import * as React from 'react';
+import "./RecordingView.scss";
+import { ReactElement, useCallback, useEffect, useRef, useState } from "react";
+import { ProgressBar } from "./ProgressBar"
+import { MdBackspace } from 'react-icons/md';
+import { FaCheckCircle } from 'react-icons/fa';
+import { IconContext } from "react-icons";
+import { Networking } from '../../../Network';
+import { Upload } from '../../../../server/SharedMediaTypes';
+
+import { RecordingApi } from '../../../util/RecordingApi';
+import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
+
+interface MediaSegment {
+    videoChunks: any[],
+    endTime: number
+}
+
+interface IRecordingViewProps {
+    setResult: (info: Upload.FileInformation, trackScreen: boolean) => void
+    setDuration: (seconds: number) => void
+    id: string
+}
+
+const MAXTIME = 100000;
+
+export function RecordingView(props: IRecordingViewProps) {
+
+    const [recording, setRecording] = useState(false);
+    const recordingTimerRef = useRef<number>(0);
+    const [recordingTimer, setRecordingTimer] = useState(0); // unit is 0.01 second
+    const [playing, setPlaying] = useState(false);
+    const [progress, setProgress] = useState(0);
+
+    const [videos, setVideos] = useState<MediaSegment[]>([]);
+    const videoRecorder = useRef<MediaRecorder | null>(null);
+    const videoElementRef = useRef<HTMLVideoElement | null>(null);
+
+    const [finished, setFinished] = useState<boolean>(false)
+    const [trackScreen, setTrackScreen] = useState<boolean>(true)
+
+
+
+    const DEFAULT_MEDIA_CONSTRAINTS = {
+        video: {
+            width: 1280,
+            height: 720,
+        },
+        audio: {
+            echoCancellation: true,
+            noiseSuppression: true,
+            sampleRate: 44100
+        }
+    }
+
+    useEffect(() => {
+
+        if (finished) {
+            props.setDuration(recordingTimer * 100)
+            let allVideoChunks: any = []
+            videos.forEach((vid) => {
+                console.log(vid.videoChunks)
+                allVideoChunks = allVideoChunks.concat(vid.videoChunks)
+            })
+
+            const videoFile = new File(allVideoChunks, "video.mkv", { type: allVideoChunks[0].type, lastModified: Date.now() });
+
+            Networking.UploadFilesToServer(videoFile)
+                .then((data) => {
+                    const result = data[0].result
+                    if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox
+                        props.setResult(result, trackScreen)
+                    } else {
+                        alert("video conversion failed");
+                    }
+                })
+
+        }
+
+
+    }, [finished])
+
+    useEffect(() => {
+        // check if the browser supports media devices on first load
+        if (!navigator.mediaDevices) {
+            console.log('This browser does not support getUserMedia.')
+        }
+        console.log('This device has the correct media devices.')
+    }, [])
+
+    useEffect(() => {
+        // get access to the video element on every render
+        videoElementRef.current = document.getElementById(`video-${props.id}`) as HTMLVideoElement;
+    })
+
+    useEffect(() => {
+        let interval: any = null;
+        if (recording) {
+            interval = setInterval(() => {
+                setRecordingTimer(unit => unit + 1);
+            }, 10);
+        } else if (!recording && recordingTimer !== 0) {
+            clearInterval(interval);
+        }
+        return () => clearInterval(interval);
+    }, [recording])
+
+    useEffect(() => {
+        setVideoProgressHelper(recordingTimer)
+        recordingTimerRef.current = recordingTimer;
+    }, [recordingTimer])
+
+    const setVideoProgressHelper = (progress: number) => {
+        const newProgress = (progress / MAXTIME) * 100;
+        setProgress(newProgress)
+    }
+    const startShowingStream = async (mediaConstraints = DEFAULT_MEDIA_CONSTRAINTS) => {
+        const stream = await navigator.mediaDevices.getUserMedia(mediaConstraints)
+
+        videoElementRef.current!.src = ""
+        videoElementRef.current!.srcObject = stream
+        videoElementRef.current!.muted = true
+
+        return stream
+    }
+
+    const record = async () => {
+        const stream = await startShowingStream();
+        videoRecorder.current = new MediaRecorder(stream)
+
+        // temporary chunks of video
+        let videoChunks: any = []
+
+        videoRecorder.current.ondataavailable = (event: any) => {
+            if (event.data.size > 0) {
+                videoChunks.push(event.data)
+            }
+        }
+
+        videoRecorder.current.onstart = (event: any) => {
+            setRecording(true);
+            trackScreen && RecordingApi.Instance.start();
+        }
+
+        videoRecorder.current.onstop = () => {
+            // if we have a last portion
+            if (videoChunks.length > 1) {
+                // append the current portion to the video pieces
+                setVideos(videos => [...videos, { videoChunks: videoChunks, endTime: recordingTimerRef.current }])
+            }
+
+            // reset the temporary chunks
+            videoChunks = []
+            setRecording(false);
+            setFinished(true);
+            trackScreen && RecordingApi.Instance.pause();
+        }
+
+        // recording paused
+        videoRecorder.current.onpause = (event: any) => {
+            // append the current portion to the video pieces
+            setVideos(videos => [...videos, { videoChunks: videoChunks, endTime: recordingTimerRef.current }])
+
+            // reset the temporary chunks
+            videoChunks = []
+            setRecording(false);
+            trackScreen && RecordingApi.Instance.pause();
+        }
+
+        videoRecorder.current.onresume = async (event: any) => {
+            await startShowingStream();
+            setRecording(true);
+            trackScreen && RecordingApi.Instance.resume();
+        }
+
+        videoRecorder.current.start(200)
+    }
+
+
+    const stop = () => {
+        if (videoRecorder.current) {
+            if (videoRecorder.current.state !== "inactive") {
+                videoRecorder.current.stop();
+                // recorder.current.stream.getTracks().forEach((track: any) => track.stop())
+            }
+        }
+    }
+
+    const pause = () => {
+        if (videoRecorder.current) {
+            if (videoRecorder.current.state === "recording") {
+                videoRecorder.current.pause();
+            }
+        }
+    }
+
+    const startOrResume = (e: React.PointerEvent) => {
+        // the code to start or resume does not get triggered if we start dragging the button
+        setupMoveUpEvents({}, e, returnTrue, returnFalse, e => {
+            if (!videoRecorder.current || videoRecorder.current.state === "inactive") {
+                record();
+            } else if (videoRecorder.current.state === "paused") {
+                videoRecorder.current.resume();
+            }
+            return true; // cancels propagation to documentView to avoid selecting it.
+        }, false, false);
+    }
+
+    const clearPrevious = () => {
+        const numVideos = videos.length
+        setRecordingTimer(numVideos == 1 ? 0 : videos[numVideos - 2].endTime)
+        setVideoProgressHelper(numVideos == 1 ? 0 : videos[numVideos - 2].endTime)
+        setVideos(videos.filter((_, idx) => idx !== numVideos - 1));
+    }
+
+    const handleOnTimeUpdate = () => {
+        if (playing) {
+            setVideoProgressHelper(videoElementRef.current!.currentTime)
+        }
+    };
+
+    const millisecondToMinuteSecond = (milliseconds: number) => {
+        const toTwoDigit = (digit: number) => {
+            return String(digit).length == 1 ? "0" + digit : digit
+        }
+        const minutes = Math.floor((milliseconds % (1000 * 60 * 60)) / (1000 * 60));
+        const seconds = Math.floor((milliseconds % (1000 * 60)) / 1000);
+        return toTwoDigit(minutes) + " : " + toTwoDigit(seconds);
+    }
+
+    return (
+        <div className="recording-container">
+            <div className="video-wrapper">
+                <video id={`video-${props.id}`}
+                    autoPlay
+                    muted
+                    onTimeUpdate={handleOnTimeUpdate}
+                />
+                <div className="recording-sign">
+                    <span className="dot" />
+                    <p className="timer">{millisecondToMinuteSecond(recordingTimer * 10)}</p>
+                </div>
+                <div className="controls">
+
+                    <div className="controls-inner-container">
+                        <div className="record-button-wrapper">
+                            {recording ?
+                                <button className="stop-button" onPointerDown={pause} /> :
+                                <button className="record-button" onPointerDown={startOrResume} />
+                            }
+                        </div>
+
+                        {!recording && (videos.length > 0 ?
+
+                            <div className="options-wrapper video-edit-wrapper">
+                                {/* <IconContext.Provider value={{ color: "grey", className: "video-edit-buttons" }}>
+                                                      <MdBackspace onClick={clearPrevious} />
+                                                </IconContext.Provider> */}
+                                <IconContext.Provider value={{ color: "#cc1c08", className: "video-edit-buttons" }}>
+                                    <FaCheckCircle onClick={stop} />
+                                </IconContext.Provider>
+                            </div>
+
+                            : <div className="options-wrapper track-screen-wrapper">
+                                <label className="track-screen">
+                                    <input type="checkbox" checked={trackScreen} onChange={(e) => { setTrackScreen(e.target.checked) }} />
+                                    <span className="checkmark"></span>
+                                    Track Screen
+                                </label>
+                            </div>)}
+
+                    </div>
+
+                    <ProgressBar
+                        progress={progress}
+                        marks={videos.map((elt) => elt.endTime / MAXTIME * 100)}
+                    // playSegment={playSegment}
+                    />
+                </div>
+            </div>
+        </div>)
+}
\ No newline at end of file
diff --git a/src/client/views/nodes/RecordingBox/index.ts b/src/client/views/nodes/RecordingBox/index.ts
new file mode 100644
index 000000000..ff21eaed6
--- /dev/null
+++ b/src/client/views/nodes/RecordingBox/index.ts
@@ -0,0 +1,2 @@
+export * from './RecordingView'
+export * from './RecordingBox'
\ No newline at end of file
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index dbb567d3a..0c7d7dc31 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -26,7 +26,7 @@ import { FormattedTextBox } from "./formattedText/FormattedTextBox";
 import "./ScreenshotBox.scss";
 import { VideoBox } from "./VideoBox";
 declare class MediaRecorder {
-    constructor(e: any, options?: any);  // whatever MediaRecorder has
+      constructor(e: any, options?: any);  // whatever MediaRecorder has
 }
 
 // interface VideoTileProps {
@@ -107,225 +107,226 @@ declare class MediaRecorder {
 
 @observer
 export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScreenshotBox, fieldKey); }
-    private _audioRec: any;
-    private _videoRec: any;
-    @observable private _videoRef: HTMLVideoElement | null = null;
-    @observable _screenCapture = false;
-    @computed get recordingStart() { return Cast(this.dataDoc[this.props.fieldKey + "-recordingStart"], DateField)?.date.getTime(); }
+      public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScreenshotBox, fieldKey); }
+      private _audioRec: any;
+      private _videoRec: any;
+      @observable private _videoRef: HTMLVideoElement | null = null;
+      @observable _screenCapture = false;
+      @computed get recordingStart() { return Cast(this.dataDoc[this.props.fieldKey + "-recordingStart"], DateField)?.date.getTime(); }
 
-    constructor(props: any) {
-        super(props);
-        if (this.rootDoc.videoWall) {
-            this.rootDoc.nativeWidth = undefined;
-            this.rootDoc.nativeHeight = undefined;
-            this.layoutDoc.popOff = 0;
-            this.layoutDoc.popOut = 1;
-        } else {
-            this.setupDictation();
-        }
-    }
-    getAnchor = () => {
-        const startTime = Cast(this.layoutDoc._currentTimecode, "number", null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
-        return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow", "_timecodeToHide",
-            startTime, startTime === undefined ? undefined : startTime + 3)
-            || this.rootDoc;
-    }
+      constructor(props: any) {
+            super(props);
+            if (this.rootDoc.videoWall) {
+                  this.rootDoc.nativeWidth = undefined;
+                  this.rootDoc.nativeHeight = undefined;
+                  this.layoutDoc.popOff = 0;
+                  this.layoutDoc.popOut = 1;
+            } else {
+                  this.setupDictation();
+            }
+      }
+      getAnchor = () => {
+            const startTime = Cast(this.layoutDoc._currentTimecode, "number", null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
+            return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow", "_timecodeToHide",
+                  startTime, startTime === undefined ? undefined : startTime + 3)
+                  || this.rootDoc;
+      }
 
-    videoLoad = () => {
-        const aspect = this._videoRef!.videoWidth / this._videoRef!.videoHeight;
-        const nativeWidth = Doc.NativeWidth(this.layoutDoc);
-        const nativeHeight = Doc.NativeHeight(this.layoutDoc);
-        if (!nativeWidth || !nativeHeight) {
-            if (!nativeWidth) Doc.SetNativeWidth(this.dataDoc, 1200);
-            Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 1200) / aspect);
-            this.layoutDoc._height = (this.layoutDoc[WidthSym]() || 0) / aspect;
-        }
-    }
+      videoLoad = () => {
+            const aspect = this._videoRef!.videoWidth / this._videoRef!.videoHeight;
+            const nativeWidth = Doc.NativeWidth(this.layoutDoc);
+            const nativeHeight = Doc.NativeHeight(this.layoutDoc);
+            if (!nativeWidth || !nativeHeight) {
+                  if (!nativeWidth) Doc.SetNativeWidth(this.dataDoc, 1200);
+                  Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 1200) / aspect);
+                  this.layoutDoc._height = (this.layoutDoc[WidthSym]() || 0) / aspect;
+            }
+      }
 
-    componentDidMount() {
-        this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = 0;
-        this.props.setContentView?.(this); // this tells the DocumentView that this ScreenshotBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
-        // this.rootDoc.videoWall && reaction(() => ({ width: this.props.PanelWidth(), height: this.props.PanelHeight() }),
-        //     ({ width, height }) => {
-        //         if (this._camera) {
-        //             const angle = -Math.abs(1 - width / height);
-        //             const xz = [0, (this._numScreens - 2) / Math.abs(1 + angle)];
-        //             this._camera.position.set(this._numScreens / 2 + xz[1] * Math.sin(angle), this._numScreens / 2, xz[1] * Math.cos(angle));
-        //             this._camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
-        //             (this._camera as any).updateProjectionMatrix();
-        //         }
-        //     });
-    }
-    componentWillUnmount() {
-        const ind = DocUtils.ActiveRecordings.indexOf(this);
-        ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1));
-    }
+      componentDidMount() {
+            this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = 0;
+            this.props.setContentView?.(this); // this tells the DocumentView that this ScreenshotBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
+            // this.rootDoc.videoWall && reaction(() => ({ width: this.props.PanelWidth(), height: this.props.PanelHeight() }),
+            //     ({ width, height }) => {
+            //         if (this._camera) {
+            //             const angle = -Math.abs(1 - width / height);
+            //             const xz = [0, (this._numScreens - 2) / Math.abs(1 + angle)];
+            //             this._camera.position.set(this._numScreens / 2 + xz[1] * Math.sin(angle), this._numScreens / 2, xz[1] * Math.cos(angle));
+            //             this._camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
+            //             (this._camera as any).updateProjectionMatrix();
+            //         }
+            //     });
+      }
+      componentWillUnmount() {
+            const ind = DocUtils.ActiveRecordings.indexOf(this);
+            ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1));
+      }
 
-    specificContextMenu = (e: React.MouseEvent): void => {
-        const subitems = [{ description: "Screen Capture", event: this.toggleRecording, icon: "expand-arrows-alt" as any }];
-        ContextMenu.Instance.addItem({ description: "Options...", subitems, icon: "video" });
-    }
+      specificContextMenu = (e: React.MouseEvent): void => {
+            const subitems = [{ description: "Screen Capture", event: this.toggleRecording, icon: "expand-arrows-alt" as any }];
+            ContextMenu.Instance.addItem({ description: "Options...", subitems, icon: "video" });
+      }
 
-    @computed get content() {
-        if (this.rootDoc.videoWall) return (null);
-        return <video className={"videoBox-content"} key="video"
-            ref={r => {
-                this._videoRef = r;
-                setTimeout(() => {
-                    if (this.rootDoc.mediaState === "pendingRecording" && this._videoRef) {
-                        this.toggleRecording();
-                    }
-                }, 100);
-            }}
-            autoPlay={this._screenCapture}
-            style={{ width: this._screenCapture ? "100%" : undefined, height: this._screenCapture ? "100%" : undefined }}
-            onCanPlay={this.videoLoad}
-            controls={true}
-            onClick={e => e.preventDefault()}>
-            <source type="video/mp4" />
-            Not supported.
-        </video>;
-    }
+      @computed get content() {
+            if (this.rootDoc.videoWall) return (null);
+            return <video className={"videoBox-content"} key="video"
+                  ref={r => {
+                        this._videoRef = r;
+                        setTimeout(() => {
+                              if (this.rootDoc.mediaState === "pendingRecording" && this._videoRef) {
+                                    this.toggleRecording();
+                              }
+                        }, 100);
+                  }}
+                  autoPlay={this._screenCapture}
+                  style={{ width: this._screenCapture ? "100%" : undefined, height: this._screenCapture ? "100%" : undefined }}
+                  onCanPlay={this.videoLoad}
+                  controls={true}
+                  onClick={e => e.preventDefault()}>
+                  <source type="video/mp4" />
+                  Not supported.
+            </video>;
+      }
 
-    // _numScreens = 5;
-    // _camera: Camera | undefined;
-    // @observable _raised = [] as { coord: Vector2, off: Vector3 }[];
-    // @action setRaised = (r: { coord: Vector2, off: Vector3 }[]) => this._raised = r;
-    @computed get threed() {
-        // if (this.rootDoc.videoWall) {
-        //     const screens: any[] = [];
-        //     const colors = ["yellow", "red", "orange", "brown", "maroon", "gray"];
-        //     let count = 0;
-        //     numberRange(this._numScreens).forEach(x => numberRange(this._numScreens).forEach(y => screens.push(
-        //         <VideoTile rootDoc={this.rootDoc} color={colors[count++ % colors.length]} x={x} y={y} raised={this._raised} setRaised={this.setRaised} />)));
-        //     return <Canvas key="canvas" id="CANCAN" style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }} gl={{ antialias: false }} colorManagement={false} onCreated={props => {
-        //         this._camera = props.camera;
-        //         props.camera.position.set(this._numScreens / 2, this._numScreens / 2, this._numScreens - 2);
-        //         props.camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
-        //     }}>
-        //         {/* <ambientLight />*/}
-        //         <pointLight position={[10, 10, 10]} intensity={1} />
-        //         {screens}
-        //     </ Canvas>;
-        // }
-        return (null);
-    }
-    toggleRecording = async () => {
-        if (!this._screenCapture) {
-            this._audioRec = new MediaRecorder(await navigator.mediaDevices.getUserMedia({ audio: true }));
-            const aud_chunks: any = [];
-            this._audioRec.ondataavailable = (e: any) => aud_chunks.push(e.data);
-            this._audioRec.onstop = async (e: any) => {
-                const [{ result }] = await Networking.UploadFilesToServer(aud_chunks);
-                if (!(result instanceof Error)) {
-                    this.dataDoc[this.props.fieldKey + "-audio"] = new AudioField(result.accessPaths.agnostic.client);
-                }
-            };
-            this._videoRef!.srcObject = await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
-            this._videoRec = new MediaRecorder(this._videoRef!.srcObject);
-            const vid_chunks: any = [];
-            this._videoRec.onstart = () => this.dataDoc[this.props.fieldKey + "-recordingStart"] = new DateField(new Date());
-            this._videoRec.ondataavailable = (e: any) => vid_chunks.push(e.data);
-            this._videoRec.onstop = async (e: any) => {
-                const file = new File(vid_chunks, `${this.rootDoc[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() });
-                const [{ result }] = await Networking.UploadFilesToServer(file);
-                this.dataDoc[this.fieldKey + "-duration"] = (new Date().getTime() - this.recordingStart!) / 1000;
-                if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox
-                    this.dataDoc.type = DocumentType.VID;
-                    this.layoutDoc.layout = VideoBox.LayoutString(this.fieldKey);
-                    this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = undefined;
-                    this.layoutDoc._fitWidth = undefined;
-                    this.dataDoc[this.props.fieldKey] = new VideoField(result.accessPaths.agnostic.client);
-                } else alert("video conversion failed");
-            };
-            this._audioRec.start();
-            this._videoRec.start();
-            runInAction(() => {
-                this._screenCapture = true;
-                this.dataDoc.mediaState = "recording";
-            });
-            DocUtils.ActiveRecordings.push(this);
-        } else {
-            this._audioRec?.stop();
-            this._videoRec?.stop();
-            runInAction(() => {
-                this._screenCapture = false;
-                this.dataDoc.mediaState = "paused";
-            });
-            const ind = DocUtils.ActiveRecordings.indexOf(this);
-            ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1));
+      // _numScreens = 5;
+      // _camera: Camera | undefined;
+      // @observable _raised = [] as { coord: Vector2, off: Vector3 }[];
+      // @action setRaised = (r: { coord: Vector2, off: Vector3 }[]) => this._raised = r;
+      @computed get threed() {
+            // if (this.rootDoc.videoWall) {
+            //     const screens: any[] = [];
+            //     const colors = ["yellow", "red", "orange", "brown", "maroon", "gray"];
+            //     let count = 0;
+            //     numberRange(this._numScreens).forEach(x => numberRange(this._numScreens).forEach(y => screens.push(
+            //         <VideoTile rootDoc={this.rootDoc} color={colors[count++ % colors.length]} x={x} y={y} raised={this._raised} setRaised={this.setRaised} />)));
+            //     return <Canvas key="canvas" id="CANCAN" style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }} gl={{ antialias: false }} colorManagement={false} onCreated={props => {
+            //         this._camera = props.camera;
+            //         props.camera.position.set(this._numScreens / 2, this._numScreens / 2, this._numScreens - 2);
+            //         props.camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
+            //     }}>
+            //         {/* <ambientLight />*/}
+            //         <pointLight position={[10, 10, 10]} intensity={1} />
+            //         {screens}
+            //     </ Canvas>;
+            // }
+            return (null);
+      }
+      toggleRecording = async () => {
+            if (!this._screenCapture) {
+                  this._audioRec = new MediaRecorder(await navigator.mediaDevices.getUserMedia({ audio: true }));
+                  const aud_chunks: any = [];
+                  this._audioRec.ondataavailable = (e: any) => aud_chunks.push(e.data);
+                  this._audioRec.onstop = async (e: any) => {
+                        const [{ result }] = await Networking.UploadFilesToServer(aud_chunks);
+                        if (!(result instanceof Error)) {
+                              this.dataDoc[this.props.fieldKey + "-audio"] = new AudioField(result.accessPaths.agnostic.client);
+                        }
+                  };
+                  this._videoRef!.srcObject = await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
+                  this._videoRec = new MediaRecorder(this._videoRef!.srcObject);
+                  const vid_chunks: any = [];
+                  this._videoRec.onstart = () => this.dataDoc[this.props.fieldKey + "-recordingStart"] = new DateField(new Date());
+                  this._videoRec.ondataavailable = (e: any) => vid_chunks.push(e.data);
+                  this._videoRec.onstop = async (e: any) => {
+                        console.log("screenshotbox: upload")
+                        const file = new File(vid_chunks, `${this.rootDoc[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() });
+                        const [{ result }] = await Networking.UploadFilesToServer(file);
+                        this.dataDoc[this.fieldKey + "-duration"] = (new Date().getTime() - this.recordingStart!) / 1000;
+                        if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox
+                              this.dataDoc.type = DocumentType.VID;
+                              this.layoutDoc.layout = VideoBox.LayoutString(this.fieldKey);
+                              this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = undefined;
+                              this.layoutDoc._fitWidth = undefined;
+                              this.dataDoc[this.props.fieldKey] = new VideoField(result.accessPaths.agnostic.client);
+                        } else alert("video conversion failed");
+                  };
+                  this._audioRec.start();
+                  this._videoRec.start();
+                  runInAction(() => {
+                        this._screenCapture = true;
+                        this.dataDoc.mediaState = "recording";
+                  });
+                  DocUtils.ActiveRecordings.push(this);
+            } else {
+                  this._audioRec?.stop();
+                  this._videoRec?.stop();
+                  runInAction(() => {
+                        this._screenCapture = false;
+                        this.dataDoc.mediaState = "paused";
+                  });
+                  const ind = DocUtils.ActiveRecordings.indexOf(this);
+                  ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1));
 
-            CaptureManager.Instance.open(this.rootDoc);
-        }
-    }
+                  CaptureManager.Instance.open(this.rootDoc);
+            }
+      }
 
-    setupDictation = () => {
-        if (this.dataDoc[this.fieldKey + "-dictation"]) return;
-        const dictationText = CurrentUserUtils.GetNewTextDoc("dictation",
-            NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10,
-            NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
-        dictationText._autoHeight = false;
-        const dictationTextProto = Doc.GetProto(dictationText);
-        dictationTextProto.recordingSource = this.dataDoc;
-        dictationTextProto.recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}-recordingStart"]`);
-        dictationTextProto.mediaState = ComputedField.MakeFunction("self.recordingSource.mediaState");
-        this.dataDoc[this.fieldKey + "-dictation"] = dictationText;
-    }
-    contentFunc = () => [this.threed, this.content];
-    videoPanelHeight = () => NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"], this.layoutDoc[HeightSym]()) / NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"], this.layoutDoc[WidthSym]()) * this.props.PanelWidth();
-    formattedPanelHeight = () => Math.max(0, this.props.PanelHeight() - this.videoPanelHeight());
-    render() {
-        TraceMobx();
-        return <div className="videoBox" onContextMenu={this.specificContextMenu} style={{ width: "100%", height: "100%" }} >
-            <div className="videoBox-viewer" >
-                <div style={{ position: "relative", height: this.videoPanelHeight() }}>
-                    <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
-                        PanelHeight={this.videoPanelHeight}
-                        PanelWidth={this.props.PanelWidth}
-                        focus={this.props.focus}
-                        isSelected={this.props.isSelected}
-                        isAnnotationOverlay={true}
-                        select={emptyFunction}
-                        isContentActive={returnFalse}
-                        scaling={returnOne}
-                        whenChildContentsActiveChanged={emptyFunction}
-                        removeDocument={returnFalse}
-                        moveDocument={returnFalse}
-                        addDocument={returnFalse}
-                        CollectionView={undefined}
-                        ScreenToLocalTransform={this.props.ScreenToLocalTransform}
-                        renderDepth={this.props.renderDepth + 1}
-                        ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
-                        {this.contentFunc}
-                    </CollectionFreeFormView></div>
-                <div style={{ position: "relative", height: this.formattedPanelHeight() }}>
-                    {!(this.dataDoc[this.fieldKey + "-dictation"] instanceof Doc) ? (null) :
-                        <FormattedTextBox {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
-                            Document={this.dataDoc[this.fieldKey + "-dictation"]}
-                            fieldKey={"text"}
-                            PanelHeight={this.formattedPanelHeight}
-                            isAnnotationOverlay={true}
-                            select={emptyFunction}
-                            isContentActive={emptyFunction}
-                            scaling={returnOne}
-                            xPadding={25}
-                            yPadding={10}
-                            whenChildContentsActiveChanged={emptyFunction}
-                            removeDocument={returnFalse}
-                            moveDocument={returnFalse}
-                            addDocument={returnFalse}
-                            CollectionView={undefined}
-                            renderDepth={this.props.renderDepth + 1}
-                            ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
-                        </FormattedTextBox>}
-                </div>
-            </div>
-            {!this.props.isSelected() ? (null) : <div className="screenshotBox-uiButtons">
-                <div className="screenshotBox-recorder" key="snap" onPointerDown={this.toggleRecording} >
-                    <FontAwesomeIcon icon="file" size="lg" />
-                </div>
-            </div>}
-        </div >;
-    }
+      setupDictation = () => {
+            if (this.dataDoc[this.fieldKey + "-dictation"]) return;
+            const dictationText = CurrentUserUtils.GetNewTextDoc("dictation",
+                  NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10,
+                  NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
+            dictationText._autoHeight = false;
+            const dictationTextProto = Doc.GetProto(dictationText);
+            dictationTextProto.recordingSource = this.dataDoc;
+            dictationTextProto.recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}-recordingStart"]`);
+            dictationTextProto.mediaState = ComputedField.MakeFunction("self.recordingSource.mediaState");
+            this.dataDoc[this.fieldKey + "-dictation"] = dictationText;
+      }
+      contentFunc = () => [this.threed, this.content];
+      videoPanelHeight = () => NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"], this.layoutDoc[HeightSym]()) / NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"], this.layoutDoc[WidthSym]()) * this.props.PanelWidth();
+      formattedPanelHeight = () => Math.max(0, this.props.PanelHeight() - this.videoPanelHeight());
+      render() {
+            TraceMobx();
+            return <div className="videoBox" onContextMenu={this.specificContextMenu} style={{ width: "100%", height: "100%" }} >
+                  <div className="videoBox-viewer" >
+                        <div style={{ position: "relative", height: this.videoPanelHeight() }}>
+                              <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
+                                    PanelHeight={this.videoPanelHeight}
+                                    PanelWidth={this.props.PanelWidth}
+                                    focus={this.props.focus}
+                                    isSelected={this.props.isSelected}
+                                    isAnnotationOverlay={true}
+                                    select={emptyFunction}
+                                    isContentActive={returnFalse}
+                                    scaling={returnOne}
+                                    whenChildContentsActiveChanged={emptyFunction}
+                                    removeDocument={returnFalse}
+                                    moveDocument={returnFalse}
+                                    addDocument={returnFalse}
+                                    CollectionView={undefined}
+                                    ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+                                    renderDepth={this.props.renderDepth + 1}
+                                    ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
+                                    {this.contentFunc}
+                              </CollectionFreeFormView></div>
+                        <div style={{ position: "relative", height: this.formattedPanelHeight() }}>
+                              {!(this.dataDoc[this.fieldKey + "-dictation"] instanceof Doc) ? (null) :
+                                    <FormattedTextBox {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
+                                          Document={this.dataDoc[this.fieldKey + "-dictation"]}
+                                          fieldKey={"text"}
+                                          PanelHeight={this.formattedPanelHeight}
+                                          isAnnotationOverlay={true}
+                                          select={emptyFunction}
+                                          isContentActive={emptyFunction}
+                                          scaling={returnOne}
+                                          xPadding={25}
+                                          yPadding={10}
+                                          whenChildContentsActiveChanged={emptyFunction}
+                                          removeDocument={returnFalse}
+                                          moveDocument={returnFalse}
+                                          addDocument={returnFalse}
+                                          CollectionView={undefined}
+                                          renderDepth={this.props.renderDepth + 1}
+                                          ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
+                                    </FormattedTextBox>}
+                        </div>
+                  </div>
+                  {!this.props.isSelected() ? (null) : <div className="screenshotBox-uiButtons">
+                        <div className="screenshotBox-recorder" key="snap" onPointerDown={this.toggleRecording} >
+                              <FontAwesomeIcon icon="file" size="lg" />
+                        </div>
+                  </div>}
+            </div >;
+      }
 }
\ No newline at end of file
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index ef3b0d105..5d2fae1ed 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -7,7 +7,7 @@ import * as rp from 'request-promise';
 import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
 import { InkTool } from "../../../fields/InkField";
 import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { AudioField, ImageField, VideoField } from "../../../fields/URLField";
+import { AudioField, ImageField, RecordingField, VideoField } from "../../../fields/URLField";
 import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from "../../../Utils";
 import { Docs, DocUtils } from "../../documents/Documents";
 import { DocumentType } from "../../documents/DocumentTypes";
@@ -28,8 +28,9 @@ import { AnchorMenu } from "../pdf/AnchorMenu";
 import { StyleProp } from "../StyleProvider";
 import { FieldView, FieldViewProps } from './FieldView';
 import "./VideoBox.scss";
-import { Image } from "wikijs";
 import { List } from "../../../fields/List";
+import { RecordingApi } from "../../util/RecordingApi";
+import { RecordingBox } from "./RecordingBox";
 const path = require('path');
 
 /**
@@ -46,936 +47,1264 @@ const path = require('path');
 
 @observer
 export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(VideoBox, fieldKey); }
-    /**
-     * Uploads an image buffer to the server and stores with specified filename.  by default the image
-     * is stored at multiple resolutions each retrieved by using the filename appended with _o, _s, _m, _l (indicating original, small, medium, or large)
-     * @param imageUri the bytes of the image
-     * @param returnedFilename the base filename to store the image on the server
-     * @param nosuffix optionally suppress creating multiple resolution images
-     */
-    public static async convertDataUri(imageUri: string, returnedFilename: string, nosuffix = false, replaceRootFilename?: string) {
-        try {
-            const posting = Utils.prepend("/uploadURI");
-            const returnedUri = await rp.post(posting, {
-                body: {
-                    uri: imageUri,
-                    name: returnedFilename,
-                    nosuffix,
-                    replaceRootFilename
-                },
-                json: true,
-            });
-            return returnedUri;
-
-        } catch (e) {
-            console.log("VideoBox :" + e);
-        }
-    }
-
-    static _youtubeIframeCounter: number = 0;
-    static heightPercent = 80; // height of video relative to videoBox when timeline is open
-    static numThumbnails = 20;
-    private _disposers: { [name: string]: IReactionDisposer } = {};
-    private _youtubePlayer: YT.Player | undefined = undefined;
-    private _videoRef: HTMLVideoElement | null = null; // <video> ref
-    private _contentRef: HTMLDivElement | null = null; // ref to div that wraps video and controls for full screen
-    private _youtubeIframeId: number = -1;
-    private _youtubeContentCreated = false;
-    private _audioPlayer: HTMLAudioElement | null = null;
-    private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); // outermost div
-    private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
-    private _playRegionTimer: any = null; // timeout for playback
-    private _controlsFadeTimer: any = null; // timeout for controls fade
-
-    public onMakeAnchor: () => Opt<Doc> = () => undefined; // Method to get anchor from text search
-
-    @observable _stackedTimeline: any; // CollectionStackedTimeline ref
-    @observable static _nativeControls: boolean; // default html controls
-    @observable _marqueeing: number[] | undefined; // coords for marquee selection
-    @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
-    @observable _screenCapture = false;
-    @observable _clicking = false; // used for transition between showing/hiding timeline
-    @observable _forceCreateYouTubeIFrame = false;
-    @observable _playTimer?: NodeJS.Timeout = undefined;
-    @observable _fullScreen = false;
-    @observable _playing = false;
-    @observable _finished: boolean = false; // has playback reached end of clip
-    @observable _volume: number = 1;
-    @observable _muted: boolean = false;
-    @observable _controlsTransform?: { X: number, Y: number };
-    @observable _controlsVisible: boolean = true;
-
-    @computed get links() { return DocListCast(this.dataDoc.links); }
-    @computed get heightPercent() { return NumCast(this.layoutDoc._timelineHeightPercent, 100); } // current percent of video relative to VideoBox height
-    // @computed get rawDuration() { return NumCast(this.dataDoc[this.fieldKey + "-duration"]); }
-    @observable rawDuration: number = 0;
-
-
-    @computed get youtubeVideoId() {
-        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("/")) : "";
-    }
-
-
-    // returns the path of the audio file
-    @computed get audiopath() {
-        const field = Cast(this.props.Document[this.props.fieldKey + '-audio'], AudioField, null);
-        const vfield = Cast(this.dataDoc[this.fieldKey], VideoField, null);
-        return field?.url.href ?? vfield?.url.href ?? "";
-    }
-
-
-    @computed private get timeline() { return this._stackedTimeline; }
-    private get transition() { return this._clicking ? "left 0.5s, width 0.5s, height 0.5s" : ""; } // css transition for hiding/showing timeline
-    public get player(): HTMLVideoElement | null { return this._videoRef; }
-
-
-    componentDidMount() {
-        this.props.setContentView?.(this); // this tells the DocumentView that this VideoBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the VideoBox when making a link.
-        if (this.youtubeVideoId) {
-            const youtubeaspect = 400 / 315;
-            const nativeWidth = Doc.NativeWidth(this.layoutDoc);
-            const nativeHeight = Doc.NativeHeight(this.layoutDoc);
-            if (!nativeWidth || !nativeHeight) {
-                if (!nativeWidth) Doc.SetNativeWidth(this.dataDoc, 600);
-                Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 600) / youtubeaspect);
-                this.layoutDoc._height = NumCast(this.layoutDoc._width) / youtubeaspect;
-            }
-        }
-        this.player && this.setPlayheadTime(0);
-    }
-
-    componentWillUnmount() {
-        this.removeCurrentlyPlaying();
-        this.Pause();
-        Object.keys(this._disposers).forEach(d => this._disposers[d]?.());
-    }
-
-
-    // plays video
-    @action public Play = (update: boolean = true) => {
-        if (this._playRegionTimer) return;
-        this._playing = true;
-        const eleTime = this.player?.currentTime || 0;
-        if (this.timeline) {
-            let start = eleTime >= this.timeline.trimEnd || eleTime <= this.timeline.trimStart ? this.timeline.trimStart : eleTime;
-
-            if (this._finished) {
-                // restarts video if reached end on previous play
-                this._finished = false;
-                start = this.timeline.trimStart;
+      public static LayoutString(fieldKey: string) { return FieldView.LayoutString(VideoBox, fieldKey); }
+      /**
+       * Uploads an image buffer to the server and stores with specified filename.  by default the image
+       * is stored at multiple resolutions each retrieved by using the filename appended with _o, _s, _m, _l (indicating original, small, medium, or large)
+       * @param imageUri the bytes of the image
+       * @param returnedFilename the base filename to store the image on the server
+       * @param nosuffix optionally suppress creating multiple resolution images
+       */
+      public static async convertDataUri(imageUri: string, returnedFilename: string, nosuffix = false, replaceRootFilename?: string) {
+            try {
+                  const posting = Utils.prepend("/uploadURI");
+                  const returnedUri = await rp.post(posting, {
+                        body: {
+                              uri: imageUri,
+                              name: returnedFilename,
+                              nosuffix,
+                              replaceRootFilename
+                        },
+                        json: true,
+                  });
+                  return returnedUri;
+
+            } catch (e) {
+                  console.log("VideoBox :" + e);
+            }
+      }
+
+      static _youtubeIframeCounter: number = 0;
+      static heightPercent = 80; // height of video relative to videoBox when timeline is open
+      static numThumbnails = 20;
+      private _disposers: { [name: string]: IReactionDisposer } = {};
+      private _youtubePlayer: YT.Player | undefined = undefined;
+      private _videoRef: HTMLVideoElement | null = null; // <video> ref
+      private _contentRef: HTMLDivElement | null = null; // ref to div that wraps video and controls for full screen
+      private _youtubeIframeId: number = -1;
+      private _youtubeContentCreated = false;
+      private _audioPlayer: HTMLAudioElement | null = null;
+      private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); // outermost div
+      private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
+      private _playRegionTimer: any = null; // timeout for playback
+      private _controlsFadeTimer: any = null; // timeout for controls fade
+
+      public onMakeAnchor: () => Opt<Doc> = () => undefined; // Method to get anchor from text search
+
+      @observable _stackedTimeline: any; // CollectionStackedTimeline ref
+      @observable static _nativeControls: boolean; // default html controls
+      @observable _marqueeing: number[] | undefined; // coords for marquee selection
+      @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
+      @observable _screenCapture = false;
+      @observable _clicking = false; // used for transition between showing/hiding timeline
+      @observable _forceCreateYouTubeIFrame = false;
+      @observable _playTimer?: NodeJS.Timeout = undefined;
+      @observable _fullScreen = false;
+      @observable _playing = false;
+      @observable _finished: boolean = false; // has playback reached end of clip
+      @observable _volume: number = 1;
+      @observable _muted: boolean = false;
+      @observable _controlsTransform?: { X: number, Y: number };
+      @observable _controlsVisible: boolean = true;
+
+      @computed get links() { return DocListCast(this.dataDoc.links); }
+      @computed get heightPercent() { return NumCast(this.layoutDoc._timelineHeightPercent, 100); } // current percent of video relative to VideoBox height
+      // @computed get rawDuration() { return NumCast(this.dataDoc[this.fieldKey + "-duration"]); }
+      @observable rawDuration: number = 0;
+
+
+      @computed get youtubeVideoId() {
+            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("/")) : "";
+      }
+
+
+      // returns the path of the audio file
+      @computed get audiopath() {
+            const field = Cast(this.props.Document[this.props.fieldKey + '-audio'], AudioField, null);
+            const vfield = Cast(this.dataDoc[this.fieldKey], VideoField, null);
+            return field?.url.href ?? vfield?.url.href ?? "";
+      }
+
+
+      @computed private get timeline() { return this._stackedTimeline; }
+      private get transition() { return this._clicking ? "left 0.5s, width 0.5s, height 0.5s" : ""; } // css transition for hiding/showing timeline
+      public get player(): HTMLVideoElement | null { return this._videoRef; }
+
+
+      componentDidMount() {
+            this.props.setContentView?.(this); // this tells the DocumentView that this VideoBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the VideoBox when making a link.
+            if (this.youtubeVideoId) {
+                  const youtubeaspect = 400 / 315;
+                  const nativeWidth = Doc.NativeWidth(this.layoutDoc);
+                  const nativeHeight = Doc.NativeHeight(this.layoutDoc);
+                  if (!nativeWidth || !nativeHeight) {
+                        if (!nativeWidth) Doc.SetNativeWidth(this.dataDoc, 600);
+                        Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 600) / youtubeaspect);
+                        this.layoutDoc._height = NumCast(this.layoutDoc._width) / youtubeaspect;
+                  }
+            }
+            this.player && this.setPlayheadTime(0);
+      }
+
+      componentWillUnmount() {
+            this.removeCurrentlyPlaying();
+            this.Pause();
+            Object.keys(this._disposers).forEach(d => this._disposers[d]?.());
+      }
+
+
+      // plays video
+      @action public Play = (update: boolean = true) => {
+            if (this._playRegionTimer) return;
+            this._playing = true;
+            const eleTime = this.player?.currentTime || 0;
+            if (this.timeline) {
+                  let start = eleTime >= this.timeline.trimEnd || eleTime <= this.timeline.trimStart ? this.timeline.trimStart : eleTime;
+
+                  if (this._finished) {
+                        // restarts video if reached end on previous play
+                        this._finished = false;
+                        start = this.timeline.trimStart;
+                  }
+
+                  try {
+                        const posting = Utils.prepend("/uploadURI");
+                        const returnedUri = await rp.post(posting, {
+                              body: {
+                                    uri: imageUri,
+                                    name: returnedFilename,
+                                    nosuffix,
+                                    replaceRootFilename
+                              },
+                              json: true,
+                        });
+                        return returnedUri;
+
+                  } catch (e) {
+                        console.log("VideoBox :" + e);
+                  }
             }
 
+      static _youtubeIframeCounter: number = 0;
+      static heightPercent = 80; // height of video relative to videoBox when timeline is open
+      private _disposers: { [name: string]: IReactionDisposer } = {};
+      private _youtubePlayer: YT.Player | undefined = undefined;
+      private _videoRef: HTMLVideoElement | null = null; // <video> ref
+      private _contentRef: HTMLDivElement | null = null; // ref to div that wraps video and controls for full screen
+      private _youtubeIframeId: number = -1;
+      private _youtubeContentCreated = false;
+      private _audioPlayer: HTMLAudioElement | null = null;
+      private _mainCont: React.RefObject<HTMLDivElement> = React.createRef(); // outermost div
+      private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
+      private _playRegionTimer: any = null; // timeout for playback
+      @observable _stackedTimeline: any; // CollectionStackedTimeline ref
+      @observable static _nativeControls: boolean; // default html controls
+      @observable _marqueeing: number[] | undefined; // coords for marquee selection
+      @observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
+      @observable _screenCapture = false;
+      @observable _clicking = false; // used for transition between showing/hiding timeline
+      @observable _forceCreateYouTubeIFrame = false;
+      @observable _playTimer?: NodeJS.Timeout = undefined;
+      @observable _fullScreen = false;
+      @observable _playing = false;
+      @observable _finished: boolean = false; // has playback reached end of clip
+      @observable _volume: number = 1;
+      @observable _muted: boolean = false;
+
+      // pauses video
+      @action public Pause = (update: boolean = true) => {
+            this._playing = false;
+            this.removeCurrentlyPlaying();
             try {
-                this._audioPlayer && this.player && (this._audioPlayer.currentTime = this.player?.currentTime);
-                update && this.player && this.playFrom(start, undefined, true);
-                update && this._audioPlayer?.play();
-                update && this._youtubePlayer?.playVideo();
-                this._youtubePlayer && !this._playTimer && (this._playTimer = setInterval(this.updateTimecode, 5));
+                  update && this.player?.pause();
+                  update && this._audioPlayer?.pause();
+                  update && this._youtubePlayer?.pauseVideo();
+                  this._youtubePlayer && this._playTimer && clearInterval(this._playTimer);
+                  this._youtubePlayer?.seekTo(this._youtubePlayer?.getCurrentTime(), true);
             } catch (e) {
-                console.log("Video Play Exception:", e);
-            }
-        }
-        this.updateTimecode();
-    }
-
-    // goes to time
-    @action public Seek(time: number) {
-        try {
-            this._youtubePlayer?.seekTo(Math.round(time), true);
-        } catch (e) {
-            console.log("Video Seek Exception:", e);
-        }
-        this.player && (this.player.currentTime = time);
-        this._audioPlayer && (this._audioPlayer.currentTime = time);
-    }
-
-    // pauses video
-    @action public Pause = (update: boolean = true) => {
-        this._playing = false;
-        this.removeCurrentlyPlaying();
-        try {
-            update && this.player?.pause();
-            update && this._audioPlayer?.pause();
-            update && this._youtubePlayer?.pauseVideo();
-            this._youtubePlayer && this._playTimer && clearInterval(this._playTimer);
-            this._youtubePlayer?.seekTo(this._youtubePlayer?.getCurrentTime(), true);
-        } catch (e) {
-            console.log("Video Pause Exception:", e);
-        }
-        this._youtubePlayer && SelectionManager.DeselectAll(); // if we don't deselect the player, then we get an annoying YouTube spinner I guess telling us we're paused.
-        this._playTimer = undefined;
-        this.updateTimecode();
-        if (!this._finished) {
-            clearTimeout(this._playRegionTimer); // if paused in the middle of playback, prevents restart on next play
-        }
-        this._playRegionTimer = undefined;
-    }
-
-    // toggles video full screen
-    @action public FullScreen = () => {
-        if (document.fullscreenElement === this._contentRef) {
-            this._fullScreen = false;
-            this.player && this._contentRef && document.exitFullscreen();
-        }
-        else {
-            this._fullScreen = true;
-            this.player && this._contentRef && this._contentRef.requestFullscreen();
-        }
-        try {
-            this._youtubePlayer && this.props.addDocTab(this.rootDoc, "add");
-        } catch (e) {
-            console.log("Video FullScreen Exception:", e);
-        }
-    }
-
-    // fades out controls in fullscreen after mouse stops moving
-    @action controlsFade = (e: PointerEvent) => {
-        e.stopPropagation();
-        this._controlsVisible = true;
-        clearTimeout(this._controlsFadeTimer);
-        this._controlsFadeTimer = setTimeout(action(() => this._controlsVisible = false), 3000);
-    }
-
-
-    // drag controls around window in fulls screen
-    @action controlsDrag = (e: React.PointerEvent) => {
-        e.preventDefault();
-        e.stopPropagation();
-        const eleStyle = getComputedStyle(e.target as Element);
-        this._controlsTransform = { X: parseInt(eleStyle.left), Y: parseInt(eleStyle.top) };
-
-        setupMoveUpEvents(e.target,
-            e,
-            action((e, down, delta) => {
-                if (this._controlsTransform) {
-                    this._controlsTransform.X = Math.max(0, Math.min(delta[0] + this._controlsTransform.X, window.innerWidth));
-                    this._controlsTransform.Y = Math.max(0, Math.min(delta[1] + this._controlsTransform.Y, window.innerHeight));
-                }
-                return false;
-            }),
-            emptyFunction,
-            emptyFunction)
-    }
-
-
-    // creates and links snapshot photo of current video frame
-    @action public Snapshot = (downX?: number, downY?: number, cb?: (filename: string, x: number | undefined, y: number | undefined) => void) => {
-        const width = NumCast(this.layoutDoc._width);
-        const canvas = document.createElement('canvas');
-        canvas.width = 640;
-        canvas.height = 640 * Doc.NativeHeight(this.layoutDoc) / (Doc.NativeWidth(this.layoutDoc) || 1);
-        const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
-        if (ctx) {
-            this._videoRef && ctx.drawImage(this._videoRef, 0, 0, canvas.width, canvas.height);
-        }
-
-        if (!this._videoRef) {
-            const b = Docs.Create.LabelDocument({
-                x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y, 1),
-                _width: 150, _height: 50, title: (this.layoutDoc._currentTimecode || 0).toString(),
-                _isLinkButton: true
-            });
-            this.props.addDocument?.(b);
-            DocUtils.MakeLink({ doc: b }, { doc: this.rootDoc }, "video snapshot");
-            Networking.PostToServer("/youtubeScreenshot", {
-                id: this.youtubeVideoId,
-                timecode: this.layoutDoc._currentTimecode
-            }).then(response => {
-                const resolved = response?.accessPaths?.agnostic?.client;
-                if (resolved) {
-                    this.props.removeDocument?.(b);
-                    this.createRealSummaryLink(resolved);
-                }
-            });
-        } else {
-            //convert to desired file format
-            const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
-            // if you want to preview the captured image,
-            const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, "");
-            const encodedFilename = encodeURIComponent("snapshot" + retitled + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_"));
-            const filename = basename(encodedFilename);
-            VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) =>
-                returnedFilename && (cb ?? this.createRealSummaryLink)(returnedFilename, downX, downY));
-        }
-    }
-
-    updateIcon = () => {
-        const makeIcon = (returnedfilename: string) => {
-            this.dataDoc.icon = new ImageField(returnedfilename);
-            this.dataDoc["icon-nativeWidth"] = this.layoutDoc[WidthSym]();
-            this.dataDoc["icon-nativeHeight"] = this.layoutDoc[HeightSym]();
-        };
-        this.Snapshot(undefined, undefined, makeIcon);
-    }
-
-    // creates link for snapshot
-    createRealSummaryLink = (imagePath: string, downX?: number, downY?: number) => {
-        const url = !imagePath.startsWith("/") ? Utils.CorsProxy(imagePath) : imagePath;
-        const width = NumCast(this.layoutDoc._width) || 1;
-        const height = NumCast(this.layoutDoc._height);
-        const imageSummary = Docs.Create.ImageDocument(url, {
-            _nativeWidth: Doc.NativeWidth(this.layoutDoc), _nativeHeight: Doc.NativeHeight(this.layoutDoc),
-            x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y), _isLinkButton: true,
-            _width: 150, _height: height / width * 150, title: "--snapshot" + NumCast(this.layoutDoc._currentTimecode) + " image-"
-        });
-        Doc.SetNativeWidth(Doc.GetProto(imageSummary), Doc.NativeWidth(this.layoutDoc));
-        Doc.SetNativeHeight(Doc.GetProto(imageSummary), Doc.NativeHeight(this.layoutDoc));
-        this.props.addDocument?.(imageSummary);
-        const link = DocUtils.MakeLink({ doc: imageSummary }, { doc: this.getAnchor() }, "video snapshot");
-        link && (Doc.GetProto(link.anchor2 as Doc).timecodeToHide = NumCast((link.anchor2 as Doc).timecodeToShow) + 3);
-        setTimeout(() =>
-            (downX !== undefined && downY !== undefined) && DocumentManager.Instance.getFirstDocumentView(imageSummary)?.startDragging(downX, downY, "move", true));
-    }
-
-
-    getAnchor = () => {
-        const timecode = Cast(this.layoutDoc._currentTimecode, "number", null);
-        const marquee = AnchorMenu.Instance.GetAnchor?.();
-        return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow"/* videoStart */, "_timecodeToHide" /* videoEnd */, timecode ? timecode : undefined, undefined, marquee) || this.rootDoc;
-    }
-
-
-    // sets video info on load
-    videoLoad = action(() => {
-        const aspect = this.player!.videoWidth / this.player!.videoHeight;
-        Doc.SetNativeWidth(this.dataDoc, this.player!.videoWidth);
-        Doc.SetNativeHeight(this.dataDoc, this.player!.videoHeight);
-        this.layoutDoc._height = NumCast(this.layoutDoc._width) / aspect;
-        if (Number.isFinite(this.player!.duration)) {
-            this.rawDuration = this.player!.duration;
-        } else this.rawDuration = NumCast(this.dataDoc[this.fieldKey + "-duration"]);
-    });
-
-
-    // updates video time
-    @action
-    updateTimecode = () => {
-        this.player && (this.layoutDoc._currentTimecode = this.player.currentTime);
-        try {
-            this._youtubePlayer && (this.layoutDoc._currentTimecode = this._youtubePlayer.getCurrentTime?.());
-        } catch (e) {
-            console.log("Video Timecode Exception:", e);
-        }
-    }
-
-
-    // extracts video thumbnails and saves them as field of doc
-    getVideoThumbnails = () => {
-        const video = document.createElement('video');
-        const thumbnailPromises: Promise<any>[] = [];
-
-        video.onloadedmetadata = () => {
-            video.currentTime = 0;
-        };
-
-        video.onseeked = () => {
-            const canvas = document.createElement('canvas');
-            canvas.height = video.videoHeight;
-            canvas.width = video.videoWidth;
-            const ctx = canvas.getContext('2d');
-            ctx?.drawImage(video, 0, 0, canvas.width, canvas.height);
-            const imgUrl = canvas.toDataURL();
-            const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, "");
-            const encodedFilename = encodeURIComponent("thumbnail" + retitled + "_" + video.currentTime.toString().replace(/\./, "_"));
-            const filename = basename(encodedFilename);
-            thumbnailPromises.push(VideoBox.convertDataUri(imgUrl, filename));
-            const newTime = video.currentTime + video.duration / (VideoBox.numThumbnails - 1);
-            if (newTime < video.duration) {
-                video.currentTime = newTime;
+                  console.log("Video Pause Exception:", e);
+            }
+            this._youtubePlayer && SelectionManager.DeselectAll(); // if we don't deselect the player, then we get an annoying YouTube spinner I guess telling us we're paused.
+            this._playTimer = undefined;
+            this.updateTimecode();
+            if (!this._finished) {
+                  clearTimeout(this._playRegionTimer); // if paused in the middle of playback, prevents restart on next play
+            }
+            this._playRegionTimer = undefined;
+      }
+
+      // toggles video full screen
+      @action public FullScreen = () => {
+            if (document.fullscreenElement === this._contentRef) {
+                  this._fullScreen = false;
+                  this.player && this._contentRef && document.exitFullscreen();
             }
             else {
-                Promise.all(thumbnailPromises).then(thumbnails => { this.dataDoc.thumbnails = new List<string>(thumbnails); });
-            }
-        }
-
-        const field = Cast(this.dataDoc[this.fieldKey], VideoField);
-        field && (video.src = field.url.href);
-    }
-
-
-    // sets video element ref
-    @action
-    setVideoRef = (vref: HTMLVideoElement | null) => {
-        this._videoRef = vref;
-        if (vref) {
-            this._videoRef!.ontimeupdate = this.updateTimecode;
-            // @ts-ignore
-            // vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen);
-            this._disposers.reactionDisposer?.();
-            this._disposers.reactionDisposer = reaction(() => NumCast(this.layoutDoc._currentTimecode),
-                time => !this._playing && (vref.currentTime = time), { fireImmediately: true });
-
-            (!this.dataDoc.thumbnails || this.dataDoc.thumbnails.length != VideoBox.numThumbnails) && this.getVideoThumbnails();
-        }
-    }
-
-    // set ref for div that wraps video and controls for fullscreen
-    @action
-    setContentRef = (cref: HTMLDivElement | null) => {
-        this._contentRef = cref;
-        if (cref) {
-            cref.onfullscreenchange = action((e) => {
-                this._fullScreen = (document.fullscreenElement === cref);
-                if (this._fullScreen) {
-                    document.addEventListener('pointermove', this.controlsFade);
-                    this._controlsVisible = true;
-                    this._controlsFadeTimer = setTimeout(action(() => this._controlsVisible = false), 3000)
-                }
-                else {
-                    document.removeEventListener('pointermove', this.controlsFade);
-                }
+                  this._fullScreen = true;
+                  this.player && this._contentRef && this._contentRef.requestFullscreen();
+            }
+            try {
+                  this._youtubePlayer && this.props.addDocTab(this.rootDoc, "add");
+            } catch (e) {
+                  console.log("Video FullScreen Exception:", e);
+            }
+      }
+
+      // fades out controls in fullscreen after mouse stops moving
+      @action controlsFade = (e: PointerEvent) => {
+            e.stopPropagation();
+            this._controlsVisible = true;
+            clearTimeout(this._controlsFadeTimer);
+            this._controlsFadeTimer = setTimeout(action(() => this._controlsVisible = false), 3000);
+      }
+
+
+      // drag controls around window in fulls screen
+      @action controlsDrag = (e: React.PointerEvent) => {
+            e.preventDefault();
+            e.stopPropagation();
+            const eleStyle = getComputedStyle(e.target as Element);
+            this._controlsTransform = { X: parseInt(eleStyle.left), Y: parseInt(eleStyle.top) };
+
+            setupMoveUpEvents(e.target,
+                  e,
+                  action((e, down, delta) => {
+                        if (this._controlsTransform) {
+                              this._controlsTransform.X = Math.max(0, Math.min(delta[0] + this._controlsTransform.X, window.innerWidth));
+                              this._controlsTransform.Y = Math.max(0, Math.min(delta[1] + this._controlsTransform.Y, window.innerHeight));
+                        }
+                        return false;
+                  }),
+                  emptyFunction,
+                  emptyFunction)
+      }
+
+
+      // creates and links snapshot photo of current video frame
+      @action public Snapshot = (downX?: number, downY?: number, cb?: (filename: string, x: number | undefined, y: number | undefined) => void) => {
+            const width = NumCast(this.layoutDoc._width);
+            const canvas = document.createElement('canvas');
+            canvas.width = 640;
+            canvas.height = 640 * Doc.NativeHeight(this.layoutDoc) / (Doc.NativeWidth(this.layoutDoc) || 1);
+            const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
+            if (ctx) {
+                  this._videoRef && ctx.drawImage(this._videoRef, 0, 0, canvas.width, canvas.height);
+            }
+
+            if (!this._videoRef) {
+                  const b = Docs.Create.LabelDocument({
+                        x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y, 1),
+                        _width: 150, _height: 50, title: (this.layoutDoc._currentTimecode || 0).toString(),
+                        _isLinkButton: true
+                  });
+                  this.props.addDocument?.(b);
+                  DocUtils.MakeLink({ doc: b }, { doc: this.rootDoc }, "video snapshot");
+                  Networking.PostToServer("/youtubeScreenshot", {
+                        id: this.youtubeVideoId,
+                        timecode: this.layoutDoc._currentTimecode
+                  }).then(response => {
+                        const resolved = response?.accessPaths?.agnostic?.client;
+                        if (resolved) {
+                              this.props.removeDocument?.(b);
+                              this.createRealSummaryLink(resolved);
+                        }
+                  });
+            } else {
+                  //convert to desired file format
+                  const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
+                  // if you want to preview the captured image,
+                  const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, "");
+                  const encodedFilename = encodeURIComponent("snapshot" + retitled + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_"));
+                  const filename = basename(encodedFilename);
+                  VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) =>
+                        returnedFilename && (cb ?? this.createRealSummaryLink)(returnedFilename, downX, downY));
+            }
+      }
+
+      updateIcon = () => {
+            const makeIcon = (returnedfilename: string) => {
+                  this.dataDoc.icon = new ImageField(returnedfilename);
+                  this.dataDoc["icon-nativeWidth"] = this.layoutDoc[WidthSym]();
+                  this.dataDoc["icon-nativeHeight"] = this.layoutDoc[HeightSym]();
+            };
+            this.Snapshot(undefined, undefined, makeIcon);
+      }
+
+      // creates link for snapshot
+      createRealSummaryLink = (imagePath: string, downX?: number, downY?: number) => {
+            const url = !imagePath.startsWith("/") ? Utils.CorsProxy(imagePath) : imagePath;
+            const width = NumCast(this.layoutDoc._width) || 1;
+            const height = NumCast(this.layoutDoc._height);
+            const imageSummary = Docs.Create.ImageDocument(url, {
+                  _nativeWidth: Doc.NativeWidth(this.layoutDoc), _nativeHeight: Doc.NativeHeight(this.layoutDoc),
+                  x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y), _isLinkButton: true,
+                  _width: 150, _height: height / width * 150, title: "--snapshot" + NumCast(this.layoutDoc._currentTimecode) + " image-"
             });
-        }
-    }
-
-
-    // context menu
-    specificContextMenu = (e: React.MouseEvent): void => {
-        const field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
-        if (field) {
-            const url = field.url.href;
-            const subitems: ContextMenuProps[] = [];
-            subitems.push({ description: "Full Screen", event: this.FullScreen, icon: "expand" });
-            subitems.push({ description: "Take Snapshot", event: this.Snapshot, icon: "expand-arrows-alt" });
-            this.rootDoc.type === DocumentType.SCREENSHOT && subitems.push({
-                description: "Screen Capture", event: (async () => {
-                    runInAction(() => this._screenCapture = !this._screenCapture);
-                    this._videoRef!.srcObject = !this._screenCapture ? undefined : await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
-                }), icon: "expand-arrows-alt"
+            Doc.SetNativeWidth(Doc.GetProto(imageSummary), Doc.NativeWidth(this.layoutDoc));
+            Doc.SetNativeHeight(Doc.GetProto(imageSummary), Doc.NativeHeight(this.layoutDoc));
+            this.props.addDocument?.(imageSummary);
+            const link = DocUtils.MakeLink({ doc: imageSummary }, { doc: this.getAnchor() }, "video snapshot");
+            link && (Doc.GetProto(link.anchor2 as Doc).timecodeToHide = NumCast((link.anchor2 as Doc).timecodeToShow) + 3);
+            setTimeout(() =>
+                  (downX !== undefined && downY !== undefined) && DocumentManager.Instance.getFirstDocumentView(imageSummary)?.startDragging(downX, downY, "move", true));
+      }
+
+
+      // returns the path of the audio file
+      @computed get audiopath() {
+            const field = Cast(this.props.Document[this.props.fieldKey + '-audio'], AudioField, null);
+            const vfield = Cast(this.dataDoc[this.fieldKey], VideoField, null);
+            return field?.url.href ?? vfield?.url.href ?? "";
+      }
+
+      // returns the presentation data if it exists, null otherwise
+      @computed get presentation() {
+            const data = this.dataDoc[this.fieldKey + '-presentation'];
+            return data ? JSON.parse(data) : null;
+      }
+
+
+      @computed private get timeline() { return this._stackedTimeline; }
+      private get transition() { return this._clicking ? "left 0.5s, width 0.5s, height 0.5s" : ""; } // css transition for hiding/showing timeline
+      public get player(): HTMLVideoElement | null { return this._videoRef; }
+
+
+      // updates video time
+      @action
+      updateTimecode = () => {
+            this.player && (this.layoutDoc._currentTimecode = this.player.currentTime);
+            try {
+                  this._youtubePlayer && (this.layoutDoc._currentTimecode = this._youtubePlayer.getCurrentTime?.());
+            } catch (e) {
+                  console.log("Video Timecode Exception:", e);
+            }
+      }
+
+
+      // extracts video thumbnails and saves them as field of doc
+      getVideoThumbnails = () => {
+            const video = document.createElement('video');
+            const thumbnailPromises: Promise<any>[] = [];
+
+            video.onloadedmetadata = () => {
+                  video.currentTime = 0;
+            };
+
+            video.onseeked = () => {
+                  const canvas = document.createElement('canvas');
+                  canvas.height = video.videoHeight;
+                  canvas.width = video.videoWidth;
+                  const ctx = canvas.getContext('2d');
+                  ctx?.drawImage(video, 0, 0, canvas.width, canvas.height);
+                  const imgUrl = canvas.toDataURL();
+                  const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, "");
+                  const encodedFilename = encodeURIComponent("thumbnail" + retitled + "_" + video.currentTime.toString().replace(/\./, "_"));
+                  const filename = basename(encodedFilename);
+                  thumbnailPromises.push(VideoBox.convertDataUri(imgUrl, filename));
+                  const newTime = video.currentTime + video.duration / (VideoBox.numThumbnails - 1);
+                  if (newTime < video.duration) {
+                        video.currentTime = newTime;
+                  }
+                  else {
+                        Promise.all(thumbnailPromises).then(thumbnails => { this.dataDoc.thumbnails = new List<string>(thumbnails); });
+                  }
+            }
+
+            const field = Cast(this.dataDoc[this.fieldKey], VideoField);
+            field && (video.src = field.url.href);
+      }
+
+
+      // sets video element ref
+      @action
+      setVideoRef = (vref: HTMLVideoElement | null) => {
+            this._videoRef = vref;
+            if (vref) {
+                  this._videoRef!.ontimeupdate = this.updateTimecode;
+                  // @ts-ignore
+                  // vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen);
+                  this._disposers.reactionDisposer?.();
+                  this._disposers.reactionDisposer = reaction(() => NumCast(this.layoutDoc._currentTimecode),
+                        time => !this._playing && (vref.currentTime = time), { fireImmediately: true });
+
+                  (!this.dataDoc.thumbnails || this.dataDoc.thumbnails.length != VideoBox.numThumbnails) && this.getVideoThumbnails();
+            }
+      }
+
+      // set ref for div that wraps video and controls for fullscreen
+      @action
+      setContentRef = (cref: HTMLDivElement | null) => {
+            this._contentRef = cref;
+            if (cref) {
+                  cref.onfullscreenchange = action((e) => {
+                        this._fullScreen = (document.fullscreenElement === cref);
+                        if (this._fullScreen) {
+                              document.addEventListener('pointermove', this.controlsFade);
+                              this._controlsVisible = true;
+                              this._controlsFadeTimer = setTimeout(action(() => this._controlsVisible = false), 3000)
+                        }
+                        else {
+                              document.removeEventListener('pointermove', this.controlsFade);
+                        }
+                  });
+            }
+      }
+
+
+      // context menu
+      specificContextMenu = (e: React.MouseEvent): void => {
+            const field = Cast(this.dataDoc[this.props.fieldKey], VideoField);
+            if (field) {
+                  const url = field.url.href;
+                  const subitems: ContextMenuProps[] = [];
+                  subitems.push({ description: "Full Screen", event: this.FullScreen, icon: "expand" });
+                  subitems.push({ description: "Take Snapshot", event: this.Snapshot, icon: "expand-arrows-alt" });
+                  this.rootDoc.type === DocumentType.SCREENSHOT && subitems.push({
+                        description: "Screen Capture", event: (async () => {
+                              runInAction(() => this._screenCapture = !this._screenCapture);
+                              this._videoRef!.srcObject = !this._screenCapture ? undefined : await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
+                        }), icon: "expand-arrows-alt"
+                  });
+                  subitems.push({ description: (this.layoutDoc.dontAutoFollowLinks ? "" : "Don't") + " follow links when encountered", event: () => this.layoutDoc.dontAutoFollowLinks = !this.layoutDoc.dontAutoFollowLinks, icon: "expand-arrows-alt" });
+                  subitems.push({ description: (this.layoutDoc.dontAutoPlayFollowedLinks ? "" : "Don't") + " play when link is selected", event: () => this.layoutDoc.dontAutoPlayFollowedLinks = !this.layoutDoc.dontAutoPlayFollowedLinks, icon: "expand-arrows-alt" });
+                  subitems.push({ description: (this.layoutDoc.autoPlayAnchors ? "Don't auto play" : "Auto play") + " anchors onClick", event: () => this.layoutDoc.autoPlayAnchors = !this.layoutDoc.autoPlayAnchors, icon: "expand-arrows-alt" });
+                  // subitems.push({ description: "Toggle Native Controls", event: action(() => VideoBox._nativeControls = !VideoBox._nativeControls), icon: "expand-arrows-alt" });
+                  // subitems.push({ description: "Start Trim All", event: () => this.startTrim(TrimScope.All), icon: "expand-arrows-alt" });
+                  // subitems.push({ description: "Start Trim Clip", event: () => this.startTrim(TrimScope.Clip), icon: "expand-arrows-alt" });
+                  // subitems.push({ description: "Stop Trim", event: () => this.finishTrim(), icon: "expand-arrows-alt" });
+                  subitems.push({ description: "Copy path", event: () => { Utils.CopyText(url); }, icon: "expand-arrows-alt" });
+                  ContextMenu.Instance.addItem({ description: "Options...", subitems: subitems, icon: "video" });
+            }
+      }
+
+
+      // ref for updating time
+      setAudioRef = (e: HTMLAudioElement | null) => this._audioPlayer = e;
+
+      // renders the video and audio
+      @computed get content() {
+            const field = Cast(this.dataDoc[this.fieldKey], VideoField);
+            const interactive = CurrentUserUtils.SelectedTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive";
+            const classname = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
+            return !field ? <div key="loading">Loading</div> :
+                  <div className="videoBox-contentContainer" key="container" style={{ mixBlendMode: "multiply", cursor: this._fullScreen && !this._controlsVisible ? 'none' : 'pointer' }}>
+                        <div className={classname} ref={this.setContentRef} onPointerDown={(e) => this._fullScreen && e.stopPropagation()}>
+                              {this._fullScreen && <div className="videoBox-ui" onPointerDown={this.controlsDrag}
+                                    style={{ left: this._controlsTransform && this._controlsTransform.X, top: this._controlsTransform && this._controlsTransform.Y, visibility: this._controlsVisible ? 'visible' : 'hidden', opacity: this._controlsVisible ? 1 : 0 }}>
+                                    {this.UIButtons}
+                              </div>}
+                              <video key="video" autoPlay={this._screenCapture} ref={this.setVideoRef} style={this._fullScreen ? this.fullScreenSize() : {}}
+                                    onCanPlay={this.videoLoad}
+                                    controls={VideoBox._nativeControls}
+                                    onPlay={() => this.Play()}
+                                    onSeeked={this.updateTimecode}
+                                    onPause={() => this.Pause()}
+                                    onClick={this._fullScreen ? () => this.playing() ? this.Pause() : this.Play() : e => e.preventDefault()}>
+                                    <source src={field.url.href} type="video/mp4" />
+                                    Not supported.
+                              </video>
+                              {!this.audiopath || this.audiopath === field.url.href ? (null) :
+                                    <audio ref={this.setAudioRef} className={`audiobox-control${this.props.isContentActive() ? "-interactive" : ""}`}>
+                                          <source src={this.audiopath} type="audio/mpeg" />
+                                          Not supported.
+                                    </audio>}
+                        </div>
+                  </div>;
+      }
+
+
+      @action youtubeIframeLoaded = (e: any) => {
+            if (!this._youtubeContentCreated) {
+                  this._forceCreateYouTubeIFrame = !this._forceCreateYouTubeIFrame;
+                  return;
+            }
+            else this._youtubeContentCreated = false;
+
+            this.loadYouTube(e.target);
+      }
+
+      loadYouTube = (iframe: any) => {
+            let started = true;
+            const onYoutubePlayerStateChange = (event: any) => runInAction(() => {
+                  if (started && event.data === YT.PlayerState.PLAYING) {
+                        started = false;
+                        this._youtubePlayer?.unMute();
+                        //this.Pause();
+                        return;
+                  }
+                  this.player && this.setPlayheadTime(0);
+            }
+
+      componentWillUnmount() {
+                  this.removeCurrentlyPlaying();
+                  this.Pause();
+                  Object.keys(this._disposers).forEach(d => this._disposers[d]?.());
+            }
+
+
+      // plays video
+      @action public Play = (update: boolean = true) => {
+                  // if (Doc.UserDoc().presentationMode === 'watching' && !this._playing) {
+                  //     console.log('VideoBox : Play : presentation mode', this._playing);
+                  //     return;
+                  // }
+
+                  // if presentation isn't null, call followmovements on the recording api
+                  if (this.presentation) {
+                        console.log("presentation isn't null")
+                        const err = RecordingApi.Instance.playMovements(this.presentation, this.player?.currentTime || 0, this);
+                        err && console.log(err)
+                  } else {
+                        console.log("presentation is null")
+                  }
+
+                  this._playing = true;
+                  const eleTime = this.player?.currentTime || 0;
+                  if (this.timeline) {
+                        let start = eleTime >= this.timeline.trimEnd || eleTime <= this.timeline.trimStart ? this.timeline.trimStart : eleTime;
+
+                        if (this._finished) {
+                              // restarts video if reached end on previous play
+                              this._finished = false;
+                              start = this.timeline.trimStart;
+                        }
+
+                        try {
+                              this._audioPlayer && this.player && (this._audioPlayer.currentTime = this.player?.currentTime);
+                              update && this.player && this.playFrom(start, undefined, true);
+                              update && this._audioPlayer?.play();
+                              update && this._youtubePlayer?.playVideo();
+                              this._youtubePlayer && !this._playTimer && (this._playTimer = setInterval(this.updateTimecode, 5));
+                        } catch (e) {
+                              console.log("Video Play Exception:", e);
+                        }
+                  }
+                  this.updateTimecode();
+            }
+
+            // goes to time
+            @action public Seek(time: number) {
+                  try {
+                        this._youtubePlayer?.seekTo(Math.round(time), true);
+                  } catch (e) {
+                        console.log("Video Seek Exception:", e);
+                  }
+                  this.player && (this.player.currentTime = time);
+                  this._audioPlayer && (this._audioPlayer.currentTime = time);
+                  // TODO: revisit this and clean it
+                  if ((this.player?.currentTime || -1) < this.rawDuration) {
+                        this._finished = false;
+                  }
+            }
+
+            // pauses video
+            @action public Pause = (update: boolean = true) => {
+                  if (this.presentation) {
+                        console.log('VideoBox : Pause');
+                        const err = RecordingApi.Instance.pauseMovements();
+                        err && console.log(err);
+                  }
+
+                  this._playing = false;
+                  this.removeCurrentlyPlaying();
+                  try {
+                        update && this.player?.pause();
+                        update && this._audioPlayer?.pause();
+                        update && this._youtubePlayer?.pauseVideo();
+                        this._youtubePlayer && this._playTimer && clearInterval(this._playTimer);
+                        this._youtubePlayer?.seekTo(this._youtubePlayer?.getCurrentTime(), true);
+                  } catch (e) {
+                        console.log("Video Pause Exception:", e);
+                  }
+                  this._youtubePlayer && SelectionManager.DeselectAll(); // if we don't deselect the player, then we get an annoying YouTube spinner I guess telling us we're paused.
+                  this._playTimer = undefined;
+                  this.updateTimecode();
+                  if (!this._finished) clearTimeout(this._playRegionTimer); // if paused in the middle of playback, prevents restart on next play
+            }
+
+            // toggles video full screen
+            @action public FullScreen = () => {
+                  if (document.fullscreenElement === this._contentRef) {
+                        this._fullScreen = false;
+                        this.player && this._contentRef && document.exitFullscreen();
+                  }
+                  else {
+                        this._fullScreen = true;
+                        this.player && this._contentRef && this._contentRef.requestFullscreen();
+
+                  }
+                  try {
+                        this._youtubePlayer && this.props.addDocTab(this.rootDoc, "add");
+                  } catch (e) {
+                        console.log("Video FullScreen Exception:", e);
+                  }
+            }
+
+
+            // creates and links snapshot photo of current video frame
+            @action public Snapshot = (downX?: number, downY?: number, cb?: (filename: string, x: number | undefined, y: number | undefined) => void) => {
+                  const width = NumCast(this.layoutDoc._width);
+                  const canvas = document.createElement('canvas');
+                  canvas.width = 640;
+                  canvas.height = 640 * Doc.NativeHeight(this.layoutDoc) / (Doc.NativeWidth(this.layoutDoc) || 1);
+                  const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
+                  if (ctx) {
+                        this._videoRef && ctx.drawImage(this._videoRef, 0, 0, canvas.width, canvas.height);
+                  }
+
+                  if (!this._videoRef) {
+                        const b = Docs.Create.LabelDocument({
+                              x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y, 1),
+                              _width: 150, _height: 50, title: (this.layoutDoc._currentTimecode || 0).toString(),
+                              _isLinkButton: true
+                        });
+                        this.props.addDocument?.(b);
+                        DocUtils.MakeLink({ doc: b }, { doc: this.rootDoc }, "video snapshot");
+                        Networking.PostToServer("/youtubeScreenshot", {
+                              id: this.youtubeVideoId,
+                              timecode: this.layoutDoc._currentTimecode
+                        }).then(response => {
+                              const resolved = response?.accessPaths?.agnostic?.client;
+                              if (resolved) {
+                                    this.props.removeDocument?.(b);
+                                    this.createRealSummaryLink(resolved);
+                              }
+                        });
+                  } else {
+                        //convert to desired file format
+                        const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
+                        // if you want to preview the captured image,
+                        const retitled = StrCast(this.rootDoc.title).replace(/[ -\.]/g, "");
+                        const encodedFilename = encodeURIComponent("snapshot" + retitled + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_"));
+                        const filename = basename(encodedFilename);
+                        VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) =>
+                              returnedFilename && (cb ?? this.createRealSummaryLink)(returnedFilename, downX, downY));
+                  }
+            }
+
+            updateIcon = () => {
+                  const makeIcon = (returnedfilename: string) => {
+                        this.dataDoc.icon = new ImageField(returnedfilename);
+                        this.dataDoc["icon-nativeWidth"] = this.layoutDoc[WidthSym]();
+                        this.dataDoc["icon-nativeHeight"] = this.layoutDoc[HeightSym]();
+                  };
+                  this.Snapshot(undefined, undefined, makeIcon);
+            }
+
+            // creates link for snapshot
+            createRealSummaryLink = (imagePath: string, downX?: number, downY?: number) => {
+                  const url = !imagePath.startsWith("/") ? Utils.CorsProxy(imagePath) : imagePath;
+                  const width = NumCast(this.layoutDoc._width) || 1;
+                  const height = NumCast(this.layoutDoc._height);
+                  const imageSummary = Docs.Create.ImageDocument(url, {
+                        _nativeWidth: Doc.NativeWidth(this.layoutDoc), _nativeHeight: Doc.NativeHeight(this.layoutDoc),
+                        x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y), _isLinkButton: true,
+                        _width: 150, _height: height / width * 150, title: "--snapshot" + NumCast(this.layoutDoc._currentTimecode) + " image-"
+                  });
+                  Doc.SetNativeWidth(Doc.GetProto(imageSummary), Doc.NativeWidth(this.layoutDoc));
+                  Doc.SetNativeHeight(Doc.GetProto(imageSummary), Doc.NativeHeight(this.layoutDoc));
+                  this.props.addDocument?.(imageSummary);
+                  const link = DocUtils.MakeLink({ doc: imageSummary }, { doc: this.getAnchor() }, "video snapshot");
+                  link && (Doc.GetProto(link.anchor2 as Doc).timecodeToHide = NumCast((link.anchor2 as Doc).timecodeToShow) + 3);
+                  setTimeout(() =>
+                        (downX !== undefined && downY !== undefined) && DocumentManager.Instance.getFirstDocumentView(imageSummary)?.startDragging(downX, downY, "move", true));
+            }
+
+
+            getAnchor = () => {
+                  const timecode = Cast(this.layoutDoc._currentTimecode, "number", null);
+                  const marquee = AnchorMenu.Instance.GetAnchor?.();
+                  return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow"/* videoStart */, "_timecodeToHide" /* videoEnd */, timecode ? timecode : undefined, undefined, marquee) || this.rootDoc;
+            }
+
+
+            // sets video info on load
+            videoLoad = action(() => {
+                  const aspect = this.player!.videoWidth / this.player!.videoHeight;
+                  Doc.SetNativeWidth(this.dataDoc, this.player!.videoWidth);
+                  Doc.SetNativeHeight(this.dataDoc, this.player!.videoHeight);
+                  this.layoutDoc._height = NumCast(this.layoutDoc._width) / aspect;
+                  if (Number.isFinite(this.player!.duration)) {
+                        this.rawDuration = this.player!.duration;
+                  } else this.rawDuration = NumCast(this.dataDoc[this.fieldKey + "-duration"]);
             });
-            subitems.push({ description: (this.layoutDoc.dontAutoFollowLinks ? "" : "Don't") + " follow links when encountered", event: () => this.layoutDoc.dontAutoFollowLinks = !this.layoutDoc.dontAutoFollowLinks, icon: "expand-arrows-alt" });
-            subitems.push({ description: (this.layoutDoc.dontAutoPlayFollowedLinks ? "" : "Don't") + " play when link is selected", event: () => this.layoutDoc.dontAutoPlayFollowedLinks = !this.layoutDoc.dontAutoPlayFollowedLinks, icon: "expand-arrows-alt" });
-            subitems.push({ description: (this.layoutDoc.autoPlayAnchors ? "Don't auto play" : "Auto play") + " anchors onClick", event: () => this.layoutDoc.autoPlayAnchors = !this.layoutDoc.autoPlayAnchors, icon: "expand-arrows-alt" });
-            // subitems.push({ description: "Toggle Native Controls", event: action(() => VideoBox._nativeControls = !VideoBox._nativeControls), icon: "expand-arrows-alt" });
-            // subitems.push({ description: "Start Trim All", event: () => this.startTrim(TrimScope.All), icon: "expand-arrows-alt" });
-            // subitems.push({ description: "Start Trim Clip", event: () => this.startTrim(TrimScope.Clip), icon: "expand-arrows-alt" });
-            // subitems.push({ description: "Stop Trim", event: () => this.finishTrim(), icon: "expand-arrows-alt" });
-            subitems.push({ description: "Copy path", event: () => { Utils.CopyText(url); }, icon: "expand-arrows-alt" });
-            ContextMenu.Instance.addItem({ description: "Options...", subitems: subitems, icon: "video" });
-        }
-    }
-
-
-    // ref for updating time
-    setAudioRef = (e: HTMLAudioElement | null) => this._audioPlayer = e;
-
-    // renders the video and audio
-    @computed get content() {
-        const field = Cast(this.dataDoc[this.fieldKey], VideoField);
-        const interactive = CurrentUserUtils.SelectedTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive";
-        const classname = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
-        return !field ? <div key="loading">Loading</div> :
-            <div className="videoBox-contentContainer" key="container" style={{ mixBlendMode: "multiply", cursor: this._fullScreen && !this._controlsVisible ? 'none' : 'pointer' }}>
-                <div className={classname} ref={this.setContentRef} onPointerDown={(e) => this._fullScreen && e.stopPropagation()}>
-                    {this._fullScreen && <div className="videoBox-ui" onPointerDown={this.controlsDrag}
-                        style={{ left: this._controlsTransform && this._controlsTransform.X, top: this._controlsTransform && this._controlsTransform.Y, visibility: this._controlsVisible ? 'visible' : 'hidden', opacity: this._controlsVisible ? 1 : 0 }}>
-                        {this.UIButtons}
-                    </div>}
-                    <video key="video" autoPlay={this._screenCapture} ref={this.setVideoRef} style={this._fullScreen ? this.fullScreenSize() : {}}
-                        onCanPlay={this.videoLoad}
-                        controls={VideoBox._nativeControls}
-                        onPlay={() => this.Play()}
-                        onSeeked={this.updateTimecode}
-                        onPause={() => this.Pause()}
-                        onClick={this._fullScreen ? () => this.playing() ? this.Pause() : this.Play() : e => e.preventDefault()}>
-                        <source src={field.url.href} type="video/mp4" />
-                        Not supported.
-                    </video>
-                    {!this.audiopath || this.audiopath === field.url.href ? (null) :
-                        <audio ref={this.setAudioRef} className={`audiobox-control${this.props.isContentActive() ? "-interactive" : ""}`}>
-                            <source src={this.audiopath} type="audio/mpeg" />
-                            Not supported.
-                        </audio>}
-                </div>
-            </div>;
-    }
-
-
-    @action youtubeIframeLoaded = (e: any) => {
-        if (!this._youtubeContentCreated) {
-            this._forceCreateYouTubeIFrame = !this._forceCreateYouTubeIFrame;
-            return;
-        }
-        else this._youtubeContentCreated = false;
-
-        this.loadYouTube(e.target);
-    }
-
-    loadYouTube = (iframe: any) => {
-        let started = true;
-        const onYoutubePlayerStateChange = (event: any) => runInAction(() => {
-            if (started && event.data === YT.PlayerState.PLAYING) {
-                started = false;
-                this._youtubePlayer?.unMute();
-                //this.Pause();
-                return;
-            }
-            if (event.data === YT.PlayerState.PLAYING && !this._playing) this.Play(false);
-            if (event.data === YT.PlayerState.PAUSED && this._playing) this.Pause(false);
-        });
-        const onYoutubePlayerReady = (event: any) => {
-            this._disposers.reactionDisposer?.();
-            this._disposers.youtubeReactionDisposer?.();
-            this._disposers.reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek(NumCast(this.layoutDoc._currentTimecode)));
-            this._disposers.youtubeReactionDisposer = reaction(
-                () => CurrentUserUtils.SelectedTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
-                (interactive) => iframe.style.pointerEvents = interactive ? "all" : "none", { fireImmediately: true });
-        };
-        if (typeof (YT) === undefined) setTimeout(() => this.loadYouTube(iframe), 100);
-        else {
-            (YT as any)?.ready(() => {
-                this._youtubePlayer = new YT.Player(`${this.youtubeVideoId + this._youtubeIframeId}-player`, {
-                    events: {
-                        'onReady': this.props.dontRegisterView ? undefined : onYoutubePlayerReady,
-                        'onStateChange': this.props.dontRegisterView ? undefined : onYoutubePlayerStateChange,
-                    }
-                });
+
+
+            // updates video time
+            @action
+            updateTimecode = () => {
+                  this.player && (this.layoutDoc._currentTimecode = this.player.currentTime);
+                  try {
+                        this._youtubePlayer && (this.layoutDoc._currentTimecode = this._youtubePlayer.getCurrentTime?.());
+                  } catch (e) {
+                        console.log("Video Timecode Exception:", e);
+                  }
+            }
+
+
+            // renders video controls
+            componentUI = (boundsLeft: number, boundsTop: number) => {
+                  const bounds = this.props.docViewPath().lastElement().getBounds();
+                  const left = bounds?.left || 0;
+                  const right = bounds?.right || 0;
+                  const top = bounds?.top || 0;
+                  const height = (bounds?.bottom || 0) - top;
+                  const width = Math.max(right - left, 100);
+                  const uiHeight = Math.max(25, Math.min(50, height / 10));
+                  const uiMargin = Math.min(10, height / 20);
+                  const vidHeight = height * this.heightPercent / 100;
+                  const yPos = top + vidHeight - uiHeight - uiMargin;
+                  const xPos = uiHeight / vidHeight > 0.4 ? right + 10 : left + 10;
+                  return this._fullScreen || (right - left) < 50 ? null : <div className="videoBox-ui-wrapper" style={{ clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)` }}>
+                        <div className="videoBox-ui" style={{ left: xPos, top: yPos, height: uiHeight, width: width - 20, transition: this._clicking ? "top 0.5s" : "" }}>
+                              {this.UIButtons}
+                        </div>
+                  </div>
+            }
+
+            @computed get UIButtons() {
+                  const bounds = this.props.docViewPath().lastElement().getBounds();
+                  const width = (bounds?.right || 0) - (bounds?.left || 0);
+                  const curTime = NumCast(this.layoutDoc._currentTimecode) - (this.timeline?.clipStart || 0);
+                  return <>
+                        <div className="videobox-button"
+                              title={this._playing ? "play" : "pause"}
+                              onPointerDown={this.onPlayDown}>
+                              <FontAwesomeIcon icon={this._playing ? "pause" : "play"} />
+                        </div>
+
+                        {this.timeline && width > 150 && <div className="timecode-controls">
+                              <div className="timecode-current">
+                                    {formatTime(curTime)}
+                              </div>
+
+                              {this._fullScreen || (this.heightPercent === 100 && width > 200) ?
+                                    <div className="timeline-slider">
+                                          <input type="range" step="0.1" min={this.timeline.clipStart} max={this.timeline.clipEnd} value={curTime}
+                                                className="toolbar-slider time-progress"
+                                                onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
+                                                onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setPlayheadTime(Number(e.target.value))}
+                                          />
+                                    </div>
+                                    :
+                                    <div>/</div>}
+
+                              <div className="timecode-end">
+                                    {formatTime(this.timeline.clipDuration)}
+                              </div>
+                        </div>
+                        }
+
+                  }
+                        ContextMenu.Instance.addItem({description: "Options...", subitems: subitems, icon: "video" });
+            }
+      }
+
+                        {
+                              !this._fullScreen && width > 300 && <div className="videobox-button"
+                                    title={"show timeline"}
+                                    onPointerDown={this.onTimelineHdlDown}>
+                                    <FontAwesomeIcon icon="eye" />
+                              </div>
+                        }
+
+                        {
+                              !this._fullScreen && width > 300 && <div className="videobox-button"
+                                    title={this.timeline?.IsTrimming !== TrimScope.None ? "finish trimming" : "start trim"}
+                                    onPointerDown={this.onClipPointerDown}>
+                                    <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? "check" : "cut"} />
+                              </div>
+                        }
+
+                        <div className="videobox-button"
+                              title={this._muted ? "unmute" : "mute"}
+                              onPointerDown={(e) => { e.stopPropagation(); this.toggleMute(); }}>
+                              <FontAwesomeIcon icon={this._muted ? "volume-mute" : "volume-up"} />
+                        </div>
+                        {
+                              width > 300 && <input type="range" style={{ width: `min(25%, 50px)` }} step="0.1" min="0" max="1" value={this._muted ? 0 : this._volume}
+                                    className="toolbar-slider volume"
+                                    onPointerDown={(e: React.PointerEvent) => e.stopPropagation()}
+                                    onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setVolume(Number(e.target.value))}
+                              />
+                        }
+
+                        {
+                              !this._fullScreen && this.heightPercent !== 100 && width > 300 &&
+                              <>
+                                    <div className="videobox-button" title="zoom">
+                                          <FontAwesomeIcon icon="search-plus" />
+                                    </div>
+                                    <input type="range" step="0.1" min="1" max="5" value={this.timeline?._zoomFactor}
+                                          className="toolbar-slider zoom"
+                                          onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
+                                          onChange={(e: React.ChangeEvent<HTMLInputElement>) => { this.zoom(Number(e.target.value)); }}
+                                    />
+                              </>
+                        }
+                  </>
+            }
+
+            @action youtubeIframeLoaded = (e: any) => {
+                  if (!this._youtubeContentCreated) {
+                        this._forceCreateYouTubeIFrame = !this._forceCreateYouTubeIFrame;
+                        return;
+                  }
+                  else this._youtubeContentCreated = false;
+
+                  this.loadYouTube(e.target);
+            }
+
+            loadYouTube = (iframe: any) => {
+                  let started = true;
+                  const onYoutubePlayerStateChange = (event: any) => runInAction(() => {
+                        if (started && event.data === YT.PlayerState.PLAYING) {
+                              started = false;
+                              this._youtubePlayer?.unMute();
+                              //this.Pause();
+                              return;
+                        }
+                        if (event.data === YT.PlayerState.PLAYING && !this._playing) this.Play(false);
+                        if (event.data === YT.PlayerState.PAUSED && this._playing) this.Pause(false);
+                  });
+                  const onYoutubePlayerReady = (event: any) => {
+                        this._disposers.reactionDisposer?.();
+                        this._disposers.youtubeReactionDisposer?.();
+                        this._disposers.reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek(NumCast(this.layoutDoc._currentTimecode)));
+                        this._disposers.youtubeReactionDisposer = reaction(
+                              () => CurrentUserUtils.SelectedTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
+                              (interactive) => iframe.style.pointerEvents = interactive ? "all" : "none", { fireImmediately: true });
+                  };
+                  if (typeof (YT) === undefined) setTimeout(() => this.loadYouTube(iframe), 100);
+                  else {
+                        (YT as any)?.ready(() => {
+                              this._youtubePlayer = new YT.Player(`${this.youtubeVideoId + this._youtubeIframeId}-player`, {
+                                    events: {
+                                          'onReady': this.props.dontRegisterView ? undefined : onYoutubePlayerReady,
+                                          'onStateChange': this.props.dontRegisterView ? undefined : onYoutubePlayerStateChange,
+                                    }
+                              });
+                        });
+                  }
+            }
+
+
+            // for play button
+
+            onPlayDown = () => {
+                  console.log("PLAY DOWN");
+                  this._playing ? this.Pause() : this.Play();
+            }
+
+            // for fullscreen button
+            onFullDown = (e: React.PointerEvent) => {
+                  this.FullScreen();
+                  e.stopPropagation();
+                  e.preventDefault();
+            }
+
+            // for snapshot button
+            onSnapshotDown = (e: React.PointerEvent) => {
+                  setupMoveUpEvents(this, e, (e) => {
+                        this.Snapshot(e.clientX, e.clientY);
+                        return true;
+                  }, emptyFunction, () => this.Snapshot());
+            }
+
+            // for show/hide timeline button, transitions between show/hide
+            @action
+            onTimelineHdlDown = (e: React.PointerEvent) => {
+                  this._clicking = true;
+                  setupMoveUpEvents(this, e,
+                        action(encodeURIComponent => {
+                              this._clicking = false;
+                              if (this.props.isContentActive()) {
+                                    // const local = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformPoint(e.clientX, e.clientY);
+                                    // this.layoutDoc._timelineHeightPercent = Math.max(0, Math.min(100, local[1] / this.props.PanelHeight() * 100));
+
+                                    this.layoutDoc._timelineHeightPercent = 80;
+                              }
+                              return false;
+                        }), emptyFunction,
+                        () => {
+                              this.layoutDoc._timelineHeightPercent = this.heightPercent !== 100 ? 100 : VideoBox.heightPercent;
+                              setTimeout(action(() => this._clicking = false), 500);
+                        }, this.props.isContentActive(), this.props.isContentActive());
+            }
+
+
+            // removes video from currently playing display
+            @action
+            removeCurrentlyPlaying = () => {
+                  if (CollectionStackedTimeline.CurrentlyPlaying) {
+                        const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc);
+                        index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1);
+                  }
+            }
+
+            // adds video to currently playing display
+            @action
+            addCurrentlyPlaying = () => {
+                  if (!CollectionStackedTimeline.CurrentlyPlaying) {
+                        CollectionStackedTimeline.CurrentlyPlaying = [];
+                  }
+                  if (CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc) === -1) {
+                        CollectionStackedTimeline.CurrentlyPlaying.push(this.layoutDoc);
+                  }
+            }
+
+
+            @computed get youtubeContent() {
+                  this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
+                  this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
+                  const classname = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
+                  const start = untracked(() => Math.round(NumCast(this.layoutDoc._currentTimecode)));
+                  return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
+                        onPointerLeave={this.updateTimecode}
+                        onLoad={this.youtubeIframeLoaded} className={classname} width={Doc.NativeWidth(this.layoutDoc) || 640} height={Doc.NativeHeight(this.layoutDoc) || 390}
+                        src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=0&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._nativeControls ? 1 : 0}`} />;
+            }
+
+
+            // for annotating, adds doc with time info
+            @action.bound
+            addDocWithTimecode(doc: Doc | Doc[]): boolean {
+                  const docs = doc instanceof Doc ? [doc] : doc;
+                  const curTime = NumCast(this.layoutDoc._currentTimecode);
+                  docs.forEach(doc => doc._timecodeToHide = (doc._timecodeToShow = curTime) + 1);
+                  return this.addDocument(doc);
+            }
+
+
+            // play back the audio from seekTimeInSeconds, fullPlay tells whether clip is being played to end vs link range
+            @action
+            playFrom = (seekTimeInSeconds: number, endTime?: number, fullPlay: boolean = false) => {
+                  clearTimeout(this._playRegionTimer);
+                  if (Number.isNaN(this.player?.duration)) {
+                        setTimeout(() => this.playFrom(seekTimeInSeconds, endTime), 500);
+                  }
+                  else if (this.player) {
+                        // trimBounds override requested playback bounds
+                        const end = Math.min(this.timeline?.trimEnd ?? this.rawDuration, endTime ?? this.timeline?.trimEnd ?? this.rawDuration);
+                        const start = Math.max(this.timeline?.trimStart ?? 0, seekTimeInSeconds);
+                        const playRegionDuration = end - start;
+                        // checks if times are within clip range
+                        if (seekTimeInSeconds >= 0 && (this.timeline?.trimStart || 0) <= end && seekTimeInSeconds <= (this.timeline?.trimEnd || this.rawDuration)) {
+                              this.player.currentTime = start;
+                              this._audioPlayer && (this._audioPlayer.currentTime = seekTimeInSeconds);
+                              this.player.play();
+                              this._audioPlayer?.play();
+                              this._playing = true;
+                              this.addCurrentlyPlaying();
+                              this._playRegionTimer = setTimeout(
+                                    () => {
+                                          // need to keep track of if end of clip is reached so on next play, clip restarts
+                                          if (fullPlay) {
+                                                Doc.UserDoc().presentationMode = 'none';
+                                                this._finished = true;
+                                          }
+                                          // removes from currently playing if playback has reached end of range marker
+                                          else this.removeCurrentlyPlaying();
+                                          this.Pause();
+                                    }, playRegionDuration * 1000);
+                        } else {
+                              this.Pause();
+                        }
+                  }
+            }
+
+
+            // ends trim, hides trim controls and displays new clip
+            @undoBatch
+            finishTrim = action(() => {
+                  this.Pause();
+                  this.setPlayheadTime(Math.max(Math.min(this.timeline?.trimEnd || 0, this.player!.currentTime), this.timeline?.trimStart || 0));
+                  this.timeline?.StopTrimming();
             });
-        }
-    }
-
-
-    // for play button
-    onPlayDown = () => this._playing ? this.Pause() : this.Play();
-
-    // for fullscreen button
-    onFullDown = (e: React.PointerEvent) => {
-        this.FullScreen();
-        e.stopPropagation();
-        e.preventDefault();
-    }
-
-    // for snapshot button
-    onSnapshotDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, (e) => {
-            this.Snapshot(e.clientX, e.clientY);
-            return true;
-        }, emptyFunction, () => this.Snapshot());
-    }
-
-    // for show/hide timeline button, transitions between show/hide
-    @action
-    onTimelineHdlDown = (e: React.PointerEvent) => {
-        this._clicking = true;
-        setupMoveUpEvents(this, e,
-            action(encodeURIComponent => {
-                this._clicking = false;
-                if (this.props.isContentActive()) {
-                    // const local = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformPoint(e.clientX, e.clientY);
-                    // this.layoutDoc._timelineHeightPercent = Math.max(0, Math.min(100, local[1] / this.props.PanelHeight() * 100));
-
-                    this.layoutDoc._timelineHeightPercent = 80;
-                }
-                return false;
-            }), emptyFunction,
-            () => {
-                this.layoutDoc._timelineHeightPercent = this.heightPercent !== 100 ? 100 : VideoBox.heightPercent;
-                setTimeout(action(() => this._clicking = false), 500);
-            }, this.props.isContentActive(), this.props.isContentActive());
-    }
-
-
-    // removes video from currently playing display
-    @action
-    removeCurrentlyPlaying = () => {
-        if (CollectionStackedTimeline.CurrentlyPlaying) {
-            const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc);
-            index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1);
-        }
-    }
-
-    // adds video to currently playing display
-    @action
-    addCurrentlyPlaying = () => {
-        if (!CollectionStackedTimeline.CurrentlyPlaying) {
-            CollectionStackedTimeline.CurrentlyPlaying = [];
-        }
-        if (CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc) === -1) {
-            CollectionStackedTimeline.CurrentlyPlaying.push(this.layoutDoc);
-        }
-    }
-
-
-    @computed get youtubeContent() {
-        this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
-        this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
-        const classname = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
-        const start = untracked(() => Math.round(NumCast(this.layoutDoc._currentTimecode)));
-        return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
-            onPointerLeave={this.updateTimecode}
-            onLoad={this.youtubeIframeLoaded} className={classname} width={Doc.NativeWidth(this.layoutDoc) || 640} height={Doc.NativeHeight(this.layoutDoc) || 390}
-            src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=0&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._nativeControls ? 1 : 0}`} />;
-    }
-
-
-    // for annotating, adds doc with time info
-    @action.bound
-    addDocWithTimecode(doc: Doc | Doc[]): boolean {
-        const docs = doc instanceof Doc ? [doc] : doc;
-        const curTime = NumCast(this.layoutDoc._currentTimecode);
-        docs.forEach(doc => doc._timecodeToHide = (doc._timecodeToShow = curTime) + 1);
-        return this.addDocument(doc);
-    }
-
-
-    // play back the audio from seekTimeInSeconds, fullPlay tells whether clip is being played to end vs link range
-    @action
-    playFrom = (seekTimeInSeconds: number, endTime?: number, fullPlay: boolean = false) => {
-        clearTimeout(this._playRegionTimer);
-        this._playRegionTimer = undefined;
-        if (Number.isNaN(this.player?.duration)) {
-            setTimeout(() => this.playFrom(seekTimeInSeconds, endTime), 500);
-        }
-        else if (this.player) {
-            // trimBounds override requested playback bounds
-            const end = Math.min(this.timeline?.trimEnd ?? this.rawDuration, endTime ?? this.timeline?.trimEnd ?? this.rawDuration);
-            const start = Math.max(this.timeline?.trimStart ?? 0, seekTimeInSeconds);
-            const playRegionDuration = end - start;
-            // checks if times are within clip range
-            if (seekTimeInSeconds >= 0 && (this.timeline?.trimStart || 0) <= end && seekTimeInSeconds <= (this.timeline?.trimEnd || this.rawDuration)) {
-                this.player.currentTime = start;
-                this._audioPlayer && (this._audioPlayer.currentTime = seekTimeInSeconds);
-                this.player.play();
-                this._audioPlayer?.play();
-                this._playing = true;
-                this.addCurrentlyPlaying();
-                this._playRegionTimer = setTimeout(
-                    () => {
-                        // need to keep track of if end of clip is reached so on next play, clip restarts
-                        if (fullPlay) this._finished = true;
-                        // removes from currently playing if playback has reached end of range marker
-                        else this.removeCurrentlyPlaying();
-                        this.Pause();
-                    }, playRegionDuration * 1000);
-            } else {
-                this.Pause();
-            }
-        }
-    }
-
-
-    // ends trim, hides trim controls and displays new clip
-    @undoBatch
-    finishTrim = action(() => {
-        this.Pause();
-        this.setPlayheadTime(Math.max(Math.min(this.timeline?.trimEnd || 0, this.player!.currentTime), this.timeline?.trimStart || 0));
-        this.timeline?.StopTrimming();
-    });
-
-    // displays trim controls to start trimming clip
-    startTrim = (scope: TrimScope) => {
-        this.Pause();
-        this.timeline?.StartTrimming(scope);
-    }
-
-    // for trim button, double click displays full clip, single displays curr trim bounds
-    onClipPointerDown = (e: React.PointerEvent) => {
-        // if timeline isn't shown, show first then trim
-        this.heightPercent >= 100 && this.onTimelineHdlDown(e);
-        this.timeline && setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => {
-            if (doubleTap) {
-                this.startTrim(TrimScope.All);
-            } else if (this.timeline) {
-                this.Pause();
-                this.timeline.IsTrimming !== TrimScope.None ? this.finishTrim() : this.startTrim(TrimScope.Clip);
+
+            // displays trim controls to start trimming clip
+            startTrim = (scope: TrimScope) => {
+                  this.Pause();
+                  this.timeline?.StartTrimming(scope);
+            }
+
+            // for trim button, double click displays full clip, single displays curr trim bounds
+            onClipPointerDown = (e: React.PointerEvent) => {
+                  // if timeline isn't shown, show first then trim
+                  this.heightPercent >= 100 && this.onTimelineHdlDown(e);
+                  this.timeline && setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => {
+                        if (doubleTap) {
+                              this.startTrim(TrimScope.All);
+                        } else if (this.timeline) {
+                              this.Pause();
+                              this.timeline.IsTrimming !== TrimScope.None ? this.finishTrim() : this.startTrim(TrimScope.Clip);
+                        }
+                  }));
+            }
+
+
+            // for volume slider sets volume
+            @action
+            setVolume = (volume: number) => {
+                  if (this.player) {
+                        this._volume = volume;
+                        this.player.volume = volume;
+                        if (this._muted) {
+                              this.toggleMute();
+                        }
+                  }
+            }
+
+            // toggles video mute
+            @action
+            toggleMute = () => {
+                  if (this.player) {
+                        this._muted = !this._muted;
+                        this.player.muted = this._muted;
+                  }
             }
-        }));
-    }
-
-
-    // for volume slider sets volume
-    @action
-    setVolume = (volume: number) => {
-        if (this.player) {
-            this._volume = volume;
-            this.player.volume = volume;
-            if (this._muted) {
-                this.toggleMute();
-            }
-        }
-    }
-
-    // toggles video mute
-    @action
-    toggleMute = () => {
-        if (this.player) {
-            this._muted = !this._muted;
-            this.player.muted = this._muted;
-        }
-    }
-
-
-    // stretches vertically or horizontally depending on video orientation so video fits full screen
-    fullScreenSize() {
-        if (this._videoRef && this._videoRef.videoHeight / this._videoRef.videoWidth > 1) {
-            return { height: "100%" };
-        }
-        else {
-            return { width: "100%" };
-        }
-    }
-
-
-    // for zoom slider, sets timeline waveform zoom
-    zoom = (zoom: number) => {
-        this.timeline?.setZoom(zoom);
-    }
-
-
-    // plays link
-    playLink = (doc: Doc) => {
-        const startTime = Math.max(0, (this._stackedTimeline?.anchorStart(doc) || 0));
-        const endTime = this.timeline?.anchorEnd(doc);
-        if (startTime !== undefined) {
-            if (!this.layoutDoc.dontAutoPlayFollowedLinks) endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime);
-            else this.Seek(startTime);
-        }
-    }
-
-
-    // starts marquee selection
-    marqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
-            setupMoveUpEvents(this, e, action(e => {
-                MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
-                this._marqueeing = [e.clientX, e.clientY];
-                return true;
-            }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false);
-        }
-    }
-
-    // ends marquee selection
-    @action
-    finishMarquee = () => {
-        this._marqueeing = undefined;
-        this.props.select(true);
-    }
-
-    timelineWhenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive));
-
-    timelineScreenToLocal = () => this.props.ScreenToLocalTransform().scale(this.scaling()).translate(0, -this.heightPercent / 100 * this.props.PanelHeight());
-
-    setPlayheadTime = (time: number) => this.player!.currentTime = this.layoutDoc._currentTimecode = time;
-
-    timelineHeight = () => this.props.PanelHeight() * (100 - this.heightPercent) / 100;
-
-    playing = () => this._playing;
-
-    contentFunc = () => [this.youtubeVideoId ? this.youtubeContent : this.content];
-
-    scaling = () => this.props.scaling?.() || 1;
-
-    panelWidth = () => this.props.PanelWidth() * this.heightPercent / 100;
-    panelHeight = () => this.layoutDoc._fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : this.props.PanelHeight() * this.heightPercent / 100;
-
-    screenToLocalTransform = () => {
-        const offset = (this.props.PanelWidth() - this.panelWidth()) / 2 / this.scaling();
-        return this.props.ScreenToLocalTransform().translate(-offset, 0).scale(100 / this.heightPercent);
-    }
-
-    marqueeFitScaling = () => (this.props.scaling?.() || 1) * this.heightPercent / 100;
-    marqueeOffset = () => [this.panelWidth() / 2 * (1 - this.heightPercent / 100) / (this.heightPercent / 100), 0];
-
-    timelineDocFilter = () => [`_timelineLabel:true,${Utils.noRecursionHack}:x`];
-
-
-    // renders video controls
-    componentUI = (boundsLeft: number, boundsTop: number) => {
-        const bounds = this.props.docViewPath().lastElement().getBounds();
-        const left = bounds?.left || 0;
-        const right = bounds?.right || 0;
-        const top = bounds?.top || 0;
-        const height = (bounds?.bottom || 0) - top;
-        const width = Math.max(right - left, 100);
-        const uiHeight = Math.max(25, Math.min(50, height / 10));
-        const uiMargin = Math.min(10, height / 20);
-        const vidHeight = height * this.heightPercent / 100;
-        const yPos = top + vidHeight - uiHeight - uiMargin;
-        const xPos = uiHeight / vidHeight > 0.4 ? right + 10 : left + 10;
-        return this._fullScreen || (right - left) < 50 ? null : <div className="videoBox-ui-wrapper" style={{ clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)` }}>
-            <div className="videoBox-ui" style={{ left: xPos, top: yPos, height: uiHeight, width: width - 20, transition: this._clicking ? "top 0.5s" : "" }}>
-                {this.UIButtons}
-            </div>
-        </div>
-    }
-
-    @computed get UIButtons() {
-        const bounds = this.props.docViewPath().lastElement().getBounds();
-        const width = (bounds?.right || 0) - (bounds?.left || 0);
-        const curTime = NumCast(this.layoutDoc._currentTimecode) - (this.timeline?.clipStart || 0);
-        return <>
-            <div className="videobox-button"
-                title={this._playing ? "play" : "pause"}
-                onPointerDown={this.onPlayDown}>
-                <FontAwesomeIcon icon={this._playing ? "pause" : "play"} />
-            </div>
-
-            {this.timeline && width > 150 && <div className="timecode-controls">
-                <div className="timecode-current">
-                    {formatTime(curTime)}
-                </div>
-
-                {this._fullScreen || (this.heightPercent === 100 && width > 200) ?
-                    <div className="timeline-slider">
-                        <input type="range" step="0.1" min={this.timeline.clipStart} max={this.timeline.clipEnd} value={curTime}
-                            className="toolbar-slider time-progress"
-                            onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
-                            onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setPlayheadTime(Number(e.target.value))}
+
+
+            // stretches vertically or horizontally depending on video orientation so video fits full screen
+            fullScreenSize() {
+                  if (this._videoRef && this._videoRef.videoHeight / this._videoRef.videoWidth > 1) {
+                        return { height: "100%" };
+                  }
+                  else {
+                        return { width: "100%" };
+                  }
+            }
+
+
+            // for zoom slider, sets timeline waveform zoom
+            zoom = (zoom: number) => {
+                  this.timeline?.setZoom(zoom);
+            }
+
+
+            // plays link
+            playLink = (doc: Doc) => {
+                  const startTime = Math.max(0, (this._stackedTimeline?.anchorStart(doc) || 0));
+                  const endTime = this.timeline?.anchorEnd(doc);
+                  if (startTime !== undefined) {
+                        if (!this.layoutDoc.dontAutoPlayFollowedLinks) endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime);
+                        else this.Seek(startTime);
+                  }
+            }
+
+
+            // starts marquee selection
+            marqueeDown = (e: React.PointerEvent) => {
+                  if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
+                        setupMoveUpEvents(this, e, action(e => {
+                              MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+                              this._marqueeing = [e.clientX, e.clientY];
+                              return true;
+                        }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false);
+                  }
+            }
+
+            // ends marquee selection
+            @action
+            finishMarquee = () => {
+                  this._marqueeing = undefined;
+                  this.props.select(true);
+            }
+
+            timelineWhenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive));
+
+            timelineScreenToLocal = () => this.props.ScreenToLocalTransform().scale(this.scaling()).translate(0, -this.heightPercent / 100 * this.props.PanelHeight());
+
+            setPlayheadTime = (time: number) => this.player!.currentTime = this.layoutDoc._currentTimecode = time;
+
+            timelineHeight = () => this.props.PanelHeight() * (100 - this.heightPercent) / 100;
+
+            playing = () => this._playing;
+
+            contentFunc = () => [this.youtubeVideoId ? this.youtubeContent : this.content];
+
+            scaling = () => this.props.scaling?.() || 1;
+
+            panelWidth = () => this.props.PanelWidth() * this.heightPercent / 100;
+            panelHeight = () => this.layoutDoc._fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : this.props.PanelHeight() * this.heightPercent / 100;
+
+            screenToLocalTransform = () => {
+                  const offset = (this.props.PanelWidth() - this.panelWidth()) / 2 / this.scaling();
+                  return this.props.ScreenToLocalTransform().translate(-offset, 0).scale(100 / this.heightPercent);
+            }
+
+            marqueeFitScaling = () => (this.props.scaling?.() || 1) * this.heightPercent / 100;
+            marqueeOffset = () => [this.panelWidth() / 2 * (1 - this.heightPercent / 100) / (this.heightPercent / 100), 0];
+
+            timelineDocFilter = () => [`_timelineLabel:true,${Utils.noRecursionHack}:x`];
+
+
+            // renders video controls
+            @computed get uIButtons() {
+                  const curTime = NumCast(this.layoutDoc._currentTimecode) - (this.timeline?.clipStart || 0);
+                  return <div className="videoBox-ui" style={this._fullScreen || this.heightPercent === 100 ? { fontSize: "40px", minWidth: "80%" } : {}}>
+                        <div className="videobox-button"
+                              title={this._playing ? "play" : "pause"}
+                              onPointerDown={this.onPlayDown}>
+                              <FontAwesomeIcon icon={this._playing ? "pause" : "play"} />
+                        </div>
+
+                        {this.timeline && <div className="timecode-controls">
+                              <div className="timecode-current">
+                                    {formatTime(curTime)}
+                              </div>
+
+                              {this._fullScreen || this.heightPercent === 100 ?
+                                    <div className="timeline-slider">
+                                          <input type="range" step="0.1" min={this.timeline.clipStart} max={this.timeline.clipEnd} value={curTime}
+                                                className="toolbar-slider time-progress"
+                                                onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
+                                                onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setPlayheadTime(Number(e.target.value))}
+                                          />
+                                    </div>
+                                    :
+                                    <div>/</div>}
+
+                              <div className="timecode-end">
+                                    {formatTime(this.timeline.clipDuration)}
+                              </div>
+                        </div>}
+
+                        <div className="videobox-button"
+                              title={"full screen"}
+                              onPointerDown={this.onFullDown}>
+                              <FontAwesomeIcon icon="expand" />
+                        </div>
+
+                        {!this._fullScreen && <div className="videobox-button"
+                              title={"show timeline"}
+                              onPointerDown={this.onTimelineHdlDown}>
+                              <FontAwesomeIcon icon="eye" />
+                        </div>}
+
+                        {!this._fullScreen && <div className="videobox-button"
+                              title={this.timeline?.IsTrimming !== TrimScope.None ? "finish trimming" : "start trim"}
+                              onPointerDown={this.onClipPointerDown}>
+                              <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? "check" : "cut"} />
+                        </div>}
+
+                        <div className="videobox-button show-slider"
+                              title={this._muted ? "unmute" : "mute"}
+                              onPointerDown={(e) => { e.stopPropagation(); this.toggleMute(); }}>
+                              <FontAwesomeIcon icon={this._muted ? "volume-mute" : "volume-up"} />
+                        </div>
+                        <input type="range" step="0.1" min="0" max="1" value={this._muted ? 0 : this._volume}
+                              className="toolbar-slider volume"
+                              onPointerDown={(e: React.PointerEvent) => e.stopPropagation()}
+                              onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setVolume(Number(e.target.value))}
                         />
-                    </div>
-                    :
-                    <div>/</div>}
-
-                <div className="timecode-end">
-                    {formatTime(this.timeline.clipDuration)}
-                </div>
-            </div>
-            }
-
-            <div className="videobox-button"
-                title={"full screen"}
-                onPointerDown={this.onFullDown}>
-                <FontAwesomeIcon icon="expand" />
-            </div>
-
-            {
-                !this._fullScreen && width > 300 && <div className="videobox-button"
-                    title={"show timeline"}
-                    onPointerDown={this.onTimelineHdlDown}>
-                    <FontAwesomeIcon icon="eye" />
-                </div>
-            }
-
-            {
-                !this._fullScreen && width > 300 && <div className="videobox-button"
-                    title={this.timeline?.IsTrimming !== TrimScope.None ? "finish trimming" : "start trim"}
-                    onPointerDown={this.onClipPointerDown}>
-                    <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? "check" : "cut"} />
-                </div>
-            }
-
-            <div className="videobox-button"
-                title={this._muted ? "unmute" : "mute"}
-                onPointerDown={(e) => { e.stopPropagation(); this.toggleMute(); }}>
-                <FontAwesomeIcon icon={this._muted ? "volume-mute" : "volume-up"} />
-            </div>
-            {
-                width > 300 && <input type="range" style={{ width: `min(25%, 50px)` }} step="0.1" min="0" max="1" value={this._muted ? 0 : this._volume}
-                    className="toolbar-slider volume"
-                    onPointerDown={(e: React.PointerEvent) => e.stopPropagation()}
-                    onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setVolume(Number(e.target.value))}
-                />
-            }
-
-            {
-                !this._fullScreen && this.heightPercent !== 100 && width > 300 &&
-                <>
-                    <div className="videobox-button" title="zoom">
-                        <FontAwesomeIcon icon="search-plus" />
-                    </div>
-                    <input type="range" step="0.1" min="1" max="5" value={this.timeline?._zoomFactor}
-                        className="toolbar-slider zoom"
-                        onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
-                        onChange={(e: React.ChangeEvent<HTMLInputElement>) => { this.zoom(Number(e.target.value)); }}
-                    />
-                </>
-            }
-        </>
-    }
-
-    // renders CollectionStackedTimeline
-    @computed get renderTimeline() {
-        return <div className="videoBox-stackPanel" style={{ transition: this.transition, height: `${100 - this.heightPercent}%` }}>
-            <CollectionStackedTimeline ref={action((r: any) => this._stackedTimeline = r)} {...this.props}
-                fieldKey={this.annotationKey}
-                dictationKey={this.fieldKey + "-dictation"}
-                mediaPath={this.audiopath}
-                renderDepth={this.props.renderDepth + 1}
-                startTag={"_timecodeToShow" /* videoStart */}
-                endTag={"_timecodeToHide" /* videoEnd */}
-                bringToFront={emptyFunction}
-                CollectionView={undefined}
-                playFrom={this.playFrom}
-                setTime={this.setPlayheadTime}
-                playing={this.playing}
-                isAnyChildContentActive={this.isAnyChildContentActive}
-                whenChildContentsActiveChanged={this.timelineWhenChildContentsActiveChanged}
-                moveDocument={this.moveDocument}
-                addDocument={this.addDocument}
-                removeDocument={this.removeDocument}
-                ScreenToLocalTransform={this.timelineScreenToLocal}
-                Play={this.Play}
-                Pause={this.Pause}
-                playLink={this.playLink}
-                PanelHeight={this.timelineHeight}
-                rawDuration={this.rawDuration}
-            />
-        </div>;
-    }
-
-    // renders annotation layer
-    @computed get annotationLayer() {
-        return <div className="videoBox-annotationLayer" style={{ transition: this.transition, height: `${this.heightPercent}%` }} ref={this._annotationLayer} />;
-    }
-
-    savedAnnotations = () => this._savedAnnotations;
-    render() {
-        const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
-        const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / this.scaling()}px` : borderRad;
-        return (<div className="videoBox" onContextMenu={this.specificContextMenu} ref={this._mainCont}
-            style={{
-                pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined,
-                borderRadius,
-                overflow: this.props.docViewPath?.().slice(-1)[0].fitWidth ? "auto" : undefined
-            }} onWheel={e => { e.stopPropagation(); e.preventDefault(); }}>
-            <div className="videoBox-viewer" onPointerDown={this.marqueeDown} >
-                <div style={{
-                    position: "absolute", transition: this.transition,
-                    width: this.panelWidth(),
-                    height: this.panelHeight(),
-                    top: 0,
-                    left: (this.props.PanelWidth() - this.panelWidth()) / 2
-                }}>
-                    <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
-                        renderDepth={this.props.renderDepth + 1}
-                        fieldKey={this.annotationKey}
-                        CollectionView={undefined}
-                        isAnnotationOverlay={true}
-                        annotationLayerHostsContent={true}
-                        PanelWidth={this.panelWidth}
-                        PanelHeight={this.panelHeight}
-                        ScreenToLocalTransform={this.screenToLocalTransform}
-                        docFilters={this.timelineDocFilter}
-                        select={emptyFunction}
-                        scaling={returnOne}
-                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                        removeDocument={this.removeDocument}
-                        moveDocument={this.moveDocument}
-                        addDocument={this.addDocWithTimecode}>
-                        {this.contentFunc}
-                    </CollectionFreeFormView>
-                </div>
-                {this.annotationLayer}
-                {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
-                    <MarqueeAnnotator
-                        rootDoc={this.rootDoc}
-                        scrollTop={0}
-                        down={this._marqueeing}
-                        scaling={this.marqueeFitScaling}
-                        docView={this.props.docViewPath().slice(-1)[0]}
-                        containerOffset={this.marqueeOffset}
-                        addDocument={this.addDocWithTimecode}
-                        finishMarquee={this.finishMarquee}
-                        savedAnnotations={this.savedAnnotations}
-                        annotationLayer={this._annotationLayer.current}
-                        mainCont={this._mainCont.current}
-                    />}
-                {this.renderTimeline}
-            </div>
-        </div >);
-    }
-}
+
+                        {!this._fullScreen && this.heightPercent !== 100 &&
+                              <>
+                                    <div className="videobox-button" title="zoom">
+                                          <FontAwesomeIcon icon="search-plus" />
+                                    </div>
+                                    <input type="range" step="0.1" min="1" max="5" value={this.timeline?._zoomFactor}
+                                          className="toolbar-slider zoom"
+                                          onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
+                                          onChange={(e: React.ChangeEvent<HTMLInputElement>) => { this.zoom(Number(e.target.value)); }}
+                                    />
+                              </>}
+                  </div>;
+            }
+
+            // renders CollectionStackedTimeline
+            @computed get renderTimeline() {
+                  return <div className="videoBox-stackPanel" style={{ transition: this.transition, height: `${100 - this.heightPercent}%` }}>
+                        <CollectionStackedTimeline ref={action((r: any) => this._stackedTimeline = r)} {...this.props}
+                              fieldKey={this.annotationKey}
+                              dictationKey={this.fieldKey + "-dictation"}
+                              mediaPath={this.audiopath}
+                              renderDepth={this.props.renderDepth + 1}
+                              startTag={"_timecodeToShow" /* videoStart */}
+                              endTag={"_timecodeToHide" /* videoEnd */}
+                              bringToFront={emptyFunction}
+                              CollectionView={undefined}
+                              playFrom={this.playFrom}
+                              setTime={this.setPlayheadTime}
+                              playing={this.playing}
+                              isAnyChildContentActive={this.isAnyChildContentActive}
+                              whenChildContentsActiveChanged={this.timelineWhenChildContentsActiveChanged}
+                              moveDocument={this.moveDocument}
+                              addDocument={this.addDocument}
+                              removeDocument={this.removeDocument}
+                              ScreenToLocalTransform={this.timelineScreenToLocal}
+                              Play={this.Play}
+                              Pause={this.Pause}
+                              playLink={this.playLink}
+                              PanelHeight={this.timelineHeight}
+                              rawDuration={this.rawDuration}
+                        />
+                  </div>;
+            }
+
+            // renders annotation layer
+            @computed get annotationLayer() {
+                  return <div className="videoBox-annotationLayer" style={{ transition: this.transition, height: `${this.heightPercent}%` }} ref={this._annotationLayer} />;
+            }
+
+            savedAnnotations = () => this._savedAnnotations;
+            render() {
+                  const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
+                  const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / this.scaling()}px` : borderRad;
+                  return (<div className="videoBox" onContextMenu={this.specificContextMenu} ref={this._mainCont}
+                        style={{
+                              pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined,
+                              borderRadius,
+                              overflow: this.props.docViewPath?.().slice(-1)[0].fitWidth ? "auto" : undefined
+                        }} onWheel={e => { e.stopPropagation(); e.preventDefault(); }}>
+                        <div className="videoBox-viewer" onPointerDown={this.marqueeDown} >
+                              <div style={{
+                                    position: "absolute", transition: this.transition,
+                                    width: this.panelWidth(),
+                                    height: this.panelHeight(),
+                                    top: 0,
+                                    left: (this.props.PanelWidth() - this.panelWidth()) / 2
+                              }}>
+                                    <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
+                                          renderDepth={this.props.renderDepth + 1}
+                                          fieldKey={this.annotationKey}
+                                          CollectionView={undefined}
+                                          isAnnotationOverlay={true}
+                                          annotationLayerHostsContent={true}
+                                          PanelWidth={this.panelWidth}
+                                          PanelHeight={this.panelHeight}
+                                          ScreenToLocalTransform={this.screenToLocalTransform}
+                                          docFilters={this.timelineDocFilter}
+                                          select={emptyFunction}
+                                          scaling={returnOne}
+                                          whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                                          removeDocument={this.removeDocument}
+                                          moveDocument={this.moveDocument}
+                                          addDocument={this.addDocWithTimecode}>
+                                          {this.contentFunc}
+                                    </CollectionFreeFormView>
+                              </div>
+                              {this.annotationLayer}
+                              {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
+                                    <MarqueeAnnotator
+                                          rootDoc={this.rootDoc}
+                                          scrollTop={0}
+                                          down={this._marqueeing}
+                                          scaling={this.marqueeFitScaling}
+                                          docView={this.props.docViewPath().slice(-1)[0]}
+                                          containerOffset={this.marqueeOffset}
+                                          addDocument={this.addDocWithTimecode}
+                                          finishMarquee={this.finishMarquee}
+                                          savedAnnotations={this.savedAnnotations}
+                                          annotationLayer={this._annotationLayer.current}
+                                          mainCont={this._mainCont.current}
+                                    />}
+                              {this.renderTimeline}
+                        </div>
+                  </div >);
+            }
+      }
 
 VideoBox._nativeControls = false;
\ No newline at end of file
diff --git a/src/client/views/nodes/WebBoxRenderer.js b/src/client/views/nodes/WebBoxRenderer.js
index d9524dd6e..f3f1bcf5c 100644
--- a/src/client/views/nodes/WebBoxRenderer.js
+++ b/src/client/views/nodes/WebBoxRenderer.js
@@ -214,8 +214,8 @@ var ForeignHtmlRenderer = function (styleSheets) {
                 .replace(/noscript/g, "div").replace(/<div class="mediaset"><\/div>/g, "")  // when scripting isn't available (ie, rendering web pages here), <noscript> tags should become <div>'s.  But for Brown CS, there's a layout problem if you leave the empty <mediaset> tag
                 .replace(/<link[^>]*>/g, "")  // don't need to keep any linked style sheets because we've already processed all style sheets above
                 .replace(/srcset="([^ "]*)[^"]*"/g, "src=\"$1\""); // instead of converting each item in the srcset to a data url, just convert the first one and use that
-            let urlsFoundInHtml = getImageUrlsFromFromHtml(contentHtml);
-            const fetchedResources = await getMultipleResourcesAsBase64(webUrl, urlsFoundInHtml);
+            let urlsFoundInHtml = getImageUrlsFromFromHtml(contentHtml).filter(url => !url.startsWith("data:"));
+            const fetchedResources = webUrl ? await getMultipleResourcesAsBase64(webUrl, urlsFoundInHtml) : [];
             for (let i = 0; i < fetchedResources.length; i++) {
                 const r = fetchedResources[i];
                 if (r.resourceUrl) {
diff --git a/src/client/views/nodes/button/FontIconBadge.tsx b/src/client/views/nodes/button/FontIconBadge.tsx
index cf86b5e07..df17d603f 100644
--- a/src/client/views/nodes/button/FontIconBadge.tsx
+++ b/src/client/views/nodes/button/FontIconBadge.tsx
@@ -6,31 +6,31 @@ import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../../Utils
 import { DragManager } from "../../../util/DragManager";
 import "./FontIconBadge.scss";
 
-interface FontIconBadgeProps {
-    collection: Doc | undefined;
+interface FontIconBadgeProps {    
+    value: string | undefined;
 }
 
 @observer
 export class FontIconBadge extends React.Component<FontIconBadgeProps> {
     _notifsRef = React.createRef<HTMLDivElement>();
 
-    onPointerDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e,
-            (e: PointerEvent) => {
-                const dragData = new DragManager.DocumentDragData([this.props.collection!]);
-                DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y);
-                return true;
-            },
-            returnFalse, emptyFunction, false);
-    }
+    // onPointerDown = (e: React.PointerEvent) => {
+    //     setupMoveUpEvents(this, e,
+    //         (e: PointerEvent) => {
+    //             const dragData = new DragManager.DocumentDragData([this.props.collection!]);
+    //             DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y);
+    //             return true;
+    //         },
+    //         returnFalse, emptyFunction, false);
+    // }
 
     render() {
-        if (!(this.props.collection instanceof Doc)) return (null);
-        const length = DocListCast(this.props.collection.data).filter(d => GetEffectiveAcl(d) !== AclPrivate).length; //  Object.keys(d).length).length; // filter out any documents that we can't read
+        if (this.props.value === undefined) return (null);
         return <div className="fontIconBadge-container" ref={this._notifsRef}>
-            <div className="fontIconBadge" style={length > 0 ? { "display": "initial" } : { "display": "none" }}
-                onPointerDown={this.onPointerDown} >
-                {length}
+            <div className="fontIconBadge" style={{ "display": "initial" }}
+               // onPointerDown={this.onPointerDown} 
+               >
+                {this.props.value}
             </div>
         </div>;
     }
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 42c650a09..97e6eddfe 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -521,7 +521,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                     <div className={`menuButton ${this.type}`} style={{ color, backgroundColor }}>
                         {this.icon === "pres-trail" ? trailsIcon : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />}
                         {menuLabel}
-                        <FontIconBadge collection={Cast(this.rootDoc.watchedDocuments, Doc, null)} />
+                        <FontIconBadge value={ScriptCast(this.Document.badgeValue,null)?.script.run({self:this.Document}).result} />
                     </div >
                 );
                 break;
@@ -708,8 +708,6 @@ ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
 });
 
 
-
-
 export function checkInksToGroup() {
     // console.log("getting here to inks group");
     if (CurrentUserUtils.SelectedTool === InkTool.Write) {
@@ -793,8 +791,7 @@ export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
  **/
 
 ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boolean) {
-    createInkGroup();
-
+    InkTranscription.Instance?.createInkGroup();
     if (checkResult) {
         return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ?
             Colors.MEDIUM_BLUE : "transparent";
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 7a500ac88..16a523b40 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -255,7 +255,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             const removeSelection = (json: string | undefined) => json?.indexOf("\"storedMarks\"") === -1 ?
                 json?.replace(/"selection":.*/, "") : json?.replace(/"selection":"\"storedMarks\""/, "\"storedMarks\"");
 
-            if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin || effectiveAcl === AclSelfEdit) {
+            if ([AclEdit, AclAdmin, AclSelfEdit].includes(effectiveAcl)) {
                 const accumTags = [] as string[];
                 state.tr.doc.nodesBetween(0, state.doc.content.size, (node: any, pos: number, parent: any) => {
                     if (node.type === schema.nodes.dashField && node.attrs.fieldKey.startsWith("#")) {
@@ -382,11 +382,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
         }
     }
 
-    // creates links between terms in a document and documents which have a matching Id
+    // creates links between terms in a document and published documents (myPublishedDocs) that have titles starting with an '@'
     hyperlinkTerm = (tr: any, target: Doc, newAutoLinks: Set<Doc>) => {
         const editorView = this._editorView;
         if (editorView && (editorView as any).docView && !Doc.AreProtosEqual(target, this.rootDoc)) {
-            const autoLinkTerm = StrCast(target.title).replace(/^@/, "");
+            const autoLinkTerm = StrCast(target.title).replace(/^@/, ""); 
             const flattened1 = this.findInNode(editorView, editorView.state.doc, autoLinkTerm);
             var alink: Doc | undefined;
             flattened1.forEach((flat, i) => {
@@ -495,7 +495,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                 // embed document when dragg marked as embed
             } else if (de.embedKey) {
                 const target = dragData.droppedDocuments[0];
-                target._fitToBox = true;
+                target._fitContentsToBox = true;
                 const node = schema.nodes.dashDoc.create({
                     width: target[WidthSym](),
                     height: target[HeightSym](),
@@ -1411,9 +1411,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             const listNode = this._editorView?.state.doc.nodeAt(clickPos.pos);
             if (olistNode && olistNode.type === this._editorView?.state.schema.nodes.ordered_list && listNode) {
                 if (!highlightOnly) {
-                    if (selectOrderedList || (!collapse && listNode.attrs.visibility)) {
+                    if (selectOrderedList) {
                         this._editorView.dispatch(this._editorView.state.tr.setSelection(new NodeSelection(selectOrderedList ? $olistPos! : listPos!)));
-                    } else if (!listNode.attrs.visibility || downNode === listNode) {
+                    } else {
                         const tr = this._editorView.state.tr.setNodeMarkup(clickPos.pos, listNode.type, { ...listNode.attrs, visibility: !listNode.attrs.visibility });
                         this._editorView.dispatch(tr.setSelection(TextSelection.create(tr.doc, clickPos.pos)));
                     }
@@ -1504,9 +1504,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             this._rules!.EnteringStyle = false;
         }
         e.stopPropagation();
-        for (var i = state.selection.from; i < state.selection.to; i++) {
+        for (var i = state.selection.from; i <= state.selection.to; i++) {
             const node = state.doc.resolve(i);
-            if (node?.marks?.().some(mark => mark.type === schema.marks.user_mark &&
+            if (state.doc.content.size - 1 > i && node?.marks?.().some(mark => mark.type === schema.marks.user_mark &&
                 mark.attrs.userid !== Doc.CurrentUserEmail) &&
                 [AclAugment, AclSelfEdit].includes(GetEffectiveAcl(this.rootDoc))) {
                 e.preventDefault();
@@ -1523,7 +1523,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             case "Tab": e.preventDefault(); break;
             default: if (this._lastTimedMark?.attrs.userid === Doc.CurrentUserEmail) break;
             case " ":
-                this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({}))
+                [AclEdit, AclAdmin, AclSelfEdit].includes(GetEffectiveAcl(this.dataDoc)) && this._editorView!.dispatch(this._editorView!.state.tr.removeStoredMark(schema.marks.user_mark.create({}))
                     .addStoredMark(schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })));
         }
         this.startUndoTypingBatch();
@@ -1557,7 +1557,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             }
         }
     }
-    fitToBox = () => this.props.Document._fitToBox;
+    fitContentsToBox = () => this.props.Document._fitContentsToBox;
     sidebarContentScaling = () => (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
         if (!this.layoutDoc._showSidebar) this.toggleSidebar();
@@ -1632,7 +1632,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
                     ScreenToLocalTransform={this.sidebarScreenToLocal}
                     renderDepth={this.props.renderDepth + 1}
                     setHeight={this.setSidebarHeight}
-                    fitContentsToDoc={this.fitToBox}
+                    fitContentsToBox={this.fitContentsToBox}
                     noSidebar={true}
                     fieldKey={this.layoutDoc.sidebarViewType === "translation" ? `${this.fieldKey}-translation` : `${this.fieldKey}-annotations`} />;
         };
diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
index fb49b0698..c66cb502e 100644
--- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
+++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts
@@ -44,7 +44,7 @@ export function buildKeymap<S extends Schema<any>>(schema: S, props: any, mapKey
     }
 
     const canEdit = (state: any) => {
-        switch (GetEffectiveAcl(props.Document)) {
+        switch (GetEffectiveAcl(props.DataDoc)) {
             case AclAugment: return false;
             case AclSelfEdit:
                 for (var i = state.selection.from; i < state.selection.to; i++) {
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index e32f2826f..1dd6fef9b 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -10,17 +10,16 @@ import { InkTool } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
 import { PrefetchProxy } from "../../../../fields/Proxy";
 import { listSpec } from "../../../../fields/Schema";
-import { ScriptField } from "../../../../fields/ScriptField";
 import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
 import { emptyFunction, returnFalse, returnOne, returnTrue, setupMoveUpEvents } from '../../../../Utils';
 import { Docs } from "../../../documents/Documents";
 import { DocumentType } from "../../../documents/DocumentTypes";
 import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
 import { DocumentManager } from "../../../util/DocumentManager";
-import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { undoBatch, UndoManager } from "../../../util/UndoManager";
 import { CollectionDockingView } from "../../collections/CollectionDockingView";
+import { MarqueeViewBounds } from "../../collections/collectionFreeForm";
 import { CollectionView, CollectionViewType } from "../../collections/CollectionView";
 import { TabDocView } from "../../collections/TabDocView";
 import { ViewBoxBaseComponent } from "../../DocComponent";
@@ -30,12 +29,21 @@ import { CollectionFreeFormDocumentView } from "../CollectionFreeFormDocumentVie
 import { FieldView, FieldViewProps } from '../FieldView';
 import "./PresBox.scss";
 import { PresEffect, PresMovement, PresStatus } from "./PresEnums";
+import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
 
-export class PinProps {
+export interface PinProps {
     audioRange?: boolean;
-    unpin?: boolean;
     setPosition?: boolean;
     hidePresBox?: boolean;
+    pinWithView?: PinViewProps;
+    pinDocView?: boolean;  // whether the current view specs of the document should be saved the pinned document
+    panelWidth?: number;  // panel width and height of the document (used to compute the bounds of the pinned view area)
+    panelHeight?: number
+}
+
+export interface PinViewProps {
+  bounds: MarqueeViewBounds;
+  scale: number;
 }
 
 @observer
@@ -98,12 +106,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @observable private openMovementDropdown: boolean = false;
     @observable private openEffectDropdown: boolean = false;
     @observable private presentTools: boolean = false;
-    // @computed get childDocs() { return DocListCast(this.rootDoc[this.fieldKey]); }
-    // TODO: [AL] add childDocs() and treeMap fields
-    // @computed get childDocs() { return DocListCast(this.rootDoc.presentationaLinearizedDocuments); }
-    //_treeViewMap:Map<number[], Doc>
-    @computed get childDocs() { return this.layoutDoc._viewType === CollectionViewType.Tree ? DocListCast(this.rootDoc.presentationLinearizedDocuments) : DocListCast(this.rootDoc[this.fieldKey]); }
-    @observable _treeViewMap: Map<number, Doc> = new Map<number, Doc>();
+    @computed get isTreeOrStack() {return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(StrCast(this.layoutDoc._viewType) as any) }
+    @computed get isTree() { return this.layoutDoc._viewType === CollectionViewType.Tree;}
+    @computed get presFieldKey() { return StrCast(this.layoutDoc.presFieldKey, "data"); }
+    @computed get childDocs() { return DocListCast(this.rootDoc[this.presFieldKey]); }
+    @observable _treeViewMap: Map<Doc, number> = new Map();
 
     @computed get tagDocs() {
         const tagDocs: Doc[] = [];
@@ -132,12 +139,6 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             Doc.UserDoc().presElement = new PrefetchProxy(Docs.Create.PresElementBoxDocument({
                 title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"
             }));
-            // this script will be called by each presElement to get rendering-specific info that the PresBox knows about but which isn't written to the PresElement
-            // this is a design choice -- we could write this data to the presElements which would require a reaction to keep it up to date, and it would prevent
-            // the preselement docs from being part of multiple presentations since they would all have the same field, or we'd have to keep per-presentation data
-            // stored on each pres element.
-            (this.presElement as Doc).lookupField = ScriptField.MakeFunction("lookupPresBoxField(container, field, data)",
-                { field: "string", data: Doc.name, container: Doc.name });
         }
         this.props.Document.presentationFieldKey = this.fieldKey; // provide info to the presElement script so that it can look up rendering information about the presBox
 
@@ -157,8 +158,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
     @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
 
+    _unmounting = false;
     @action
     componentWillUnmount() {
+        this._unmounting = true;
         document.removeEventListener("keydown", PresBox.keyEventsWrapper, true);
         this._presKeyEventsActive = false;
         this.resetPresentation();
@@ -169,7 +172,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @action
     componentDidMount() {
-        this.rootDoc.presBox = this.rootDoc;
+        this._unmounting = false;
         this.rootDoc._forceRenderEngine = "timeline";
         this.layoutDoc.presStatus = PresStatus.Edit;
         this.layoutDoc._gridGap = 0;
@@ -328,22 +331,40 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     navigateToView = (targetDoc: Doc, activeItem: Doc) => {
         clearTimeout(this._navTimer);
         const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document;
-        bestTarget && runInAction(() => {
-            if (bestTarget.type === DocumentType.PDF || bestTarget.type === DocumentType.WEB || bestTarget.type === DocumentType.RTF || bestTarget._viewType === CollectionViewType.Stacking) {
-                bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
-                bestTarget._scrollTop = activeItem.presPinViewScroll;
-            } else if (bestTarget.type === DocumentType.COMPARISON) {
-                bestTarget._clipWidth = activeItem.presPinClipWidth;
-            } else if (bestTarget.type === DocumentType.VID) {
-                bestTarget._currentTimecode = activeItem.presStartTime;
+        if (bestTarget) console.log(bestTarget.title, bestTarget.type);
+        else console.log("no best target");
+        if (bestTarget)  this._navTimer = PresBox.navigateToDoc(bestTarget, activeItem, false);
+    }
+
+    // navigates to the bestTarget document by making sure it is on screen,
+    // then it applies the view specs stored in activeItem to 
+    @action
+    static navigateToDoc(bestTarget:Doc, activeItem:Doc, jumpToDoc:boolean)  {
+        if (bestTarget.type === DocumentType.PDF || bestTarget.type === DocumentType.WEB || bestTarget.type === DocumentType.RTF || bestTarget._viewType === CollectionViewType.Stacking) {
+            bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
+            bestTarget._scrollTop = activeItem.presPinViewScroll;
+        } else if (bestTarget.type === DocumentType.COMPARISON) {
+            bestTarget._clipWidth = activeItem.presPinClipWidth;
+        } else if ([DocumentType.AUDIO, DocumentType.VID].includes(bestTarget.type as any)) {
+            bestTarget._currentTimecode = activeItem.presStartTime;
+        } else {
+            const contentBounds= Cast(activeItem.contentBounds, listSpec("number"));
+            bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
+            if (contentBounds) {
+                bestTarget._panX = (contentBounds[0] + contentBounds[2])/2;
+                bestTarget._panY = (contentBounds[1] + contentBounds[3])/2;
+                const dv = DocumentManager.Instance.getDocumentView(bestTarget);
+                if (dv) {
+                    bestTarget._viewScale =  Math.min(dv.props.PanelHeight() / (contentBounds[3] - contentBounds[1]),
+                            dv.props.PanelWidth() / (contentBounds[2]- contentBounds[0]));
+                };
             } else {
-                bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
                 bestTarget._panX = activeItem.presPinViewX;
                 bestTarget._panY = activeItem.presPinViewY;
                 bestTarget._viewScale = activeItem.presPinViewScale;
             }
-            this._navTimer = setTimeout(() => bestTarget._viewTransition = undefined, activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 510);
-        });
+        }
+        return setTimeout(() => bestTarget._viewTransition = undefined, activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 510);
     }
 
     /**
@@ -401,15 +422,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             // openInTab(targetDoc);
         } else if (curDoc.presMovement === PresMovement.Pan && targetDoc) {
             LightboxView.SetLightboxDoc(undefined);
-            await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, [srcContext], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right
+            await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext ? [srcContext]:[], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right
         } else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) {
             LightboxView.SetLightboxDoc(undefined);
             //awaiting jump so that new scale can be found, since jumping is async     
-            await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, [srcContext], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right    
+            await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext ? [srcContext]:[], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right    
         }
         // After navigating to the document, if it is added as a presPinView then it will
         // adjust the pan and scale to that of the pinView when it was added.
         if (activeItem.presPinView) {
+            console.log(targetDoc.title);
+            console.log("presPinView in PresBox.tsx:420");
             // if targetDoc is not displayed but one of its aliases is, then we need to modify that alias, not the original target
             this.navigateToView(targetDoc, activeItem);
         }
@@ -621,14 +644,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
      * Called when the user changes the view type
      * Either 'List' (stacking) or 'Slides' (carousel)
      */
-    // @undoBatch
+    @undoBatch
     viewChanged = action((e: React.ChangeEvent) => {
         //@ts-ignore
         const viewType = e.target.selectedOptions[0].value as CollectionViewType;
+        this.layoutDoc.presFieldKey = this.fieldKey+(viewType === CollectionViewType.Tree ?"-linearized":"");
         // pivot field may be set by the user in timeline view (or some other way) -- need to reset it here
         [CollectionViewType.Tree || CollectionViewType.Stacking].includes(viewType) && (this.rootDoc._pivotField = undefined);
         this.rootDoc._viewType = viewType;
-        if ([CollectionViewType.Tree || CollectionViewType.Stacking].includes(viewType)) this.layoutDoc._gridGap = 0;
+        if (this.isTreeOrStack) {
+            this.layoutDoc._gridGap = 0;
+        }
     });
 
     /**
@@ -702,7 +728,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         });
         return true;
     }
-    childLayoutTemplate = () => ![CollectionViewType.Stacking, CollectionViewType.Tree].includes(this.rootDoc._viewType as any) ? undefined : this.presElement;
+    childLayoutTemplate = () => !this.isTreeOrStack ? undefined : this.presElement;
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc);
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
@@ -801,13 +827,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     //regular click
     @action
-    regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean) => {
+    regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, selectPres = true) => {
         this._selectedArray.clear();
         this._selectedArray.set(doc, undefined);
         this._eleArray.splice(0, this._eleArray.length, ref);
         this._dragArray.splice(0, this._dragArray.length, drag);
         focus && this.selectElement(doc);
-        this.selectPres();
+        selectPres && this.selectPres();
     }
 
     modifierSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, cmdClick: boolean, shiftClick: boolean) => {
@@ -976,7 +1002,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     /**
      * Method called for viewing paths which adds a single line with
      * points at the center of each document added.
-     * Design choice: When this is called it sets _fitToBox as true so the
+     * Design choice: When this is called it sets _fitContentsToBox as true so the
      * user can have an overview of all of the documents in the collection.
      * (Design needed for when documents in presentation trail are in another
      * collection)
@@ -1363,7 +1389,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                     const scroll = targetDoc._scrollTop;
                                     activeItem.presPinView = true;
                                     activeItem.presPinViewScroll = scroll;
-                                } else if (targetDoc.type === DocumentType.VID) {
+                                } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
                                     activeItem.presStartTime = targetDoc._currentTimecode;
                                     activeItem.presEndTime = NumCast(targetDoc._currentTimecode) + 0.1;
                                 } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG) {
@@ -1386,7 +1412,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF) {
                                 const scroll = targetDoc._scrollTop;
                                 activeItem.presPinViewScroll = scroll;
-                            } else if (targetDoc.type === DocumentType.VID) {
+                            } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
                                 activeItem.presStartTime = targetDoc._currentTimecode;
                                 activeItem.presStartTime = NumCast(targetDoc._currentTimecode) + 0.1;
                             } else if (targetDoc.type === DocumentType.COMPARISON) {
@@ -1779,16 +1805,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 doc = Docs.Create.FreeformDocument([], { title: input ? input : "Blank slide", _width: 400, _height: 225, x: x, y: y });
                 break;
             case 'title':
-                doc = Docs.Create.FreeformDocument([title, subtitle], { title: input ? input : "Title slide", _width: 400, _height: 225, _fitToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([title, subtitle], { title: input ? input : "Title slide", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             case 'header':
-                doc = Docs.Create.FreeformDocument([header], { title: input ? input : "Section header", _width: 400, _height: 225, _fitToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([header], { title: input ? input : "Section header", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             case 'content':
-                doc = Docs.Create.FreeformDocument([contentTitle, content], { title: input ? input : "Title and content", _width: 400, _height: 225, _fitToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([contentTitle, content], { title: input ? input : "Title and content", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             case 'twoColumns':
-                doc = Docs.Create.FreeformDocument([contentTitle, content1, content2], { title: input ? input : "Title and two columns", _width: 400, _height: 225, _fitToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([contentTitle, content1, content2], { title: input ? input : "Title and two columns", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             default:
                 break;
@@ -2454,27 +2480,27 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         for (let i = 0; i < index.length; i++) {
             indexNum += (index[i] * (10 ** (-i)));
         }
-        this._treeViewMap.set(indexNum, treeViewDoc);
-        this.props.Document.presentationLinearizedDocuments = new List<Doc>(this.sort(this._treeViewMap)); // this is a flat array of Docs
+        if (this._treeViewMap.get(treeViewDoc) !== indexNum) {
+            this._treeViewMap.set(treeViewDoc, indexNum);
+            const sorted = this.sort(this._treeViewMap);
+            const curList = DocListCast(this.dataDoc[this.presFieldKey]);
+            if (sorted.length !== curList.length || sorted.some((doc,ind) => doc !== curList[ind])) {
+                this.dataDoc[this.presFieldKey] = new List<Doc>(sorted); // this is a flat array of Docs
+            }
+        }
         return this.childDocs;
     }
 
     RemFromMap = (treeViewDoc: Doc, index: number[]): Doc[] => {
-        var indexNum = 0;
-        for (let i = 0; i < index.length; i++) {
-            indexNum += (index[i] * (10 ** (-i)));
-        }
-        console.log(String(index), treeViewDoc)
-        this._treeViewMap.delete(indexNum);
-        const overlay = (Doc.UserDoc().myOverlayDocs as Doc);
-        if (!DocListCast(overlay[Doc.LayoutFieldKey(overlay)]).includes(this.rootDoc)) {
-            this.props.Document.presentationLinearizedDocuments = new List<Doc>(this.sort(this._treeViewMap));
+        if (!this._unmounting && this.isTree) {
+            this._treeViewMap.delete(treeViewDoc);
+            this.dataDoc[this.presFieldKey] = new List<Doc>(this.sort(this._treeViewMap));
         }
         return this.childDocs;
     }
 
     // TODO: [AL] implement sort function for an array of numbers (e.g. arr[1,2,4] v arr[1,2,1])
-    sort = (treeViewMap: Map<number, Doc>) => [...treeViewMap.entries()].sort().map(kv => kv[1]);
+    sort = (treeViewMap: Map<Doc, number>) => [...treeViewMap.entries()].sort((a: [Doc, number], b: [Doc, number]) => a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0).map(kv => kv[0]);
 
     render() {
         // calling this method for keyEvents
@@ -2486,7 +2512,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const presEnd: boolean = !this.layoutDoc.presLoop && (this.itemIndex === this.childDocs.length - 1);
         const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0);
         return CurrentUserUtils.OverlayDocs.includes(this.rootDoc) ?
-            <div className="miniPres">
+            <div className="miniPres" onClick={e => e.stopPropagation()}>
                 <div className="presPanelOverlay" style={{ display: "inline-flex", height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}>
                     <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : undefined }}
                         onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, () => this.layoutDoc.presLoop = !this.layoutDoc.presLoop, false, false)}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
@@ -2530,8 +2556,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 removeDocument={returnFalse}
                                 dontRegisterView={true}
                                 focus={this.selectElement}
+                                scriptContext={this}
                                 ScreenToLocalTransform={this.getTransform}
-                                // TODO: [AL] implement AddToMap, RemFromMap functions (outside this)
                                 AddToMap={this.AddToMap}
                                 RemFromMap={this.RemFromMap}
                                 hierarchyIndex={[]}
@@ -2550,12 +2576,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             </div>;
     }
 }
-// this func communicates with PresBoxElement to send information of the doc
-ScriptingGlobals.add(function lookupPresBoxField(presBoxDoc: Doc, field: string, presEleDoc: Doc) {
-    if (field === 'indexInPres') return DocListCast(presBoxDoc._viewType === CollectionViewType.Tree ? presBoxDoc.presentationLinearizedDocuments : presBoxDoc[StrCast(presBoxDoc.presentationFieldKey)]).indexOf(presEleDoc);
-    if (field === 'presCollapsedHeight') return [CollectionViewType.Tree || CollectionViewType.Stacking].includes(presBoxDoc._viewType as any) ? 35 : 31;
-    if (field === 'presStatus') return presBoxDoc.presStatus;
-    if (field === '_itemIndex') return presBoxDoc._itemIndex;
-    if (field === 'presBox') return presBoxDoc;
-    return undefined;
+
+ScriptingGlobals.add(function navigateToDoc(bestTarget:Doc, activeItem:Doc) {
+    const srcContext = Cast(bestTarget.context, Doc, null) ?? Cast(Cast(bestTarget.annotationOn, Doc, null)?.context, Doc, null);
+    const openInTab = (doc: Doc, finished?: () => void) => {CollectionDockingView.AddSplit(doc, "right"); finished?.(); };
+    DocumentManager.Instance.jumpToDocument(bestTarget, true, openInTab, srcContext ? [srcContext]:[], 
+        undefined, undefined, undefined, () => PresBox.navigateToDoc(bestTarget, activeItem, true), undefined, true, NumCast(activeItem.presZoom));
 });
\ No newline at end of file
diff --git a/src/client/views/nodes/trails/PresElementBox.scss b/src/client/views/nodes/trails/PresElementBox.scss
index 789a6d9f3..969f034a8 100644
--- a/src/client/views/nodes/trails/PresElementBox.scss
+++ b/src/client/views/nodes/trails/PresElementBox.scss
@@ -5,155 +5,157 @@ $slide-background: #d5dce2;
 $slide-active: #5B9FDD;
 
 .presItem-container {
-    cursor: grab;
-    display: grid;
-    grid-template-columns: 20px auto;
-    font-family: Roboto;
-    letter-spacing: normal;
-    position: relative;
-    pointer-events: all;
-    width: 100%;
-    height: 100%;
-    font-weight: 400;
-    -webkit-touch-callout: none;
-    -webkit-user-select: none;
-    -khtml-user-select: none;
-    -moz-user-select: none;
-    -ms-user-select: none;
-    user-select: none;
-    align-items: center;
+      cursor: grab;
+      display: flex;
+      grid-template-columns: 20px auto;
+      font-family: Roboto;
+      letter-spacing: normal;
+      position: relative;
+      pointer-events: all;
+      width: 100%;
+      height: 100%;
+      font-weight: 400;
+      -webkit-touch-callout: none;
+      -webkit-user-select: none;
+      -khtml-user-select: none;
+      -moz-user-select: none;
+      -ms-user-select: none;
+      user-select: none;
+      align-items: center;
 
-    .presItem-number {
-        margin-top: 3.5px;
-        font-size: 12px;
-        font-weight: 700;
-        text-align: center;
-        justify-self: center;
-        align-self: flex-start;
-        position: relative;
-        display: inline-block;
-        overflow: hidden;
-    }
+      //     .presItem-number {
+      //         margin-top: 3.5px;
+      //         font-size: 12px;
+      //         font-weight: 700;
+      //         text-align: center;
+      //         justify-self: center;
+      //         align-self: flex-start;
+      //         position: relative;
+      //         display: inline-block;
+      //         overflow: hidden;
+      //     }
 
 }
 
 .presItem-slide {
-    position: relative;
-    background-color: #d5dce2;
-    border-radius: 5px;
-    height: calc(100% - 7px);
-    width: 100%;
-    display: grid;
-    grid-template-rows: 16px 10px auto;
-    grid-template-columns: max-content max-content max-content max-content auto;
-
-    .presItem-name {
-        min-width: 20px;
-        z-index: 300;
-        top: 2px;
-        align-self: center;
-        font-size: 11px;
-        font-family: Roboto;
-        font-weight: 500;
-        position: relative;
-        padding-left: 10px;
-        padding-right: 10px;
-        letter-spacing: normal;
-        width: max-content;
-        text-overflow: ellipsis;
-        overflow: hidden;
-        white-space: pre;
-    }
+      position: relative;
+      height: 100%;
+      width: 100%;
+      border-bottom: .5px solid grey;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      grid-template-rows: 16px 10px auto;
+      grid-template-columns: max-content max-content max-content max-content auto;
 
-    .presItem-docName {
-        min-width: 20px;
-        z-index: 300;
-        align-self: center;
-        font-size: 9px;
-        font-family: Roboto;
-        font-weight: 300;
-        position: relative;
-        padding-left: 10px;
-        padding-right: 10px;
-        letter-spacing: normal;
-        width: max-content;
-        text-overflow: ellipsis;
-        overflow: hidden;
-        white-space: pre;
-        grid-row: 2;
-        grid-column: 1/6;
-    }
+      .presItem-name {
+            display: flex;
+            min-width: 20px;
+            z-index: 300;
+            top: 2px;
+            align-self: center;
+            font-size: 11px;
+            font-family: Roboto;
+            font-weight: 500;
+            position: relative;
+            padding-left: 10px;
+            padding-right: 10px;
+            letter-spacing: normal;
+            width: max-content;
+            text-overflow: ellipsis;
+            overflow: hidden;
+            white-space: pre;
+      }
 
-    .presItem-time {
-        align-self: center;
-        position: relative;
-        padding-right: 10px;
-        top: 1px;
-        font-size: 10;
-        font-weight: 300;
-        font-family: Roboto;
-        z-index: 300;
-        letter-spacing: normal;
-    }
+      .presItem-docName {
+            min-width: 20px;
+            z-index: 300;
+            align-self: center;
+            font-size: 9px;
+            font-family: Roboto;
+            font-weight: 300;
+            position: relative;
+            padding-left: 10px;
+            padding-right: 10px;
+            letter-spacing: normal;
+            width: max-content;
+            text-overflow: ellipsis;
+            overflow: hidden;
+            white-space: pre;
+            grid-row: 2;
+            grid-column: 1/6;
+      }
 
-    .presItem-embedded {
-        overflow: hidden;
-        grid-row: 3;
-        grid-column: 1/8;
-        position: relative;
-        display: flex;
-        width: auto;
-        justify-content: center;
-        margin: auto;
-        margin-bottom: 2px;
-        border-bottom-left-radius: 5px;
-        border-bottom-right-radius: 5px;
-    }
+      .presItem-time {
+            align-self: center;
+            position: relative;
+            padding-right: 10px;
+            top: 1px;
+            font-size: 10;
+            font-weight: 300;
+            font-family: Roboto;
+            z-index: 300;
+            letter-spacing: normal;
+      }
 
-    .presItem-embeddedMask {
-        width: 100%;
-        height: 100%;
-        position: absolute;
-        border-radius: 3px;
-        top: 0;
-        left: 0;
-        z-index: 1;
-        overflow: hidden;
-    }
+      .presItem-embedded {
+            overflow: hidden;
+            grid-row: 3;
+            grid-column: 1/8;
+            position: relative;
+            display: flex;
+            width: auto;
+            justify-content: center;
+            margin: auto;
+            margin-bottom: 2px;
+            border-bottom-left-radius: 5px;
+            border-bottom-right-radius: 5px;
+      }
 
+      .presItem-embeddedMask {
+            width: 100%;
+            height: 100%;
+            position: absolute;
+            border-radius: 3px;
+            top: 0;
+            left: 0;
+            z-index: 1;
+            overflow: hidden;
+      }
 
-    .presItem-slideButtons {
-        display: flex;
-        grid-column: 7;
-        grid-row: 1/3;
-        width: max-content;
-        justify-self: right;
-        justify-content: flex-end;
 
-        .slideButton {
-            cursor: pointer;
-            position: relative;
-            border-radius: 100px;
-            z-index: 300;
-            width: 18px;
-            height: 18px;
+      .presItem-slideButtons {
             display: flex;
-            font-size: 12px;
-            justify-self: center;
-            align-self: center;
-            background-color: rgba(0, 0, 0, 0.5);
-            color: white;
-            justify-content: center;
-            align-items: center;
-            transition: 0.2s;
-            margin-right: 3px;
-        }
+            grid-column: 7;
+            grid-row: 1/3;
+            width: max-content;
+            justify-self: right;
+            justify-content: flex-end;
 
-        .slideButton:hover {
-            background-color: rgba(0, 0, 0, 1);
-            transform: scale(1.2);
-        }
-    }
+            .slideButton {
+                  cursor: pointer;
+                  position: relative;
+                  border-radius: 100px;
+                  z-index: 300;
+                  width: 18px;
+                  height: 18px;
+                  display: flex;
+                  font-size: 12px;
+                  justify-self: center;
+                  align-self: center;
+                  background-color: rgba(0, 0, 0, 0.5);
+                  color: white;
+                  justify-content: center;
+                  align-items: center;
+                  transition: 0.2s;
+                  margin-right: 3px;
+            }
+
+            .slideButton:hover {
+                  background-color: rgba(0, 0, 0, 1);
+                  transform: scale(1.2);
+            }
+      }
 }
 
 // .presItem-slide:hover {
@@ -192,7 +194,7 @@ $slide-active: #5B9FDD;
 // }
 
 .presItem-slide.active {
-    box-shadow: 0 0 0px 1.5px $dark-blue;
+    box-shadow: 0 0 0px 2.5px $dark-blue;
 }
 
 .presItem-slide.group {
@@ -201,7 +203,7 @@ $slide-active: #5B9FDD;
 
 .presItem-slide.activeGroup {
     border-radius: 5px;
-    box-shadow: 0 0 0px 1.5px $dark-blue;
+    box-shadow: 0 0 0px 2.5px $dark-blue;
 }
 
 .presItem-groupSlideContainer {
@@ -237,38 +239,38 @@ $slide-active: #5B9FDD;
 }
 
 .presItem-multiDrag {
-    font-family: Roboto;
-    font-weight: 600;
-    color: white;
-    text-align: center;
-    justify-content: center;
-    align-content: center;
-    width: 100px;
-    height: 30px;
-    position: absolute;
-    background-color: $dark-blue;
-    z-index: 4000;
-    border-radius: 10px;
-    box-shadow: black 0.4vw 0.4vw 0.8vw;
-    line-height: 30px;
+      font-family: Roboto;
+      font-weight: 600;
+      color: white;
+      text-align: center;
+      justify-content: center;
+      align-content: center;
+      width: 100px;
+      height: 30px;
+      position: absolute;
+      background-color: $dark-blue;
+      z-index: 4000;
+      border-radius: 10px;
+      box-shadow: black 0.4vw 0.4vw 0.8vw;
+      line-height: 30px;
 }
 
 .presItem-miniSlide {
-    font-weight: 700;
-    font-size: 12;
-    grid-column: 1/8;
-    align-self: center;
-    justify-self: center;
-    background-color: #d5dce2;
-    width: 26px;
-    text-align: center;
-    height: 26px;
-    line-height: 28px;
-    border-radius: 100%;
+      font-weight: 700;
+      font-size: 12;
+      grid-column: 1/8;
+      align-self: center;
+      justify-self: center;
+      background-color: #d5dce2;
+      width: 26px;
+      text-align: center;
+      height: 26px;
+      line-height: 28px;
+      border-radius: 100%;
 }
 
 .presItem-miniSlide.active {
-    box-shadow: 0 0 0px 1.5px $dark-blue;
+      box-shadow: 0 0 0px 1.5px $dark-blue;
 }
 
 .expandButton {
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index bbabcb2de..50df00612 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -2,17 +2,18 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
 import { Tooltip } from "@material-ui/core";
 import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
 import { observer } from "mobx-react";
-import { DataSym, Doc, DocListCast, Opt } from "../../../../fields/Doc";
+import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../../fields/Doc";
 import { Id } from "../../../../fields/FieldSymbols";
 import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
 import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from "../../../../Utils";
-import { DocUtils } from "../../../documents/Documents";
+import { Docs, DocUtils } from "../../../documents/Documents";
 import { DocumentType } from "../../../documents/DocumentTypes";
 import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
 import { DocumentManager } from "../../../util/DocumentManager";
 import { DragManager } from "../../../util/DragManager";
 import { Transform } from "../../../util/Transform";
 import { undoBatch } from "../../../util/UndoManager";
+import { CollectionViewType } from "../../collections/CollectionView";
 import { ViewBoxBaseComponent } from '../../DocComponent';
 import { EditableView } from "../../EditableView";
 import { Colors } from "../../global/globalEnums";
@@ -23,7 +24,7 @@ import { PresBox } from "./PresBox";
 import "./PresElementBox.scss";
 import { PresMovement } from "./PresEnums";
 import React = require("react");
-import { CollectionViewType } from "../../collections/CollectionView";
+import { List } from "../../../../fields/List";
 /**
  * This class models the view a document added to presentation will have in the presentation.
  * It involves some functionality for its buttons and options.
@@ -34,12 +35,10 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     _heightDisposer: IReactionDisposer | undefined;
 
     @observable _dragging = false;
-    // these fields are conditionally computed fields on the layout document that take this document as a parameter
-    @computed get indexInPres() { return Number(this.lookupField("indexInPres")); }  // the index field is where this document is in the presBox display list (since this value is different for each presentation element, the value can't be stored on the layout template which is used by all display elements)
-    @computed get collapsedHeight() { return Number(this.lookupField("presCollapsedHeight")); } // the collapsed height changes depending on the state of the presBox.  We could store this on the presentation element template if it's used by only one presentation - but if it's shared by multiple, then this value must be looked up
-    @computed get presStatus() { return StrCast(this.lookupField("presStatus")); }
-    @computed get itemIndex() { return NumCast(this.lookupField("_itemIndex")); }
-    @computed get presBox() { return Cast(this.lookupField("presBox"), Doc, null); }
+    @computed get indexInPres() { return DocListCast(this.presBox[StrCast(this.presBox.presFieldKey, "data")]).indexOf(this.rootDoc); }  // the index field is where this document is in the presBox display list (since this value is different for each presentation element, the value can't be stored on the layout template which is used by all display elements)
+    @computed get collapsedHeight() { return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(this.presBox._viewType as any) ? 35 : 31; } // the collapsed height changes depending on the state of the presBox.  We could store this on the presentation element template if it's used by only one presentation - but if it's shared by multiple, then this value must be looked up
+    @computed get presStatus() { return this.presBox.presStatus; }
+    @computed get presBox() { return (this.props.DocumentView?.().props.treeViewDoc ?? this.props.ContainingCollectionDoc)!; }
     @computed get targetDoc() { return Cast(this.rootDoc.presentationTargetDoc, Doc, null) || this.rootDoc; }
 
     componentDidMount() {
@@ -77,8 +76,8 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         return !this.rootDoc.presExpandInlineButton || !this.targetDoc ? (null) :
             <div className="presItem-embedded" style={{ height: this.embedHeight(), width: this.embedWidth() }}>
                 <DocumentView
-                    Document={this.targetDoc}
-                    DataDoc={this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
+                    Document={this.rootDoc}
+                    DataDoc={undefined}//this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
                     styleProvider={this.styleProvider}
                     docViewPath={returnEmptyDoclist}
                     rootSelected={returnTrue}
@@ -87,7 +86,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     isContentActive={this.props.isContentActive}
                     addDocTab={returnFalse}
                     pinToPres={returnFalse}
-                    fitContentsToDoc={returnTrue}
+                    fitContentsToBox={returnTrue}
                     PanelWidth={this.embedWidth}
                     PanelHeight={this.embedHeight}
                     ScreenToLocalTransform={Transform.Identity}
@@ -167,11 +166,11 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         e.preventDefault();
         if (element && !(e.ctrlKey || e.metaKey)) {
             if (PresBox.Instance._selectedArray.has(this.rootDoc)) {
-                PresBox.Instance._selectedArray.size === 1 && PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false);
+                PresBox.Instance._selectedArray.size === 1 && PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false);
                 setupMoveUpEvents(this, e, this.startDrag, emptyFunction, emptyFunction);
             } else {
                 setupMoveUpEvents(this, e, ((e: PointerEvent) => {
-                    PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false);
+                    PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false);
                     return this.startDrag(e);
                 }), emptyFunction, emptyFunction);
             }
@@ -197,7 +196,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         dragData.moveDocument = this.props.docViewPath().lastElement()?.props.moveDocument;
         const dragItem: HTMLElement[] = [];
         if (dragArray.length === 1) {
-            const doc = dragArray[0];
+            const doc = this._itemRef.current || dragArray[0];
             doc.className = miniView ? "presItem-miniSlide" : "presItem-slide";
             dragItem.push(doc);
         } else if (dragArray.length >= 1) {
@@ -261,11 +260,12 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @undoBatch
     removeItem = action((e: React.MouseEvent) => {
+      e.stopPropagation();
         this.props.removeDocument?.(this.rootDoc);
         if (PresBox.Instance._selectedArray.has(this.rootDoc)) {
             PresBox.Instance._selectedArray.delete(this.rootDoc);
         }
-        e.stopPropagation();
+        this.removeAllRecordingInOverlay() 
     });
 
     // set the value/title of the individual pres element
@@ -285,196 +285,230 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     @action
     updateView = (targetDoc: Doc, activeItem: Doc) => {
-        if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF) {
-            const scroll = targetDoc._scrollTop;
-            activeItem.presPinViewScroll = scroll;
-        } else if (targetDoc.type === DocumentType.VID) {
-            activeItem.presPinTimecode = targetDoc._currentTimecode;
-        } else if (targetDoc.type === DocumentType.COMPARISON) {
-            const clipWidth = targetDoc._clipWidth;
-            activeItem.presPinClipWidth = clipWidth;
-        } else {
-            const x = targetDoc._panX;
-            const y = targetDoc._panY;
-            const scale = targetDoc._viewScale;
-            activeItem.presPinViewX = x;
-            activeItem.presPinViewY = y;
-            activeItem.presPinViewScale = scale;
+        switch (targetDoc.type) {
+            case DocumentType.PDF: case  DocumentType.WEB: case DocumentType.RTF :
+                const scroll = targetDoc._scrollTop;
+                activeItem.presPinViewScroll = scroll;
+            break;
+            case DocumentType.VID: case DocumentType.AUDIO:
+                activeItem.presStartTime = targetDoc._currentTimecode;
+            break;
+            case  DocumentType.COMPARISON :
+                const clipWidth = targetDoc._clipWidth;
+                activeItem.presPinClipWidth = clipWidth;
+            break;
+            default:
+                const x = targetDoc._panX;
+                const y = targetDoc._panY;
+                const scale = targetDoc._viewScale;
+                activeItem.presPinViewX = x;
+                activeItem.presPinViewY = y;
+                activeItem.presPinViewScale = scale;
         }
     }
 
-    @computed
-    get toolbarWidth(): number {
-        const presBoxDocView = DocumentManager.Instance.getDocumentView(this.presBox);
-        let width: number = NumCast(this.presBox._width);
-        if (presBoxDocView) width = presBoxDocView.props.PanelWidth();
-        if (width === 0) width = 300;
-        return width;
-    }
+      @computed get recordingIsInOverlay() {
+            let isInOverlay = false
+            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
+                  if (doc.slides === this.rootDoc) {
+                        isInOverlay = true
+                        return
+                  }
+            })
+            return isInOverlay
+      }
 
-    @computed get mainItem() {
-        const isSelected: boolean = PresBox.Instance._selectedArray.has(this.rootDoc);
-        const toolbarWidth: number = this.toolbarWidth;
-        const showMore: boolean = this.toolbarWidth >= 300;
-        const miniView: boolean = this.toolbarWidth <= 110;
-        const presBox: Doc = this.presBox; //presBox
-        const presBoxColor: string = StrCast(presBox._backgroundColor);
-        const presColorBool: boolean = presBoxColor ? (presBoxColor !== Colors.WHITE && presBoxColor !== "transparent") : false;
-        const targetDoc: Doc = this.targetDoc;
-        const activeItem: Doc = this.rootDoc;
-        const isGroup: boolean = BoolCast(targetDoc._isGroup);
-        return (
-            <div className={`presItem-container`}
-                key={this.props.Document[Id] + this.indexInPres}
-                ref={this._itemRef}
-                style={{
-                    backgroundColor: presColorBool ? isSelected ? "rgba(250,250,250,0.3)" : "transparent" : isSelected ? Colors.LIGHT_BLUE : "transparent",
-                    opacity: this._dragging ? 0.3 : 1
-                }}
-                onClick={e => {
-                    e.stopPropagation();
-                    e.preventDefault();
-                    PresBox.Instance.modifierSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
-                }}
-                onDoubleClick={action(e => {
-                    if (isGroup) {
-                        this.rootDoc.presExpandGroup = !this.rootDoc.presExpandGroup;
-                    } else {
-                        this.toggleProperties();
-                        PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, true);
-                    }
-                })}
-                onPointerOver={this.onPointerOver}
-                onPointerLeave={this.onPointerLeave}
-                onPointerDown={this.headerDown}
-                onPointerUp={this.headerUp}
-            >
-                {miniView ?
-                    // when width is LESS than 110 px 
-                    <div className={`presItem-miniSlide ${isSelected ? "active" : ""}`} ref={miniView ? this._dragRef : null}>
-                        {`${this.indexInPres + 1}.`}
-                    </div>
-                    :
-                    // when width is MORE than 110 px 
-                    <div className="presItem-number">
-                        {`${this.indexInPres + 1}.`}
-                    </div>}
-                {isGroup ?
-                    <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "activeGroup" : "group"}`}
+      removeAllRecordingInOverlay = () => {
+            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
+                  if (doc.slides === this.rootDoc) {
+                        Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
+                  }
+            })
+      }
+
+      @undoBatch
+      @action
+      hideRecording = (e: React.MouseEvent) => {
+            e.stopPropagation()
+            this.removeAllRecordingInOverlay()
+      }
+
+      @undoBatch
+      @action
+      showRecording = (activeItem: Doc) => {
+            this.removeAllRecordingInOverlay()
+            if (activeItem.recording) {
+                  // if we already have an existing recording
+                  Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, Cast(activeItem.recording, Doc, null));
+
+            }
+      }
+
+      @undoBatch
+      @action
+      startRecording = (activeItem: Doc) => {
+            // Remove every recording that already exists in overlay view
+            DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => {
+                  if (doc.slides !== null) {
+                        Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc);
+                  }
+            })
+
+            if (activeItem.recording) {
+                  // if we already have an existing recording
+                  Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, Cast(activeItem.recording, Doc, null));
+
+            } else {
+                  // if we dont have any recording
+                  const recording = Docs.Create.WebCamDocument("", {
+                        _width: 400, _height: 200,
+                        // hideDocumentButtonBar: true,
+                        hideDecorationTitle: true,
+                        hideOpenButton: true,
+                        // hideDeleteButton: true,
+                        cloneFieldFilter: new List<string>(["system"])
+                  });
+
+                  // attach the recording to the slide, and attach the slide to the recording
+                  recording.slides = activeItem
+                  activeItem.recording = recording
+
+                  // make recording box appear in the bottom right corner of the screen
+                  recording.x = window.innerWidth - recording[WidthSym]() - 20;
+                  recording.y = window.innerHeight - recording[HeightSym]() - 20;
+                  Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, recording);
+            }
+      }
+
+      @computed
+      get toolbarWidth(): number {
+            const presBoxDocView = DocumentManager.Instance.getDocumentView(this.presBox);
+            let width: number = NumCast(this.presBox._width);
+            if (presBoxDocView) width = presBoxDocView.props.PanelWidth();
+            if (width === 0) width = 300;
+            return width;
+      }
+
+      @computed get mainItem() {
+            const isSelected: boolean = PresBox.Instance?._selectedArray.has(this.rootDoc);
+            const toolbarWidth: number = this.toolbarWidth;
+            const showMore: boolean = this.toolbarWidth >= 300;
+            const miniView: boolean = this.toolbarWidth <= 110;
+            const presBox: Doc = this.presBox; //presBox
+            const presBoxColor: string = StrCast(presBox._backgroundColor);
+            const presColorBool: boolean = presBoxColor ? (presBoxColor !== Colors.WHITE && presBoxColor !== "transparent") : false;
+            const targetDoc: Doc = this.targetDoc;
+            const activeItem: Doc = this.rootDoc;
+            return (
+                  <div className={`presItem-container`}
+                        key={this.props.Document[Id] + this.indexInPres}
+                        ref={this._itemRef}
                         style={{
-                            backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
-                            boxShadow: presBoxColor && presBoxColor !== "white" && presBoxColor !== "transparent" ? isSelected ? "0 0 0px 1.5px" + presBoxColor : undefined : undefined
-                        }}>
-                        <div className="presItem-name" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105, cursor: isSelected ? 'text' : 'grab' }}>
-                            <EditableView
-                                ref={this._titleRef}
-                                editing={!isSelected ? false : undefined}
-                                contents={activeItem.title}
-                                overflow={'ellipsis'}
-                                GetValue={() => StrCast(activeItem.title)}
-                                SetValue={this.onSetValue}
-                            />
-                        </div>
-                        <div className={"presItem-slideButtons"}>
-                            <Tooltip title={<><div className="dash-tooltip">{"Update view"}</div></>}>
-                                <div className="slideButton"
-                                    onClick={() => this.updateView(targetDoc, activeItem)}
-                                    style={{ fontWeight: 700, display: activeItem.presPinView ? "flex" : "none" }}>V</div>
-                            </Tooltip>
-                            {this.indexInPres === 0 ? (null) : <Tooltip title={<><div className="dash-tooltip">{activeItem.groupWithUp ? "Ungroup" : "Group with up"}</div></>}>
-                                <div className="slideButton"
-                                    onClick={() => activeItem.groupWithUp = !activeItem.groupWithUp}
-                                    style={{
-                                        zIndex: 1000 - this.indexInPres,
-                                        fontWeight: 700,
-                                        backgroundColor: activeItem.groupWithUp ? presColorBool ? presBoxColor : Colors.MEDIUM_BLUE : undefined,
-                                        height: activeItem.groupWithUp ? 53 : 18,
-                                        transform: activeItem.groupWithUp ? "translate(0, -17px)" : undefined
-                                    }}>
-                                    <div style={{ transform: activeItem.groupWithUp ? "rotate(180deg) translate(0, -17.5px)" : "rotate(0deg)" }}>
-                                        <FontAwesomeIcon icon={"arrow-up"} onPointerDown={e => e.stopPropagation()} />
-                                    </div>
-                                </div>
-                            </Tooltip>}
-                            <Tooltip title={<><div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? "Minimize" : "Expand"}</div></>}><div className={"slideButton"} onClick={e => { e.stopPropagation(); this.presExpandDocumentClick(); }}>
-                                <FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? "eye-slash" : "eye"} onPointerDown={e => e.stopPropagation()} />
-                            </div></Tooltip>
-                            <Tooltip title={<><div className="dash-tooltip">{"Remove from presentation"}</div></>}><div
-                                className={"slideButton"}
-                                onClick={this.removeItem}>
-                                <FontAwesomeIcon icon={"trash"} onPointerDown={e => e.stopPropagation()} />
-                            </div></Tooltip>
-                            <div className="group"></div>
-                        </div>
-                        <div className="presItem-groupSlideContainer" style={{ top: 28, height: 'calc(100% - 28px)' }}>
-                            {this.rootDoc.presExpandGroup ? this.renderGroupSlides : (null)}
-                        </div>
-                        <div className="presItem-docName" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105 }}>{activeItem.presPinView ? (<><i>View of </i> {targetDoc.title}</>) : targetDoc.title}</div>
-                        <div className="expandButton"
-                            onClick={e => {
-                                if (isGroup) {
-                                    this.rootDoc.presExpandGroup = !this.rootDoc.presExpandGroup;
-                                }
-                            }}
-                        >
-                            <FontAwesomeIcon icon={"caret-down"} style={{ transform: this.rootDoc.presExpandGroup ? "rotate(180deg)" : "rotate(0deg)" }} />
-                        </div>
-                    </div>
-                    : (null)}
-                {miniView || isGroup ? (null) : <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "active" : ""}`}
-                    style={{
-                        backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
-                        boxShadow: presBoxColor && presBoxColor !== "white" && presBoxColor !== "transparent" ? isSelected ? "0 0 0px 1.5px" + presBoxColor : undefined : undefined
-                    }}>
-                    <div className="presItem-name" style={{ maxWidth: showMore ? (toolbarWidth - (this.presBox._viewType === CollectionViewType.Stacking ? 195 : 220)) : toolbarWidth - (this.presBox._viewType === CollectionViewType.Stacking ? 105 : 145), cursor: isSelected ? 'text' : 'grab' }}>
-                        <EditableView
-                            ref={this._titleRef}
-                            editing={!isSelected ? false : undefined}
-                            contents={activeItem.title}
-                            overflow={'ellipsis'}
-                            GetValue={() => StrCast(activeItem.title)}
-                            SetValue={this.onSetValue}
-                        />
-                    </div>
-                    <Tooltip title={<><div className="dash-tooltip">{"Movement speed"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.transition}</div></Tooltip>
-                    <Tooltip title={<><div className="dash-tooltip">{"Duration"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.duration}</div></Tooltip>
-                    <div className={"presItem-slideButtons"}>
-                        <Tooltip title={<><div className="dash-tooltip">{"Update view"}</div></>}>
-                            <div className="slideButton"
-                                onClick={() => this.updateView(targetDoc, activeItem)}
-                                style={{ fontWeight: 700, display: activeItem.presPinView ? "flex" : "none" }}>V</div>
-                        </Tooltip>
-                        {this.indexInPres === 0 ? (null) : <Tooltip title={<><div className="dash-tooltip">{activeItem.groupWithUp ? "Ungroup" : "Group with up"}</div></>}>
-                            <div className="slideButton"
-                                onClick={() => activeItem.groupWithUp = !activeItem.groupWithUp}
-                                style={{
-                                    zIndex: 1000 - this.indexInPres,
-                                    fontWeight: 700,
-                                    backgroundColor: activeItem.groupWithUp ? presColorBool ? presBoxColor : Colors.MEDIUM_BLUE : undefined,
-                                    height: activeItem.groupWithUp ? 53 : 18,
-                                    transform: activeItem.groupWithUp ? "translate(0, -17px)" : undefined
-                                }}>
-                                <div style={{ transform: activeItem.groupWithUp ? "rotate(180deg) translate(0, -17.5px)" : "rotate(0deg)" }}>
-                                    <FontAwesomeIcon icon={"arrow-up"} onPointerDown={e => e.stopPropagation()} />
-                                </div>
-                            </div>
-                        </Tooltip>}
-                        <Tooltip title={<><div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? "Minimize" : "Expand"}</div></>}><div className={"slideButton"} onClick={e => { e.stopPropagation(); this.presExpandDocumentClick(); }}>
-                            <FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? "eye-slash" : "eye"} onPointerDown={e => e.stopPropagation()} />
-                        </div></Tooltip>
-                        <Tooltip title={<><div className="dash-tooltip">{"Remove from presentation"}</div></>}><div
-                            className={"slideButton"}
-                            onClick={this.removeItem}>
-                            <FontAwesomeIcon icon={"trash"} onPointerDown={e => e.stopPropagation()} />
-                        </div></Tooltip>
-                    </div>
-                    <div className="presItem-docName" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105 }}>{activeItem.presPinView ? (<><i>View of </i> {targetDoc.title}</>) : targetDoc.title}</div>
-                    {this.renderEmbeddedInline}
-                </div>}
-            </div >);
-    }
+                              backgroundColor: presColorBool ? isSelected ? "rgba(250,250,250,0.3)" : "transparent" : isSelected ? Colors.LIGHT_BLUE : "transparent",
+                              opacity: this._dragging ? 0.3 : 1
+                        }}
+                        onClick={e => {
+                              e.stopPropagation();
+                              e.preventDefault();
+                              PresBox.Instance.modifierSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
+                              this.showRecording(activeItem);
+                        }}
+                        onDoubleClick={action(e => {
+                              this.toggleProperties();
+                              PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, true);
+                        })}
+                        onPointerOver={this.onPointerOver}
+                        onPointerLeave={this.onPointerLeave}
+                        onPointerDown={this.headerDown}
+                        onPointerUp={this.headerUp}
+                  >
+                        {/* {miniView ?
+                              // when width is LESS than 110 px 
+                              <div className={`presItem-miniSlide ${isSelected ? "active" : ""}`} ref={miniView ? this._dragRef : null}>
+                                    {`${this.indexInPres + 1}.`}
+                              </div>
+                              :
+                              // when width is MORE than 110 px 
+                              <div className="presItem-number">
+                                    {`${this.indexInPres + 1}.`}
+                              </div>} */}
+                        {/* <div className="presItem-number">
+                              {`${this.indexInPres + 1}.`}
+                        </div> */}
+                        {miniView ? (null) : <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "active" : ""}`}
+                              style={{
+                                    backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
+                                    boxShadow: presBoxColor && presBoxColor !== "white" && presBoxColor !== "transparent" ? isSelected ? "0 0 0px 1.5px" + presBoxColor : undefined : undefined
+                              }}>
+                              <div className="presItem-name" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105, cursor: isSelected ? 'text' : 'grab' }}>
+                                    <div>{`${this.indexInPres + 1}.  `}</div>
+                                    <EditableView
+                                          ref={this._titleRef}
+                                          editing={!isSelected ? false : undefined}
+                                          contents={activeItem.title}
+                                          overflow={'ellipsis'}
+                                          GetValue={() => StrCast(activeItem.title)}
+                                          SetValue={this.onSetValue}
+                                    />
+                              </div>
+                              {/* <Tooltip title={<><div className="dash-tooltip">{"Movement speed"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.transition}</div></Tooltip> */}
+                              {/* <Tooltip title={<><div className="dash-tooltip">{"Duration"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.duration}</div></Tooltip> */}
+                              <div className={"presItem-slideButtons"}>
+                                    <Tooltip title={<><div className="dash-tooltip">{"Update view"}</div></>}>
+                                          <div className="slideButton"
+                                                onClick={() => this.updateView(targetDoc, activeItem)}
+                                                style={{ fontWeight: 700, display: activeItem.presPinView ? "flex" : "none" }}>V</div>
+                                    </Tooltip>
 
-    render() {
-        return !(this.rootDoc instanceof Doc) || this.targetDoc instanceof Promise ? (null) : this.mainItem;
-    }
+                                    {this.recordingIsInOverlay ?
+                                          <Tooltip title={<><div className="dash-tooltip">{"Hide Recording"}</div></>}>
+                                                <div className="slideButton"
+                                                      onClick={this.hideRecording}
+                                                      style={{ fontWeight: 700 }}>
+                                                      <FontAwesomeIcon icon={"video-slash"} onPointerDown={e => e.stopPropagation()} />
+                                                </div>
+                                          </Tooltip> :
+                                          <Tooltip title={<><div className="dash-tooltip">{"Start recording"}</div></>}>
+                                                <div className="slideButton"
+                                                      onClick={() => this.startRecording(activeItem)}
+                                                      style={{ fontWeight: 700 }}>
+                                                      <FontAwesomeIcon icon={"video"} onPointerDown={e => e.stopPropagation()} />
+                                                </div>
+                                          </Tooltip>
+                                    }
+
+
+                                    {/* {this.indexInPres === 0 ? (null) : <Tooltip title={<><div className="dash-tooltip">{activeItem.groupWithUp ? "Ungroup" : "Group with up"}</div></>}>
+                                          <div className="slideButton"
+                                                onClick={() => activeItem.groupWithUp = !activeItem.groupWithUp}
+                                                style={{
+                                                      zIndex: 1000 - this.indexInPres,
+                                                      fontWeight: 700,
+                                                      backgroundColor: activeItem.groupWithUp ? presColorBool ? presBoxColor : Colors.MEDIUM_BLUE : undefined,
+                                                      height: activeItem.groupWithUp ? 53 : 18,
+                                                      transform: activeItem.groupWithUp ? "translate(0, -17px)" : undefined
+                                                }}>
+                                                <div style={{ transform: activeItem.groupWithUp ? "rotate(180deg) translate(0, -17.5px)" : "rotate(0deg)" }}>
+                                                      <FontAwesomeIcon icon={"arrow-up"} onPointerDown={e => e.stopPropagation()} />
+                                                </div>
+                                          </div>
+                                    </Tooltip>} */}
+                                <Tooltip title={<><div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? "Minimize" : "Expand"}</div></>}><div className={"slideButton"} onClick={e => { e.stopPropagation(); this.presExpandDocumentClick(); }}>
+                                    <FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? "eye-slash" : "eye"} onPointerDown={e => e.stopPropagation()} />
+                                </div></Tooltip>
+                                    <Tooltip title={<><div className="dash-tooltip">{"Remove from presentation"}</div></>}><div
+                                          className={"slideButton"}
+                                          onClick={this.removeItem}>
+                                          <FontAwesomeIcon icon={"trash"} onPointerDown={e => e.stopPropagation()} />
+                                    </div></Tooltip>
+                              </div>
+                              {/* <div className="presItem-docName" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105 }}>{activeItem.presPinView ? (<><i>View of </i> {targetDoc.title}</>) : targetDoc.title}</div> */}
+                              {this.renderEmbeddedInline}
+                        </div>}
+                  </div >);
+      }
+
+      render() {
+            return !(this.rootDoc instanceof Doc) || this.targetDoc instanceof Promise ? (null) : this.mainItem;
+      }
 }
\ No newline at end of file
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index f706be6c6..0ded1bb3c 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -274,6 +274,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
         }
     }
 
+    @observable private _scrollTimer: any;
+
     onScroll = (e: React.UIEvent<HTMLElement>) => {
         if (this._mainCont.current && !this._forcedScroll) {
             this._ignoreScroll = true; // the pdf scrolled, so we need to tell the Doc to scroll but we don't want the doc to then try to set the PDF scroll pos (which would interfere with the smooth scroll animation)
@@ -281,6 +283,11 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 this.props.layoutDoc._scrollTop = this._mainCont.current.scrollTop;
             }
             this._ignoreScroll = false;
+            if (this._scrollTimer) clearTimeout(this._scrollTimer); // wait until a scrolling pause, then create an anchor to audio
+            this._scrollTimer = setTimeout(() => {
+                DocUtils.MakeLinkToActiveAudio(() => this.props.DocumentView?.().ComponentView?.getAnchor!()!, false);
+                this._scrollTimer = undefined;
+            }, 200);
         }
     }
 
diff --git a/src/client/views/topbar/TopBar.scss b/src/client/views/topbar/TopBar.scss
index 923f1892e..c5b340514 100644
--- a/src/client/views/topbar/TopBar.scss
+++ b/src/client/views/topbar/TopBar.scss
@@ -1,29 +1,44 @@
 @import "../global/globalCssVariables";
 
+
 .topbar-container {
-    display: flex;
-    flex-direction: column;
-    width: 100%;
-    position: relative;
-    font-size: 10px;
-    line-height: 1;
-    overflow-y: auto;
-    overflow-x: visible;
-    background: $dark-gray;
-    overflow: visible;
-    z-index: 1000;
-
-    .topbar-bar {
-        height: $topbar-height;
-        display: grid;
-        grid-auto-columns: 33.3% 33.3% 33.3%;
-        align-items: center;
-        background-color: $dark-gray;
-
-        .topBar-icon {
+      display: flex;
+      flex-direction: column;
+      font-size: 10px;
+      line-height: 1;
+      overflow-y: auto;
+      overflow-x: visible;
+      background: $dark-gray;
+      overflow: visible;
+      z-index: 1000;
+      height: $topbar-height;
+      background-color: $dark-gray;
+
+      .topbar-inner-container {
+            display: flex;
+            flex-direction: row;
+            position: relative;
+            display: grid;
+            grid-auto-columns: 33.3% 33.3% 33.3%;
+            align-items: center;
+
+            // &:first-child {
+            //       height: 20px;
+            // }
+      }
+
+      .topbar-button-text {
+            color: $white;
+            padding: 10px;
+            size: 15;
+
+            &:hover {
+                  font-weight: 500;
+            }
+      }
+
+      .topbar-button-icon {
             cursor: pointer;
-            font-size: 12.5px;
-            font-family: 'Roboto';
             width: fit-content;
             display: flex;
             justify-content: center;
@@ -31,21 +46,22 @@
             align-items: center;
             justify-self: center;
             align-self: center;
-            border-radius: $standard-border-radius;
             padding: 5px;
             transition: linear 0.2s;
-            color: $black;
-            background-color: $light-gray;
+            color: $white;
 
             &:hover {
-                background-color: darken($color: $light-gray, $amount: 20);
+                  background-color: darken($color: $light-gray, $amount: 20);
             }
-        }
+      }
 
+      .topbar-title {
+            color: $white;
+            font-size: 17;
+            font-weight: 500;
+      }
 
-
-
-        .topbar-center {
+      .topbar-center {
             grid-column: 2;
             display: inline-flex;
             justify-content: center;
@@ -53,42 +69,23 @@
             gap: 5px;
 
             .topbar-dashboards {
-                display: flex;
-                flex-direction: row;
-                gap: 5px;
-            }
-
-            .topbar-lozenge-dashboard {
-                display: flex;
-
-
-
-                .topbar-dashSelect {
-                    border: none;
-                    background-color: $dark-gray;
-                    color: $white;
-                    font-family: 'Roboto';
-                    font-size: 17;
-                    font-weight: 500;
-
-                    &:hover {
-                        cursor: pointer;
-                    }
-                }
+                  display: flex;
+                  flex-direction: row;
+                  gap: 5px;
             }
-        }
+      }
 
 
-        .topbar-right {
+      .topbar-right {
             grid-column: 3;
             position: relative;
             display: flex;
             justify-content: flex-end;
             gap: 5px;
             margin-right: 5px;
-        }
+      }
 
-        .topbar-left {
+      .topbar-left {
             grid-column: 1;
             color: black;
             font-family: 'Roboto';
@@ -98,123 +95,122 @@
             gap: 5px;
 
             .topBar-icon:hover {
-                background-color: $close-red;
+                  background-color: $close-red;
             }
 
             .topbar-lozenge-user,
             .topbar-lozenge {
-                height: 23;
-                font-size: 12;
-                color: white;
-                font-family: 'Roboto';
-                font-weight: 400;
-                padding: 4px;
-                align-self: center;
-                margin-left: 7px;
-                display: flex;
-                align-items: center;
-
-                .topbar-dashSelect {
-                    border: none;
-                    background-color: transparent;
-                    color: black;
-                    font-family: 'Roboto';
-                    font-size: 17;
-                    font-weight: 500;
-
-                    &:hover {
-                        cursor: pointer;
-                    }
-                }
+                  height: 23;
+                  font-size: 12;
+                  color: white;
+                  font-family: 'Roboto';
+                  font-weight: 400;
+                  padding: 4px;
+                  align-self: center;
+                  margin-left: 7px;
+                  display: flex;
+                  align-items: center;
+
+                  .topbar-dashSelect {
+                        border: none;
+                        background-color: transparent;
+                        color: black;
+                        font-family: 'Roboto';
+                        font-size: 17;
+                        font-weight: 500;
+
+                        &:hover {
+                              cursor: pointer;
+                        }
+                  }
             }
 
             .topbar-logoff {
-                border-radius: 3px;
-                background: olivedrab;
-                color: white;
-                display: none;
-                margin-left: 5px;
-                padding: 1px 2px 1px 2px;
-                cursor: pointer;
+                  border-radius: 3px;
+                  background: olivedrab;
+                  color: white;
+                  display: none;
+                  margin-left: 5px;
+                  padding: 1px 2px 1px 2px;
+                  cursor: pointer;
             }
 
             .topbar-logoff {
-                background: red;
+                  background: red;
             }
 
             .topbar-lozenge-user:hover {
-                .topbar-logoff {
-                    display: inline-block;
-                }
+                  .topbar-logoff {
+                        display: inline-block;
+                  }
             }
-        }
+      }
 
-        .topbar-barChild {
+      .topbar-barChild {
 
             &.topbar-collection {
-                flex: 0 1 auto;
-                margin-left: 2px;
-                margin-right: 2px
+                  flex: 0 1 auto;
+                  margin-left: 2px;
+                  margin-right: 2px
             }
 
             &.topbar-input {
-                margin: 5px;
-                border-radius: 20px;
-                border: $dark-gray;
-                display: block;
-                width: 130px;
-                -webkit-transition: width 0.4s;
-                transition: width 0.4s;
-                /* align-self: stretch; */
-                outline: none;
-
-                &:focus {
-                    width: 500px;
-                    outline: none;
-                }
+                  margin: 5px;
+                  border-radius: 20px;
+                  border: $dark-gray;
+                  display: block;
+                  width: 130px;
+                  -webkit-transition: width 0.4s;
+                  transition: width 0.4s;
+                  /* align-self: stretch; */
+                  outline: none;
+
+                  &:focus {
+                        width: 500px;
+                        outline: none;
+                  }
             }
 
             &.topbar-filter {
-                align-self: stretch;
+                  align-self: stretch;
 
-                button {
-                    transform: none;
-
-                    &:hover {
+                  button {
                         transform: none;
-                    }
-                }
+
+                        &:hover {
+                              transform: none;
+                        }
+                  }
             }
 
             &.topbar-submit {
-                margin-left: 2px;
-                margin-right: 2px
+                  margin-left: 2px;
+                  margin-right: 2px
             }
 
             &.topbar-close {
-                color: $white;
-                max-height: $topbar-height;
+                  color: $white;
+                  max-height: $topbar-height;
             }
-        }
-    }
+      }
 }
 
 .topbar-results {
-    display: flex;
-    flex-direction: column;
-    top: 300px;
-    display: flex;
-    flex-direction: column;
-    height: 100%;
-    overflow: visible;
-
-    .no-result {
-        width: 500px;
-        background: $light-gray;
-        padding: 10px;
-        height: 50px;
-        text-transform: uppercase;
-        text-align: left;
-        font-weight: bold;
-    }
+      display: flex;
+      flex-direction: column;
+      top: 300px;
+      display: flex;
+      flex-direction: column;
+      height: 100%;
+      overflow: visible;
+
+      .no-result {
+            width: 500px;
+            background: $light-gray;
+            padding: 10px;
+            height: 50px;
+            text-transform: uppercase;
+            text-align: left;
+            font-weight: bold;
+      }
 }
\ No newline at end of file
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index be248ab92..0db3950a2 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -3,13 +3,18 @@ import { Tooltip } from "@material-ui/core";
 import { action } from "mobx";
 import { observer } from "mobx-react";
 import * as React from 'react';
-import { Doc, DocListCast } from '../../../fields/Doc';
+import { AclAdmin, Doc, DocListCast } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
-import { StrCast } from '../../../fields/Types';
+import { Cast, StrCast } from '../../../fields/Types';
+import { GetEffectiveAcl } from "../../../fields/util";
 import { Utils } from '../../../Utils';
 import { CurrentUserUtils } from "../../util/CurrentUserUtils";
+import { DocumentManager } from "../../util/DocumentManager";
+import { SelectionManager } from "../../util/SelectionManager";
 import { SettingsManager } from "../../util/SettingsManager";
+import { SharingManager } from "../../util/SharingManager";
 import { undoBatch, UndoManager } from "../../util/UndoManager";
+import { ContextMenu } from "../ContextMenu";
 import { Borders, Colors } from "../global/globalEnums";
 import { MainView } from "../MainView";
 import "./TopBar.scss";
@@ -20,64 +25,63 @@ import "./TopBar.scss";
  */
 @observer
 export class TopBar extends React.Component {
-    render() {
-        const myDashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
-        return (
-            //TODO:glr Add support for light / dark mode
-            <div style={{ pointerEvents: "all" }} className="topbar-container">
-                <div className="topbar-bar" style={{ background: Colors.DARK_GRAY, borderBottom: Borders.STANDARD }}>
-                    <div className="topbar-left">
-                        <div className="topbar-lozenge-user">
-                            {`${Doc.CurrentUserEmail}`}
+      navigateToHome = () => {
+            CurrentUserUtils.CaptureDashboardThumbnail()?.then(() => {
+                Doc.UserDoc().activePage = "home";
+                CurrentUserUtils.closeActiveDashboard(); // bcz: if we do this, we need some other way to keep track, for user convenience, of the last dashboard in use
+            });
+      }
+      render() {
+            const activeDashboard = Cast(Doc.UserDoc().activeDashboard, Doc, null)
+            return (
+                  //TODO:glr Add support for light / dark mode
+                  <div style={{ pointerEvents: "all", background: Colors.DARK_GRAY, borderBottom: Borders.STANDARD }} className="topbar-container">
+                        <div className="topbar-inner-container">
+                              <div className="topbar-left">
+                                    {activeDashboard ? <div className="topbar-button-text" onClick={e => {
+                                    ContextMenu.Instance.addItem({ description: "Logout", event: () => window.location.assign(Utils.prepend("/logout")), icon: "edit" });
+                                    ContextMenu.Instance.displayMenu(e.clientX +5, e.clientY + 10);
+                                    }}>{Doc.CurrentUserEmail}</div> : (null)}
+                              </div>
+                              <div className="topbar-center" >
+                                    <div className="topbar-title" onClick={() => SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(activeDashboard)!, false)}>
+                                          {activeDashboard ? StrCast(activeDashboard.title) : "Dash"}
+                                    </div>
+                                    <div className="topbar-button-icon" onClick={e => {
+                                        const dashView = DocumentManager.Instance.getDocumentView(activeDashboard);
+                                        ContextMenu.Instance.addItem({ description: "Open Dashboard View", event: this.navigateToHome, icon: "edit" });
+                                        ContextMenu.Instance.addItem({ description: "Snapshot Dashboard", event: async () => {
+                                            const batch = UndoManager.StartBatch("snapshot");
+                                            await CurrentUserUtils.snapshotDashboard(Doc.UserDoc());
+                                            batch.end();
+                                        }, icon: "edit" });
+                                        dashView?.showContextMenu(e.clientX+20, e.clientY+30);
+                                    }}>
+                                    <FontAwesomeIcon color="white" size="lg" icon="bars" />
+                                    </div>
+                                    <Tooltip title={<div className="dash-tooltip">Browsing mode for directly navigating to documents</div>} placement="bottom">
+                                        <div className="topbar-icon" style={{ background: MainView.Instance._exploreMode ? Colors.LIGHT_BLUE : undefined }} onClick={action(() => MainView.Instance._exploreMode = !MainView.Instance._exploreMode)}>
+                                            <FontAwesomeIcon color={MainView.Instance._exploreMode ? "red":"white"} icon="eye"  size="lg" />
+                                        </div>
+                                    </Tooltip>
+                              </div>
+                              <div className="topbar-right" >
+                                    <div className="topbar-button-text" onClick={() => {SharingManager.Instance.open(undefined, activeDashboard)}}>
+                                        {/* TODO: if this is my dashboard, display share
+                                        if this is a shared dashboard, display "view original or view annotated" */}
+                                        { Doc.GetProto(CurrentUserUtils.ActiveDashboard)?.author ===  Doc.CurrentUserEmail ?  "Share": "view original" }
+                                    </div>
+                                    <div className="topbar-button-icon" onClick={() => window.open(
+                                          "https://brown-dash.github.io/Dash-Documentation/", "_blank")}>
+                                          <FontAwesomeIcon icon="question-circle" />
+                                    </div>
+                                    <div className="topbar-button-icon" onClick={() => SettingsManager.Instance.open()}>
+                                          <FontAwesomeIcon icon="cog" />
+                                    </div>
+                              </div>
                         </div>
-                        <div className="topbar-icon" onClick={() => window.location.assign(Utils.prepend("/logout"))}>
-                            {"Log out"}
-                        </div>
-                    </div>
-                    <div className="topbar-center" >
-                        <div className="topbar-lozenge-dashboard">
-                            <select className="topbar-dashSelect" onChange={undoBatch(e => CurrentUserUtils.openDashboard(Doc.UserDoc(), myDashboards[Number(e.target.value)]))}
-                                value={myDashboards.indexOf(CurrentUserUtils.ActiveDashboard)}
-                                style={{ color: Colors.WHITE }}>
-                                {myDashboards.map((dash, i) => <option key={dash[Id]} value={i}> {StrCast(dash.title)} </option>)}
-                            </select>
-                        </div>
-                        <div className="topbar-dashboards">
-                            <Tooltip title={<div className="dash-tooltip">Create a new dashboard </div>} placement="bottom"><div className="topbar-icon" onClick={async () => {
-                                const batch = UndoManager.StartBatch("new dash");
-                                await CurrentUserUtils.createNewDashboard(Doc.UserDoc());
-                                batch.end();
-                            }}>
-                                {"New"}<FontAwesomeIcon icon="plus" />
-                            </div>
-                            </Tooltip>
-                            <Tooltip title={<div className="dash-tooltip">Work on a copy of the dashboard layout</div>} placement="bottom">
-                                <div className="topbar-icon" onClick={async () => {
-                                    const batch = UndoManager.StartBatch("snapshot");
-                                    await CurrentUserUtils.snapshotDashboard(Doc.UserDoc());
-                                    batch.end();
-                                }}>
-                                    {"Snapshot"}<FontAwesomeIcon icon="camera" />
-                                </div>
-                            </Tooltip>
-                            <Tooltip title={<div className="dash-tooltip">Browsing mode for directly navigating to documents</div>} placement="bottom">
-                                <div className="topbar-icon" style={{ background: MainView.Instance._exploreMode ? Colors.LIGHT_BLUE : undefined }} onClick={action(() => MainView.Instance._exploreMode = !MainView.Instance._exploreMode)}>
-                                    {"Explore"}<FontAwesomeIcon icon="map" />
-                                </div>
-                            </Tooltip>
-                        </div>
-                    </div>
-                    <div className="topbar-right" >
-                        <div className="topbar-icon" onClick={() => window.open(
-                            "https://brown-dash.github.io/Dash-Documentation/", "_blank")}>
-                            {"Help"}<FontAwesomeIcon icon="question-circle" />
-                        </div>
-                        <div className="topbar-icon" onClick={() => SettingsManager.Instance.open()}>
-                            {"Settings"}<FontAwesomeIcon icon="cog" />
-                        </div>
-                    </div>
-                </div>
-            </div >
-        );
-    }
+                  </div>
+                        
+            );
+      }
 }
\ No newline at end of file
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 6abc27b23..7b72787d1 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -1436,6 +1436,7 @@ ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) { return Doc.cop
 ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) { return Doc.delegateDragFactory(dragFactory); });
 ScriptingGlobals.add(function copyField(field: any) { return Field.Copy(field); });
 ScriptingGlobals.add(function docList(field: any) { return DocListCast(field); });
+ScriptingGlobals.add(function addDocToList(doc: Doc, field: string, added:Doc) { return Doc.AddDocToList(doc,field, added); });
 ScriptingGlobals.add(function setInPlace(doc: any, field: any, value: any) { return Doc.SetInPlace(doc, field, value, false); });
 ScriptingGlobals.add(function sameDocs(doc1: any, doc2: any) { return Doc.AreProtosEqual(doc1, doc2); });
 ScriptingGlobals.add(function undo() { SelectionManager.DeselectAll(); return UndoManager.Undo(); });
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
index 7e2aa5681..bf40a0d7b 100644
--- a/src/fields/Types.ts
+++ b/src/fields/Types.ts
@@ -76,6 +76,10 @@ export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal
     return defaultVal === null ? undefined : defaultVal;
 }
 
+export function DocCast(field: FieldResult, defaultVal?: Doc) {
+    return Cast(field, Doc, null) ?? defaultVal;
+}
+
 export function NumCast(field: FieldResult, defaultVal: number | null = 0) {
     return Cast(field, "number", defaultVal);
 }
diff --git a/src/fields/URLField.ts b/src/fields/URLField.ts
index 1d4bbaed0..3e7542e46 100644
--- a/src/fields/URLField.ts
+++ b/src/fields/URLField.ts
@@ -53,6 +53,7 @@ export abstract class URLField extends ObjectField {
 export const nullAudio = "https://actions.google.com/sounds/v1/alarms/beep_short.ogg";
 
 @scriptingGlobal @Deserializable("audio") export class AudioField extends URLField { }
+@scriptingGlobal @Deserializable("recording") export class RecordingField extends URLField { }
 @scriptingGlobal @Deserializable("image") export class ImageField extends URLField { }
 @scriptingGlobal @Deserializable("video") export class VideoField extends URLField { }
 @scriptingGlobal @Deserializable("pdf") export class PdfField extends URLField { }
diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts
index e532becb5..be39e0709 100644
--- a/src/fields/documentSchemas.ts
+++ b/src/fields/documentSchemas.ts
@@ -36,8 +36,8 @@ export const documentSchema = createSchema({
     _nativeHeight: "number",    // "
     _width: "number",           // width of document in its container's coordinate system
     _height: "number",          // "
-    _xPadding: "number",        // pixels of padding on left/right of collectionfreeformview contents when fitToBox is set
-    _yPadding: "number",        // pixels of padding on top/bottom of collectionfreeformview contents when fitToBox is set
+    _xPadding: "number",        // pixels of padding on left/right of collectionfreeformview contents when fitContentsToBox is set
+    _yPadding: "number",        // pixels of padding on top/bottom of collectionfreeformview contents when fitContentsToBox is set
     _xMargin: "number",         // margin added on left/right of most documents to add separation from their container
     _yMargin: "number",         // margin added on top/bottom of most documents to add separation from their container
     _overflow: "string",        // sets overflow behvavior for CollectionFreeForm views
@@ -59,7 +59,7 @@ export const documentSchema = createSchema({
     borderRounding: "string",   // border radius rounding of document
     boxShadow: "string",        // the amount of shadow around the perimeter of a document
     color: "string",            // foreground color of document
-    fitToBox: "boolean",        // whether freeform view contents should be zoomed/panned to fill the area of the document view
+    fitContentsToBox: "boolean",// whether freeform view contents should be zoomed/panned to fill the area of the document view box
     fontSize: "string",
     hidden: "boolean",          // whether a document should not be displayed
     isInkMask: "boolean",       // is the document a mask (ie, sits on top of other documents, has an unbounded width/height that is dark, and content uses 'hard-light' mix-blend-mode to let other documents pop through)
diff --git a/src/mobile/AudioUpload.tsx b/src/mobile/AudioUpload.tsx
index 418464f0e..64baa351c 100644
--- a/src/mobile/AudioUpload.tsx
+++ b/src/mobile/AudioUpload.tsx
@@ -19,7 +19,7 @@ import React = require('react');
 
 @observer
 export class AudioUpload extends React.Component {
-    @observable public _audioCol: Doc = FieldValue(Cast(Docs.Create.FreeformDocument([Cast(Docs.Create.AudioDocument(nullAudio, { title: "mobile audio", _width: 500, _height: 100 }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
+    @observable public _audioCol: Doc = FieldValue(Cast(Docs.Create.FreeformDocument([Cast(Docs.Create.AudioDocument(nullAudio, { title: "mobile audio", _width: 500, _height: 100 }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitContentsToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
 
     /**
      * Handles the onclick functionality for the 'Restart' button
@@ -36,7 +36,7 @@ export class AudioUpload extends React.Component {
                     title: "mobile audio",
                     _width: 500,
                     _height: 100
-                }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
+                }), Doc) as Doc], { title: "mobile audio", _width: 300, _height: 300, _fitContentsToBox: true, boxShadow: "0 0" }), Doc)) as Doc;
     }
 
     /** 
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 2c667ba3e..e7b7056a1 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -17,239 +17,239 @@ import { SolrManager } from "./SearchManager";
 const fs = require('fs');
 
 export enum Directory {
-    parsed_files = "parsed_files",
-    images = "images",
-    videos = "videos",
-    pdfs = "pdfs",
-    text = "text",
-    pdf_thumbnails = "pdf_thumbnails",
-    audio = "audio",
+      parsed_files = "parsed_files",
+      images = "images",
+      videos = "videos",
+      pdfs = "pdfs",
+      text = "text",
+      pdf_thumbnails = "pdf_thumbnails",
+      audio = "audio",
 }
 
 export function serverPathToFile(directory: Directory, filename: string) {
-    return normalize(`${filesDirectory}/${directory}/${filename}`);
+      return normalize(`${filesDirectory}/${directory}/${filename}`);
 }
 
 export function pathToDirectory(directory: Directory) {
-    return normalize(`${filesDirectory}/${directory}`);
+      return normalize(`${filesDirectory}/${directory}`);
 }
 
 export function clientPathToFile(directory: Directory, filename: string) {
-    return `/files/${directory}/${filename}`;
+      return `/files/${directory}/${filename}`;
 }
 
 export default class UploadManager extends ApiManager {
 
-    protected initialize(register: Registration): void {
+      protected initialize(register: Registration): void {
 
-        register({
-            method: Method.POST,
-            subscription: "/uploadFormData",
-            secureHandler: async ({ req, res }) => {
-                const form = new formidable.IncomingForm();
-                form.keepExtensions = true;
-                form.uploadDir = pathToDirectory(Directory.parsed_files);
-                return new Promise<void>(resolve => {
-                    form.parse(req, async (_err, _fields, files) => {
-                        const results: Upload.FileResponse[] = [];
-                        for (const key in files) {
-                            const f = files[key];
-                            if (!Array.isArray(f)) {
-                                const result = await DashUploadUtils.upload(f);
-                                result && !(result.result instanceof Error) && results.push(result);
-                            }
-                        }
-                        _success(res, results);
-                        resolve();
-                    });
-                });
-            }
-        });
+            register({
+                  method: Method.POST,
+                  subscription: "/uploadFormData",
+                  secureHandler: async ({ req, res }) => {
+                        const form = new formidable.IncomingForm();
+                        form.keepExtensions = true;
+                        form.uploadDir = pathToDirectory(Directory.parsed_files);
+                        return new Promise<void>(resolve => {
+                              form.parse(req, async (_err, _fields, files) => {
+                                    const results: Upload.FileResponse[] = [];
+                                    for (const key in files) {
+                                          const f = files[key];
+                                          if (!Array.isArray(f)) {
+                                                const result = await DashUploadUtils.upload(f);
+                                                result && !(result.result instanceof Error) && results.push(result);
+                                          }
+                                    }
+                                    _success(res, results);
+                                    resolve();
+                              });
+                        });
+                  }
+            });
 
-        register({
-            method: Method.POST,
-            subscription: "/uploadYoutubeVideo",
-            secureHandler: async ({ req, res }) => {
-                //req.readableBuffer.head.data
-                return new Promise<void>(async resolve => {
-                    req.addListener("data", async (args) => {
-                        console.log(args);
-                        const payload = String.fromCharCode.apply(String, args);
-                        const videoId = JSON.parse(payload).videoId;
-                        const results: Upload.FileResponse[] = [];
-                        const result = await DashUploadUtils.uploadYoutube(videoId);
-                        result && !(result.result instanceof Error) && results.push(result);
-                        _success(res, results);
-                        resolve();
-                    });
-                });
-            }
-        });
+            register({
+                  method: Method.POST,
+                  subscription: "/uploadYoutubeVideo",
+                  secureHandler: async ({ req, res }) => {
+                        //req.readableBuffer.head.data
+                        return new Promise<void>(async resolve => {
+                              req.addListener("data", async (args) => {
+                                    console.log(args);
+                                    const payload = String.fromCharCode.apply(String, args);
+                                    const videoId = JSON.parse(payload).videoId;
+                                    const results: Upload.FileResponse[] = [];
+                                    const result = await DashUploadUtils.uploadYoutube(videoId);
+                                    result && !(result.result instanceof Error) && results.push(result);
+                                    _success(res, results);
+                                    resolve();
+                              });
+                        });
+                  }
+            });
 
-        register({
-            method: Method.POST,
-            subscription: new RouteSubscriber("youtubeScreenshot"),
-            secureHandler: async ({ req, res }) => {
-                const { id, timecode } = req.body;
-                const convert = (raw: string) => {
-                    const number = Math.floor(Number(raw));
-                    const seconds = number % 60;
-                    const minutes = (number - seconds) / 60;
-                    return `${minutes}m${seconds}s`;
-                };
-                const suffix = timecode ? `&t=${convert(timecode)}` : ``;
-                const targetUrl = `https://www.youtube.com/watch?v=${id}${suffix}`;
-                const buffer = await captureYoutubeScreenshot(targetUrl);
-                if (!buffer) {
-                    return res.send();
-                }
-                const resolvedName = `youtube_capture_${id}_${suffix}.png`;
-                const resolvedPath = serverPathToFile(Directory.images, resolvedName);
-                return new Promise<void>(resolve => {
-                    writeFile(resolvedPath, buffer, async error => {
-                        if (error) {
-                            return res.send();
+            register({
+                  method: Method.POST,
+                  subscription: new RouteSubscriber("youtubeScreenshot"),
+                  secureHandler: async ({ req, res }) => {
+                        const { id, timecode } = req.body;
+                        const convert = (raw: string) => {
+                              const number = Math.floor(Number(raw));
+                              const seconds = number % 60;
+                              const minutes = (number - seconds) / 60;
+                              return `${minutes}m${seconds}s`;
+                        };
+                        const suffix = timecode ? `&t=${convert(timecode)}` : ``;
+                        const targetUrl = `https://www.youtube.com/watch?v=${id}${suffix}`;
+                        const buffer = await captureYoutubeScreenshot(targetUrl);
+                        if (!buffer) {
+                              return res.send();
                         }
-                        await DashUploadUtils.outputResizedImages(() => createReadStream(resolvedPath), resolvedName, pathToDirectory(Directory.images));
-                        res.send({
-                            accessPaths: {
-                                agnostic: DashUploadUtils.getAccessPaths(Directory.images, resolvedName)
-                            }
-                        } as Upload.FileInformation);
-                        resolve();
-                    });
-                });
-            }
-        });
+                        const resolvedName = `youtube_capture_${id}_${suffix}.png`;
+                        const resolvedPath = serverPathToFile(Directory.images, resolvedName);
+                        return new Promise<void>(resolve => {
+                              writeFile(resolvedPath, buffer, async error => {
+                                    if (error) {
+                                          return res.send();
+                                    }
+                                    await DashUploadUtils.outputResizedImages(() => createReadStream(resolvedPath), resolvedName, pathToDirectory(Directory.images));
+                                    res.send({
+                                          accessPaths: {
+                                                agnostic: DashUploadUtils.getAccessPaths(Directory.images, resolvedName)
+                                          }
+                                    } as Upload.FileInformation);
+                                    resolve();
+                              });
+                        });
+                  }
+            });
 
-        register({
-            method: Method.POST,
-            subscription: "/uploadRemoteImage",
-            secureHandler: async ({ req, res }) => {
+            register({
+                  method: Method.POST,
+                  subscription: "/uploadRemoteImage",
+                  secureHandler: async ({ req, res }) => {
 
-                const { sources } = req.body;
-                if (Array.isArray(sources)) {
-                    const results = await Promise.all(sources.map(source => DashUploadUtils.UploadImage(source)));
-                    return res.send(results);
-                }
-                res.send();
-            }
-        });
+                        const { sources } = req.body;
+                        if (Array.isArray(sources)) {
+                              const results = await Promise.all(sources.map(source => DashUploadUtils.UploadImage(source)));
+                              return res.send(results);
+                        }
+                        res.send();
+                  }
+            });
 
-        register({
-            method: Method.POST,
-            subscription: "/uploadDoc",
-            secureHandler: ({ req, res }) => {
+            register({
+                  method: Method.POST,
+                  subscription: "/uploadDoc",
+                  secureHandler: ({ req, res }) => {
 
-                const form = new formidable.IncomingForm();
-                form.keepExtensions = true;
-                // let path = req.body.path;
-                const ids: { [id: string]: string } = {};
-                let remap = true;
-                const getId = (id: string): string => {
-                    if (!remap) return id;
-                    if (id.endsWith("Proto")) return id;
-                    if (id in ids) {
-                        return ids[id];
-                    } else {
-                        return ids[id] = v4();
-                    }
-                };
-                const mapFn = (doc: any) => {
-                    if (doc.id) {
-                        doc.id = getId(doc.id);
-                    }
-                    for (const key in doc.fields) {
-                        if (!doc.fields.hasOwnProperty(key)) { continue; }
-                        const field = doc.fields[key];
-                        if (field === undefined || field === null) { continue; }
+                        const form = new formidable.IncomingForm();
+                        form.keepExtensions = true;
+                        // let path = req.body.path;
+                        const ids: { [id: string]: string } = {};
+                        let remap = true;
+                        const getId = (id: string): string => {
+                              if (!remap) return id;
+                              if (id.endsWith("Proto")) return id;
+                              if (id in ids) {
+                                    return ids[id];
+                              } else {
+                                    return ids[id] = v4();
+                              }
+                        };
+                        const mapFn = (doc: any) => {
+                              if (doc.id) {
+                                    doc.id = getId(doc.id);
+                              }
+                              for (const key in doc.fields) {
+                                    if (!doc.fields.hasOwnProperty(key)) { continue; }
+                                    const field = doc.fields[key];
+                                    if (field === undefined || field === null) { continue; }
 
-                        if (field.__type === "Doc") {
-                            mapFn(field);
-                        } else if (field.__type === "proxy" || field.__type === "prefetch_proxy") {
-                            field.fieldId = getId(field.fieldId);
-                        } else if (field.__type === "script" || field.__type === "computed") {
-                            if (field.captures) {
-                                field.captures.fieldId = getId(field.captures.fieldId);
-                            }
-                        } else if (field.__type === "list") {
-                            mapFn(field);
-                        } else if (typeof field === "string") {
-                            const re = /("(?:dataD|d)ocumentId"\s*:\s*")([\w\-]*)"/g;
-                            doc.fields[key] = (field as any).replace(re, (match: any, p1: string, p2: string) => {
-                                return `${p1}${getId(p2)}"`;
-                            });
-                        } else if (field.__type === "RichTextField") {
-                            const re = /("href"\s*:\s*")(.*?)"/g;
-                            field.Data = field.Data.replace(re, (match: any, p1: string, p2: string) => {
-                                return `${p1}${getId(p2)}"`;
-                            });
-                        }
-                    }
-                };
-                return new Promise<void>(resolve => {
-                    form.parse(req, async (_err, fields, files) => {
-                        remap = fields.remap !== "false";
-                        let id: string = "";
-                        try {
-                            for (const name in files) {
-                                const f = files[name];
-                                const path_2 = Array.isArray(f) ? "" : f.path;
-                                const zip = new AdmZip(path_2);
-                                zip.getEntries().forEach((entry: any) => {
-                                    if (!entry.entryName.startsWith("files/")) return;
-                                    let directory = dirname(entry.entryName) + "/";
-                                    const extension = extname(entry.entryName);
-                                    const base = basename(entry.entryName).split(".")[0];
+                                    if (field.__type === "Doc") {
+                                          mapFn(field);
+                                    } else if (field.__type === "proxy" || field.__type === "prefetch_proxy") {
+                                          field.fieldId = getId(field.fieldId);
+                                    } else if (field.__type === "script" || field.__type === "computed") {
+                                          if (field.captures) {
+                                                field.captures.fieldId = getId(field.captures.fieldId);
+                                          }
+                                    } else if (field.__type === "list") {
+                                          mapFn(field);
+                                    } else if (typeof field === "string") {
+                                          const re = /("(?:dataD|d)ocumentId"\s*:\s*")([\w\-]*)"/g;
+                                          doc.fields[key] = (field as any).replace(re, (match: any, p1: string, p2: string) => {
+                                                return `${p1}${getId(p2)}"`;
+                                          });
+                                    } else if (field.__type === "RichTextField") {
+                                          const re = /("href"\s*:\s*")(.*?)"/g;
+                                          field.Data = field.Data.replace(re, (match: any, p1: string, p2: string) => {
+                                                return `${p1}${getId(p2)}"`;
+                                          });
+                                    }
+                              }
+                        };
+                        return new Promise<void>(resolve => {
+                              form.parse(req, async (_err, fields, files) => {
+                                    remap = fields.remap !== "false";
+                                    let id: string = "";
                                     try {
-                                        zip.extractEntryTo(entry.entryName, publicDirectory, true, false);
-                                        directory = "/" + directory;
+                                          for (const name in files) {
+                                                const f = files[name];
+                                                const path_2 = Array.isArray(f) ? "" : f.path;
+                                                const zip = new AdmZip(path_2);
+                                                zip.getEntries().forEach((entry: any) => {
+                                                      if (!entry.entryName.startsWith("files/")) return;
+                                                      let directory = dirname(entry.entryName) + "/";
+                                                      const extension = extname(entry.entryName);
+                                                      const base = basename(entry.entryName).split(".")[0];
+                                                      try {
+                                                            zip.extractEntryTo(entry.entryName, publicDirectory, true, false);
+                                                            directory = "/" + directory;
 
-                                        createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + "_o" + extension));
-                                        createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + "_s" + extension));
-                                        createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + "_m" + extension));
-                                        createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + "_l" + extension));
-                                    } catch (e) {
-                                        console.log(e);
-                                    }
-                                });
-                                const json = zip.getEntry("doc.json");
-                                try {
-                                    const data = JSON.parse(json.getData().toString("utf8"));
-                                    const datadocs = data.docs;
-                                    id = getId(data.id);
-                                    const docs = Object.keys(datadocs).map(key => datadocs[key]);
-                                    docs.forEach(mapFn);
-                                    await Promise.all(docs.map((doc: any) => new Promise<void>(res => {
-                                        Database.Instance.replace(doc.id, doc, (err, r) => {
-                                            err && console.log(err);
-                                            res();
-                                        }, true);
-                                    })));
-                                } catch (e) { console.log(e); }
-                                unlink(path_2, () => { });
-                            }
-                            SolrManager.update();
-                            res.send(JSON.stringify(id || "error"));
-                        } catch (e) { console.log(e); }
-                        resolve();
-                    });
-                });
-            }
-        });
+                                                            createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + "_o" + extension));
+                                                            createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + "_s" + extension));
+                                                            createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + "_m" + extension));
+                                                            createReadStream(publicDirectory + directory + base + extension).pipe(createWriteStream(publicDirectory + directory + base + "_l" + extension));
+                                                      } catch (e) {
+                                                            console.log(e);
+                                                      }
+                                                });
+                                                const json = zip.getEntry("doc.json");
+                                                try {
+                                                      const data = JSON.parse(json.getData().toString("utf8"));
+                                                      const datadocs = data.docs;
+                                                      id = getId(data.id);
+                                                      const docs = Object.keys(datadocs).map(key => datadocs[key]);
+                                                      docs.forEach(mapFn);
+                                                      await Promise.all(docs.map((doc: any) => new Promise<void>(res => {
+                                                            Database.Instance.replace(doc.id, doc, (err, r) => {
+                                                                  err && console.log(err);
+                                                                  res();
+                                                            }, true);
+                                                      })));
+                                                } catch (e) { console.log(e); }
+                                                unlink(path_2, () => { });
+                                          }
+                                          SolrManager.update();
+                                          res.send(JSON.stringify(id || "error"));
+                                    } catch (e) { console.log(e); }
+                                    resolve();
+                              });
+                        });
+                  }
+            });
 
-        register({
-            method: Method.POST,
-            subscription: "/inspectImage",
-            secureHandler: async ({ req, res }) => {
+            register({
+                  method: Method.POST,
+                  subscription: "/inspectImage",
+                  secureHandler: async ({ req, res }) => {
 
-                const { source } = req.body;
-                if (typeof source === "string") {
-                    return res.send(await DashUploadUtils.InspectImage(source));
-                }
-                res.send({});
-            }
-        });
+                        const { source } = req.body;
+                        if (typeof source === "string") {
+                              return res.send(await DashUploadUtils.InspectImage(source));
+                        }
+                        res.send({});
+                  }
+            });
 
         register({
             method: Method.POST,
@@ -296,17 +296,17 @@ export default class UploadManager extends ApiManager {
                                 .pipe(createWriteStream(path).on("error", e => console.log("Resizing write: " + e)));
                         });
 
-                    }
-                    res.send(clientPathToFile(Directory.images, filename + ext));
-                });
-            }
-        });
+                              }
+                              res.send(clientPathToFile(Directory.images, filename + ext));
+                        });
+                  }
+            });
 
-    }
+      }
 
 }
 function delay(ms: number) {
-    return new Promise(resolve => setTimeout(resolve, ms));
+      return new Promise(resolve => setTimeout(resolve, ms));
 }
 /**
  * On success, returns a buffer containing the bytes of a screenshot
@@ -315,25 +315,25 @@ function delay(ms: number) {
  * On failure, returns undefined.
  */
 async function captureYoutubeScreenshot(targetUrl: string) {
-    // const browser = await launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
-    // const page = await browser.newPage();
-    // // await page.setViewport({ width: 1920, height: 1080 });
+      // const browser = await launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
+      // const page = await browser.newPage();
+      // // await page.setViewport({ width: 1920, height: 1080 });
 
-    // // await page.goto(targetUrl, { waitUntil: 'domcontentloaded' as any });
+      // // await page.goto(targetUrl, { waitUntil: 'domcontentloaded' as any });
 
-    // const videoPlayer = await page.$('.html5-video-player');
-    // videoPlayer && await page.focus("video");
-    // await delay(7000);
-    // const ad = await page.$('.ytp-ad-skip-button-text');
-    // await ad?.click();
-    // await videoPlayer?.click();
-    // await delay(1000);
-    // // hide youtube player controls.
-    // await page.evaluate(() => (document.querySelector('.ytp-chrome-bottom') as HTMLElement).style.display = 'none');
+      // const videoPlayer = await page.$('.html5-video-player');
+      // videoPlayer && await page.focus("video");
+      // await delay(7000);
+      // const ad = await page.$('.ytp-ad-skip-button-text');
+      // await ad?.click();
+      // await videoPlayer?.click();
+      // await delay(1000);
+      // // hide youtube player controls.
+      // await page.evaluate(() => (document.querySelector('.ytp-chrome-bottom') as HTMLElement).style.display = 'none');
 
-    // const buffer = await videoPlayer?.screenshot({ encoding: "binary" });
-    // await browser.close();
+      // const buffer = await videoPlayer?.screenshot({ encoding: "binary" });
+      // await browser.close();
 
-    // return buffer;
-    return null;
+      // return buffer;
+      return null;
 }
\ No newline at end of file
diff --git a/tslint.json b/tslint.json
deleted file mode 100644
index 83255a798..000000000
--- a/tslint.json
+++ /dev/null
@@ -1,32 +0,0 @@
-{
-    "linterOptions": {
-        "exclude": [
-            "./src/client/northstar/**",
-            "**/*.js"
-        ]
-    },
-    "rules": {
-        "no-return-await": true,
-        "no-string-literal": true,
-        "prefer-object-spread": true,
-        "prefer-for-of": true,
-        "no-unnecessary-type-assertion": true,
-        "triple-equals": true,
-        "prefer-const": true,
-        "no-unnecessary-callback-wrapper": true,
-        "class-name": true,
-        "arrow-return-shorthand": true,
-        "semicolon": [
-            true,
-            "always"
-        ],
-        "curly": [
-            true,
-            "ignore-same-line"
-        ],
-        "no-tautology-expression": true,
-        "unnecessary-constructor": true
-    },
-    "defaultSeverity": "warning",
-    "jsRules": {}
-}
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2


From bf47265596c0b1c54a30779164238a8849f7f496 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 20 Jun 2022 20:53:15 -0400
Subject: restored undo/redo buttons

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

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index f5e609411..61cca0421 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -526,7 +526,7 @@ export class MainView extends React.Component {
     }
 
     @computed get docButtons() {
-        return !(this.userDoc.dockedBtns instanceof Doc) ? (null) :
+        return !CurrentUserUtils.MyDockedBtns ? (null) :
             <div className="mainView-docButtons" ref={this._docBtnRef} style={{ height: !CurrentUserUtils.MyDockedBtns.linearViewIsExpanded ? "42px" : undefined }} >
                 <CollectionLinearView
                     Document={CurrentUserUtils.MyDockedBtns}
-- 
cgit v1.2.3-70-g09d2


From 3e22a44461095d95062e3b809b525a0a87342ac3 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 20 Jun 2022 22:18:42 -0400
Subject: more cleanup to get rid of unecessary references to Doc.UserDoc().

---
 src/client/documents/Documents.ts                  |  4 +-
 src/client/util/CurrentUserUtils.ts                | 48 +++++++++++++---------
 src/client/util/DragManager.ts                     |  4 +-
 src/client/util/History.ts                         |  2 +-
 src/client/util/SettingsManager.tsx                | 19 ++++-----
 src/client/util/SnappingManager.ts                 |  4 ++
 src/client/views/DashboardView.tsx                 |  2 +-
 src/client/views/DocumentDecorations.tsx           |  4 +-
 src/client/views/MainView.tsx                      | 21 ++++------
 .../views/collections/CollectionDockingView.tsx    |  4 +-
 src/client/views/collections/CollectionMenu.tsx    |  4 +-
 src/client/views/collections/CollectionView.tsx    |  2 +-
 src/client/views/collections/TabDocView.tsx        |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  4 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |  4 +-
 src/client/views/nodes/DocumentView.tsx            |  2 +-
 src/client/views/nodes/button/FontIconBox.tsx      | 30 +++++++-------
 .../nodes/button/colorDropdown/ColorDropdown.tsx   |  3 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |  4 +-
 src/client/views/nodes/trails/PresBox.tsx          | 11 +----
 src/client/views/topbar/TopBar.tsx                 |  2 +-
 src/mobile/MobileInterface.tsx                     |  2 +-
 22 files changed, 93 insertions(+), 89 deletions(-)

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

diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index e36103938..73b756a8d 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -662,7 +662,7 @@ export namespace Docs {
             const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, "^_");
 
             dataProps["acl-Override"] = "None";
-            dataProps["acl-Public"] = options["acl-Public"] ? options["acl-Public"] : Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            dataProps["acl-Public"] = options["acl-Public"] ? options["acl-Public"] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
 
             dataProps.system = viewProps.system;
             dataProps.isPrototype = true;
@@ -679,7 +679,7 @@ export namespace Docs {
             const dataDoc = Doc.assign(Doc.MakeDelegate(proto, protoId), dataProps, undefined, true);
 
             const viewFirstProps: { [id: string]: any } = {};
-            viewFirstProps["acl-Public"] = options["_acl-Public"] ? options["_acl-Public"] : Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            viewFirstProps["acl-Public"] = options["_acl-Public"] ? options["_acl-Public"] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
             viewFirstProps["acl-Override"] = "None";
             viewFirstProps.author = Doc.CurrentUserEmail;
             const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true);
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 1d5a9fa09..9b5fc5b98 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -181,7 +181,12 @@ export class CurrentUserUtils {
 
     /// Initializes collection of templates for notes and click functions
     static setupDocTemplates(doc: Doc, field="myTemplates") {
+        const preEleOpts:DocumentOptions = {
+            title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"
+        };
+        this.AssignOpts(DocCast(doc.presElement), preEleOpts) ?? (doc.presElement= Docs.Create.PresElementBoxDocument(preEleOpts));
         const templates = [
+            DocCast(doc.presElement),
             CurrentUserUtils.setupNoteTemplates(doc),
             CurrentUserUtils.setupClickEditorTemplates(doc)
         ];
@@ -323,8 +328,7 @@ export class CurrentUserUtils {
             { toolTip: "Tap or drag to create a scripting box",   title: "Script", icon: "terminal",    dragFactory: doc.emptyScript as Doc,      scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))',  onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, funcs: { hidden: 'IsNoviceMode()'}},
             { toolTip: "Tap or drag to create a mobile view",     title: "Phone", icon: "mobile",       dragFactory: doc.activeMobileMenu as Doc, scripts: {onClick: 'openOnRight(Doc.UserDoc().activeMobileMenu)',     onDragStart: 'this.dragFactory'},                             funcs: {hidden: 'IsNoviceMode()'} },
             { toolTip: "Toggle a Calculator REPL",                title: "repl", icon: "calculator",                                              scripts: {onClick: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' } },
-         // { toolTip: "Tap or drag to create a presentation",    title: "Trails", icon: "pres-trail",  dragFactory: doc.emptyPresentation as Doc,scripts: {onClick: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', onDragStart: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`}, funcs: {hidden: 'IsNoviceMode()'} },
-             ];
+          ];
     }
 
     /// Initalizes the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools
@@ -533,7 +537,7 @@ export class CurrentUserUtils {
     static setupDashboards(doc: Doc, field:string) {
         var myDashboards = DocCast(doc[field]);
 
-        const newDashboard = `createNewDashboard(Doc.UserDoc())`;
+        const newDashboard = `createNewDashboard()`;
         const reqdBtnOpts:DocumentOptions = { _forceActive: true,  _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true,
              title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", system: true };
         const reqdBtnScript = {onClick: newDashboard,}
@@ -830,6 +834,11 @@ export class CurrentUserUtils {
         const reqdOpts = { title: "overlay documents", backgroundColor: "#aca3a6", system: true };
         return this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.FreeformDocument([], reqdOpts));
     }
+
+    static setupPublished(doc:Doc, field = "myPublishedDocs") {
+        const reqdOpts = { title: "published docs", backgroundColor: "#aca3a6", system: true };
+        return this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
+    }
     
     /// The database of all links on all documents
     static async setupLinkDocs(doc: Doc, linkDatabaseId: string) {
@@ -958,7 +967,6 @@ export class CurrentUserUtils {
         Doc.noviceMode ?? (Doc.noviceMode = true);
         doc._raiseWhenDragged ?? (doc._raiseWhenDragged = true);
         doc._showLabel ?? (doc._showLabel = true);
-        doc._showMenuLabel ?? (doc._showMenuLabel = true);
         doc.textAlign ?? (doc.textAlign = "left");
         doc.activeInkColor ?? (doc.activeInkColor = "rgb(0, 0, 0)");;
         doc.activeInkWidth ?? (doc.activeInkWidth = 1);
@@ -975,17 +983,17 @@ export class CurrentUserUtils {
         doc.savedFilters ?? (doc.savedFilters = new List<Doc>());
         doc.filterDocCount = 0;
         doc.freezeChildren = "remove|add";
-        doc.myPublishedDocs ?? (doc.myPublishedDocs = new List<Doc>());
         doc.myHeaderBar ?? (doc.myHeaderBar = Docs.Create.MulticolumnDocument([], { title: "header bar", system: true })); // drop down panel at top of dashboard for stashing documents
         await this.setupLinkDocs(doc, linkDatabaseId);
         await this.setupSharedDocs(doc, sharingDocumentId);  // sets up the right sidebar collection for mobile upload documents and sharing
         this.setupDefaultIconTemplates(doc);  // creates a set of icon templates triggered by the document deoration icon
-        this.setupDocTemplates(doc); // sets up the template menu of templates
         this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile
         this.setupOverlays(doc);  // sets up the overlay panel where documents and other widgets can be added to float over the rest of the dashboard
+        this.setupPublished(doc);  // sets up the list doc of all docs that have been published (meaning that they can be auto-linked by typing their title into another text box)
         this.setupContextMenuButtons(doc); // set up the row of buttons at the top of the dashboard that change depending on what is selected
         this.setupDockedButtons(doc);  // the bottom bar of font icons
         this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left
+        this.setupDocTemplates(doc); // sets up the template menu of templates
         doc.globalScriptDatabase ?? ( doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument());
 
         setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500);
@@ -1037,7 +1045,8 @@ export class CurrentUserUtils {
 
     public static _urlState: HistoryUtil.DocUrl;
 
-    public static openDashboard = (userDoc: Doc, doc: Doc, fromHistory = false) => {
+    public static openDashboard = (doc: Doc, fromHistory = false) => {
+        const userDoc =  Doc.UserDoc();
         CurrentUserUtils.MainDocId = doc[Id];
         if (!DocListCast(CurrentUserUtils.MyDashboards.data).includes(doc)) {
             Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", doc);
@@ -1045,7 +1054,7 @@ export class CurrentUserUtils {
 
         if (doc) {  // this has the side-effect of setting the main container since we're assigning the active/guest dashboard
             !("presentationView" in doc) && (doc.presentationView = new List<Doc>([Docs.Create.TreeDocument([], { title: "Presentation" })]));
-            userDoc ? (userDoc.activeDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc);
+            userDoc ? (CurrentUserUtils.ActiveDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc);
         }
         const state = CurrentUserUtils._urlState;
         if (state.sharing === true && !userDoc) {
@@ -1138,11 +1147,11 @@ export class CurrentUserUtils {
 
     }
 
-    public static async snapshotDashboard(userDoc: Doc) {
+    public static async snapshotDashboard() {
         if (CurrentUserUtils.ActiveDashboard) {
             const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard);
             Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", copy);
-            CurrentUserUtils.openDashboard(userDoc, copy);
+            CurrentUserUtils.openDashboard(copy);
         }
     }
 
@@ -1150,9 +1159,9 @@ export class CurrentUserUtils {
         CurrentUserUtils.ActiveDashboard = undefined;
     }
 
-    public static createNewDashboard = async (userDoc: Doc, id?: string) => {
-        const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true);
-        const dashboards = await Cast(userDoc.myDashboards, Doc) as Doc;
+    public static createNewDashboard = (id?: string) => {
+        const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true);
+        const dashboards = CurrentUserUtils.MyDashboards;
         const dashboardCount = DocListCast(dashboards.data).length + 1;
         const freeformOptions: DocumentOptions = {
             x: 0,
@@ -1172,10 +1181,10 @@ export class CurrentUserUtils {
         dashboardDoc.data = new List<Doc>(dashboardTabs);
         dashboardDoc["pane-count"] = 1;
 
-        userDoc.activePresentation = presentation;
+        CurrentUserUtils.ActivePresentation = presentation;
 
         Doc.AddDocToList(dashboards, "data", dashboardDoc);
-      //   CurrentUserUtils.openDashboard(userDoc, dashboardDoc);
+      //   CurrentUserUtils.openDashboard(dashboardDoc);
     }
 
     public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) {
@@ -1209,6 +1218,7 @@ export class CurrentUserUtils {
     public static get MyContextMenuBtns() { return DocCast(Doc.UserDoc().myContextMenuBtns); }
     public static get MyRecentlyClosed() { return DocCast(Doc.UserDoc().myRecentlyClosed); }
     public static get MyOverlayDocs() { return DocCast(Doc.UserDoc().myOverlayDocs); }
+    public static get MyPublishedDocs() { return DocCast(Doc.UserDoc().myPublishedDocs); }
     public static get ActiveDashboard() { return DocCast(Doc.UserDoc().activeDashboard); }
     public static set ActiveDashboard(val:Doc|undefined) { Doc.UserDoc().activeDashboard = val; }
     public static get ActivePresentation() { return DocCast(Doc.UserDoc().activePresentation); }
@@ -1230,8 +1240,8 @@ ScriptingGlobals.add(function openDragFactory(dragFactory: Doc) {
 ScriptingGlobals.add(function MySharedDocs() { return CurrentUserUtils.MySharedDocs; }, "document containing all shared Docs");
 ScriptingGlobals.add(function IsNoviceMode() { return Doc.noviceMode; }, "is Dash in novice mode");
 ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering");
-ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); }, "creates a snapshot copy of a dashboard");
-ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); }, "creates a new dashboard when called");
+ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(); }, "creates a snapshot copy of a dashboard");
+ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(); }, "creates a new dashboard when called");
 ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance.createNewPresentation(); }, "creates a new presentation when called");
 ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called");
 ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, "returns all the links to the document or its annotations", "(doc: any)");
@@ -1240,7 +1250,7 @@ ScriptingGlobals.add(function shareDashboard(dashboard: Doc) { SharingManager.In
 ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) {
     const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data);
     if (dashboards && dashboards.length > 1) {
-        if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboards.find(doc => doc !== dashboard)!);
+        if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(dashboards.find(doc => doc !== dashboard)!);
         Doc.RemoveDocFromList(CurrentUserUtils.MyDashboards, "data", dashboard);
     }
 },
@@ -1248,7 +1258,7 @@ ScriptingGlobals.add(async function removeDashboard(dashboard: Doc) {
 ScriptingGlobals.add(function addToDashboards(dashboard: Doc) {
     const dashboardAlias = Doc.MakeAlias(dashboard);
     Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", dashboardAlias);
-    CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboardAlias);
+    CurrentUserUtils.openDashboard(dashboardAlias);
 },
     "adds Dashboard to set of Dashboards");
 
diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts
index dbfba8992..2b62945af 100644
--- a/src/client/util/DragManager.ts
+++ b/src/client/util/DragManager.ts
@@ -6,7 +6,7 @@ import { PrefetchProxy } from "../../fields/Proxy";
 import { listSpec } from "../../fields/Schema";
 import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
 import { ScriptField } from "../../fields/ScriptField";
-import { Cast, NumCast, ScriptCast, StrCast } from "../../fields/Types";
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../fields/Types";
 import { emptyFunction, Utils } from "../../Utils";
 import { Docs, DocUtils } from "../documents/Documents";
 import * as globalCssVariables from "../views/global/globalCssVariables.scss";
@@ -72,6 +72,8 @@ export namespace DragManager {
     export let StartWindowDrag: Opt<((e: { pageX: number, pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => void)>;
     export let CompleteWindowDrag: Opt<(aborted: boolean) => void>;
 
+    export function GetRaiseWhenDragged() { return BoolCast(Doc.UserDoc()._raiseWhenDragged); }
+    export function SetRaiseWhenDragged(val:boolean) { Doc.UserDoc()._raiseWhenDragged = val }
     export function Root() {
         const root = document.getElementById("root");
         if (!root) {
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 632348306..7dcff9c56 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -197,7 +197,7 @@ export namespace HistoryUtil {
             await Promise.all(Object.keys(init).map(id => initDoc(id, init[id])));
         }
         if (field instanceof Doc) {
-            CurrentUserUtils.openDashboard(Doc.UserDoc(), field, true);
+            CurrentUserUtils.openDashboard(field, true);
         }
     }
 
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 382274462..22e33ab1e 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -10,7 +10,9 @@ import { GoogleAuthenticationManager } from "../apis/GoogleAuthenticationManager
 import { DocServer } from "../DocServer";
 import { Networking } from "../Network";
 import { MainViewModal } from "../views/MainViewModal";
+import { FontIconBox } from "../views/nodes/button/FontIconBox";
 import { CurrentUserUtils } from "./CurrentUserUtils";
+import { DragManager } from "./DragManager";
 import { GroupManager } from "./GroupManager";
 import "./SettingsManager.scss";
 import { undoBatch } from "./UndoManager";
@@ -155,19 +157,14 @@ export class SettingsManager extends React.Component<{}> {
                 <div className="preferences-check">Show full toolbar</div>
             </div>
             <div>
-                <input type="checkbox" onChange={e => Doc.UserDoc()._raiseWhenDragged = !Doc.UserDoc()._raiseWhenDragged}
-                    checked={BoolCast(Doc.UserDoc()._raiseWhenDragged)} />
+                <input type="checkbox" onChange={e => DragManager.SetRaiseWhenDragged(!DragManager.GetRaiseWhenDragged())}
+                    checked={DragManager.GetRaiseWhenDragged()} />
                 <div className="preferences-check">Raise on drag</div>
             </div>
             <div>
-                <input type="checkbox" onChange={e => Doc.UserDoc()._showLabel = !Doc.UserDoc()._showLabel}
-                    checked={BoolCast(Doc.UserDoc()._showLabel)} />
-                <div className="preferences-check">Show tool button labels</div>
-            </div>
-            <div>
-                <input type="checkbox" onChange={e => Doc.UserDoc()._showMenuLabel = !Doc.UserDoc()._showMenuLabel}
-                    checked={BoolCast(Doc.UserDoc()._showMenuLabel)} />
-                <div className="preferences-check">Show menu button labels</div>
+                <input type="checkbox" onChange={e => FontIconBox.SetShowLabels(!FontIconBox.GetShowLabels())}
+                    checked={FontIconBox.GetShowLabels()} />
+                <div className="preferences-check">Show button labels</div>
             </div>
         </div>;
     }
@@ -271,7 +268,7 @@ export class SettingsManager extends React.Component<{}> {
                 <div className="tab-column-content">
                     <button onClick={() => GroupManager.Instance?.open()}>Manage groups</button>
                     <div className="default-acl">
-                        <input className="acl-check" type="checkbox" checked={BoolCast(Doc.UserDoc()?.defaultAclPrivate)}
+                        <input className="acl-check" type="checkbox" checked={BoolCast(Doc.defaultAclPrivate)}
                             onChange={action(() => Doc.defaultAclPrivate = !Doc.defaultAclPrivate)} />
                         <div className="acl-text">Default access private</div>
                     </div>
diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts
index 069f81d38..057843c68 100644
--- a/src/client/util/SnappingManager.ts
+++ b/src/client/util/SnappingManager.ts
@@ -1,5 +1,6 @@
 import { observable, action, runInAction } from "mobx";
 import { computedFn } from "mobx-utils";
+import { Doc } from "../../fields/Doc";
 
 export namespace SnappingManager {
 
@@ -30,6 +31,9 @@ export namespace SnappingManager {
     export function SetIsDragging(dragging: boolean) { runInAction(() => manager.IsDragging = dragging); }
     export function GetIsDragging() { return manager.IsDragging; }
 
+    export function SetShowSnapLines(show: boolean) { runInAction(() => Doc.UserDoc().showSnapLines = show); }
+    export function GetShowSnapLines() { return Doc.UserDoc().showSnapLines; }
+
     /// bcz; argh!! TODO;   These do not belong here, but there were include order problems with leaving them in util.ts
     // need to investigate further what caused the mobx update problems and move to a better location.
     const getCachedGroupByNameCache = computedFn(function (name: string) { return manager.cachedGroups.includes(name); }, true);
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 9a0f25fe3..a126218c4 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -28,7 +28,7 @@ export class DashboardView extends React.Component {
 
       newDashboard = async () => {
             const batch = UndoManager.StartBatch("new dash");
-            await CurrentUserUtils.createNewDashboard(Doc.UserDoc());
+            await CurrentUserUtils.createNewDashboard();
             batch.end();
       }
 
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 18cf785b9..4247501bb 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -87,10 +87,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number, P
                         if (titleFieldKey === "title") {
                               d.dataDoc["title-custom"] = !this._accumulatedTitle.startsWith("-");
                               if (StrCast(d.rootDoc.title).startsWith("@") && !this._accumulatedTitle.startsWith("@")) {
-                                    Doc.RemoveDocFromList(Doc.UserDoc(), "myPublishedDocs", d.rootDoc);
+                                    Doc.RemoveDocFromList(CurrentUserUtils.MyPublishedDocs, undefined, d.rootDoc);
                               }
                               if (!StrCast(d.rootDoc.title).startsWith("@") && this._accumulatedTitle.startsWith("@")) {
-                                    Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", d.rootDoc);
+                                    Doc.AddDocToList(CurrentUserUtils.MyPublishedDocs, undefined, d.rootDoc);
                               }
                         }
                         //@ts-ignore
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 61cca0421..6ee8065f5 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -225,26 +225,21 @@ export class MainView extends React.Component {
         // Load the user's active dashboard, or create a new one if initial session after signup
         const received = CurrentUserUtils.MainDocId;
         if (received && !this.userDoc) {
-            reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()), { fireImmediately: true });
+            reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(), { fireImmediately: true });
         } else {
             PromiseValue(this.userDoc.activeDashboard).then(dash => {
-                if (dash instanceof Doc) CurrentUserUtils.openDashboard(this.userDoc, dash);
-                else CurrentUserUtils.createNewDashboard(this.userDoc);
+                if (dash instanceof Doc) CurrentUserUtils.openDashboard(dash);
+                else CurrentUserUtils.createNewDashboard();
             });
         }
     }
 
     @action
     createNewPresentation = async () => {
-        if (!await this.userDoc.myTrails) {
-            this.userDoc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], {
-                title: "TRAILS", childDontRegisterViews: true, _height: 100, _forceActive: true, boxShadow: "0 0", _lockedPosition: true, treeViewOpen: true, system: true
-            }));
-        }
         const pres = Docs.Create.PresDocument({ title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
         CollectionDockingView.AddSplit(pres, "left");
-        this.userDoc.activePresentation = pres;
-        Doc.AddDocToList(this.userDoc.myTrails as Doc, "data", pres);
+        CurrentUserUtils.ActivePresentation = pres;
+        Doc.AddDocToList(CurrentUserUtils.MyTrails, "data", pres);
     }
 
     @action
@@ -353,9 +348,9 @@ export class MainView extends React.Component {
     addDocTabFunc = (doc: Doc, location: string): boolean => {
         const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":");
         const locationParams = locationFields.length > 1 ? locationFields[1] : "";
-        if (doc.dockingConfig) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+        if (doc.dockingConfig) return CurrentUserUtils.openDashboard(doc);
         switch (locationFields[0]) {
-            case "dashboard": return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+            case "dashboard": return CurrentUserUtils.openDashboard(doc);
             case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
             case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
             case "lightbox": return LightboxView.AddDocTab(doc, location);
@@ -563,7 +558,7 @@ export class MainView extends React.Component {
             </div>;
     }
     @computed get snapLines() {
-        return !this.userDoc.showSnapLines ? (null) : <div className="mainView-snapLines">
+        return !SnappingManager.GetShowSnapLines() ? (null) : <div className="mainView-snapLines">
             <svg style={{ width: "100%", height: "100%" }}>
                 {SnappingManager.horizSnapLines().map(l => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
                 {SnappingManager.vertSnapLines().map(l => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 5f36a7a51..19355b8e1 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -121,7 +121,7 @@ export class CollectionDockingView extends CollectionSubView() {
         SelectionManager.DeselectAll();
         const instance = CollectionDockingView.Instance;
         if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") {
-            return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+            return CurrentUserUtils.openDashboard(doc);
         }
         const newItemStackConfig = {
             type: 'stack',
@@ -172,7 +172,7 @@ export class CollectionDockingView extends CollectionSubView() {
     @undoBatch
     @action
     public static AddSplit(document: Doc, pullSide: string, stack?: any, panelName?: string) {
-        if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(Doc.UserDoc(), document);
+        if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(document);
 
         const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document);
         if (tab) {
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 9b1bb5b97..668d82387 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -164,7 +164,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
         //     </button>
         // </Tooltip>;
 
-        // OLD BUTTONS
+        // //OLD BUTTONS
         // return this.getElement(!this.SelectedCollection ? [/*button*/] :
         //     [<CollectionViewBaseChrome key="chrome"
         //         docView={this.SelectedCollection}
@@ -306,7 +306,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
 
     @undoBatch
     viewChanged = (e: React.ChangeEvent) => {
-        const target = this.document !== Doc.UserDoc().sidebar ? this.document : this.document.proto as Doc;
+        const target = this.document !== CurrentUserUtils.MyLeftSidebarPanel ? this.document : this.document.proto as Doc;
         //@ts-ignore
         target._viewType = e.target.selectedOptions[0].value;
     }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 4b5c5e3fb..63616263e 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -205,7 +205,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
                 });
             }
             if (this.Document._viewType === CollectionViewType.Docking) {
-                optionItems.push({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(Doc.UserDoc()), icon: "project-diagram" });
+                optionItems.push({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(), icon: "project-diagram" });
             }
 
             !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "hand-point-right" });
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 70db121d1..62d07b0e4 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -329,7 +329,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":");
         const locationParams = locationFields.length > 1 ? locationFields[1] : "";
         switch (locationFields[0]) {
-            case "dashboard": return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc);
+            case "dashboard": return CurrentUserUtils.openDashboard(doc);
             case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
             case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
             case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index ffe146ae4..99eec7892 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -253,7 +253,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             const nd = [Doc.NativeWidth(layoutDoc), Doc.NativeHeight(layoutDoc)];
             layoutDoc._width = NumCast(layoutDoc._width, 300);
             layoutDoc._height = NumCast(layoutDoc._height, nd[0] && nd[1] ? nd[1] / nd[0] * NumCast(layoutDoc._width) : 300);
-            (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
+            (d._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged(): d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
         }
 
         (docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments);
@@ -1645,7 +1645,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
         const viewctrls = ContextMenu.Instance.findByDescription("UI Controls...");
         const viewCtrlItems = viewctrls && "subitems" in viewctrls ? viewctrls.subitems : [];
-        !Doc.noviceMode ? viewCtrlItems.push({ description: (Doc.UserDoc().showSnapLines ? "Hide" : "Show") + " Snap Lines", event: () => Doc.UserDoc().showSnapLines = !Doc.UserDoc().showSnapLines, icon: "compress-arrows-alt" }) : null;
+        !Doc.noviceMode ? viewCtrlItems.push({ description: (SnappingManager.GetShowSnapLines() ? "Hide" : "Show") + " Snap Lines", event: () => SnappingManager.SetShowSnapLines(!SnappingManager.GetShowSnapLines()), icon: "compress-arrows-alt" }) : null;
         !Doc.noviceMode ? viewCtrlItems.push({ description: (this.Document._useClusters ? "Hide" : "Show") + " Clusters", event: () => this.updateClusters(!this.Document._useClusters), icon: "braille" }) : null;
         !viewctrls && ContextMenu.Instance.addItem({ description: "UI Controls...", subitems: viewCtrlItems, icon: "eye" });
 
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index b62020a04..081a1a924 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -6,7 +6,7 @@ import { InkData, InkField, InkTool } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
 import { RichTextField } from "../../../../fields/RichTextField";
 import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
-import { Cast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
+import { Cast, DocCast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
 import { ImageField } from "../../../../fields/URLField";
 import { GetEffectiveAcl } from "../../../../fields/util";
 import { intersectRect, returnFalse, Utils } from "../../../../Utils";
@@ -156,7 +156,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                     }));
             } else if (e.key === "s" && e.ctrlKey) {
                 e.preventDefault();
-                const slide = Doc.copyDragFactory(Doc.UserDoc().emptySlide as Doc)!;
+                const slide = Doc.copyDragFactory(DocCast(Doc.UserDoc().emptySlide))!;
                 slide.x = x;
                 slide.y = y;
                 FormattedTextBox.SelectOnLoad = slide[Id];
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 4d84a8ad2..89096c948 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -484,7 +484,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             let stopPropagate = true;
             let preventDefault = true;
             const isScriptBox = () => StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name);
-            (this.rootDoc._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
+            (this.rootDoc._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged() : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
             if (this._doubleTap && (this.props.Document.type !== DocumentType.FONTICON || this.onDoubleClickHandler)) {// && !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
                 if (this._timeout) {
                     clearTimeout(this._timeout);
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 3af6a3d51..1b37e729b 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -79,6 +79,9 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         }
     }
 
+    static GetShowLabels() { return BoolCast(Doc.UserDoc()._showLabel); }
+    static SetShowLabels(show:boolean) { Doc.UserDoc()._showLabel = show; }
+
     // Determining UI Specs
     @observable private label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title));
     @observable private icon = StrCast(this.dataDoc.icon, "user") as any;
@@ -111,7 +114,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         // Script for checking the outcome of the toggle
         const checkResult: number = numScript?.script.run({ value: 0, _readOnly_: true }).result || 0;
 
-        const label = !Doc.UserDoc()._showLabel ? (null) :
+        const label = !FontIconBox.GetShowLabels() ? (null) :
             <div className="fontIconBox-label">
                 {this.label}
             </div>;
@@ -212,7 +215,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
                 onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}>
                 <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
-                {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
+                {!this.label || !FontIconBox.GetShowLabels() ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
                 <div
                     className="menuButton-dropdown"
                     style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
@@ -283,7 +286,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
             </div>;
         });
 
-        const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
+        const label = !this.label || !FontIconBox.GetShowLabels() ? (null) :
             <div className="fontIconBox-label" style={{ bottom: 0, position: "absolute", color: color, backgroundColor: backgroundColor }}>
                 {this.label}
             </div>;
@@ -337,7 +340,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
         const curColor = this.colorScript?.script.run({ value: undefined, _readOnly_: true }).result ?? "transparent";
 
-        const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
+        const label = !this.label || !FontIconBox.GetShowLabels() ? (null) :
             <div className="fontIconBox-label" style={{ color, backgroundColor }}>
                 {this.label}
             </div>;
@@ -349,7 +352,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         </div>;
         setTimeout(() => this.colorPicker(curColor)); // cause an update to the color picker rendered in MainView
         return (
-            <div className={`menuButton ${this.type + (Doc.UserDoc()._showLabel ? "Label" : "")} ${this.colorPickerClosed}`}
+            <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? "Label" : "")} ${this.colorPickerClosed}`}
                 style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }}
                 onClick={action(() => this.colorPickerClosed = !this.colorPickerClosed)}
                 onPointerDown={e => e.stopPropagation()}>
@@ -381,7 +384,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
 
         // Button label
-        const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
+        const label = !this.label || !FontIconBox.GetShowLabels() ? (null) :
             <div className="fontIconBox-label" style={{ color, backgroundColor }}>
                 {this.label}
             </div>;
@@ -400,7 +403,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
             );
         } else {
             return (
-                <div className={`menuButton ${this.type + (Doc.UserDoc()._showLabel ? "Label" : "")}`}
+                <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? "Label" : "")}`}
                     style={{ opacity: 1, backgroundColor, color }}>
                     <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                     {label}
@@ -423,7 +426,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 style={{ backgroundColor: "transparent", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}>
                 <div className="menuButton-wrap">
                     <FontAwesomeIcon className={`menuButton-icon-${this.type}`} icon={this.icon} color={"black"} size={"sm"} />
-                    {!this.label || !Doc.UserDoc()._showLabel ? (null) :
+                    {!this.label || !FontIconBox.GetShowLabels() ? (null) :
                         <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>}
                 </div>
             </div>
@@ -450,12 +453,12 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
     render() {
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
-        const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
+        const label = !this.label || !FontIconBox.GetShowLabels() ? (null) :
             <div className="fontIconBox-label" style={{ color, backgroundColor }}>
                 {this.label}
             </div>;
 
-        const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) :
+        const menuLabel = !this.label || !FontIconBox.GetShowLabels()  ? (null) :
             <div className="fontIconBox-label" style={{ color, backgroundColor: "transparent" }}>
                 {this.label}
             </div>;
@@ -497,7 +500,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 break;
             case ButtonType.ToolButton:
                 button = (
-                    <div className={`menuButton ${this.type + (Doc.UserDoc()._showLabel ? "Label" : "")}`} style={{ opacity: 1, backgroundColor, color }}>
+                    <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? "Label" : "")}`} style={{ opacity: 1, backgroundColor, color }}>
                         <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                         {label}
                     </div>
@@ -509,7 +512,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() {
                 break;
             case ButtonType.ClickButton:
                 button = (
-                    <div className={`menuButton ${this.type + (Doc.UserDoc()._showLabel ? "Label" : "")}`} style={{ color, backgroundColor, opacity: 1 }}>
+                    <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? "Label" : "")}`} style={{ color, backgroundColor, opacity: 1 }}>
                         <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />
                         {label}
                     </div>
@@ -675,10 +678,9 @@ ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: b
 ScriptingGlobals.add(function toggleNoAutoLinkAnchor(checkResult?: boolean) {
     const editorView = RichTextMenu.Instance?.TextView?.EditorView;
     if (checkResult) {
-        return (editorView ? RichTextMenu.Instance.noAutoLink : Doc.UserDoc().noAutoLink) ? Colors.MEDIUM_BLUE : "transparent";
+        return (editorView ? RichTextMenu.Instance.noAutoLink : false) ? Colors.MEDIUM_BLUE : "transparent";
     }
     if (editorView) RichTextMenu.Instance?.toggleNoAutoLinkAnchor();
-    else Doc.UserDoc().noAutoLink = Doc.UserDoc().noAutoLink ? true : false;
 });
 
 ScriptingGlobals.add(function toggleBold(checkResult?: boolean) {
diff --git a/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx b/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx
index 235495250..7f414ddbb 100644
--- a/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx
+++ b/src/client/views/nodes/button/colorDropdown/ColorDropdown.tsx
@@ -5,6 +5,7 @@ import { IButtonProps } from '../ButtonInterface';
 import { ColorState, SketchPicker } from 'react-color';
 import { ScriptField } from '../../../../../fields/ScriptField';
 import { Doc } from '../../../../../fields/Doc';
+import { FontIconBox } from '../FontIconBox';
 
 export class ColorDropdown extends Component<IButtonProps> {
     render() {
@@ -31,7 +32,7 @@ export class ColorDropdown extends Component<IButtonProps> {
             disableAlpha={!stroke}
             onChange={func} color={boolResult ? boolResult : "#FFFFFF"}
             presetColors={colorOptions} />;
-        const label = !this.props.label || !Doc.UserDoc()._showLabel ? (null) :
+        const label = !this.props.label || !FontIconBox.GetShowLabels() ? (null) :
             <div className="fontIconBox-label" style={{ color: this.props.color, backgroundColor: this.props.backgroundColor, position: "absolute" }}>
                 {this.props.label}
             </div>;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 90199618b..600730d87 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -355,7 +355,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             var tr = this._editorView.state.tr as any;
             const autoAnch = this._editorView.state.schema.marks.autoLinkAnchor;
             tr = tr.removeMark(0, tr.doc.content.size, autoAnch);
-            DocListCast(Doc.UserDoc().myPublishedDocs).forEach(term => tr = this.hyperlinkTerm(tr, term, newAutoLinks));
+            DocListCast(CurrentUserUtils.MyPublishedDocs.data).forEach(term => tr = this.hyperlinkTerm(tr, term, newAutoLinks));
             tr = tr.setSelection(new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t)));
             this._editorView?.dispatch(tr);
             oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.anchor2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink);
@@ -376,7 +376,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
             if (!(cfield instanceof ComputedField)) {
                 this.dataDoc.title = prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? "..." : "");
                 if (str.startsWith("@") && str.length > 1) {
-                    Doc.AddDocToList(Doc.UserDoc(), "myPublishedDocs", this.rootDoc);
+                    Doc.AddDocToList(CurrentUserUtils.MyPublishedDocs, undefined, this.rootDoc);
                 }
             }
         }
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 0d2cffc2c..9f858539f 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -10,7 +10,7 @@ import { InkTool } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
 import { PrefetchProxy } from "../../../../fields/Proxy";
 import { listSpec } from "../../../../fields/Schema";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types";
+import { BoolCast, Cast, DocCast, NumCast, StrCast } from "../../../../fields/Types";
 import { emptyFunction, returnFalse, returnOne, returnTrue, setupMoveUpEvents } from '../../../../Utils';
 import { Docs } from "../../../documents/Documents";
 import { DocumentType } from "../../../documents/DocumentTypes";
@@ -131,17 +131,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if ((this.targetDoc.type === DocumentType.COL && this.targetDoc._viewType === CollectionViewType.Freeform) || this.targetDoc.type === DocumentType.IMG) return true;
         else return false;
     }
-    @computed get presElement() { return Cast(Doc.UserDoc().presElement, Doc, null); }
     constructor(props: any) {
         super(props);
         if (CurrentUserUtils.ActivePresentation = this.rootDoc) runInAction(() => PresBox.Instance = this);
-        if (!this.presElement) { // create exactly one presElmentBox template to use by any and all presentations.
-            Doc.UserDoc().presElement = new PrefetchProxy(Docs.Create.PresElementBoxDocument({
-                title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"
-            }));
-        }
         this.props.Document.presentationFieldKey = this.fieldKey; // provide info to the presElement script so that it can look up rendering information about the presBox
-
     }
     @computed get selectedDocumentView() {
         if (SelectionManager.Views().length) return SelectionManager.Views()[0];
@@ -728,7 +721,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         });
         return true;
     }
-    childLayoutTemplate = () => !this.isTreeOrStack ? undefined : this.presElement;
+    childLayoutTemplate = () => !this.isTreeOrStack ? undefined : DocCast(Doc.UserDoc().presElement);
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc);
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 1de0c0b10..6a4deca38 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -52,7 +52,7 @@ export class TopBar extends React.Component {
                                         ContextMenu.Instance.addItem({ description: "Open Dashboard View", event: this.navigateToHome, icon: "edit" });
                                         ContextMenu.Instance.addItem({ description: "Snapshot Dashboard", event: async () => {
                                             const batch = UndoManager.StartBatch("snapshot");
-                                            await CurrentUserUtils.snapshotDashboard(Doc.UserDoc());
+                                            await CurrentUserUtils.snapshotDashboard();
                                             batch.end();
                                         }, icon: "edit" });
                                         dashView?.showContextMenu(e.clientX+20, e.clientY+30);
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index fe8100997..bf06faeb9 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -391,7 +391,7 @@ export class MobileInterface extends React.Component {
      * Handles the 'Create New Dashboard' button in the menu (taken from MainView.tsx)
      */
     @action
-    createNewDashboard = async (id?: string) => {
+    createNewDashboard = (id?: string) => {
         const scens = CurrentUserUtils.MyDashboards;
         const dashboardCount = DocListCast(scens.data).length + 1;
         const freeformOptions: DocumentOptions = {
-- 
cgit v1.2.3-70-g09d2


From 8e8d93553152c7af03673f34ba8c98512e17f8f4 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 22 Jun 2022 14:35:23 -0400
Subject: fixed overlay view dragging & opening up audio/video recordings in
 overlay.

---
 src/client/util/CurrentUserUtils.ts | 46 ++++++++++++++++---------------------
 src/client/views/MainView.tsx       |  7 ++----
 src/client/views/OverlayView.scss   |  1 +
 3 files changed, 23 insertions(+), 31 deletions(-)

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

diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 52fbda9a9..ff6ef4809 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -146,7 +146,7 @@ export class CurrentUserUtils {
                 doc.isTemplateDoc = makeTemplate(doc);
                 return doc;
             }
-            return this.AssignScripts(assignBtnAndTempOpts(tempBtn, btnOpts, templateOpts) ?? CurrentUserUtils.createToolButton( {...btnOpts, dragFactory: makeTemp(template(templateOpts))}), reqdScripts);
+            return this.AssignScripts(assignBtnAndTempOpts(tempBtn, btnOpts, templateOpts) ?? this.createToolButton( {...btnOpts, dragFactory: makeTemp(template(templateOpts))}), reqdScripts);
         });
 
         const reqdOpts:DocumentOptions = {
@@ -201,34 +201,28 @@ export class CurrentUserUtils {
         const reqdOpts = { title: "icon templates", _height: 75, system: true };
         const templateIconsDoc = this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
 
-        const makeIconTemplate = (type: DocumentType | undefined, templateField: string, iconTemplate: () => Doc) => {
+        const makeIconTemplate = (type: DocumentType | undefined, templateField: string, iconTemplate: (opts:DocumentOptions) => Doc) => {
             const iconFieldName = "icon" + (type ? "_" + type : "");
-            return DocCast(templateIconsDoc[iconFieldName] ?? (templateIconsDoc[iconFieldName] = MakeTemplate(iconTemplate(), true, iconFieldName, templateField))) ;
+            return DocCast(templateIconsDoc[iconFieldName] ?? (templateIconsDoc[iconFieldName] = MakeTemplate(iconTemplate({onClick:deiconifyScript(), system: true}), true, iconFieldName, templateField))) ;
         };
         const deiconifyScript = () => ScriptField.MakeScript("deiconifyView(documentView)", { documentView: "any" });
-        const labelBox = (extra: object) => Docs.Create.LabelDocument({
-            textTransform: "unset", letterSpacing: "unset", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px",
-            _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, system: true, onClick: deiconifyScript(), ...extra,
-        });
-        const imageBox = (url: string, extra: object) => Docs.Create.ImageDocument(url, {
-            "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, _width: 360 / 4, _height: 270 / 4,
-            _showTitle: "title", system: true, onClick: deiconifyScript(), ...extra
-        });
-        const fontBox = () => Docs.Create.FontIconDocument({
-            _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, system: true, onClick: deiconifyScript()
+        const labelBox = (opts: object) => Docs.Create.LabelDocument({
+            textTransform: "unset", letterSpacing: "unset", _singleLine: false, _minFontSize: 14, _maxFontSize: 24, borderRounding: "5px",  _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, ...opts
         });
+        const imageBox = (url: Opt<string>, opts: object) => Docs.Create.ImageDocument(url ?? "http://www.cs.brown.edu/~bcz/noImage.png", { "icon-nativeWidth": 360 / 4, "icon-nativeHeight": 270 / 4, _width: 360 / 4, _height: 270 / 4,  _showTitle: "title", ...opts });
+        const fontBox = (opts:DocumentOptions) => Docs.Create.FontIconDocument({ _nativeHeight: 30, _nativeWidth: 30, _width: 30, _height: 30, ...opts  });
         const iconTemplates = [
-            makeIconTemplate(undefined, "title", () => labelBox({ _backgroundColor: "dimgray" })),
-            makeIconTemplate(DocumentType.AUDIO, "title", () => labelBox({ _backgroundColor: "lightgreen" })),
-            makeIconTemplate(DocumentType.PDF, "title", () => labelBox({ _backgroundColor: "pink" })),
-            makeIconTemplate(DocumentType.WEB, "title", () => labelBox({ _backgroundColor: "brown" })),
-            makeIconTemplate(DocumentType.RTF, "text", () => labelBox({ _showTitle: "creationDate" })),
-            makeIconTemplate(DocumentType.IMG, "data", () => imageBox("", { _height: undefined, })),
-            makeIconTemplate(DocumentType.COL, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})),
-            makeIconTemplate(DocumentType.VID, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {})),
-            makeIconTemplate(DocumentType.BUTTON, "data", fontBox),
+            makeIconTemplate(undefined, "title", (opts) => labelBox({ ...opts, _backgroundColor: "dimgray" })),
+            makeIconTemplate(DocumentType.AUDIO, "title", (opts) => labelBox({ ...opts, _backgroundColor: "lightgreen" })),
+            makeIconTemplate(DocumentType.PDF, "title", (opts) => labelBox({ ...opts, _backgroundColor: "pink" })),
+            makeIconTemplate(DocumentType.WEB, "title", (opts) => labelBox({...opts,  _backgroundColor: "brown" })),
+            makeIconTemplate(DocumentType.RTF, "text", (opts) => labelBox({ ...opts, _showTitle: "creationDate" })),
+            makeIconTemplate(DocumentType.IMG, "data", (opts) => imageBox("", { ...opts, _height: undefined, })),
+            makeIconTemplate(DocumentType.COL, "icon", (opts) => imageBox(undefined, opts)),
+            makeIconTemplate(DocumentType.VID, "icon", (opts) => imageBox(undefined, opts)),
+            makeIconTemplate(DocumentType.BUTTON, "data", (opts) => fontBox(opts)),
             //nasty hack .. templates are looked up exclusively by type -- but we want a template for a document with a certain field (transcription) .. so this hack and the companion hack in createCustomView does this for now
-            makeIconTemplate("transcription" as any, "transcription", () => labelBox({ _backgroundColor: "orange" })),
+            makeIconTemplate("transcription" as any, "transcription", (opts) => labelBox({ ...opts, _backgroundColor: "orange" })),
             // makeIconTemplate(DocumentType.PDF, "icon", () => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", {}))
         ].filter(d => d).map(d => d!);
         this.AssignOpts(DocCast(doc[field]), {}, iconTemplates);
@@ -315,10 +309,10 @@ export class CurrentUserUtils {
             { toolTip: "Tap or drag to create an equation",       title: "Math", icon: "calculator",    dragFactory: doc.emptyEquation as Doc, },
             { toolTip: "Tap or drag to create a webpage",         title: "Web", icon: "globe-asia",     dragFactory: doc.emptyWebpage as Doc, },
             { toolTip: "Tap or drag to create a comparison box",  title: "Compare", icon: "columns",    dragFactory: doc.emptyComparison as Doc, },
-            { toolTip: "Tap or drag to create an audio recorder", title: "Audio", icon: "microphone",   dragFactory: doc.emptyAudio as Doc, },
+            { toolTip: "Tap or drag to create an audio recorder", title: "Audio", icon: "microphone",   dragFactory: doc.emptyAudio as Doc,       scripts: { onClick: 'openInOverlay(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, },
             { toolTip: "Tap or drag to create a map",             title: "Map", icon: "map-marker-alt", dragFactory: doc.emptyMap as Doc, },
-            { toolTip: "Tap or drag to create a screen grabber",  title: "Grab", icon: "photo-video",   dragFactory: doc.emptyScreengrab as Doc,  funcs: { hidden: 'IsNoviceMode()'} },
-            { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc,      funcs: { hidden: 'IsNoviceMode()'}},
+            { toolTip: "Tap or drag to create a screen grabber",  title: "Grab", icon: "photo-video",   dragFactory: doc.emptyScreengrab as Doc,  scripts: { onClick: 'openInOverlay(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'},funcs: { hidden: 'IsNoviceMode()'} },
+            { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc,      scripts: { onClick: 'openInOverlay(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'},funcs: { hidden: 'IsNoviceMode()'}},
             { toolTip: "Tap or drag to create a button",          title: "Button", icon: "bolt",        dragFactory: doc.emptyButton as Doc,      funcs: { hidden: 'IsNoviceMode()'} },
             { toolTip: "Tap or drag to create a scripting box",   title: "Script", icon: "terminal",    dragFactory: doc.emptyScript as Doc,      funcs: { hidden: 'IsNoviceMode()'}},
             { toolTip: "Tap or drag to create a data viz node",   title: "DataViz", icon: "file",       dragFactory: doc.emptyDataViz as Doc, },
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 6ee8065f5..67a73feff 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -8,9 +8,8 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import * as ReactDOM from 'react-dom';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
-import { PrefetchProxy } from '../../fields/Proxy';
 import { ScriptField } from '../../fields/ScriptField';
-import { DocCast, PromiseValue, StrCast } from '../../fields/Types';
+import { PromiseValue, StrCast } from '../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
@@ -32,8 +31,7 @@ import { CollectionDockingView } from './collections/CollectionDockingView';
 import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu';
 import { CollectionLinearView } from './collections/collectionLinear';
 import { CollectionMenu } from './collections/CollectionMenu';
-import { TreeViewType } from './collections/CollectionTreeView';
-import { CollectionView, CollectionViewType } from './collections/CollectionView';
+import { CollectionViewType } from './collections/CollectionView';
 import "./collections/TreeView.scss";
 import { ComponentDecorations } from './ComponentDecorations';
 import { ContextMenu } from './ContextMenu';
@@ -49,7 +47,6 @@ import { LightboxView } from './LightboxView';
 import { LinkMenu } from './linking/LinkMenu';
 import "./MainView.scss";
 import { AudioBox } from './nodes/AudioBox';
-import { ButtonType } from './nodes/button/FontIconBox';
 import { DocumentLinksButton } from './nodes/DocumentLinksButton';
 import { DocumentView } from './nodes/DocumentView';
 import { DashFieldViewMenu } from './nodes/formattedText/DashFieldView';
diff --git a/src/client/views/OverlayView.scss b/src/client/views/OverlayView.scss
index 302e7a5e3..033cdf1f7 100644
--- a/src/client/views/OverlayView.scss
+++ b/src/client/views/OverlayView.scss
@@ -15,6 +15,7 @@
     flex-direction: column;
     top: 0;
     left: 0;
+    pointer-events: all;
 }
 
 .overlayWindow-outerDiv,
-- 
cgit v1.2.3-70-g09d2


From dd16695b0c5fe8c54bc276a230381ae36e19e5ac Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 7 Jul 2022 13:02:33 -0400
Subject: trying to fix errors in compiles

---
 package-lock.json                                  | 2282 +++++++++++++++++--
 package.json                                       |   40 +-
 src/client/util/CaptureManager.tsx                 |  157 +-
 src/client/util/CurrentUserUtils.ts                |    2 +-
 src/client/util/GroupManager.tsx                   |  253 ++-
 src/client/util/ScrollBox.tsx                      |   23 +-
 src/client/util/SharingManager.tsx                 |  566 +++--
 src/client/views/Main.tsx                          |   44 +-
 src/client/views/MainView.tsx                      | 1086 ++++++----
 src/client/views/Touchable.tsx                     |   68 +-
 src/client/views/animationtimeline/Timeline.tsx    |  286 ++-
 .../views/collections/CollectionStackingView.tsx   |  657 +++---
 .../views/collections/CollectionTreeView.tsx       |  321 +--
 src/client/views/collections/CollectionView.tsx    |  266 ++-
 .../CollectionFreeFormLinksView.tsx                |   34 +-
 .../views/collections/collectionGrid/Grid.tsx      |   17 +-
 .../collectionLinear/CollectionLinearView.tsx      |  291 +--
 src/client/views/linking/LinkEditor.tsx            |  346 +--
 src/client/views/linking/LinkMenuItem.tsx          |  226 +-
 src/client/views/nodes/DocumentContentsView.tsx    |  259 ++-
 src/client/views/nodes/LinkDocPreview.tsx          |  192 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |  505 ++---
 src/client/views/nodes/PDFBox.tsx                  |  451 ++--
 src/client/views/nodes/formattedText/schema_rts.ts |   13 +-
 src/client/views/nodes/trails/PresBox.tsx          | 2285 +++++++++++++-------
 src/client/views/search/SearchBox.tsx              |  213 +-
 src/mobile/MobileInterface.tsx                     |  602 ++++--
 src/server/authentication/AuthenticationManager.ts |  306 +--
 28 files changed, 7658 insertions(+), 4133 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index e895161b5..7e413d5c5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -462,6 +462,76 @@
             "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz",
             "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA=="
         },
+        "@eslint/eslintrc": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz",
+            "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==",
+            "dev": true,
+            "requires": {
+                "ajv": "^6.12.4",
+                "debug": "^4.3.2",
+                "espree": "^9.3.2",
+                "globals": "^13.15.0",
+                "ignore": "^5.2.0",
+                "import-fresh": "^3.2.1",
+                "js-yaml": "^4.1.0",
+                "minimatch": "^3.1.2",
+                "strip-json-comments": "^3.1.1"
+            },
+            "dependencies": {
+                "argparse": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+                    "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+                    "dev": true
+                },
+                "debug": {
+                    "version": "4.3.4",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "2.1.2"
+                    }
+                },
+                "globals": {
+                    "version": "13.16.0",
+                    "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz",
+                    "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==",
+                    "dev": true,
+                    "requires": {
+                        "type-fest": "^0.20.2"
+                    }
+                },
+                "ignore": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
+                    "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
+                    "dev": true
+                },
+                "js-yaml": {
+                    "version": "4.1.0",
+                    "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+                    "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+                    "dev": true,
+                    "requires": {
+                        "argparse": "^2.0.1"
+                    }
+                },
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+                    "dev": true
+                },
+                "strip-json-comments": {
+                    "version": "3.1.1",
+                    "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+                    "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+                    "dev": true
+                }
+            }
+        },
         "@ffmpeg/core": {
             "version": "0.10.0",
             "resolved": "https://registry.npmjs.org/@ffmpeg/core/-/core-0.10.0.tgz",
@@ -547,9 +617,9 @@
             }
         },
         "@fortawesome/react-fontawesome": {
-            "version": "0.1.18",
-            "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.18.tgz",
-            "integrity": "sha512-RwLIB4TZw0M9gvy5u+TusAA0afbwM4JQIimNH/j3ygd6aIvYPQLqXMhC9ErY26J23rDPyDZldIfPq/HpTTJ/tQ==",
+            "version": "0.1.19",
+            "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.19.tgz",
+            "integrity": "sha512-Hyb+lB8T18cvLNX0S3llz7PcSOAJMLwiVKBuuzwM/nI5uoBw+gQjnf9il0fR1C3DKOI5Kc79pkJ4/xB0Uw9aFQ==",
             "requires": {
                 "prop-types": "^15.8.1"
             }
@@ -580,6 +650,19 @@
                 "emotion": "^10.0.0",
                 "prop-types": "^15.7.1",
                 "react-transition-group": "^2.3.1"
+            },
+            "dependencies": {
+                "react-transition-group": {
+                    "version": "2.9.0",
+                    "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.6.2",
+                        "react-lifecycles-compat": "^3.0.4"
+                    }
+                }
             }
         },
         "@hig/theme-context": {
@@ -608,6 +691,40 @@
                 "lodash.memoize": "^4.1.2"
             }
         },
+        "@humanwhocodes/config-array": {
+            "version": "0.9.5",
+            "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz",
+            "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==",
+            "dev": true,
+            "requires": {
+                "@humanwhocodes/object-schema": "^1.2.1",
+                "debug": "^4.1.1",
+                "minimatch": "^3.0.4"
+            },
+            "dependencies": {
+                "debug": {
+                    "version": "4.3.4",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "2.1.2"
+                    }
+                },
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+                    "dev": true
+                }
+            }
+        },
+        "@humanwhocodes/object-schema": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+            "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+            "dev": true
+        },
         "@hypnosphi/create-react-context": {
             "version": "0.3.1",
             "resolved": "https://registry.npmjs.org/@hypnosphi/create-react-context/-/create-react-context-0.3.1.tgz",
@@ -1184,9 +1301,9 @@
             }
         },
         "@types/express-session": {
-            "version": "1.17.4",
-            "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.4.tgz",
-            "integrity": "sha512-7cNlSI8+oOBUHTfPXMwDxF/Lchx5aJ3ho7+p9jJZYVg9dVDJFh3qdMXmJtRsysnvS+C6x46k9DRYmrmCkE+MVg==",
+            "version": "1.17.5",
+            "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.5.tgz",
+            "integrity": "sha512-l0DhkvNVfyUPEEis8fcwbd46VptfA/jmMwHfob2TfDMf3HyPLiB9mKD71LXhz5TMUobODXPD27zXSwtFQLHm+w==",
             "dev": true,
             "requires": {
                 "@types/express": "*"
@@ -1332,6 +1449,12 @@
             "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
             "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ=="
         },
+        "@types/json5": {
+            "version": "0.0.29",
+            "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+            "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+            "dev": true
+        },
         "@types/keygrip": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz",
@@ -1461,9 +1584,9 @@
             "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g=="
         },
         "@types/passport": {
-            "version": "1.0.8",
-            "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.8.tgz",
-            "integrity": "sha512-Gdcvis7+7G/Mobm+25AeFi+oe5teBhHzpbCOFWeN10Bj8tnoEE1L5lkraQjzmDEKkJQuM7xSJUGIFGl/giyRfQ==",
+            "version": "1.0.9",
+            "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.9.tgz",
+            "integrity": "sha512-9+ilzUhmZQR4JP49GdC2O4UdDE3POPLwpmaTC/iLkW7l0TZCXOo1zsTnnlXPq6rP1UsUZPfbAV4IUdiwiXyC7g==",
             "dev": true,
             "requires": {
                 "@types/express": "*"
@@ -1663,10 +1786,9 @@
             }
         },
         "@types/react": {
-            "version": "16.14.26",
-            "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.26.tgz",
-            "integrity": "sha512-c/5CYyciOO4XdFcNhZW1O2woVx86k4T+DO2RorHZL7EhitkNQgSD/SgpdZJAUJa/qjVgOmTM44gHkAdZSXeQuQ==",
-            "dev": true,
+            "version": "18.0.15",
+            "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.15.tgz",
+            "integrity": "sha512-iz3BtLuIYH1uWdsv6wXYdhozhqj20oD4/Hk2DNXIn1kFsmp9x8d9QB6FnPhfkbhd2PgEONt9Q1x/ebkwjfFLow==",
             "requires": {
                 "@types/prop-types": "*",
                 "@types/scheduler": "*",
@@ -1676,8 +1798,7 @@
                 "csstype": {
                     "version": "3.1.0",
                     "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
-                    "dev": true
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
                 }
             }
         },
@@ -1720,18 +1841,18 @@
             }
         },
         "@types/react-dom": {
-            "version": "16.9.16",
-            "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.16.tgz",
-            "integrity": "sha512-Oqc0RY4fggGA3ltEgyPLc3IV9T73IGoWjkONbsyJ3ZBn+UPPCYpU2ec0i3cEbJuEdZtkqcCF2l1zf2pBdgUGSg==",
+            "version": "18.0.6",
+            "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.6.tgz",
+            "integrity": "sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==",
             "dev": true,
             "requires": {
-                "@types/react": "^16"
+                "@types/react": "*"
             }
         },
         "@types/react-grid-layout": {
-            "version": "0.17.2",
-            "resolved": "https://registry.npmjs.org/@types/react-grid-layout/-/react-grid-layout-0.17.2.tgz",
-            "integrity": "sha512-iLKQ5rI9KyPGs/BlWmeBfoikRnoOmTuddK8V5mOCgJ46e2JaecZuPhuIX1kV0mkQSk6X3XoEZALVW0XQbz9g5Q==",
+            "version": "1.3.2",
+            "resolved": "https://registry.npmjs.org/@types/react-grid-layout/-/react-grid-layout-1.3.2.tgz",
+            "integrity": "sha512-ZzpBEOC1JTQ7MGe1h1cPKSLP4jSWuxc+yvT4TsAlEW9+EFPzAf8nxQfFd7ea9gL17Em7PbwJZAsiwfQQBUklZQ==",
             "dev": true,
             "requires": {
                 "@types/react": "*"
@@ -1741,6 +1862,7 @@
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/@types/react-icons/-/react-icons-3.0.0.tgz",
             "integrity": "sha512-Vefs6LkLqF61vfV7AiAqls+vpR94q67gunhMueDznG+msAkrYgRxl7gYjNem/kZ+as2l2mNChmF1jRZzzQQtMg==",
+            "dev": true,
             "requires": {
                 "react-icons": "*"
             }
@@ -1758,6 +1880,7 @@
             "version": "0.26.7",
             "resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.26.7.tgz",
             "integrity": "sha512-mBDYl8x+oyPX/VBb3E638N0B7xG+SPk/EAMcVPeexqus/5aTpTphQi0curhhshOqRrc9t6OPoJfEUkbymse/lQ==",
+            "dev": true,
             "requires": {
                 "@types/react": "*"
             },
@@ -1766,6 +1889,7 @@
                     "version": "18.0.12",
                     "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.12.tgz",
                     "integrity": "sha512-duF1OTASSBQtcigUvhuiTB1Ya3OvSy+xORCiEf20H0P0lzx+/KeVsA99U5UjLXSbyo1DRJDlLKqTeM1ngosqtg==",
+                    "dev": true,
                     "requires": {
                         "@types/prop-types": "*",
                         "@types/scheduler": "*",
@@ -1775,7 +1899,8 @@
                 "csstype": {
                     "version": "3.1.0",
                     "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
+                    "dev": true
                 }
             }
         },
@@ -1844,28 +1969,11 @@
             }
         },
         "@types/react-transition-group": {
-            "version": "4.4.4",
-            "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz",
-            "integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==",
+            "version": "4.4.5",
+            "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz",
+            "integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==",
             "requires": {
                 "@types/react": "*"
-            },
-            "dependencies": {
-                "@types/react": {
-                    "version": "18.0.12",
-                    "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.12.tgz",
-                    "integrity": "sha512-duF1OTASSBQtcigUvhuiTB1Ya3OvSy+xORCiEf20H0P0lzx+/KeVsA99U5UjLXSbyo1DRJDlLKqTeM1ngosqtg==",
-                    "requires": {
-                        "@types/prop-types": "*",
-                        "@types/scheduler": "*",
-                        "csstype": "^3.0.2"
-                    }
-                },
-                "csstype": {
-                    "version": "3.1.0",
-                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
-                }
             }
         },
         "@types/reactcss": {
@@ -2359,22 +2467,22 @@
             }
         },
         "@webpack-cli/configtest": {
-            "version": "1.1.1",
-            "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.1.tgz",
-            "integrity": "sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg=="
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz",
+            "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg=="
         },
         "@webpack-cli/info": {
-            "version": "1.4.1",
-            "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.1.tgz",
-            "integrity": "sha512-PKVGmazEq3oAo46Q63tpMr4HipI3OPfP7LiNOEJg963RMgT0rqheag28NCML0o3GIzA3DmxP1ZIAv9oTX1CUIA==",
+            "version": "1.5.0",
+            "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz",
+            "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==",
             "requires": {
                 "envinfo": "^7.7.3"
             }
         },
         "@webpack-cli/serve": {
-            "version": "1.6.1",
-            "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.6.1.tgz",
-            "integrity": "sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw=="
+            "version": "1.7.0",
+            "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz",
+            "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q=="
         },
         "@webscopeio/react-textarea-autocomplete": {
             "version": "4.9.1",
@@ -2487,7 +2595,7 @@
         "after": {
             "version": "0.8.2",
             "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
-            "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
+            "integrity": "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA=="
         },
         "agent-base": {
             "version": "6.0.2",
@@ -2617,6 +2725,23 @@
                 }
             }
         },
+        "ansi-escapes": {
+            "version": "4.3.2",
+            "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+            "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+            "dev": true,
+            "requires": {
+                "type-fest": "^0.21.3"
+            },
+            "dependencies": {
+                "type-fest": {
+                    "version": "0.21.3",
+                    "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+                    "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+                    "dev": true
+                }
+            }
+        },
         "ansi-html-community": {
             "version": "0.0.8",
             "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz",
@@ -2735,6 +2860,16 @@
                 "sprintf-js": "~1.0.2"
             }
         },
+        "aria-query": {
+            "version": "4.2.2",
+            "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz",
+            "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==",
+            "dev": true,
+            "requires": {
+                "@babel/runtime": "^7.10.2",
+                "@babel/runtime-corejs3": "^7.10.2"
+            }
+        },
         "arr-diff": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
@@ -2852,6 +2987,19 @@
             "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
             "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
         },
+        "array-includes": {
+            "version": "3.1.5",
+            "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz",
+            "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==",
+            "dev": true,
+            "requires": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.4",
+                "es-abstract": "^1.19.5",
+                "get-intrinsic": "^1.1.1",
+                "is-string": "^1.0.7"
+            }
+        },
         "array-union": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
@@ -2872,6 +3020,30 @@
             "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
             "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="
         },
+        "array.prototype.flat": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz",
+            "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==",
+            "dev": true,
+            "requires": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.19.2",
+                "es-shim-unscopables": "^1.0.0"
+            }
+        },
+        "array.prototype.flatmap": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz",
+            "integrity": "sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==",
+            "dev": true,
+            "requires": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.19.2",
+                "es-shim-unscopables": "^1.0.0"
+            }
+        },
         "array.prototype.reduce": {
             "version": "1.0.4",
             "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz",
@@ -2922,6 +3094,18 @@
             "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
             "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c="
         },
+        "ast-types-flow": {
+            "version": "0.0.7",
+            "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
+            "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==",
+            "dev": true
+        },
+        "astral-regex": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
+            "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+            "dev": true
+        },
         "async": {
             "version": "2.6.4",
             "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
@@ -3006,6 +3190,12 @@
             "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
             "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA=="
         },
+        "axe-core": {
+            "version": "4.4.2",
+            "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.2.tgz",
+            "integrity": "sha512-LVAaGp/wkkgYJcjmHsoKx4juT1aQvJyPcW09MLCjVTh3V2cc6PnyempiLMNH5iMdfIX/zdbjUx2KDjMLCTdPeA==",
+            "dev": true
+        },
         "axios": {
             "version": "0.19.2",
             "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
@@ -3014,6 +3204,12 @@
                 "follow-redirects": "1.5.10"
             }
         },
+        "axobject-query": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",
+            "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==",
+            "dev": true
+        },
         "babel": {
             "version": "6.23.0",
             "resolved": "https://registry.npmjs.org/babel/-/babel-6.23.0.tgz",
@@ -3078,6 +3274,28 @@
                 }
             }
         },
+        "babel-eslint": {
+            "version": "10.1.0",
+            "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz",
+            "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==",
+            "dev": true,
+            "requires": {
+                "@babel/code-frame": "^7.0.0",
+                "@babel/parser": "^7.7.0",
+                "@babel/traverse": "^7.7.0",
+                "@babel/types": "^7.7.0",
+                "eslint-visitor-keys": "^1.0.0",
+                "resolve": "^1.12.0"
+            },
+            "dependencies": {
+                "eslint-visitor-keys": {
+                    "version": "1.3.0",
+                    "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+                    "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+                    "dev": true
+                }
+            }
+        },
         "babel-plugin-emotion": {
             "version": "10.2.2",
             "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.2.2.tgz",
@@ -3169,7 +3387,7 @@
         "backo2": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
-            "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
+            "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA=="
         },
         "balanced-match": {
             "version": "1.0.2",
@@ -3234,7 +3452,7 @@
         "base64-arraybuffer": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
-            "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
+            "integrity": "sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg=="
         },
         "base64-js": {
             "version": "1.5.1",
@@ -3752,9 +3970,9 @@
             "integrity": "sha512-u+Ll+RDaiQEproTQjZLjZwyfNgNezA1fERMT7/54npcz+PkbVJUAHXMUz4bkXQYRPWrcFNO0Fbi1mwjfXg6N5g=="
         },
         "canvas": {
-            "version": "2.9.1",
-            "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.9.1.tgz",
-            "integrity": "sha512-vSQti1uG/2gjv3x6QLOZw7TctfufaerTWbVe+NSduHxxLGB+qf3kFgQ6n66DSnuoINtVUjrLLIK2R+lxrBG07A==",
+            "version": "2.9.3",
+            "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.9.3.tgz",
+            "integrity": "sha512-WOUM7ghii5TV2rbhaZkh1youv/vW1/Canev6Yx6BG2W+1S07w8jKZqKkPnbiPpQEDsnJdN8ouDd7OvQEGXDcUw==",
             "requires": {
                 "@mapbox/node-pre-gyp": "^1.0.0",
                 "nan": "^2.15.0",
@@ -3822,6 +4040,12 @@
                 "is-regex": "^1.0.3"
             }
         },
+        "chardet": {
+            "version": "0.7.0",
+            "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+            "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+            "dev": true
+        },
         "check-error": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
@@ -3932,6 +4156,21 @@
             "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
             "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM="
         },
+        "cli-cursor": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+            "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+            "dev": true,
+            "requires": {
+                "restore-cursor": "^3.1.0"
+            }
+        },
+        "cli-width": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
+            "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
+            "dev": true
+        },
         "clipboard": {
             "version": "1.7.1",
             "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-1.7.1.tgz",
@@ -4140,7 +4379,7 @@
         "component-bind": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
-            "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
+            "integrity": "sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw=="
         },
         "component-emitter": {
             "version": "1.3.0",
@@ -4150,7 +4389,7 @@
         "component-inherit": {
             "version": "0.0.3",
             "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
-            "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
+            "integrity": "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA=="
         },
         "compress-brotli": {
             "version": "1.3.8",
@@ -4295,6 +4534,12 @@
                 }
             }
         },
+        "confusing-browser-globals": {
+            "version": "1.0.11",
+            "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz",
+            "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==",
+            "dev": true
+        },
         "connect-flash": {
             "version": "0.1.1",
             "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz",
@@ -4860,16 +5105,6 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
-        "d": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
-            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
-            "dev": true,
-            "requires": {
-                "es5-ext": "^0.10.50",
-                "type": "^1.0.1"
-            }
-        },
         "d3-array": {
             "version": "2.12.1",
             "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
@@ -4995,6 +5230,12 @@
                 "d3-transition": "2"
             }
         },
+        "damerau-levenshtein": {
+            "version": "1.0.8",
+            "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
+            "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
+            "dev": true
+        },
         "dashdash": {
             "version": "1.14.1",
             "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
@@ -5584,6 +5825,15 @@
                 "buffer-indexof": "^1.0.0"
             }
         },
+        "doctrine": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+            "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+            "dev": true,
+            "requires": {
+                "esutils": "^2.0.2"
+            }
+        },
         "doctypes": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz",
@@ -5809,9 +6059,9 @@
             }
         },
         "engine.io": {
-            "version": "3.5.0",
-            "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.5.0.tgz",
-            "integrity": "sha512-21HlvPUKaitDGE4GXNtQ7PLP0Sz4aWLddMPw2VTyFz1FVZqu/kZsJUO8WNpKuE/OCL7nkfRaOui2ZCJloGznGA==",
+            "version": "3.6.0",
+            "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.6.0.tgz",
+            "integrity": "sha512-Kc8fo5bbg8F4a2f3HPHTEpGyq/IRIQpyeHu3H1ThR14XDD7VrLcsGBo16HUpahgp8YkHJDaU5gNxJZbuGcuueg==",
             "requires": {
                 "accepts": "~1.3.4",
                 "base64id": "2.0.0",
@@ -5865,7 +6115,7 @@
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
                 },
                 "ws": {
                     "version": "7.4.6",
@@ -5985,6 +6235,15 @@
             "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==",
             "dev": true
         },
+        "es-shim-unscopables": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
+            "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
+            "dev": true,
+            "requires": {
+                "has": "^1.0.3"
+            }
+        },
         "es-to-primitive": {
             "version": "1.2.1",
             "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
@@ -5995,28 +6254,6 @@
                 "is-symbol": "^1.0.2"
             }
         },
-        "es5-ext": {
-            "version": "0.10.61",
-            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz",
-            "integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==",
-            "dev": true,
-            "requires": {
-                "es6-iterator": "^2.0.3",
-                "es6-symbol": "^3.1.3",
-                "next-tick": "^1.1.0"
-            }
-        },
-        "es6-iterator": {
-            "version": "2.0.3",
-            "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
-            "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
-            "dev": true,
-            "requires": {
-                "d": "1",
-                "es5-ext": "^0.10.35",
-                "es6-symbol": "^3.1.1"
-            }
-        },
         "es6-promise": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
@@ -6028,7 +6265,6 @@
             "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
             "dev": true,
             "requires": {
-                "d": "^1.0.1",
                 "ext": "^1.1.2"
             }
         },
@@ -6069,36 +6305,1192 @@
                 }
             }
         },
-        "eslint-scope": {
-            "version": "5.1.1",
-            "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
-            "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
-            "dev": true,
-            "requires": {
-                "esrecurse": "^4.3.0",
-                "estraverse": "^4.1.1"
-            }
-        },
-        "esprima": {
-            "version": "4.0.1",
-            "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
-            "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
-        },
-        "esrecurse": {
-            "version": "4.3.0",
-            "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
-            "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+        "eslint": {
+            "version": "8.19.0",
+            "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.19.0.tgz",
+            "integrity": "sha512-SXOPj3x9VKvPe81TjjUJCYlV4oJjQw68Uek+AM0X4p+33dj2HY5bpTZOgnQHcG2eAm1mtCU9uNMnJi7exU/kYw==",
             "dev": true,
             "requires": {
-                "estraverse": "^5.2.0"
+                "@eslint/eslintrc": "^1.3.0",
+                "@humanwhocodes/config-array": "^0.9.2",
+                "ajv": "^6.10.0",
+                "chalk": "^4.0.0",
+                "cross-spawn": "^7.0.2",
+                "debug": "^4.3.2",
+                "doctrine": "^3.0.0",
+                "escape-string-regexp": "^4.0.0",
+                "eslint-scope": "^7.1.1",
+                "eslint-utils": "^3.0.0",
+                "eslint-visitor-keys": "^3.3.0",
+                "espree": "^9.3.2",
+                "esquery": "^1.4.0",
+                "esutils": "^2.0.2",
+                "fast-deep-equal": "^3.1.3",
+                "file-entry-cache": "^6.0.1",
+                "functional-red-black-tree": "^1.0.1",
+                "glob-parent": "^6.0.1",
+                "globals": "^13.15.0",
+                "ignore": "^5.2.0",
+                "import-fresh": "^3.0.0",
+                "imurmurhash": "^0.1.4",
+                "is-glob": "^4.0.0",
+                "js-yaml": "^4.1.0",
+                "json-stable-stringify-without-jsonify": "^1.0.1",
+                "levn": "^0.4.1",
+                "lodash.merge": "^4.6.2",
+                "minimatch": "^3.1.2",
+                "natural-compare": "^1.4.0",
+                "optionator": "^0.9.1",
+                "regexpp": "^3.2.0",
+                "strip-ansi": "^6.0.1",
+                "strip-json-comments": "^3.1.0",
+                "text-table": "^0.2.0",
+                "v8-compile-cache": "^2.0.3"
             },
             "dependencies": {
-                "estraverse": {
-                    "version": "5.3.0",
-                    "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
-                    "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+                "ansi-regex": {
+                    "version": "5.0.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                    "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
                     "dev": true
-                }
+                },
+                "ansi-styles": {
+                    "version": "4.3.0",
+                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+                    "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+                    "dev": true,
+                    "requires": {
+                        "color-convert": "^2.0.1"
+                    }
+                },
+                "argparse": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+                    "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+                    "dev": true
+                },
+                "chalk": {
+                    "version": "4.1.2",
+                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+                    "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-styles": "^4.1.0",
+                        "supports-color": "^7.1.0"
+                    }
+                },
+                "color-convert": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+                    "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+                    "dev": true,
+                    "requires": {
+                        "color-name": "~1.1.4"
+                    }
+                },
+                "color-name": {
+                    "version": "1.1.4",
+                    "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+                    "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+                    "dev": true
+                },
+                "cross-spawn": {
+                    "version": "7.0.3",
+                    "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+                    "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+                    "dev": true,
+                    "requires": {
+                        "path-key": "^3.1.0",
+                        "shebang-command": "^2.0.0",
+                        "which": "^2.0.1"
+                    }
+                },
+                "debug": {
+                    "version": "4.3.4",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "2.1.2"
+                    }
+                },
+                "escape-string-regexp": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+                    "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+                    "dev": true
+                },
+                "eslint-scope": {
+                    "version": "7.1.1",
+                    "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
+                    "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+                    "dev": true,
+                    "requires": {
+                        "esrecurse": "^4.3.0",
+                        "estraverse": "^5.2.0"
+                    }
+                },
+                "estraverse": {
+                    "version": "5.3.0",
+                    "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+                    "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+                    "dev": true
+                },
+                "glob-parent": {
+                    "version": "6.0.2",
+                    "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+                    "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+                    "dev": true,
+                    "requires": {
+                        "is-glob": "^4.0.3"
+                    }
+                },
+                "globals": {
+                    "version": "13.16.0",
+                    "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz",
+                    "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==",
+                    "dev": true,
+                    "requires": {
+                        "type-fest": "^0.20.2"
+                    }
+                },
+                "has-flag": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+                    "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+                    "dev": true
+                },
+                "ignore": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
+                    "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
+                    "dev": true
+                },
+                "js-yaml": {
+                    "version": "4.1.0",
+                    "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+                    "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+                    "dev": true,
+                    "requires": {
+                        "argparse": "^2.0.1"
+                    }
+                },
+                "levn": {
+                    "version": "0.4.1",
+                    "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+                    "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+                    "dev": true,
+                    "requires": {
+                        "prelude-ls": "^1.2.1",
+                        "type-check": "~0.4.0"
+                    }
+                },
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+                    "dev": true
+                },
+                "optionator": {
+                    "version": "0.9.1",
+                    "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+                    "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+                    "dev": true,
+                    "requires": {
+                        "deep-is": "^0.1.3",
+                        "fast-levenshtein": "^2.0.6",
+                        "levn": "^0.4.1",
+                        "prelude-ls": "^1.2.1",
+                        "type-check": "^0.4.0",
+                        "word-wrap": "^1.2.3"
+                    }
+                },
+                "path-key": {
+                    "version": "3.1.1",
+                    "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+                    "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+                    "dev": true
+                },
+                "prelude-ls": {
+                    "version": "1.2.1",
+                    "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+                    "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+                    "dev": true
+                },
+                "shebang-command": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+                    "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+                    "dev": true,
+                    "requires": {
+                        "shebang-regex": "^3.0.0"
+                    }
+                },
+                "shebang-regex": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+                    "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+                    "dev": true
+                },
+                "strip-ansi": {
+                    "version": "6.0.1",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                    "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^5.0.1"
+                    }
+                },
+                "strip-json-comments": {
+                    "version": "3.1.1",
+                    "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+                    "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+                    "dev": true
+                },
+                "supports-color": {
+                    "version": "7.2.0",
+                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+                    "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+                    "dev": true,
+                    "requires": {
+                        "has-flag": "^4.0.0"
+                    }
+                },
+                "type-check": {
+                    "version": "0.4.0",
+                    "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+                    "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+                    "dev": true,
+                    "requires": {
+                        "prelude-ls": "^1.2.1"
+                    }
+                },
+                "which": {
+                    "version": "2.0.2",
+                    "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+                    "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+                    "dev": true,
+                    "requires": {
+                        "isexe": "^2.0.0"
+                    }
+                }
+            }
+        },
+        "eslint-config-airbnb": {
+            "version": "19.0.4",
+            "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz",
+            "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==",
+            "dev": true,
+            "requires": {
+                "eslint-config-airbnb-base": "^15.0.0",
+                "object.assign": "^4.1.2",
+                "object.entries": "^1.1.5"
+            },
+            "dependencies": {
+                "object.assign": {
+                    "version": "4.1.2",
+                    "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+                    "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+                    "dev": true,
+                    "requires": {
+                        "call-bind": "^1.0.0",
+                        "define-properties": "^1.1.3",
+                        "has-symbols": "^1.0.1",
+                        "object-keys": "^1.1.1"
+                    }
+                }
+            }
+        },
+        "eslint-config-airbnb-base": {
+            "version": "15.0.0",
+            "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz",
+            "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==",
+            "dev": true,
+            "requires": {
+                "confusing-browser-globals": "^1.0.10",
+                "object.assign": "^4.1.2",
+                "object.entries": "^1.1.5",
+                "semver": "^6.3.0"
+            },
+            "dependencies": {
+                "object.assign": {
+                    "version": "4.1.2",
+                    "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+                    "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+                    "dev": true,
+                    "requires": {
+                        "call-bind": "^1.0.0",
+                        "define-properties": "^1.1.3",
+                        "has-symbols": "^1.0.1",
+                        "object-keys": "^1.1.1"
+                    }
+                },
+                "semver": {
+                    "version": "6.3.0",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+                    "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-config-esnext": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/eslint-config-esnext/-/eslint-config-esnext-4.1.0.tgz",
+            "integrity": "sha512-GhfVEXdqYKEIIj7j+Fw2SQdL9qyZMekgXfq6PyXM66cQw0B435ddjz3P3kxOBVihMRJ0xGYjosaveQz5Y6z0uA==",
+            "dev": true,
+            "requires": {
+                "babel-eslint": "^10.0.1",
+                "eslint": "^6.8.0",
+                "eslint-plugin-babel": "^5.2.1",
+                "eslint-plugin-import": "^2.14.0"
+            },
+            "dependencies": {
+                "acorn": {
+                    "version": "7.4.1",
+                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+                    "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+                    "dev": true
+                },
+                "ansi-regex": {
+                    "version": "4.1.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+                    "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+                    "dev": true
+                },
+                "cross-spawn": {
+                    "version": "6.0.5",
+                    "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+                    "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+                    "dev": true,
+                    "requires": {
+                        "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": {
+                            "version": "5.7.1",
+                            "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+                            "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+                            "dev": true
+                        }
+                    }
+                },
+                "debug": {
+                    "version": "4.3.4",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "2.1.2"
+                    }
+                },
+                "eslint": {
+                    "version": "6.8.0",
+                    "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz",
+                    "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==",
+                    "dev": true,
+                    "requires": {
+                        "@babel/code-frame": "^7.0.0",
+                        "ajv": "^6.10.0",
+                        "chalk": "^2.1.0",
+                        "cross-spawn": "^6.0.5",
+                        "debug": "^4.0.1",
+                        "doctrine": "^3.0.0",
+                        "eslint-scope": "^5.0.0",
+                        "eslint-utils": "^1.4.3",
+                        "eslint-visitor-keys": "^1.1.0",
+                        "espree": "^6.1.2",
+                        "esquery": "^1.0.1",
+                        "esutils": "^2.0.2",
+                        "file-entry-cache": "^5.0.1",
+                        "functional-red-black-tree": "^1.0.1",
+                        "glob-parent": "^5.0.0",
+                        "globals": "^12.1.0",
+                        "ignore": "^4.0.6",
+                        "import-fresh": "^3.0.0",
+                        "imurmurhash": "^0.1.4",
+                        "inquirer": "^7.0.0",
+                        "is-glob": "^4.0.0",
+                        "js-yaml": "^3.13.1",
+                        "json-stable-stringify-without-jsonify": "^1.0.1",
+                        "levn": "^0.3.0",
+                        "lodash": "^4.17.14",
+                        "minimatch": "^3.0.4",
+                        "mkdirp": "^0.5.1",
+                        "natural-compare": "^1.4.0",
+                        "optionator": "^0.8.3",
+                        "progress": "^2.0.0",
+                        "regexpp": "^2.0.1",
+                        "semver": "^6.1.2",
+                        "strip-ansi": "^5.2.0",
+                        "strip-json-comments": "^3.0.1",
+                        "table": "^5.2.3",
+                        "text-table": "^0.2.0",
+                        "v8-compile-cache": "^2.0.3"
+                    }
+                },
+                "eslint-utils": {
+                    "version": "1.4.3",
+                    "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz",
+                    "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==",
+                    "dev": true,
+                    "requires": {
+                        "eslint-visitor-keys": "^1.1.0"
+                    }
+                },
+                "eslint-visitor-keys": {
+                    "version": "1.3.0",
+                    "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+                    "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+                    "dev": true
+                },
+                "espree": {
+                    "version": "6.2.1",
+                    "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz",
+                    "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==",
+                    "dev": true,
+                    "requires": {
+                        "acorn": "^7.1.1",
+                        "acorn-jsx": "^5.2.0",
+                        "eslint-visitor-keys": "^1.1.0"
+                    }
+                },
+                "file-entry-cache": {
+                    "version": "5.0.1",
+                    "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
+                    "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
+                    "dev": true,
+                    "requires": {
+                        "flat-cache": "^2.0.1"
+                    }
+                },
+                "flat-cache": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
+                    "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+                    "dev": true,
+                    "requires": {
+                        "flatted": "^2.0.0",
+                        "rimraf": "2.6.3",
+                        "write": "1.0.3"
+                    }
+                },
+                "flatted": {
+                    "version": "2.0.2",
+                    "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
+                    "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
+                    "dev": true
+                },
+                "glob-parent": {
+                    "version": "5.1.2",
+                    "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+                    "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+                    "dev": true,
+                    "requires": {
+                        "is-glob": "^4.0.1"
+                    }
+                },
+                "globals": {
+                    "version": "12.4.0",
+                    "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
+                    "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
+                    "dev": true,
+                    "requires": {
+                        "type-fest": "^0.8.1"
+                    }
+                },
+                "ignore": {
+                    "version": "4.0.6",
+                    "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+                    "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+                    "dev": true
+                },
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+                    "dev": true
+                },
+                "regexpp": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
+                    "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
+                    "dev": true
+                },
+                "rimraf": {
+                    "version": "2.6.3",
+                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+                    "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+                    "dev": true,
+                    "requires": {
+                        "glob": "^7.1.3"
+                    }
+                },
+                "semver": {
+                    "version": "6.3.0",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+                    "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+                    "dev": true
+                },
+                "strip-ansi": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+                    "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^4.1.0"
+                    }
+                },
+                "strip-json-comments": {
+                    "version": "3.1.1",
+                    "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+                    "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+                    "dev": true
+                },
+                "type-fest": {
+                    "version": "0.8.1",
+                    "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+                    "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-config-node": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/eslint-config-node/-/eslint-config-node-4.1.0.tgz",
+            "integrity": "sha512-Wz17xV5O2WFG8fGdMYEBdbiL6TL7YNJSJvSX9V4sXQownewfYmoqlly7wxqLkOUv/57pq6LnnotMiQQrrPjCqQ==",
+            "dev": true,
+            "requires": {
+                "eslint": "^6.8.0",
+                "eslint-config-esnext": "^4.1.0"
+            },
+            "dependencies": {
+                "acorn": {
+                    "version": "7.4.1",
+                    "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+                    "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+                    "dev": true
+                },
+                "ansi-regex": {
+                    "version": "4.1.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+                    "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+                    "dev": true
+                },
+                "cross-spawn": {
+                    "version": "6.0.5",
+                    "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+                    "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+                    "dev": true,
+                    "requires": {
+                        "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": {
+                            "version": "5.7.1",
+                            "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+                            "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+                            "dev": true
+                        }
+                    }
+                },
+                "debug": {
+                    "version": "4.3.4",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+                    "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "2.1.2"
+                    }
+                },
+                "eslint": {
+                    "version": "6.8.0",
+                    "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz",
+                    "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==",
+                    "dev": true,
+                    "requires": {
+                        "@babel/code-frame": "^7.0.0",
+                        "ajv": "^6.10.0",
+                        "chalk": "^2.1.0",
+                        "cross-spawn": "^6.0.5",
+                        "debug": "^4.0.1",
+                        "doctrine": "^3.0.0",
+                        "eslint-scope": "^5.0.0",
+                        "eslint-utils": "^1.4.3",
+                        "eslint-visitor-keys": "^1.1.0",
+                        "espree": "^6.1.2",
+                        "esquery": "^1.0.1",
+                        "esutils": "^2.0.2",
+                        "file-entry-cache": "^5.0.1",
+                        "functional-red-black-tree": "^1.0.1",
+                        "glob-parent": "^5.0.0",
+                        "globals": "^12.1.0",
+                        "ignore": "^4.0.6",
+                        "import-fresh": "^3.0.0",
+                        "imurmurhash": "^0.1.4",
+                        "inquirer": "^7.0.0",
+                        "is-glob": "^4.0.0",
+                        "js-yaml": "^3.13.1",
+                        "json-stable-stringify-without-jsonify": "^1.0.1",
+                        "levn": "^0.3.0",
+                        "lodash": "^4.17.14",
+                        "minimatch": "^3.0.4",
+                        "mkdirp": "^0.5.1",
+                        "natural-compare": "^1.4.0",
+                        "optionator": "^0.8.3",
+                        "progress": "^2.0.0",
+                        "regexpp": "^2.0.1",
+                        "semver": "^6.1.2",
+                        "strip-ansi": "^5.2.0",
+                        "strip-json-comments": "^3.0.1",
+                        "table": "^5.2.3",
+                        "text-table": "^0.2.0",
+                        "v8-compile-cache": "^2.0.3"
+                    }
+                },
+                "eslint-utils": {
+                    "version": "1.4.3",
+                    "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz",
+                    "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==",
+                    "dev": true,
+                    "requires": {
+                        "eslint-visitor-keys": "^1.1.0"
+                    }
+                },
+                "eslint-visitor-keys": {
+                    "version": "1.3.0",
+                    "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+                    "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+                    "dev": true
+                },
+                "espree": {
+                    "version": "6.2.1",
+                    "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz",
+                    "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==",
+                    "dev": true,
+                    "requires": {
+                        "acorn": "^7.1.1",
+                        "acorn-jsx": "^5.2.0",
+                        "eslint-visitor-keys": "^1.1.0"
+                    }
+                },
+                "file-entry-cache": {
+                    "version": "5.0.1",
+                    "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
+                    "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
+                    "dev": true,
+                    "requires": {
+                        "flat-cache": "^2.0.1"
+                    }
+                },
+                "flat-cache": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
+                    "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+                    "dev": true,
+                    "requires": {
+                        "flatted": "^2.0.0",
+                        "rimraf": "2.6.3",
+                        "write": "1.0.3"
+                    }
+                },
+                "flatted": {
+                    "version": "2.0.2",
+                    "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
+                    "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
+                    "dev": true
+                },
+                "glob-parent": {
+                    "version": "5.1.2",
+                    "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+                    "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+                    "dev": true,
+                    "requires": {
+                        "is-glob": "^4.0.1"
+                    }
+                },
+                "globals": {
+                    "version": "12.4.0",
+                    "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
+                    "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
+                    "dev": true,
+                    "requires": {
+                        "type-fest": "^0.8.1"
+                    }
+                },
+                "ignore": {
+                    "version": "4.0.6",
+                    "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+                    "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+                    "dev": true
+                },
+                "ms": {
+                    "version": "2.1.2",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+                    "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+                    "dev": true
+                },
+                "regexpp": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
+                    "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
+                    "dev": true
+                },
+                "rimraf": {
+                    "version": "2.6.3",
+                    "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+                    "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+                    "dev": true,
+                    "requires": {
+                        "glob": "^7.1.3"
+                    }
+                },
+                "semver": {
+                    "version": "6.3.0",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+                    "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+                    "dev": true
+                },
+                "strip-ansi": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+                    "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^4.1.0"
+                    }
+                },
+                "strip-json-comments": {
+                    "version": "3.1.1",
+                    "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+                    "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+                    "dev": true
+                },
+                "type-fest": {
+                    "version": "0.8.1",
+                    "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+                    "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-config-prettier": {
+            "version": "8.5.0",
+            "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz",
+            "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==",
+            "dev": true
+        },
+        "eslint-import-resolver-node": {
+            "version": "0.3.6",
+            "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz",
+            "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==",
+            "dev": true,
+            "requires": {
+                "debug": "^3.2.7",
+                "resolve": "^1.20.0"
+            },
+            "dependencies": {
+                "debug": {
+                    "version": "3.2.7",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+                    "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "^2.1.1"
+                    }
+                }
+            }
+        },
+        "eslint-module-utils": {
+            "version": "2.7.3",
+            "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz",
+            "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==",
+            "dev": true,
+            "requires": {
+                "debug": "^3.2.7",
+                "find-up": "^2.1.0"
+            },
+            "dependencies": {
+                "debug": {
+                    "version": "3.2.7",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+                    "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "^2.1.1"
+                    }
+                },
+                "find-up": {
+                    "version": "2.1.0",
+                    "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+                    "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==",
+                    "dev": true,
+                    "requires": {
+                        "locate-path": "^2.0.0"
+                    }
+                },
+                "locate-path": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+                    "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==",
+                    "dev": true,
+                    "requires": {
+                        "p-locate": "^2.0.0",
+                        "path-exists": "^3.0.0"
+                    }
+                },
+                "p-limit": {
+                    "version": "1.3.0",
+                    "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+                    "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+                    "dev": true,
+                    "requires": {
+                        "p-try": "^1.0.0"
+                    }
+                },
+                "p-locate": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+                    "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==",
+                    "dev": true,
+                    "requires": {
+                        "p-limit": "^1.1.0"
+                    }
+                },
+                "p-try": {
+                    "version": "1.0.0",
+                    "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+                    "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-plugin-babel": {
+            "version": "5.3.1",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-babel/-/eslint-plugin-babel-5.3.1.tgz",
+            "integrity": "sha512-VsQEr6NH3dj664+EyxJwO4FCYm/00JhYb3Sk3ft8o+fpKuIfQ9TaW6uVUfvwMXHcf/lsnRIoyFPsLMyiWCSL/g==",
+            "dev": true,
+            "requires": {
+                "eslint-rule-composer": "^0.3.0"
+            }
+        },
+        "eslint-plugin-es": {
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz",
+            "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==",
+            "dev": true,
+            "requires": {
+                "eslint-utils": "^2.0.0",
+                "regexpp": "^3.0.0"
+            },
+            "dependencies": {
+                "eslint-utils": {
+                    "version": "2.1.0",
+                    "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+                    "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+                    "dev": true,
+                    "requires": {
+                        "eslint-visitor-keys": "^1.1.0"
+                    }
+                },
+                "eslint-visitor-keys": {
+                    "version": "1.3.0",
+                    "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+                    "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-plugin-import": {
+            "version": "2.26.0",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz",
+            "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==",
+            "dev": true,
+            "requires": {
+                "array-includes": "^3.1.4",
+                "array.prototype.flat": "^1.2.5",
+                "debug": "^2.6.9",
+                "doctrine": "^2.1.0",
+                "eslint-import-resolver-node": "^0.3.6",
+                "eslint-module-utils": "^2.7.3",
+                "has": "^1.0.3",
+                "is-core-module": "^2.8.1",
+                "is-glob": "^4.0.3",
+                "minimatch": "^3.1.2",
+                "object.values": "^1.1.5",
+                "resolve": "^1.22.0",
+                "tsconfig-paths": "^3.14.1"
+            },
+            "dependencies": {
+                "debug": {
+                    "version": "2.6.9",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+                    "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "2.0.0"
+                    }
+                },
+                "doctrine": {
+                    "version": "2.1.0",
+                    "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+                    "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+                    "dev": true,
+                    "requires": {
+                        "esutils": "^2.0.2"
+                    }
+                },
+                "ms": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-plugin-jsx-a11y": {
+            "version": "6.6.0",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.0.tgz",
+            "integrity": "sha512-kTeLuIzpNhXL2CwLlc8AHI0aFRwWHcg483yepO9VQiHzM9bZwJdzTkzBszbuPrbgGmq2rlX/FaT2fJQsjUSHsw==",
+            "dev": true,
+            "requires": {
+                "@babel/runtime": "^7.18.3",
+                "aria-query": "^4.2.2",
+                "array-includes": "^3.1.5",
+                "ast-types-flow": "^0.0.7",
+                "axe-core": "^4.4.2",
+                "axobject-query": "^2.2.0",
+                "damerau-levenshtein": "^1.0.8",
+                "emoji-regex": "^9.2.2",
+                "has": "^1.0.3",
+                "jsx-ast-utils": "^3.3.1",
+                "language-tags": "^1.0.5",
+                "minimatch": "^3.1.2",
+                "semver": "^6.3.0"
+            },
+            "dependencies": {
+                "emoji-regex": {
+                    "version": "9.2.2",
+                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+                    "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+                    "dev": true
+                },
+                "semver": {
+                    "version": "6.3.0",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+                    "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-plugin-node": {
+            "version": "11.1.0",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz",
+            "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==",
+            "dev": true,
+            "requires": {
+                "eslint-plugin-es": "^3.0.0",
+                "eslint-utils": "^2.0.0",
+                "ignore": "^5.1.1",
+                "minimatch": "^3.0.4",
+                "resolve": "^1.10.1",
+                "semver": "^6.1.0"
+            },
+            "dependencies": {
+                "eslint-utils": {
+                    "version": "2.1.0",
+                    "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+                    "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+                    "dev": true,
+                    "requires": {
+                        "eslint-visitor-keys": "^1.1.0"
+                    }
+                },
+                "eslint-visitor-keys": {
+                    "version": "1.3.0",
+                    "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+                    "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+                    "dev": true
+                },
+                "ignore": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
+                    "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
+                    "dev": true
+                },
+                "semver": {
+                    "version": "6.3.0",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+                    "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-plugin-prettier": {
+            "version": "4.2.1",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz",
+            "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==",
+            "dev": true,
+            "requires": {
+                "prettier-linter-helpers": "^1.0.0"
+            }
+        },
+        "eslint-plugin-react": {
+            "version": "7.30.1",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.30.1.tgz",
+            "integrity": "sha512-NbEvI9jtqO46yJA3wcRF9Mo0lF9T/jhdHqhCHXiXtD+Zcb98812wvokjWpU7Q4QH5edo6dmqrukxVvWWXHlsUg==",
+            "dev": true,
+            "requires": {
+                "array-includes": "^3.1.5",
+                "array.prototype.flatmap": "^1.3.0",
+                "doctrine": "^2.1.0",
+                "estraverse": "^5.3.0",
+                "jsx-ast-utils": "^2.4.1 || ^3.0.0",
+                "minimatch": "^3.1.2",
+                "object.entries": "^1.1.5",
+                "object.fromentries": "^2.0.5",
+                "object.hasown": "^1.1.1",
+                "object.values": "^1.1.5",
+                "prop-types": "^15.8.1",
+                "resolve": "^2.0.0-next.3",
+                "semver": "^6.3.0",
+                "string.prototype.matchall": "^4.0.7"
+            },
+            "dependencies": {
+                "doctrine": {
+                    "version": "2.1.0",
+                    "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+                    "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+                    "dev": true,
+                    "requires": {
+                        "esutils": "^2.0.2"
+                    }
+                },
+                "estraverse": {
+                    "version": "5.3.0",
+                    "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+                    "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+                    "dev": true
+                },
+                "resolve": {
+                    "version": "2.0.0-next.4",
+                    "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz",
+                    "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==",
+                    "dev": true,
+                    "requires": {
+                        "is-core-module": "^2.9.0",
+                        "path-parse": "^1.0.7",
+                        "supports-preserve-symlinks-flag": "^1.0.0"
+                    }
+                },
+                "semver": {
+                    "version": "6.3.0",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+                    "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-plugin-react-hooks": {
+            "version": "4.6.0",
+            "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
+            "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
+            "dev": true
+        },
+        "eslint-rule-composer": {
+            "version": "0.3.0",
+            "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz",
+            "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==",
+            "dev": true
+        },
+        "eslint-scope": {
+            "version": "5.1.1",
+            "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+            "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+            "dev": true,
+            "requires": {
+                "esrecurse": "^4.3.0",
+                "estraverse": "^4.1.1"
+            }
+        },
+        "eslint-utils": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz",
+            "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==",
+            "dev": true,
+            "requires": {
+                "eslint-visitor-keys": "^2.0.0"
+            },
+            "dependencies": {
+                "eslint-visitor-keys": {
+                    "version": "2.1.0",
+                    "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+                    "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+                    "dev": true
+                }
+            }
+        },
+        "eslint-visitor-keys": {
+            "version": "3.3.0",
+            "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
+            "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
+            "dev": true
+        },
+        "espree": {
+            "version": "9.3.2",
+            "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz",
+            "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==",
+            "dev": true,
+            "requires": {
+                "acorn": "^8.7.1",
+                "acorn-jsx": "^5.3.2",
+                "eslint-visitor-keys": "^3.3.0"
+            }
+        },
+        "esprima": {
+            "version": "4.0.1",
+            "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+            "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
+        },
+        "esquery": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
+            "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
+            "dev": true,
+            "requires": {
+                "estraverse": "^5.1.0"
+            },
+            "dependencies": {
+                "estraverse": {
+                    "version": "5.3.0",
+                    "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+                    "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+                    "dev": true
+                }
+            }
+        },
+        "esrecurse": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+            "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+            "dev": true,
+            "requires": {
+                "estraverse": "^5.2.0"
+            },
+            "dependencies": {
+                "estraverse": {
+                    "version": "5.3.0",
+                    "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+                    "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+                    "dev": true
+                }
             }
         },
         "estraverse": {
@@ -6451,6 +7843,28 @@
                 }
             }
         },
+        "external-editor": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
+            "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+            "dev": true,
+            "requires": {
+                "chardet": "^0.7.0",
+                "iconv-lite": "^0.4.24",
+                "tmp": "^0.0.33"
+            },
+            "dependencies": {
+                "iconv-lite": {
+                    "version": "0.4.24",
+                    "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+                    "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+                    "dev": true,
+                    "requires": {
+                        "safer-buffer": ">= 2.1.2 < 3"
+                    }
+                }
+            }
+        },
         "extglob": {
             "version": "2.0.4",
             "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
@@ -6554,6 +7968,12 @@
             "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
             "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
         },
+        "fast-diff": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
+            "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+            "dev": true
+        },
         "fast-json-stable-stringify": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -6614,6 +8034,24 @@
                 "when": ">= 0.0.1"
             }
         },
+        "figures": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
+            "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+            "dev": true,
+            "requires": {
+                "escape-string-regexp": "^1.0.5"
+            }
+        },
+        "file-entry-cache": {
+            "version": "6.0.1",
+            "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+            "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+            "dev": true,
+            "requires": {
+                "flat-cache": "^3.0.4"
+            }
+        },
         "file-loader": {
             "version": "3.0.1",
             "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-3.0.1.tgz",
@@ -6818,6 +8256,22 @@
                 "is-buffer": "~2.0.3"
             }
         },
+        "flat-cache": {
+            "version": "3.0.4",
+            "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+            "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+            "dev": true,
+            "requires": {
+                "flatted": "^3.1.0",
+                "rimraf": "^3.0.2"
+            }
+        },
+        "flatted": {
+            "version": "3.2.6",
+            "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz",
+            "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==",
+            "dev": true
+        },
         "flexlayout-react": {
             "version": "0.3.11",
             "resolved": "https://registry.npmjs.org/flexlayout-react/-/flexlayout-react-0.3.11.tgz",
@@ -7144,6 +8598,12 @@
                 "functions-have-names": "^1.2.2"
             }
         },
+        "functional-red-black-tree": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+            "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
+            "dev": true
+        },
         "functions-have-names": {
             "version": "1.2.3",
             "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
@@ -7645,14 +9105,14 @@
                 "isarray": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
-                    "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+                    "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
                 }
             }
         },
         "has-cors": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
-            "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
+            "integrity": "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA=="
         },
         "has-flag": {
             "version": "3.0.0",
@@ -7963,11 +9423,6 @@
                 }
             }
         },
-        "human-signals": {
-            "version": "2.1.0",
-            "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
-            "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="
-        },
         "hyntax": {
             "version": "1.1.9",
             "resolved": "https://registry.npmjs.org/hyntax/-/hyntax-1.1.9.tgz",
@@ -8153,7 +9608,7 @@
         "indexof": {
             "version": "0.0.1",
             "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
-            "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
+            "integrity": "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg=="
         },
         "inflight": {
             "version": "1.0.6",
@@ -8198,6 +9653,116 @@
                 "css-in-js-utils": "^2.0.0"
             }
         },
+        "inquirer": {
+            "version": "7.3.3",
+            "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz",
+            "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==",
+            "dev": true,
+            "requires": {
+                "ansi-escapes": "^4.2.1",
+                "chalk": "^4.1.0",
+                "cli-cursor": "^3.1.0",
+                "cli-width": "^3.0.0",
+                "external-editor": "^3.0.3",
+                "figures": "^3.0.0",
+                "lodash": "^4.17.19",
+                "mute-stream": "0.0.8",
+                "run-async": "^2.4.0",
+                "rxjs": "^6.6.0",
+                "string-width": "^4.1.0",
+                "strip-ansi": "^6.0.0",
+                "through": "^2.3.6"
+            },
+            "dependencies": {
+                "ansi-regex": {
+                    "version": "5.0.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+                    "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+                    "dev": true
+                },
+                "ansi-styles": {
+                    "version": "4.3.0",
+                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+                    "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+                    "dev": true,
+                    "requires": {
+                        "color-convert": "^2.0.1"
+                    }
+                },
+                "chalk": {
+                    "version": "4.1.2",
+                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+                    "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-styles": "^4.1.0",
+                        "supports-color": "^7.1.0"
+                    }
+                },
+                "color-convert": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+                    "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+                    "dev": true,
+                    "requires": {
+                        "color-name": "~1.1.4"
+                    }
+                },
+                "color-name": {
+                    "version": "1.1.4",
+                    "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+                    "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+                    "dev": true
+                },
+                "emoji-regex": {
+                    "version": "8.0.0",
+                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+                    "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+                    "dev": true
+                },
+                "has-flag": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+                    "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+                    "dev": true
+                },
+                "is-fullwidth-code-point": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+                    "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+                    "dev": true
+                },
+                "string-width": {
+                    "version": "4.2.3",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+                    "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+                    "dev": true,
+                    "requires": {
+                        "emoji-regex": "^8.0.0",
+                        "is-fullwidth-code-point": "^3.0.0",
+                        "strip-ansi": "^6.0.1"
+                    }
+                },
+                "strip-ansi": {
+                    "version": "6.0.1",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+                    "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^5.0.1"
+                    }
+                },
+                "supports-color": {
+                    "version": "7.2.0",
+                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+                    "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+                    "dev": true,
+                    "requires": {
+                        "has-flag": "^4.0.0"
+                    }
+                }
+            }
+        },
         "inspect-function": {
             "version": "0.2.2",
             "resolved": "https://registry.npmjs.org/inspect-function/-/inspect-function-0.2.2.tgz",
@@ -9055,6 +10620,12 @@
             "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
             "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
         },
+        "json-stable-stringify-without-jsonify": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+            "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+            "dev": true
+        },
         "json-stringify-safe": {
             "version": "5.0.1",
             "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
@@ -9195,6 +10766,30 @@
                 "promise": "^7.0.1"
             }
         },
+        "jsx-ast-utils": {
+            "version": "3.3.2",
+            "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz",
+            "integrity": "sha512-4ZCADZHRkno244xlNnn4AOG6sRQ7iBZ5BbgZ4vW4y5IZw7cVUD1PPeblm1xx/nfmMxPdt/LHsXZW8z/j58+l9Q==",
+            "dev": true,
+            "requires": {
+                "array-includes": "^3.1.5",
+                "object.assign": "^4.1.2"
+            },
+            "dependencies": {
+                "object.assign": {
+                    "version": "4.1.2",
+                    "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+                    "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+                    "dev": true,
+                    "requires": {
+                        "call-bind": "^1.0.0",
+                        "define-properties": "^1.1.3",
+                        "has-symbols": "^1.0.1",
+                        "object-keys": "^1.1.1"
+                    }
+                }
+            }
+        },
         "jszip": {
             "version": "3.10.0",
             "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.0.tgz",
@@ -9292,6 +10887,21 @@
                 "graceful-fs": "^4.1.9"
             }
         },
+        "language-subtag-registry": {
+            "version": "0.3.22",
+            "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz",
+            "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==",
+            "dev": true
+        },
+        "language-tags": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz",
+            "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==",
+            "dev": true,
+            "requires": {
+                "language-subtag-registry": "~0.3.2"
+            }
+        },
         "latest-version": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz",
@@ -9460,7 +11070,7 @@
         "lodash.isequal": {
             "version": "4.5.0",
             "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
-            "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+            "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="
         },
         "lodash.isplainobject": {
             "version": "4.0.6",
@@ -9711,7 +11321,7 @@
         "mathquill": {
             "version": "0.10.1-a",
             "resolved": "https://registry.npmjs.org/mathquill/-/mathquill-0.10.1-a.tgz",
-            "integrity": "sha1-vyylaQEAY6w0vNXVKa3Ag3zVPD8=",
+            "integrity": "sha512-snSAEwAtwdwBFSor+nVBnWWQtTw67kgAgKMyAIxuz4ZPboy0qkWZmd7BL3lfOXp/INihhRlU1PcfaAtDaRhmzA==",
             "requires": {
                 "jquery": "^1.12.3"
             },
@@ -9719,7 +11329,7 @@
                 "jquery": {
                     "version": "1.12.4",
                     "resolved": "https://registry.npmjs.org/jquery/-/jquery-1.12.4.tgz",
-                    "integrity": "sha1-AeHfuikP5z3rp3zurLD5ui/sngw="
+                    "integrity": "sha512-UEVp7PPK9xXYSk8xqXCJrkXnKZtlgWkd2GsAQbMRFK6S/ePU2JN5G2Zum8hIVjzR3CpdfSqdqAzId/xd4TJHeg=="
                 }
             }
         },
@@ -9817,7 +11427,8 @@
         "merge-stream": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
-            "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
+            "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+            "dev": true
         },
         "methods": {
             "version": "1.1.2",
@@ -9871,7 +11482,8 @@
         "mimic-fn": {
             "version": "2.1.0",
             "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
-            "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
+            "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+            "dev": true
         },
         "mimic-response": {
             "version": "2.1.0",
@@ -10301,6 +11913,12 @@
             "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
             "dev": true
         },
+        "mute-stream": {
+            "version": "0.0.8",
+            "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+            "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
+            "dev": true
+        },
         "nan": {
             "version": "2.16.0",
             "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz",
@@ -10339,6 +11957,12 @@
             "resolved": "https://registry.npmjs.org/native-or-bluebird/-/native-or-bluebird-1.2.0.tgz",
             "integrity": "sha1-OcR7/Xgl0fuf+tMiEK4l2q3xAck="
         },
+        "natural-compare": {
+            "version": "1.4.0",
+            "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+            "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+            "dev": true
+        },
         "negotiator": {
             "version": "0.6.3",
             "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@@ -14468,6 +16092,28 @@
                 "object-keys": "^1.0.11"
             }
         },
+        "object.entries": {
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.5.tgz",
+            "integrity": "sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==",
+            "dev": true,
+            "requires": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.19.1"
+            }
+        },
+        "object.fromentries": {
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.5.tgz",
+            "integrity": "sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==",
+            "dev": true,
+            "requires": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.19.1"
+            }
+        },
         "object.getownpropertydescriptors": {
             "version": "2.1.4",
             "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz",
@@ -14479,6 +16125,16 @@
                 "es-abstract": "^1.20.1"
             }
         },
+        "object.hasown": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.1.tgz",
+            "integrity": "sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==",
+            "dev": true,
+            "requires": {
+                "define-properties": "^1.1.4",
+                "es-abstract": "^1.19.5"
+            }
+        },
         "object.pick": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
@@ -14487,6 +16143,17 @@
                 "isobject": "^3.0.1"
             }
         },
+        "object.values": {
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz",
+            "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==",
+            "dev": true,
+            "requires": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.19.1"
+            }
+        },
         "obuf": {
             "version": "1.1.2",
             "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
@@ -14518,6 +16185,7 @@
             "version": "5.1.2",
             "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
             "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+            "dev": true,
             "requires": {
                 "mimic-fn": "^2.1.0"
             }
@@ -14563,9 +16231,9 @@
             }
         },
         "orderedmap": {
-            "version": "1.1.8",
-            "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-1.1.8.tgz",
-            "integrity": "sha512-eWEYOAggZZpZbJ9CTsqAKOTxlbBHdHZ8pzcfEvNTxGrjQ/m+Q25nSWUiMlT9MTbgpB6FOiBDKqsgJ2FlLDVNaw=="
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.0.0.tgz",
+            "integrity": "sha512-buf4PoAMlh45b8a8gsGy/X6w279TSqkyAS0C0wdTSJwFSU+ljQFJON5I8NfjLHoCXwpSROIo2wr0g33T+kQshQ=="
         },
         "os-homedir": {
             "version": "1.0.2",
@@ -15270,6 +16938,21 @@
             "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
             "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw="
         },
+        "prettier": {
+            "version": "2.7.1",
+            "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
+            "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
+            "dev": true
+        },
+        "prettier-linter-helpers": {
+            "version": "1.0.0",
+            "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+            "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+            "dev": true,
+            "requires": {
+                "fast-diff": "^1.1.2"
+            }
+        },
         "pretty-error": {
             "version": "4.0.0",
             "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
@@ -15402,11 +17085,11 @@
             }
         },
         "prosemirror-model": {
-            "version": "1.18.0",
-            "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.18.0.tgz",
-            "integrity": "sha512-EZLl0mvVIixEL1V538GsPP4Iw+D83gBRdLcjL7VouzoPPWlhhv/0Y0V9ttT5lUhF7jieIrNHOa3XNFs1FwgrOw==",
+            "version": "1.18.1",
+            "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.18.1.tgz",
+            "integrity": "sha512-IxSVBKAEMjD7s3n8cgtwMlxAXZrC7Mlag7zYsAKDndAqnDScvSmp/UdnRTV/B33lTCVU3CCm7dyAn/rVVD0mcw==",
             "requires": {
-                "orderedmap": "^1.1.0"
+                "orderedmap": "^2.0.0"
             }
         },
         "prosemirror-schema-list": {
@@ -15420,9 +17103,9 @@
             }
         },
         "prosemirror-state": {
-            "version": "1.4.0",
-            "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.0.tgz",
-            "integrity": "sha512-mVDZdjNX/YT5FvypiwbphJe9psA5h+j9apsSszVRFc6oKFoIInvzdujh8QW9f9lwHtSYajLxNiM1hPhd0Sl1XA==",
+            "version": "1.4.1",
+            "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.1.tgz",
+            "integrity": "sha512-U/LBDW2gNmVa07sz/D229XigSdDQ5CLFwVB1Vb32MJbAHHhWe/6pOc721faI17tqw4pZ49i1xfY/jEZ9tbIhPg==",
             "requires": {
                 "prosemirror-model": "^1.0.0",
                 "prosemirror-transform": "^1.0.0"
@@ -15437,9 +17120,9 @@
             }
         },
         "prosemirror-view": {
-            "version": "1.26.0",
-            "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.26.0.tgz",
-            "integrity": "sha512-tT8ICUAw6YfOTRIwVQJoCdF65BYxoWFjAqmv44Kn4phYjdLJ8Vc92dhL3LnmwAQuZ9yfPHzRR6Lfb3/5EVJHWg==",
+            "version": "1.26.5",
+            "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.26.5.tgz",
+            "integrity": "sha512-SO+AX6WwdbJZHVvuloXI0qfO+YJAnZAat8qrYwfiqTQwL/FewLUnr0m3EXZ6a60hQs8/Q/lzeJXiFR/dOPaaKQ==",
             "requires": {
                 "prosemirror-model": "^1.16.0",
                 "prosemirror-state": "^1.0.0",
@@ -15981,15 +17664,26 @@
             }
         },
         "react-grid-layout": {
-            "version": "0.18.3",
-            "resolved": "https://registry.npmjs.org/react-grid-layout/-/react-grid-layout-0.18.3.tgz",
-            "integrity": "sha512-lHkrk941Tk5nTwZPa9uj6ttHBT0VehSHwEhWbINBJKvM1GRaFNOefvjcuxSyuCI5JWjVUP+Qm3ARt2470AlxMA==",
+            "version": "1.3.4",
+            "resolved": "https://registry.npmjs.org/react-grid-layout/-/react-grid-layout-1.3.4.tgz",
+            "integrity": "sha512-sB3rNhorW77HUdOjB4JkelZTdJGQKuXLl3gNg+BI8gJkTScspL1myfZzW/EM0dLEn+1eH+xW+wNqk0oIM9o7cw==",
             "requires": {
-                "classnames": "2.x",
+                "clsx": "^1.1.1",
                 "lodash.isequal": "^4.0.0",
-                "prop-types": "^15.0.0",
+                "prop-types": "^15.8.1",
                 "react-draggable": "^4.0.0",
-                "react-resizable": "^1.9.0"
+                "react-resizable": "^3.0.4"
+            },
+            "dependencies": {
+                "react-resizable": {
+                    "version": "3.0.4",
+                    "resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-3.0.4.tgz",
+                    "integrity": "sha512-StnwmiESiamNzdRHbSSvA65b0ZQJ7eVQpPusrSmcpyGKzC0gojhtO62xxH6YOBmepk9dQTBi9yxidL3W4s3EBA==",
+                    "requires": {
+                        "prop-types": "15.x",
+                        "react-draggable": "^4.0.3"
+                    }
+                }
             }
         },
         "react-icons": {
@@ -16189,9 +17883,9 @@
             }
         },
         "react-refresh-typescript": {
-            "version": "2.0.5",
-            "resolved": "https://registry.npmjs.org/react-refresh-typescript/-/react-refresh-typescript-2.0.5.tgz",
-            "integrity": "sha512-TlcxF9VMBBS8dHvMD+3dklgArXeIhjbtT7R09ycsXvhPPYmv70J67kiGT8mxe6/tYzkql9aCHaZAY6QYdutLdw=="
+            "version": "2.0.7",
+            "resolved": "https://registry.npmjs.org/react-refresh-typescript/-/react-refresh-typescript-2.0.7.tgz",
+            "integrity": "sha512-KbuW57FauO11e6a9gU836sCm3M3z0b2+J2qPhUudg0QplOfz0eAF3gMeshcUC/ChfNLJCK1SZxvYmUtRxiZE5A=="
         },
         "react-resizable": {
             "version": "1.11.1",
@@ -16298,14 +17992,30 @@
             "integrity": "sha512-EblIqTAsIpkYeM8bZtC4lcpTE0A2zCEGipFB52RgcQq/q+0oryrk7Sxt+sqhIjUu6xMNEVywV8dr74lz5yWO6A=="
         },
         "react-transition-group": {
-            "version": "2.9.0",
-            "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz",
-            "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==",
+            "version": "4.4.2",
+            "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz",
+            "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==",
             "requires": {
-                "dom-helpers": "^3.4.0",
+                "@babel/runtime": "^7.5.5",
+                "dom-helpers": "^5.0.1",
                 "loose-envify": "^1.4.0",
-                "prop-types": "^15.6.2",
-                "react-lifecycles-compat": "^3.0.4"
+                "prop-types": "^15.6.2"
+            },
+            "dependencies": {
+                "csstype": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
+                    "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
+                },
+                "dom-helpers": {
+                    "version": "5.2.1",
+                    "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+                    "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+                    "requires": {
+                        "@babel/runtime": "^7.8.7",
+                        "csstype": "^3.0.2"
+                    }
+                }
             }
         },
         "react-use-measure": {
@@ -16492,6 +18202,12 @@
                 "functions-have-names": "^1.2.2"
             }
         },
+        "regexpp": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+            "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
+            "dev": true
+        },
         "registry-auth-token": {
             "version": "3.4.0",
             "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz",
@@ -16811,6 +18527,16 @@
                 }
             }
         },
+        "restore-cursor": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+            "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+            "dev": true,
+            "requires": {
+                "onetime": "^5.1.0",
+                "signal-exit": "^3.0.2"
+            }
+        },
         "ret": {
             "version": "0.1.15",
             "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
@@ -16856,6 +18582,12 @@
                 "sdp": "^2.6.0"
             }
         },
+        "run-async": {
+            "version": "2.4.1",
+            "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
+            "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
+            "dev": true
+        },
         "run-queue": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
@@ -16873,6 +18605,23 @@
                 }
             }
         },
+        "rxjs": {
+            "version": "6.6.7",
+            "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
+            "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==",
+            "dev": true,
+            "requires": {
+                "tslib": "^1.9.0"
+            },
+            "dependencies": {
+                "tslib": {
+                    "version": "1.14.1",
+                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+                    "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+                    "dev": true
+                }
+            }
+        },
         "safe-buffer": {
             "version": "5.1.2",
             "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@@ -17446,6 +19195,17 @@
             "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
             "dev": true
         },
+        "slice-ansi": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
+            "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+            "dev": true,
+            "requires": {
+                "ansi-styles": "^3.2.0",
+                "astral-regex": "^1.0.0",
+                "is-fullwidth-code-point": "^2.0.0"
+            }
+        },
         "sliced": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz",
@@ -17567,15 +19327,15 @@
             }
         },
         "socket.io": {
-            "version": "2.4.1",
-            "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.4.1.tgz",
-            "integrity": "sha512-Si18v0mMXGAqLqCVpTxBa8MGqriHGQh8ccEOhmsmNS3thNCGBwO8WGrwMibANsWtQQ5NStdZwHqZR3naJVFc3w==",
+            "version": "2.5.0",
+            "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.5.0.tgz",
+            "integrity": "sha512-gGunfS0od3VpwDBpGwVkzSZx6Aqo9uOcf1afJj2cKnKFAoyl16fvhpsUhmUFd4Ldbvl5JvRQed6eQw6oQp6n8w==",
             "requires": {
                 "debug": "~4.1.0",
-                "engine.io": "~3.5.0",
+                "engine.io": "~3.6.0",
                 "has-binary2": "~1.0.2",
                 "socket.io-adapter": "~1.1.0",
-                "socket.io-client": "2.4.0",
+                "socket.io-client": "2.5.0",
                 "socket.io-parser": "~3.4.0"
             },
             "dependencies": {
@@ -17595,9 +19355,9 @@
             "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g=="
         },
         "socket.io-client": {
-            "version": "2.4.0",
-            "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.4.0.tgz",
-            "integrity": "sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ==",
+            "version": "2.5.0",
+            "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.5.0.tgz",
+            "integrity": "sha512-lOO9clmdgssDykiOmVQQitwBAF3I6mYcQAo7hQ7AM6Ny5X7fp8hIJ3HcQs3Rjz4SoggoxA1OgrQyY8EgTbcPYw==",
             "requires": {
                 "backo2": "1.0.2",
                 "component-bind": "1.0.0",
@@ -17623,12 +19383,12 @@
                 "isarray": {
                     "version": "2.0.1",
                     "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
-                    "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+                    "integrity": "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
                 },
                 "ms": {
                     "version": "2.0.0",
                     "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+                    "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
                 },
                 "socket.io-parser": {
                     "version": "3.3.2",
@@ -18052,6 +19812,22 @@
             "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz",
             "integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg=="
         },
+        "string.prototype.matchall": {
+            "version": "4.0.7",
+            "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz",
+            "integrity": "sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==",
+            "dev": true,
+            "requires": {
+                "call-bind": "^1.0.2",
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.19.1",
+                "get-intrinsic": "^1.1.1",
+                "has-symbols": "^1.0.3",
+                "internal-slot": "^1.0.3",
+                "regexp.prototype.flags": "^1.4.1",
+                "side-channel": "^1.0.4"
+            }
+        },
         "string.prototype.trimend": {
             "version": "1.0.5",
             "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz",
@@ -18128,11 +19904,6 @@
             "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
             "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
         },
-        "strip-final-newline": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
-            "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="
-        },
         "strip-indent": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
@@ -18240,6 +20011,46 @@
             "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
             "dev": true
         },
+        "table": {
+            "version": "5.4.6",
+            "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
+            "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
+            "dev": true,
+            "requires": {
+                "ajv": "^6.10.2",
+                "lodash": "^4.17.14",
+                "slice-ansi": "^2.1.0",
+                "string-width": "^3.0.0"
+            },
+            "dependencies": {
+                "ansi-regex": {
+                    "version": "4.1.1",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+                    "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+                    "dev": true
+                },
+                "string-width": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+                    "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+                    "dev": true,
+                    "requires": {
+                        "emoji-regex": "^7.0.1",
+                        "is-fullwidth-code-point": "^2.0.0",
+                        "strip-ansi": "^5.1.0"
+                    }
+                },
+                "strip-ansi": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+                    "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^4.1.0"
+                    }
+                }
+            }
+        },
         "table-layout": {
             "version": "0.4.5",
             "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz",
@@ -18373,6 +20184,12 @@
                 }
             }
         },
+        "text-table": {
+            "version": "0.2.0",
+            "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+            "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+            "dev": true
+        },
         "textarea-caret": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/textarea-caret/-/textarea-caret-3.1.0.tgz",
@@ -18451,10 +20268,19 @@
             "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.2.tgz",
             "integrity": "sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA=="
         },
+        "tmp": {
+            "version": "0.0.33",
+            "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+            "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+            "dev": true,
+            "requires": {
+                "os-tmpdir": "~1.0.2"
+            }
+        },
         "to-array": {
             "version": "0.1.4",
             "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
-            "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
+            "integrity": "sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A=="
         },
         "to-fast-properties": {
             "version": "2.0.0",
@@ -18835,6 +20661,26 @@
                 }
             }
         },
+        "tsconfig-paths": {
+            "version": "3.14.1",
+            "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz",
+            "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==",
+            "dev": true,
+            "requires": {
+                "@types/json5": "^0.0.29",
+                "json5": "^1.0.1",
+                "minimist": "^1.2.6",
+                "strip-bom": "^3.0.0"
+            },
+            "dependencies": {
+                "strip-bom": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+                    "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+                    "dev": true
+                }
+            }
+        },
         "tslib": {
             "version": "2.4.0",
             "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
@@ -18946,12 +20792,6 @@
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
             "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
         },
-        "type": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
-            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
-            "dev": true
-        },
         "type-check": {
             "version": "0.3.2",
             "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
@@ -18966,6 +20806,12 @@
             "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
             "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="
         },
+        "type-fest": {
+            "version": "0.20.2",
+            "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+            "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+            "dev": true
+        },
         "type-is": {
             "version": "1.6.18",
             "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@@ -18986,9 +20832,9 @@
             "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
         },
         "typescript": {
-            "version": "4.7.3",
-            "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.3.tgz",
-            "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==",
+            "version": "4.7.4",
+            "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
+            "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
             "dev": true
         },
         "typescript-collections": {
@@ -19415,6 +21261,12 @@
             "resolved": "https://registry.npmjs.org/uuid-js/-/uuid-js-0.7.5.tgz",
             "integrity": "sha1-bIhtAqU9LUDc8l2RoXC0p7JblNA="
         },
+        "v8-compile-cache": {
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
+            "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
+            "dev": true
+        },
         "valid-url": {
             "version": "1.0.9",
             "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz",
@@ -19637,17 +21489,17 @@
             }
         },
         "webpack-cli": {
-            "version": "4.9.2",
-            "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.2.tgz",
-            "integrity": "sha512-m3/AACnBBzK/kMTcxWHcZFPrw/eQuY4Df1TxvIWfWM2x7mRqBQCqKEd96oCUa9jkapLBaFfRce33eGDb4Pr7YQ==",
+            "version": "4.10.0",
+            "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz",
+            "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==",
             "requires": {
                 "@discoveryjs/json-ext": "^0.5.0",
-                "@webpack-cli/configtest": "^1.1.1",
-                "@webpack-cli/info": "^1.4.1",
-                "@webpack-cli/serve": "^1.6.1",
+                "@webpack-cli/configtest": "^1.2.0",
+                "@webpack-cli/info": "^1.5.0",
+                "@webpack-cli/serve": "^1.7.0",
                 "colorette": "^2.0.14",
                 "commander": "^7.0.0",
-                "execa": "^5.0.0",
+                "cross-spawn": "^7.0.3",
                 "fastest-levenshtein": "^1.0.12",
                 "import-local": "^3.0.2",
                 "interpret": "^2.2.0",
@@ -19670,40 +21522,11 @@
                         "which": "^2.0.1"
                     }
                 },
-                "execa": {
-                    "version": "5.1.1",
-                    "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
-                    "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
-                    "requires": {
-                        "cross-spawn": "^7.0.3",
-                        "get-stream": "^6.0.0",
-                        "human-signals": "^2.1.0",
-                        "is-stream": "^2.0.0",
-                        "merge-stream": "^2.0.0",
-                        "npm-run-path": "^4.0.1",
-                        "onetime": "^5.1.2",
-                        "signal-exit": "^3.0.3",
-                        "strip-final-newline": "^2.0.0"
-                    }
-                },
                 "interpret": {
                     "version": "2.2.0",
                     "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
                     "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw=="
                 },
-                "is-stream": {
-                    "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
-                    "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="
-                },
-                "npm-run-path": {
-                    "version": "4.0.1",
-                    "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
-                    "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
-                    "requires": {
-                        "path-key": "^3.0.0"
-                    }
-                },
                 "path-key": {
                     "version": "3.1.1",
                     "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
@@ -20281,6 +22104,15 @@
             "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
             "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
         },
+        "write": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
+            "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
+            "dev": true,
+            "requires": {
+                "mkdirp": "^0.5.1"
+            }
+        },
         "write-file-atomic": {
             "version": "2.4.3",
             "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz",
@@ -20445,7 +22277,7 @@
         "yeast": {
             "version": "0.1.2",
             "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
-            "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
+            "integrity": "sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg=="
         },
         "yn": {
             "version": "2.0.0",
diff --git a/package.json b/package.json
index e4ec2798d..93968dce4 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,7 @@
         "@types/exif": "^0.6.3",
         "@types/express": "^4.17.13",
         "@types/express-flash": "0.0.0",
-        "@types/express-session": "^1.17.4",
+        "@types/express-session": "^1.17.5",
         "@types/express-validator": "^3.0.0",
         "@types/file-saver": "^2.0.5",
         "@types/google-maps-react": "^2.0.5",
@@ -49,7 +49,7 @@
         "@types/mongoose": "^5.11.97",
         "@types/node": "^10.17.60",
         "@types/nodemailer": "^4.6.6",
-        "@types/passport": "^1.0.7",
+        "@types/passport": "^1.0.9",
         "@types/passport-google-oauth20": "^2.0.11",
         "@types/passport-local": "^1.0.34",
         "@types/pdfjs-dist": "^2.10.378",
@@ -65,12 +65,15 @@
         "@types/prosemirror-transform": "^1.1.5",
         "@types/prosemirror-view": "^1.23.1",
         "@types/rc-switch": "^1.9.2",
-        "@types/react": "^16.14.23",
+        "@types/react": "^18.0.15",
+        "@types/react-icons": "^3.0.0",
+        "@types/react-reconciler": "^0.26.4",
+        "@types/react-transition-group": "^4.4.5",
         "@types/react-autosuggest": "^9.3.14",
         "@types/react-color": "^2.17.6",
         "@types/react-datepicker": "^3.1.8",
-        "@types/react-dom": "^16.9.14",
-        "@types/react-grid-layout": "^0.17.2",
+        "@types/react-dom": "^18.0.6",
+        "@types/react-grid-layout": "^1.3.2",
         "@types/react-measure": "^2.0.8",
         "@types/react-select": "^3.1.2",
         "@types/react-table": "^6.8.9",
@@ -117,7 +120,7 @@
         "ts-node-dev": "^1.1.8",
         "tslint": "^5.20.1",
         "tslint-loader": "^3.6.0",
-        "typescript": "^4.6.2",
+        "typescript": "^4.7.4",
         "webpack": "^5.69.1",
         "webpack-dev-server": "^3.11.3",
         "webpack-hot-middleware": "^2.25.1"
@@ -129,7 +132,7 @@
         "@fortawesome/free-brands-svg-icons": "^5.15.4",
         "@fortawesome/free-regular-svg-icons": "^5.15.4",
         "@fortawesome/free-solid-svg-icons": "^5.15.4",
-        "@fortawesome/react-fontawesome": "^0.1.17",
+        "@fortawesome/react-fontawesome": "^0.1.19",
         "@hig/flyout": "^1.3.1",
         "@hig/theme-context": "^2.1.3",
         "@hig/theme-data": "^2.23.1",
@@ -145,14 +148,13 @@
         "@types/dom-speech-recognition": "0.0.1",
         "@types/formidable": "1.0.31",
         "@types/google-maps": "^3.2.3",
-        "@types/react-icons": "^3.0.0",
-        "@types/react-reconciler": "^0.26.4",
         "@types/reveal": "^3.3.33",
         "@types/supercluster": "^7.1.0",
         "@types/three": "^0.126.2",
         "@types/web": "0.0.53",
         "@types/webscopeio__react-textarea-autocomplete": "^4.7.2",
         "@webscopeio/react-textarea-autocomplete": "^4.9.1",
+        "D": "^1.0.0",
         "adm-zip": "^0.4.16",
         "archiver": "^3.1.1",
         "array-batcher": "^1.2.3",
@@ -167,7 +169,7 @@
         "bootstrap": "^4.6.1",
         "browser-assert": "^1.2.1",
         "bson": "^4.6.1",
-        "canvas": "^2.9.0",
+        "canvas": "^2.9.3",
         "child_process": "^1.0.2",
         "chrome": "^0.1.0",
         "class-transformer": "^0.2.0",
@@ -178,7 +180,6 @@
         "cookie-parser": "^1.4.6",
         "cookie-session": "^2.0.0",
         "cors": "^2.8.5",
-        "D": "^1.0.0",
         "depcheck": "^0.9.2",
         "equation-editor-react": "github:bobzel/equation-editor-react#useLocally",
         "exif": "^0.6.0",
@@ -250,11 +251,11 @@
         "prosemirror-history": "^1.2.0",
         "prosemirror-inputrules": "^1.1.3",
         "prosemirror-keymap": "^1.1.5",
-        "prosemirror-model": "^1.16.1",
+        "prosemirror-model": "^1.18.1",
         "prosemirror-schema-list": "^1.1.6",
-        "prosemirror-state": "^1.3.4",
+        "prosemirror-state": "^1.4.1",
         "prosemirror-transform": "^1.3.4",
-        "prosemirror-view": "^1.23.7",
+        "prosemirror-view": "^1.26.5",
         "pug": "^2.0.4",
         "puppeteer": "^3.3.0",
         "query-string": "^6.14.1",
@@ -268,18 +269,19 @@
         "react-compound-slider": "^2.5.0",
         "react-datepicker": "^3.8.0",
         "react-dom": "^16.14.0",
-        "react-grid-layout": "^0.18.3",
+        "react-grid-layout": "^1.3.4",
         "react-icons": "^4.3.1",
         "react-image-lightbox-with-rotate": "^5.1.1",
         "react-jsx-parser": "^1.29.0",
         "react-loading": "^2.0.3",
         "react-measure": "^2.5.2",
-        "react-refresh-typescript": "^2.0.3",
+        "react-refresh-typescript": "^2.0.7",
         "react-resizable": "^1.11.1",
         "react-resizable-rotatable-draggable": "^0.2.0",
         "react-reveal": "^1.2.2",
         "react-select": "^3.2.0",
         "react-table": "^6.11.5",
+        "react-transition-group": "^4.4.2",
         "readline": "^1.3.0",
         "request": "^2.88.2",
         "request-promise": "^4.2.6",
@@ -288,8 +290,8 @@
         "serializr": "^1.5.4",
         "sharp": "^0.23.4",
         "shelljs": "^0.8.5",
-        "socket.io": "^2.4.1",
-        "socket.io-client": "^2.4.0",
+        "socket.io": "^2.5.0",
+        "socket.io-client": "^2.5.0",
         "solr-node": "^1.2.1",
         "standard-http-error": "^2.0.1",
         "stream-browserify": "^3.0.0",
@@ -306,7 +308,7 @@
         "uuid": "^3.4.0",
         "valid-url": "^1.0.9",
         "web-request": "^1.0.7",
-        "webpack-cli": "^4.9.2",
+        "webpack-cli": "^4.10.0",
         "webpack-dev-middleware": "^5.3.1",
         "webrtc-adapter": "^7.7.1",
         "wikijs": "^6.3.3",
diff --git a/src/client/util/CaptureManager.tsx b/src/client/util/CaptureManager.tsx
index c247afa26..0b5957fac 100644
--- a/src/client/util/CaptureManager.tsx
+++ b/src/client/util/CaptureManager.tsx
@@ -1,17 +1,17 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import { convertToObject } from "typescript";
-import { Doc, DocListCast } from "../../fields/Doc";
-import { BoolCast, StrCast, Cast } from "../../fields/Types";
-import { addStyleSheet, addStyleSheetRule, Utils } from "../../Utils";
-import { LightboxView } from "../views/LightboxView";
-import { MainViewModal } from "../views/MainViewModal";
-import "./CaptureManager.scss";
-import { SelectionManager } from "./SelectionManager";
-import { undoBatch } from "./UndoManager";
-const higflyout = require("@hig/flyout");
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { convertToObject } from 'typescript';
+import { Doc, DocListCast } from '../../fields/Doc';
+import { BoolCast, StrCast, Cast } from '../../fields/Types';
+import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils';
+import { LightboxView } from '../views/LightboxView';
+import { MainViewModal } from '../views/MainViewModal';
+import './CaptureManager.scss';
+import { SelectionManager } from './SelectionManager';
+import { undoBatch } from './UndoManager';
+const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
@@ -22,32 +22,31 @@ export class CaptureManager extends React.Component<{}> {
     @observable _document: any;
     @observable isOpen: boolean = false; // whether the CaptureManager is to be displayed or not.
 
-
     constructor(props: {}) {
         super(props);
         CaptureManager.Instance = this;
     }
 
-    public close = action(() => this.isOpen = false);
+    public close = action(() => (this.isOpen = false));
     public open = action((doc: Doc) => {
         this.isOpen = true;
         this._document = doc;
     });
 
-
     @computed get visibilityContent() {
-
-        return <div className="capture-block">
-            <div className="capture-block-title">Visibility</div>
-            <div className="capture-block-radio">
-                <div className="radio-container">
-                    <input type="radio" value="private" name="visibility" style={{ margin: 0, marginRight: 5 }} /> Private
-                </div>
-                <div className="radio-container">
-                    <input type="radio" value="public" name="visibility" style={{ margin: 0, marginRight: 5 }} /> Public
+        return (
+            <div className="capture-block">
+                <div className="capture-block-title">Visibility</div>
+                <div className="capture-block-radio">
+                    <div className="radio-container">
+                        <input type="radio" value="private" name="visibility" style={{ margin: 0, marginRight: 5 }} /> Private
+                    </div>
+                    <div className="radio-container">
+                        <input type="radio" value="public" name="visibility" style={{ margin: 0, marginRight: 5 }} /> Public
+                    </div>
                 </div>
             </div>
-        </div>;
+        );
     }
 
     @computed get linksContent() {
@@ -66,75 +65,79 @@ export class CaptureManager extends React.Component<{}> {
                     order.push(
                         <div className="list-item">
                             <div className="number">{i}</div>
-                            {(l.anchor1 as Doc).title}
+                            {StrCast((l.anchor1 as Doc).title)}
                         </div>
                     );
                 }
             });
         }
 
-        return <div className="capture-block">
-            <div className="capture-block-title">Links</div>
-            <div className="capture-block-list">
-                {order}
+        return (
+            <div className="capture-block">
+                <div className="capture-block-title">Links</div>
+                <div className="capture-block-list">{order}</div>
             </div>
-        </div>;
+        );
     }
 
     @computed get closeButtons() {
-        return <div className="capture-block">
-            <div className="buttons">
-                <div className="save" onClick={() => {
-                    LightboxView.SetLightboxDoc(this._document);
-                    this.close();
-                }}>
-                    Save
-                </div>
-                <div className="cancel" onClick={() => {
-                    const selected = SelectionManager.Views().slice();
-                    SelectionManager.DeselectAll();
-                    selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
-                    this.close();
-                }}>
-                    Cancel
+        return (
+            <div className="capture-block">
+                <div className="buttons">
+                    <div
+                        className="save"
+                        onClick={() => {
+                            LightboxView.SetLightboxDoc(this._document);
+                            this.close();
+                        }}>
+                        Save
+                    </div>
+                    <div
+                        className="cancel"
+                        onClick={() => {
+                            const selected = SelectionManager.Views().slice();
+                            SelectionManager.DeselectAll();
+                            selected.map(dv => dv.props.removeDocument?.(dv.props.Document));
+                            this.close();
+                        }}>
+                        Cancel
+                    </div>
                 </div>
             </div>
-        </div>;
+        );
     }
 
-
-
     private get captureInterface() {
-        return <div className="capture-interface">
-            <div className="capture-t1">
-                <div className="recordButtonOutline" style={{}}>
-                    <div className="recordButtonInner" style={{}}>
+        return (
+            <div className="capture-interface">
+                <div className="capture-t1">
+                    <div className="recordButtonOutline" style={{}}>
+                        <div className="recordButtonInner" style={{}}></div>
                     </div>
+                    Conversation Capture
                 </div>
-                Conversation Capture
-            </div>
-            <div className="capture-t2">
-
-            </div>
-            {this.visibilityContent}
-            {this.linksContent}
-            <div className="close-button" onClick={this.close}>
-                <FontAwesomeIcon icon={"times"} color="black" size={"lg"} />
+                <div className="capture-t2"></div>
+                {this.visibilityContent}
+                {this.linksContent}
+                <div className="close-button" onClick={this.close}>
+                    <FontAwesomeIcon icon={'times'} color="black" size={'lg'} />
+                </div>
+                {this.closeButtons}
             </div>
-            {this.closeButtons}
-        </div>;
-
+        );
     }
 
     render() {
-        return <MainViewModal
-            contents={this.captureInterface}
-            isDisplayed={this.isOpen}
-            interactive={true}
-            closeOnExternalClick={this.close}
-            dialogueBoxStyle={{ width: "500px", height: "350px", border: "none", background: "whitesmoke" }}
-            overlayStyle={{ background: "black" }}
-            overlayDisplayedOpacity={0.6}
-        />;
+        return (
+            <MainViewModal
+                contents={this.captureInterface}
+                isDisplayed={this.isOpen}
+                interactive={true}
+                closeOnExternalClick={this.close}
+                dialogueBoxStyle={{ width: '500px', height: '350px', border: 'none', background: 'whitesmoke' }}
+                overlayStyle={{ background: 'black' }}
+                overlayDisplayedOpacity={0.6}
+            />
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index b2a5fddcd..84efcb966 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -957,7 +957,7 @@ export class CurrentUserUtils {
         }
         new LinkManager();
 
-        DocServer.UPDATE_SERVER_CACHE();
+        setTimeout(DocServer.UPDATE_SERVER_CACHE, 2500);
         return doc;
     }
     static setupFieldInfos(doc:Doc, field="fieldInfos") {
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index 62d656c5d..59334f6a2 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -1,19 +1,19 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
 import Select from 'react-select';
-import * as RequestPromise from "request-promise";
-import { Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc";
-import { StrCast, Cast } from "../../fields/Types";
-import { Utils } from "../../Utils";
-import { MainViewModal } from "../views/MainViewModal";
-import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
-import "./GroupManager.scss";
-import { GroupMemberView } from "./GroupMemberView";
-import { SharingManager, User } from "./SharingManager";
-import { listSpec } from "../../fields/Schema";
-import { DateField } from "../../fields/DateField";
+import * as RequestPromise from 'request-promise';
+import { Doc, DocListCast, DocListCastAsync, Opt } from '../../fields/Doc';
+import { StrCast, Cast } from '../../fields/Types';
+import { Utils } from '../../Utils';
+import { MainViewModal } from '../views/MainViewModal';
+import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
+import './GroupManager.scss';
+import { GroupMemberView } from './GroupMemberView';
+import { SharingManager, User } from './SharingManager';
+import { listSpec } from '../../fields/Schema';
+import { DateField } from '../../fields/DateField';
 
 /**
  * Interface for options for the react-select component
@@ -25,7 +25,6 @@ export interface UserOptions {
 
 @observer
 export class GroupManager extends React.Component<{}> {
-
     static Instance: GroupManager;
     @observable isOpen: boolean = false; // whether the GroupManager is to be displayed or not.
     @observable private users: string[] = []; // list of users populated from the database.
@@ -34,24 +33,26 @@ export class GroupManager extends React.Component<{}> {
     @observable private createGroupModalOpen: boolean = false;
     private inputRef: React.RefObject<HTMLInputElement> = React.createRef(); // the ref for the input box.
     private createGroupButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); // the ref for the group creation button
-    @observable private buttonColour: "#979797" | "black" = "#979797";
-    @observable private groupSort: "ascending" | "descending" | "none" = "none";
+    @observable private buttonColour: '#979797' | 'black' = '#979797';
+    @observable private groupSort: 'ascending' | 'descending' | 'none' = 'none';
 
     constructor(props: Readonly<{}>) {
         super(props);
         GroupManager.Instance = this;
     }
 
-    componentDidMount() { this.populateUsers(); }
+    componentDidMount() {
+        this.populateUsers();
+    }
 
     /**
      * Fetches the list of users stored on the database.
      */
     populateUsers = async () => {
-        const userList = await RequestPromise.get(Utils.prepend("/getUsers"));
+        const userList = await RequestPromise.get(Utils.prepend('/getUsers'));
         const raw = JSON.parse(userList) as User[];
         raw.map(action(user => !this.users.some(umail => umail === user.email) && this.users.push(user.email)));
-    }
+    };
 
     /**
      * @returns the options to be rendered in the dropdown menu to add users and create a group.
@@ -68,11 +69,11 @@ export class GroupManager extends React.Component<{}> {
         // SelectionManager.DeselectAll();
         this.isOpen = true;
         this.populateUsers();
-    }
+    };
 
     /**
      * Hides the GroupManager.
-    */
+     */
     @action
     close = () => {
         this.isOpen = false;
@@ -81,26 +82,32 @@ export class GroupManager extends React.Component<{}> {
         // this.users = [];
         this.createGroupModalOpen = false;
         TaskCompletionBox.taskCompleted = false;
-    }
+    };
 
     /**
      * @returns the database of groups.
      */
-    @computed get GroupManagerDoc(): Doc | undefined { return Doc.UserDoc().globalGroupDatabase as Doc; }
+    @computed get GroupManagerDoc(): Doc | undefined {
+        return Doc.UserDoc().globalGroupDatabase as Doc;
+    }
 
     /**
      * @returns a list of all group documents.
      */
-    @computed get allGroups(): Doc[] { return DocListCast(this.GroupManagerDoc?.data); }
+    @computed get allGroups(): Doc[] {
+        return DocListCast(this.GroupManagerDoc?.data);
+    }
 
     /**
      * @returns the members of the admin group.
      */
-    @computed get adminGroupMembers(): string[] { return this.getGroup("Admin") ? JSON.parse(StrCast(this.getGroup("Admin")!.members)) : ""; }
+    @computed get adminGroupMembers(): string[] {
+        return this.getGroup('Admin') ? JSON.parse(StrCast(this.getGroup('Admin')!.members)) : '';
+    }
 
     /**
      * @returns a group document based on the group name.
-     * @param groupName 
+     * @param groupName
      */
     getGroup(groupName: string): Doc | undefined {
         return this.allGroups.find(group => group.title === groupName);
@@ -114,10 +121,9 @@ export class GroupManager extends React.Component<{}> {
         return JSON.parse(StrCast(this.getGroup(group)!.members)) as string[];
     }
 
-
     /**
      * @returns a boolean indicating whether the current user has access to edit group documents.
-     * @param groupDoc 
+     * @param groupDoc
      */
     hasEditAccess(groupDoc: Doc): boolean {
         if (!groupDoc) return false;
@@ -127,12 +133,12 @@ export class GroupManager extends React.Component<{}> {
 
     /**
      * Helper method that sets up the group document.
-     * @param groupName 
-     * @param memberEmails 
+     * @param groupName
+     * @param memberEmails
      */
     createGroupDoc(groupName: string, memberEmails: string[] = []) {
-        const name = groupName.toLowerCase() === "admin" ? "Admin" : groupName;
-        const groupDoc = new Doc("GROUP:" + name, true);
+        const name = groupName.toLowerCase() === 'admin' ? 'Admin' : groupName;
+        const groupDoc = new Doc('GROUP:' + name, true);
         groupDoc.title = name;
         groupDoc.owners = JSON.stringify([Doc.CurrentUserEmail]);
         groupDoc.members = JSON.stringify(memberEmails);
@@ -141,12 +147,12 @@ export class GroupManager extends React.Component<{}> {
 
     /**
      * Helper method that adds a group document to the database of group documents and @returns whether it was successfully added or not.
-     * @param groupDoc 
+     * @param groupDoc
      */
     addGroup(groupDoc: Doc): boolean {
         if (this.GroupManagerDoc) {
-            Doc.AddDocToList(this.GroupManagerDoc, "data", groupDoc);
-            this.GroupManagerDoc["data-lastModified"] = new DateField;
+            Doc.AddDocToList(this.GroupManagerDoc, 'data', groupDoc);
+            this.GroupManagerDoc['data-lastModified'] = new DateField();
             return true;
         }
         return false;
@@ -154,20 +160,20 @@ export class GroupManager extends React.Component<{}> {
 
     /**
      * Deletes a group from the database of group documents and @returns whether the group was deleted or not.
-     * @param group 
+     * @param group
      */
     @action
     deleteGroup(group: Doc): boolean {
         if (group) {
             if (this.GroupManagerDoc && this.hasEditAccess(group)) {
-                Doc.RemoveDocFromList(this.GroupManagerDoc, "data", group);
+                Doc.RemoveDocFromList(this.GroupManagerDoc, 'data', group);
                 SharingManager.Instance.removeGroup(group);
                 const members = JSON.parse(StrCast(group.members));
                 if (members.includes(Doc.CurrentUserEmail)) {
                     const index = DocListCast(this.GroupManagerDoc.data).findIndex(grp => grp === group);
                     index !== -1 && Cast(this.GroupManagerDoc.data, listSpec(Doc), [])?.splice(index, 1);
                 }
-                this.GroupManagerDoc["data-lastModified"] = new DateField;
+                this.GroupManagerDoc['data-lastModified'] = new DateField();
                 if (group === this.currentGroup) {
                     this.currentGroup = undefined;
                 }
@@ -179,8 +185,8 @@ export class GroupManager extends React.Component<{}> {
 
     /**
      * Adds a member to a group.
-     * @param groupDoc 
-     * @param email 
+     * @param groupDoc
+     * @param email
      */
     addMemberToGroup(groupDoc: Doc, email: string) {
         if (this.hasEditAccess(groupDoc)) {
@@ -188,14 +194,14 @@ export class GroupManager extends React.Component<{}> {
             !memberList.includes(email) && memberList.push(email);
             groupDoc.members = JSON.stringify(memberList);
             SharingManager.Instance.shareWithAddedMember(groupDoc, email);
-            this.GroupManagerDoc && (this.GroupManagerDoc["data-lastModified"] = new DateField);
+            this.GroupManagerDoc && (this.GroupManagerDoc['data-lastModified'] = new DateField());
         }
     }
 
     /**
      * Removes a member from the group.
-     * @param groupDoc 
-     * @param email 
+     * @param groupDoc
+     * @param email
      */
     removeMemberFromGroup(groupDoc: Doc, email: string) {
         if (this.hasEditAccess(groupDoc)) {
@@ -205,27 +211,27 @@ export class GroupManager extends React.Component<{}> {
                 const user = memberList.splice(index, 1)[0];
                 groupDoc.members = JSON.stringify(memberList);
                 SharingManager.Instance.removeMember(groupDoc, email);
-                this.GroupManagerDoc && (this.GroupManagerDoc["data-lastModified"] = new DateField);
+                this.GroupManagerDoc && (this.GroupManagerDoc['data-lastModified'] = new DateField());
             }
         }
     }
 
     /**
      * Handles changes in the users selected in the "Select users" dropdown.
-     * @param selectedOptions 
+     * @param selectedOptions
      */
     @action
     handleChange = (selectedOptions: any) => {
         this.selectedUsers = selectedOptions as UserOptions[];
-    }
+    };
 
     /**
      * Creates the group when the enter key has been pressed (when in the input).
-     * @param e 
+     * @param e
      */
     handleKeyDown = (e: React.KeyboardEvent) => {
-        e.key === "Enter" && this.createGroup();
-    }
+        e.key === 'Enter' && this.createGroup();
+    };
 
     /**
      * Handles the input of required fields in the setup of a group and resets the relevant variables.
@@ -234,32 +240,37 @@ export class GroupManager extends React.Component<{}> {
     createGroup = () => {
         const { value } = this.inputRef.current!;
         if (!value) {
-            alert("Please enter a group name");
+            alert('Please enter a group name');
             return;
         }
-        if (["admin", "public", "override"].includes(value.toLowerCase())) {
-            if (value.toLowerCase() !== "admin" || (value.toLowerCase() === "admin" && this.getGroup("Admin"))) {
+        if (['admin', 'public', 'override'].includes(value.toLowerCase())) {
+            if (value.toLowerCase() !== 'admin' || (value.toLowerCase() === 'admin' && this.getGroup('Admin'))) {
                 alert(`You cannot override the ${value.charAt(0).toUpperCase() + value.slice(1)} group`);
                 return;
             }
         }
         if (this.getGroup(value)) {
-            alert("Please select a unique group name");
+            alert('Please select a unique group name');
             return;
         }
-        this.createGroupDoc(value, this.selectedUsers?.map(user => user.value));
+        this.createGroupDoc(
+            value,
+            this.selectedUsers?.map(user => user.value)
+        );
         this.selectedUsers = null;
-        this.inputRef.current!.value = "";
-        this.buttonColour = "#979797";
+        this.inputRef.current!.value = '';
+        this.buttonColour = '#979797';
 
         const { left, width, top } = this.createGroupButtonRef.current!.getBoundingClientRect();
         TaskCompletionBox.popupX = left - 2 * width;
         TaskCompletionBox.popupY = top;
-        TaskCompletionBox.textDisplayed = "Group created!";
+        TaskCompletionBox.textDisplayed = 'Group created!';
         TaskCompletionBox.taskCompleted = true;
-        setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2000);
-
-    }
+        setTimeout(
+            action(() => (TaskCompletionBox.taskCompleted = false)),
+            2000
+        );
+    };
 
     /**
      * @returns the MainViewModal which allows the user to create groups.
@@ -268,50 +279,43 @@ export class GroupManager extends React.Component<{}> {
         const contents = (
             <div className="group-create">
                 <div className="group-heading" style={{ marginBottom: 0 }}>
-                    <p><b>New Group</b></p>
-                    <div className={"close-button"} onClick={action(() => {
-                        this.createGroupModalOpen = false; TaskCompletionBox.taskCompleted = false;
-                    })}>
-                        <FontAwesomeIcon icon={"times"} color={"black"} size={"lg"} />
+                    <p>
+                        <b>New Group</b>
+                    </p>
+                    <div
+                        className={'close-button'}
+                        onClick={action(() => {
+                            this.createGroupModalOpen = false;
+                            TaskCompletionBox.taskCompleted = false;
+                        })}>
+                        <FontAwesomeIcon icon={'times'} color={'black'} size={'lg'} />
                     </div>
                 </div>
-                <input
-                    className="group-input"
-                    ref={this.inputRef}
-                    onKeyDown={this.handleKeyDown}
-                    autoFocus
-                    type="text"
-                    placeholder="Group name"
-                    onChange={action(() => this.buttonColour = this.inputRef.current?.value ? "black" : "#979797")} />
+                <input className="group-input" ref={this.inputRef} onKeyDown={this.handleKeyDown} autoFocus type="text" placeholder="Group name" onChange={action(() => (this.buttonColour = this.inputRef.current?.value ? 'black' : '#979797'))} />
                 <Select
                     isMulti
                     options={this.options}
                     onChange={this.handleChange}
-                    placeholder={"Select users"}
+                    placeholder={'Select users'}
                     value={this.selectedUsers}
                     closeMenuOnSelect={false}
                     styles={{
                         dropdownIndicator: (base, state) => ({
                             ...base,
                             transition: '0.5s all ease',
-                            transform: state.selectProps.menuIsOpen ? 'rotate(180deg)' : undefined
+                            transform: state.selectProps.menuIsOpen ? 'rotate(180deg)' : undefined,
                         }),
-                        multiValue: (base) => ({
+                        multiValue: base => ({
                             ...base,
-                            maxWidth: "50%",
+                            maxWidth: '50%',
 
                             '&:hover': {
-                                maxWidth: "unset"
-                            }
-                        })
+                                maxWidth: 'unset',
+                            },
+                        }),
                     }}
                 />
-                <button
-                    ref={this.createGroupButtonRef}
-                    onClick={this.createGroup}
-                    style={{ background: this.buttonColour }}
-                    disabled={this.buttonColour === "#979797"}
-                >
+                <button ref={this.createGroupButtonRef} onClick={this.createGroup} style={{ background: this.buttonColour }} disabled={this.buttonColour === '#979797'}>
                     Create
                 </button>
             </div>
@@ -322,8 +326,12 @@ export class GroupManager extends React.Component<{}> {
                 isDisplayed={this.createGroupModalOpen}
                 interactive={true}
                 contents={contents}
-                dialogueBoxStyle={{ width: "90%", height: "70%" }}
-                closeOnExternalClick={action(() => { this.createGroupModalOpen = false; this.selectedUsers = null; TaskCompletionBox.taskCompleted = false; })}
+                dialogueBoxStyle={{ width: '90%', height: '70%' }}
+                closeOnExternalClick={action(() => {
+                    this.createGroupModalOpen = false;
+                    this.selectedUsers = null;
+                    TaskCompletionBox.taskCompleted = false;
+                })}
             />
         );
     }
@@ -332,7 +340,6 @@ export class GroupManager extends React.Component<{}> {
      * A getter that @returns the main interface for the GroupManager.
      */
     private get groupInterface() {
-
         const sortGroups = (d1: Doc, d2: Doc) => {
             const g1 = StrCast(d1.title);
             const g2 = StrCast(d2.title);
@@ -340,62 +347,50 @@ export class GroupManager extends React.Component<{}> {
             return g1 < g2 ? -1 : g1 === g2 ? 0 : 1;
         };
 
-        const groups = this.groupSort === "ascending" ? this.allGroups.sort(sortGroups) : this.groupSort === "descending" ? this.allGroups.sort(sortGroups).reverse() : this.allGroups;
+        const groups = this.groupSort === 'ascending' ? this.allGroups.sort(sortGroups) : this.groupSort === 'descending' ? this.allGroups.sort(sortGroups).reverse() : this.allGroups;
 
         return (
             <div className="group-interface">
                 {this.groupCreationModal}
-                {this.currentGroup ?
-                    <GroupMemberView
-                        group={this.currentGroup}
-                        onCloseButtonClick={action(() => this.currentGroup = undefined)}
-                    />
-                    : null}
+                {this.currentGroup ? <GroupMemberView group={this.currentGroup} onCloseButtonClick={action(() => (this.currentGroup = undefined))} /> : null}
                 <div className="group-heading">
-                    <p><b>Manage Groups</b></p>
-                    <button onClick={action(() => this.createGroupModalOpen = true)}>
-                        <FontAwesomeIcon icon={"plus"} size={"sm"} /> Create Group
+                    <p>
+                        <b>Manage Groups</b>
+                    </p>
+                    <button onClick={action(() => (this.createGroupModalOpen = true))}>
+                        <FontAwesomeIcon icon={'plus'} size={'sm'} /> Create Group
                     </button>
-                    <div className={"close-button"} onClick={this.close}>
-                        <FontAwesomeIcon icon={"times"} color={"black"} size={"lg"} />
+                    <div className={'close-button'} onClick={this.close}>
+                        <FontAwesomeIcon icon={'times'} color={'black'} size={'lg'} />
                     </div>
                 </div>
                 <div className="main-container">
-                    <div
-                        className="sort-groups"
-                        onClick={action(() => this.groupSort = this.groupSort === "ascending" ? "descending" : this.groupSort === "descending" ? "none" : "ascending")}>
-                        Name {this.groupSort === "ascending" ? <FontAwesomeIcon icon={"caret-up"} size={"xs"} />
-                            : this.groupSort === "descending" ? <FontAwesomeIcon icon={"caret-down"} size={"xs"} />
-                                : <FontAwesomeIcon icon={"caret-right"} size={"xs"} />
-                        }
+                    <div className="sort-groups" onClick={action(() => (this.groupSort = this.groupSort === 'ascending' ? 'descending' : this.groupSort === 'descending' ? 'none' : 'ascending'))}>
+                        Name{' '}
+                        {this.groupSort === 'ascending' ? (
+                            <FontAwesomeIcon icon={'caret-up'} size={'xs'} />
+                        ) : this.groupSort === 'descending' ? (
+                            <FontAwesomeIcon icon={'caret-down'} size={'xs'} />
+                        ) : (
+                            <FontAwesomeIcon icon={'caret-right'} size={'xs'} />
+                        )}
                     </div>
                     <div className="group-body">
-                        {groups.map(group =>
-                            <div
-                                className="group-row"
-                                key={StrCast(group.title || group.groupName)}
-                            >
-                                <div className="group-name" >{group.title || group.groupName}</div>
-                                <div className="group-info" onClick={action(() => this.currentGroup = group)}>
-                                    <FontAwesomeIcon icon={"info-circle"} color={"#e8e8e8"} size={"sm"} style={{ backgroundColor: "#1e89d7", borderRadius: "100%", border: "1px solid #1e89d7" }} />
+                        {groups.map(group => (
+                            <div className="group-row" key={StrCast(group.title || group.groupName)}>
+                                <div className="group-name">{StrCast(group.title || group.groupName)}</div>
+                                <div className="group-info" onClick={action(() => (this.currentGroup = group))}>
+                                    <FontAwesomeIcon icon={'info-circle'} color={'#e8e8e8'} size={'sm'} style={{ backgroundColor: '#1e89d7', borderRadius: '100%', border: '1px solid #1e89d7' }} />
                                 </div>
                             </div>
-                        )}
+                        ))}
                     </div>
                 </div>
-
             </div>
         );
     }
 
     render() {
-        return <MainViewModal
-            contents={this.groupInterface}
-            isDisplayed={this.isOpen}
-            interactive={true}
-            dialogueBoxStyle={{ zIndex: 1002 }}
-            overlayStyle={{ zIndex: 1001 }}
-            closeOnExternalClick={this.close}
-        />;
+        return <MainViewModal contents={this.groupInterface} isDisplayed={this.isOpen} interactive={true} dialogueBoxStyle={{ zIndex: 1002 }} overlayStyle={{ zIndex: 1001 }} closeOnExternalClick={this.close} />;
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/util/ScrollBox.tsx b/src/client/util/ScrollBox.tsx
index a209874a3..d4620ae3f 100644
--- a/src/client/util/ScrollBox.tsx
+++ b/src/client/util/ScrollBox.tsx
@@ -1,21 +1,24 @@
-import React = require("react");
+import React = require('react');
 
-export class ScrollBox extends React.Component {
+export class ScrollBox extends React.Component<React.PropsWithChildren<{}>> {
     onWheel = (e: React.WheelEvent) => {
-        if (e.currentTarget.scrollHeight > e.currentTarget.clientHeight) { // If the element has a scroll bar, then we don't want the containing collection to zoom
+        if (e.currentTarget.scrollHeight > e.currentTarget.clientHeight) {
+            // If the element has a scroll bar, then we don't want the containing collection to zoom
             e.stopPropagation();
         }
-    }
+    };
 
     render() {
         return (
-            <div style={{
-                overflow: "auto",
-                width: "100%",
-                height: "100%",
-            }} onWheel={this.onWheel}>
+            <div
+                style={{
+                    overflow: 'auto',
+                    width: '100%',
+                    height: '100%',
+                }}
+                onWheel={this.onWheel}>
                 {this.props.children}
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index d77633b8d..1acb3ab38 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -1,29 +1,29 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { intersection } from "lodash";
-import { action, computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import Select from "react-select";
-import * as RequestPromise from "request-promise";
-import { AclAugment, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, DocListCast, DocListCastAsync, Opt, AclSelfEdit } from "../../fields/Doc";
-import { List } from "../../fields/List";
-import { Cast, NumCast, StrCast } from "../../fields/Types";
-import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from "../../fields/util";
-import { Utils } from "../../Utils";
-import { DocServer } from "../DocServer";
-import { CollectionView } from "../views/collections/CollectionView";
-import { DictationOverlay } from "../views/DictationOverlay";
-import { MainViewModal } from "../views/MainViewModal";
-import { DocumentView } from "../views/nodes/DocumentView";
-import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
-import { SearchBox } from "../views/search/SearchBox";
-import { CurrentUserUtils } from "./CurrentUserUtils";
-import { DocumentManager } from "./DocumentManager";
-import { GroupManager, UserOptions } from "./GroupManager";
-import { GroupMemberView } from "./GroupMemberView";
-import { SelectionManager } from "./SelectionManager";
-import "./SharingManager.scss";
-import { LinkManager } from "./LinkManager";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { intersection } from 'lodash';
+import { action, computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import Select from 'react-select';
+import * as RequestPromise from 'request-promise';
+import { AclAugment, AclAdmin, AclEdit, AclPrivate, AclReadonly, AclSym, AclUnset, DataSym, Doc, DocListCast, DocListCastAsync, Opt, AclSelfEdit } from '../../fields/Doc';
+import { List } from '../../fields/List';
+import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { distributeAcls, GetEffectiveAcl, normalizeEmail, SharingPermissions, TraceMobx } from '../../fields/util';
+import { Utils } from '../../Utils';
+import { DocServer } from '../DocServer';
+import { CollectionView } from '../views/collections/CollectionView';
+import { DictationOverlay } from '../views/DictationOverlay';
+import { MainViewModal } from '../views/MainViewModal';
+import { DocumentView } from '../views/nodes/DocumentView';
+import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
+import { SearchBox } from '../views/search/SearchBox';
+import { CurrentUserUtils } from './CurrentUserUtils';
+import { DocumentManager } from './DocumentManager';
+import { GroupManager, UserOptions } from './GroupManager';
+import { GroupMemberView } from './GroupMemberView';
+import { SelectionManager } from './SelectionManager';
+import './SharingManager.scss';
+import { LinkManager } from './LinkManager';
 
 export interface User {
     email: string;
@@ -44,19 +44,19 @@ interface GroupedOptions {
 // const DefaultColor = "black";
 
 // used to differentiate between individuals and groups when sharing
-const indType = "!indType/";
-const groupType = "!groupType/";
+const indType = '!indType/';
+const groupType = '!groupType/';
 
-const storage = "data";
+const storage = 'data';
 
 /**
  * A user who also has a sharing doc.
  */
 interface ValidatedUser {
-    user: User;         // database minimal info to identify / communicate with a user (email, sharing doc id)
-    sharingDoc: Doc;    // document to share/message another user
+    user: User; // database minimal info to identify / communicate with a user (email, sharing doc id)
+    sharingDoc: Doc; // document to share/message another user
     linkDatabase: Doc;
-    userColor: string;  // stored on the sharinDoc, extracted for convenience?
+    userColor: string; // stored on the sharinDoc, extracted for convenience?
 }
 
 @observer
@@ -71,8 +71,8 @@ export class SharingManager extends React.Component<{}> {
     @observable private overlayOpacity = 0.4; // for the modal
     @observable private selectedUsers: UserOptions[] | null = null; // users (individuals/groups) selected to share with
     @observable private permissions: SharingPermissions = SharingPermissions.Edit; // the permission with which to share with other users
-    @observable private individualSort: "ascending" | "descending" | "none" = "none"; // sorting options for the list of individuals
-    @observable private groupSort: "ascending" | "descending" | "none" = "none"; // sorting options for the list of groups
+    @observable private individualSort: 'ascending' | 'descending' | 'none' = 'none'; // sorting options for the list of individuals
+    @observable private groupSort: 'ascending' | 'descending' | 'none' = 'none'; // sorting options for the list of groups
     private shareDocumentButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); // ref for the share button, used for the position of the popup
     private distributeAclsButtonRef: React.RefObject<HTMLButtonElement> = React.createRef(); // ref for the distribute button, used for the position of the popup
     // if both showUserOptions and showGroupOptions are false then both are displayed
@@ -89,7 +89,7 @@ export class SharingManager extends React.Component<{}> {
         [AclAugment, SharingPermissions.Augment],
         [AclSelfEdit, SharingPermissions.SelfEdit],
         [AclEdit, SharingPermissions.Edit],
-        [AclAdmin, SharingPermissions.Admin]
+        [AclAdmin, SharingPermissions.Admin],
     ]);
 
     // private get linkVisible() {
@@ -105,17 +105,20 @@ export class SharingManager extends React.Component<{}> {
             this.isOpen = this.targetDoc !== undefined;
             this.permissions = SharingPermissions.Augment;
         });
-    }
+    };
 
     public close = action(() => {
         this.isOpen = false;
         this.selectedUsers = null; // resets the list of users and selected users (in the react-select component)
         TaskCompletionBox.taskCompleted = false;
-        setTimeout(action(() => {
-            // this.copied = false;
-            DictationOverlay.Instance.hasActiveModal = false;
-            this.targetDoc = undefined;
-        }), 500);
+        setTimeout(
+            action(() => {
+                // this.copied = false;
+                DictationOverlay.Instance.hasActiveModal = false;
+                this.targetDoc = undefined;
+            }),
+            500
+        );
     });
 
     constructor(props: {}) {
@@ -136,7 +139,7 @@ export class SharingManager extends React.Component<{}> {
     populateUsers = async () => {
         if (!this.populating) {
             this.populating = true;
-            const userList = await RequestPromise.get(Utils.prepend("/getUsers"));
+            const userList = await RequestPromise.get(Utils.prepend('/getUsers'));
             const raw = JSON.parse(userList) as User[];
             const sharingDocs: ValidatedUser[] = [];
             const evaluating = raw.map(async user => {
@@ -146,7 +149,8 @@ export class SharingManager extends React.Component<{}> {
                     const linkDatabase = await DocServer.GetRefField(user.linkDatabaseId);
                     if (sharingDoc instanceof Doc && linkDatabase instanceof Doc) {
                         await DocListCastAsync(linkDatabase.data);
-                        (await DocListCastAsync(Cast(linkDatabase, Doc, null).data))?.forEach(async link => { // makes sure link anchors are loaded to avoid incremental updates to computedFns in LinkManager
+                        (await DocListCastAsync(Cast(linkDatabase, Doc, null).data))?.forEach(async link => {
+                            // makes sure link anchors are loaded to avoid incremental updates to computedFns in LinkManager
                             const a1 = await Cast(link?.anchor1, Doc, null);
                             const a2 = await Cast(link?.anchor2, Doc, null);
                         });
@@ -166,7 +170,7 @@ export class SharingManager extends React.Component<{}> {
                 this.populating = false;
             });
         }
-    }
+    };
 
     /**
      * Shares the document with a user.
@@ -179,31 +183,31 @@ export class SharingManager extends React.Component<{}> {
         const isDashboard = DocListCast(CurrentUserUtils.MyDashboards.data).indexOf(target) !== -1;
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
-        return !docs.map(doc => {
-            doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
-
-            if (permission === SharingPermissions.None) {
-                if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 1) - 1;
-            }
-            else {
-                if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 0) + 1;
-            }
+        return !docs
+            .map(doc => {
+                doc.author === Doc.CurrentUserEmail && !doc[myAcl] && distributeAcls(myAcl, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
+
+                if (permission === SharingPermissions.None) {
+                    if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 1) - 1;
+                } else {
+                    if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numUsersShared = NumCast(doc.numUsersShared, 0) + 1;
+                }
 
-            distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
+                distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
 
-            this.setDashboardBackground(doc, permission as SharingPermissions);
-            if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc);
-            else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc || doc));
-        }).some(success => !success);
-    }
+                this.setDashboardBackground(doc, permission as SharingPermissions);
+                if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc);
+                else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc) || doc);
+            })
+            .some(success => !success);
+    };
 
     /**
      * Sets the permission on the target for the group.
-     * @param group 
-     * @param permission 
+     * @param group
+     * @param permission
      */
     setInternalGroupSharing = (group: Doc | { title: string }, permission: string, targetDoc?: Doc) => {
-
         const target = targetDoc || this.targetDoc!;
         const key = normalizeEmail(StrCast(group.title));
         const acl = `acl-${key}`;
@@ -212,38 +216,41 @@ export class SharingManager extends React.Component<{}> {
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
 
         // ! ensures it returns true if document has been shared successfully, false otherwise
-        return !docs.map(doc => {
-            doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
-
-            if (permission === SharingPermissions.None) {
-                if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numGroupsShared = NumCast(doc.numGroupsShared, 1) - 1;
-            }
-            else {
-                if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numGroupsShared = NumCast(doc.numGroupsShared, 0) + 1;
-            }
+        return !docs
+            .map(doc => {
+                doc.author === Doc.CurrentUserEmail && !doc[`acl-${Doc.CurrentUserEmailNormalized}`] && distributeAcls(`acl-${Doc.CurrentUserEmailNormalized}`, SharingPermissions.Admin, doc, undefined, undefined, isDashboard);
+
+                if (permission === SharingPermissions.None) {
+                    if (doc[acl] && doc[acl] !== SharingPermissions.None) doc.numGroupsShared = NumCast(doc.numGroupsShared, 1) - 1;
+                } else {
+                    if (!doc[acl] || doc[acl] === SharingPermissions.None) doc.numGroupsShared = NumCast(doc.numGroupsShared, 0) + 1;
+                }
 
-            distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
-            this.setDashboardBackground(doc, permission as SharingPermissions);
+                distributeAcls(acl, permission as SharingPermissions, doc, undefined, undefined, isDashboard);
+                this.setDashboardBackground(doc, permission as SharingPermissions);
 
-            if (group instanceof Doc) {
-                const members: string[] = JSON.parse(StrCast(group.members));
-                const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
+                if (group instanceof Doc) {
+                    const members: string[] = JSON.parse(StrCast(group.members));
+                    const users: ValidatedUser[] = this.users.filter(({ user: { email } }) => members.includes(email));
 
-                // if documents have been shared, add the doc to that list if it doesn't already exist, otherwise create a new list with the doc
-                group.docsShared ? Doc.IndexOf(doc, DocListCast(group.docsShared)) === -1 && (group.docsShared as List<Doc>).push(doc) : group.docsShared = new List<Doc>([doc]);
+                    // if documents have been shared, add the doc to that list if it doesn't already exist, otherwise create a new list with the doc
+                    group.docsShared ? Doc.IndexOf(doc, DocListCast(group.docsShared)) === -1 && (group.docsShared as List<Doc>).push(doc) : (group.docsShared = new List<Doc>([doc]));
 
-                return users.map(({ user, sharingDoc }) => {
-                    if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc); // add the doc to the sharingDoc if it hasn't already been added
-                    else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc || doc)); // remove the doc from the list if it already exists
-                }).some(success => !success);
-            }
-        }).some(success => success);
-    }
+                    return users
+                        .map(({ user, sharingDoc }) => {
+                            if (permission !== SharingPermissions.None) return Doc.AddDocToList(sharingDoc, storage, doc); // add the doc to the sharingDoc if it hasn't already been added
+                            else return GetEffectiveAcl(doc, user.email) === AclPrivate && Doc.RemoveDocFromList(sharingDoc, storage, (doc.aliasOf as Doc) || doc); // remove the doc from the list if it already exists
+                        })
+                        .some(success => !success);
+                }
+            })
+            .some(success => success);
+    };
 
     /**
      * Shares the documents shared with a group with a new user who has been added to that group.
-     * @param group 
-     * @param emailId 
+     * @param group
+     * @param emailId
      */
     shareWithAddedMember = (group: Doc, emailId: string, retry: boolean = true) => {
         const user = this.users.find(({ user: { email } }) => email === emailId)!;
@@ -255,30 +262,30 @@ export class SharingManager extends React.Component<{}> {
                     DocListCastAsync(group.docsShared).then(dl => {
                         const filtered = dl?.filter(doc => !userdocs?.includes(doc));
                         filtered && userdocs?.push(...filtered);
-                    }));
+                    })
+                );
             }
         }
-    }
+    };
 
     /**
      * Called from the properties sidebar to change permissions of a user.
      */
     shareFromPropertiesSidebar = (shareWith: string, permission: SharingPermissions, docs: Doc[]) => {
-        if (shareWith !== "Public" && shareWith !== "Override") {
-            const user = this.users.find(({ user: { email } }) => email === (shareWith === "Me" ? Doc.CurrentUserEmail : shareWith));
+        if (shareWith !== 'Public' && shareWith !== 'Override') {
+            const user = this.users.find(({ user: { email } }) => email === (shareWith === 'Me' ? Doc.CurrentUserEmail : shareWith));
             docs.forEach(doc => {
                 if (user) this.setInternalSharing(user, permission, doc);
                 else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, doc);
             });
-        }
-        else {
+        } else {
             const dashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
             docs.forEach(doc => {
                 const isDashboard = dashboards.indexOf(doc) !== -1;
                 if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard);
             });
         }
-    }
+    };
 
     /**
      * Sets the background of the Dashboard if it has been shared as a visual indicator
@@ -287,22 +294,21 @@ export class SharingManager extends React.Component<{}> {
         if (Doc.IndexOf(doc, DocListCast(CurrentUserUtils.MyDashboards.data)) !== -1) {
             if (permission !== SharingPermissions.None) {
                 doc.isShared = true;
-                doc.backgroundColor = "green";
-            }
-            else {
+                doc.backgroundColor = 'green';
+            } else {
                 const acls = doc[DataSym][AclSym];
-                if (Object.keys(acls).every(key => key === `acl-${Doc.CurrentUserEmailNormalized}` ? true : [AclUnset, AclPrivate].includes(acls[key]))) {
+                if (Object.keys(acls).every(key => (key === `acl-${Doc.CurrentUserEmailNormalized}` ? true : [AclUnset, AclPrivate].includes(acls[key])))) {
                     doc.isShared = undefined;
                     doc.backgroundColor = undefined;
                 }
             }
         }
-    }
+    };
 
     /**
      * Removes the documents shared with a user through a group when the user is removed from the group.
-     * @param group 
-     * @param emailId 
+     * @param group
+     * @param emailId
      */
     removeMember = (group: Doc, emailId: string) => {
         const user: ValidatedUser = this.users.find(({ user: { email } }) => email === emailId)!;
@@ -315,11 +321,11 @@ export class SharingManager extends React.Component<{}> {
                 })
             );
         }
-    }
+    };
 
     /**
      * Removes a group's permissions from documents that have been shared with it.
-     * @param group 
+     * @param group
      */
     removeGroup = (group: Doc) => {
         if (group.docsShared) {
@@ -335,9 +341,7 @@ export class SharingManager extends React.Component<{}> {
                 users.forEach(({ sharingDoc }) => Doc.RemoveDocFromList(sharingDoc, storage, doc));
             });
         }
-    }
-
-
+    };
 
     // private setExternalSharing = (permission: string) => {
     //     const targetDoc = this.targetDoc;
@@ -367,23 +371,23 @@ export class SharingManager extends React.Component<{}> {
      */
     private sharingOptions(uniform: boolean, override?: boolean) {
         const dropdownValues: string[] = Object.values(SharingPermissions);
-        if (!uniform) dropdownValues.unshift("-multiple-");
-        if (override) dropdownValues.unshift("None");
-        return dropdownValues.filter(permission => !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
-        (
-            <option key={permission} value={permission}>
-                {permission}
-            </option>
-        )
-        );
+        if (!uniform) dropdownValues.unshift('-multiple-');
+        if (override) dropdownValues.unshift('None');
+        return dropdownValues
+            .filter(permission => !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any))
+            .map(permission => (
+                <option key={permission} value={permission}>
+                    {permission}
+                </option>
+            ));
     }
 
     private focusOn = (contents: string) => {
-        const title = this.targetDoc ? StrCast(this.targetDoc.title) : "";
+        const title = this.targetDoc ? StrCast(this.targetDoc.title) : '';
         const docs = SelectionManager.Views().length > 1 ? SelectionManager.Views().map(docView => docView.props.Document) : [this.targetDoc];
         return (
             <span
-                className={"focus-span"}
+                className={'focus-span'}
                 title={title}
                 onClick={() => {
                     let context: Opt<CollectionView>;
@@ -404,12 +408,11 @@ export class SharingManager extends React.Component<{}> {
                         this.dialogueBoxOpacity = 1;
                         this.overlayOpacity = 0.4;
                     }
-                })}
-            >
+                })}>
                 {contents}
             </span>
         );
-    }
+    };
 
     /**
      * Handles changes in the users selected in react-select
@@ -417,7 +420,7 @@ export class SharingManager extends React.Component<{}> {
     @action
     handleUsersChange = (selectedOptions: any) => {
         this.selectedUsers = selectedOptions as UserOptions[];
-    }
+    };
 
     /**
      * Handles changes in the permission chosen to share with someone with
@@ -425,7 +428,7 @@ export class SharingManager extends React.Component<{}> {
     @action
     handlePermissionsChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
         this.permissions = event.currentTarget.value as SharingPermissions;
-    }
+    };
 
     /**
      * Calls the relevant method for sharing, displays the popup, and resets the relevant variables.
@@ -436,8 +439,7 @@ export class SharingManager extends React.Component<{}> {
             this.selectedUsers.forEach(user => {
                 if (user.value.includes(indType)) {
                     this.setInternalSharing(this.users.find(u => u.user.email === user.label)!, this.permissions);
-                }
-                else {
+                } else {
                     this.setInternalGroupSharing(GroupManager.Instance.getGroup(user.label)!, this.permissions);
                 }
             });
@@ -445,13 +447,16 @@ export class SharingManager extends React.Component<{}> {
             const { left, width, top, height } = this.shareDocumentButtonRef.current!.getBoundingClientRect();
             TaskCompletionBox.popupX = left - 1.5 * width;
             TaskCompletionBox.popupY = top - 1.5 * height;
-            TaskCompletionBox.textDisplayed = "Document shared!";
+            TaskCompletionBox.textDisplayed = 'Document shared!';
             TaskCompletionBox.taskCompleted = true;
-            setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2000);
+            setTimeout(
+                action(() => (TaskCompletionBox.taskCompleted = false)),
+                2000
+            );
 
             this.selectedUsers = null;
         }
-    }
+    };
 
     // distributeOverCollection = (targetDoc?: Doc) => {
     //     const target = targetDoc || this.targetDoc!;
@@ -471,7 +476,7 @@ export class SharingManager extends React.Component<{}> {
         const { email: e1 } = u1.user;
         const { email: e2 } = u2.user;
         return e1 < e2 ? -1 : e1 === e2 ? 0 : 1;
-    }
+    };
 
     /**
      * Sorting algorithm to sort groups.
@@ -480,7 +485,7 @@ export class SharingManager extends React.Component<{}> {
         const g1 = StrCast(group1.title);
         const g2 = StrCast(group2.title);
         return g1 < g2 ? -1 : g1 === g2 ? 0 : 1;
-    }
+    };
 
     /**
      * @returns the main interface of the SharingManager.
@@ -488,28 +493,29 @@ export class SharingManager extends React.Component<{}> {
     @computed get sharingInterface() {
         TraceMobx();
         const groupList = GroupManager.Instance?.allGroups || [];
-        const sortedUsers = this.users.slice().sort(this.sortUsers).map(({ user: { email } }) => ({ label: email, value: indType + email }));
-        const sortedGroups = groupList.slice().sort(this.sortGroups).map(({ title }) => ({ label: StrCast(title), value: groupType + StrCast(title) }));
+        const sortedUsers = this.users
+            .slice()
+            .sort(this.sortUsers)
+            .map(({ user: { email } }) => ({ label: email, value: indType + email }));
+        const sortedGroups = groupList
+            .slice()
+            .sort(this.sortGroups)
+            .map(({ title }) => ({ label: StrCast(title), value: groupType + StrCast(title) }));
 
         // the next block handles the users shown (individuals/groups/both)
         const options: GroupedOptions[] = [];
         if (GroupManager.Instance) {
             if ((this.showUserOptions && this.showGroupOptions) || (!this.showUserOptions && !this.showGroupOptions)) {
-                options.push(
-                    { label: 'Individuals', options: sortedUsers },
-                    { label: 'Groups', options: sortedGroups });
-            }
-            else if (this.showUserOptions) options.push({ label: 'Individuals', options: sortedUsers });
+                options.push({ label: 'Individuals', options: sortedUsers }, { label: 'Groups', options: sortedGroups });
+            } else if (this.showUserOptions) options.push({ label: 'Individuals', options: sortedUsers });
             else options.push({ label: 'Groups', options: sortedGroups });
         }
 
-        const users = this.individualSort === "ascending" ? this.users.slice().sort(this.sortUsers) : this.individualSort === "descending" ? this.users.slice().sort(this.sortUsers).reverse() : this.users;
-        const groups = this.groupSort === "ascending" ? groupList.slice().sort(this.sortGroups) : this.groupSort === "descending" ? groupList.slice().sort(this.sortGroups).reverse() : groupList;
+        const users = this.individualSort === 'ascending' ? this.users.slice().sort(this.sortUsers) : this.individualSort === 'descending' ? this.users.slice().sort(this.sortUsers).reverse() : this.users;
+        const groups = this.groupSort === 'ascending' ? groupList.slice().sort(this.sortGroups) : this.groupSort === 'descending' ? groupList.slice().sort(this.sortGroups).reverse() : groupList;
 
         // handles the case where multiple documents are selected
-        let docs = SelectionManager.Views().length < 2 ?
-            [this.layoutDocAcls ? this.targetDoc : this.targetDoc?.[DataSym]]
-            : SelectionManager.Views().map(docView => this.layoutDocAcls ? docView.props.Document : docView.props.Document?.[DataSym]);
+        let docs = SelectionManager.Views().length < 2 ? [this.layoutDocAcls ? this.targetDoc : this.targetDoc?.[DataSym]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document?.[DataSym]));
 
         if (this.myDocAcls) {
             const newDocs: Doc[] = [];
@@ -524,107 +530,78 @@ export class SharingManager extends React.Component<{}> {
         const admin = this.myDocAcls ? Boolean(docs.length) : effectiveAcls.every(acl => acl === AclAdmin);
 
         // users in common between all docs
-        const commonKeys = intersection(...docs.map(doc => this.layoutDocAcls ? doc?.[AclSym] && Object.keys(doc[AclSym]) : doc?.[DataSym]?.[AclSym] && Object.keys(doc[DataSym][AclSym])));
+        const commonKeys = intersection(...docs.map(doc => (this.layoutDocAcls ? doc?.[AclSym] && Object.keys(doc[AclSym]) : doc?.[DataSym]?.[AclSym] && Object.keys(doc[DataSym][AclSym]))));
 
         // the list of users shared with
-        const userListContents: (JSX.Element | null)[] = users.filter(({ user }) => docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email).map(({ user, linkDatabase, sharingDoc, userColor }) => {
-            const userKey = `acl-${normalizeEmail(user.email)}`;
-            const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[userKey] === docs[0]?.[AclSym]?.[userKey] : doc?.[DataSym]?.[AclSym]?.[userKey] === docs[0]?.[DataSym]?.[AclSym]?.[userKey]);
-            const permissions = uniform ? StrCast(targetDoc?.[userKey]) : "-multiple-";
-
-            return !permissions ? (null) : (
-                <div
-                    key={userKey}
-                    className={"container"}
-                >
-                    <span className={"padding"}>{user.email}</span>
-                    <div className="edit-actions">
-                        {admin || this.myDocAcls ? (
-                            <select
-                                className={"permissions-dropdown"}
-                                value={permissions}
-                                onChange={e => this.setInternalSharing({ user, linkDatabase, sharingDoc, userColor }, e.currentTarget.value)}
-                            >
-                                {this.sharingOptions(uniform)}
-                            </select>
-                        ) : (
-                            <div className={"permissions-dropdown"}>
-                                {permissions}
-                            </div>
-                        )}
+        const userListContents: (JSX.Element | null)[] = users
+            .filter(({ user }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(user.email)}`) : docs[0]?.author !== user.email))
+            .map(({ user, linkDatabase, sharingDoc, userColor }) => {
+                const userKey = `acl-${normalizeEmail(user.email)}`;
+                const uniform = docs.every(doc => (this.layoutDocAcls ? doc?.[AclSym]?.[userKey] === docs[0]?.[AclSym]?.[userKey] : doc?.[DataSym]?.[AclSym]?.[userKey] === docs[0]?.[DataSym]?.[AclSym]?.[userKey]));
+                const permissions = uniform ? StrCast(targetDoc?.[userKey]) : '-multiple-';
+
+                return !permissions ? null : (
+                    <div key={userKey} className={'container'}>
+                        <span className={'padding'}>{user.email}</span>
+                        <div className="edit-actions">
+                            {admin || this.myDocAcls ? (
+                                <select className={'permissions-dropdown'} value={permissions} onChange={e => this.setInternalSharing({ user, linkDatabase, sharingDoc, userColor }, e.currentTarget.value)}>
+                                    {this.sharingOptions(uniform)}
+                                </select>
+                            ) : (
+                                <div className={'permissions-dropdown'}>{permissions}</div>
+                            )}
+                        </div>
                     </div>
-                </div>
-            );
-        });
+                );
+            });
 
         // checks if every doc has the same author
         const sameAuthor = docs.every(doc => doc?.author === docs[0]?.author);
 
         // the owner of the doc and the current user are placed at the top of the user list.
         userListContents.unshift(
-            sameAuthor ?
-                (
-                    <div
-                        key={"owner"}
-                        className={"container"}
-                    >
-                        <span className={"padding"}>{targetDoc?.author === Doc.CurrentUserEmail ? "Me" : targetDoc?.author}</span>
-                        <div className="edit-actions">
-                            <div className={"permissions-dropdown"}>
-                                Owner
-                            </div>
-                        </div>
+            sameAuthor ? (
+                <div key={'owner'} className={'container'}>
+                    <span className={'padding'}>{targetDoc?.author === Doc.CurrentUserEmail ? 'Me' : targetDoc?.author}</span>
+                    <div className="edit-actions">
+                        <div className={'permissions-dropdown'}>Owner</div>
                     </div>
-                ) : null,
-            sameAuthor && targetDoc?.author !== Doc.CurrentUserEmail ?
-                (
-                    <div
-                        key={"me"}
-                        className={"container"}
-                    >
-                        <span className={"padding"}>Me</span>
-                        <div className="edit-actions">
-                            <div className={"permissions-dropdown"}>
-                                {effectiveAcls.every(acl => acl === effectiveAcls[0]) ? this.AclMap.get(effectiveAcls[0])! : "-multiple-"}
-                            </div>
-                        </div>
+                </div>
+            ) : null,
+            sameAuthor && targetDoc?.author !== Doc.CurrentUserEmail ? (
+                <div key={'me'} className={'container'}>
+                    <span className={'padding'}>Me</span>
+                    <div className="edit-actions">
+                        <div className={'permissions-dropdown'}>{effectiveAcls.every(acl => acl === effectiveAcls[0]) ? this.AclMap.get(effectiveAcls[0])! : '-multiple-'}</div>
                     </div>
-                ) : null
+                </div>
+            ) : null
         );
 
-
         // the list of groups shared with
-        const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true);
-        groupListMap.unshift({ title: "Public" });//, { title: "ALL" });
+        const groupListMap: (Doc | { title: string })[] = groups.filter(({ title }) => (docs.length > 1 ? commonKeys.includes(`acl-${normalizeEmail(StrCast(title))}`) : true));
+        groupListMap.unshift({ title: 'Public' }); //, { title: "ALL" });
         const groupListContents = groupListMap.map(group => {
             const groupKey = `acl-${StrCast(group.title)}`;
-            const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[groupKey] === docs[0]?.[AclSym]?.[groupKey] : doc?.[DataSym]?.[AclSym]?.[groupKey] === docs[0]?.[DataSym]?.[AclSym]?.[groupKey]);
-            const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : "-multiple-";
-
-            return !permissions ? (null) : (
-                <div
-                    key={groupKey}
-                    className={"container"}
-                >
-                    <div className={"padding"}>{group.title}</div>
-                    {group instanceof Doc ?
-                        (<div className="group-info" onClick={action(() => GroupManager.Instance.currentGroup = group)}>
-                            <FontAwesomeIcon icon={"info-circle"} color={"#e8e8e8"} size={"sm"} style={{ backgroundColor: "#1e89d7", borderRadius: "100%", border: "1px solid #1e89d7" }} />
-                        </div>)
-                        : (null)}
+            const uniform = docs.every(doc => (this.layoutDocAcls ? doc?.[AclSym]?.[groupKey] === docs[0]?.[AclSym]?.[groupKey] : doc?.[DataSym]?.[AclSym]?.[groupKey] === docs[0]?.[DataSym]?.[AclSym]?.[groupKey]));
+            const permissions = uniform ? StrCast(targetDoc?.[`acl-${StrCast(group.title)}`]) : '-multiple-';
+
+            return !permissions ? null : (
+                <div key={groupKey} className={'container'}>
+                    <div className={'padding'}>{StrCast(group.title)}</div>
+                    {group instanceof Doc ? (
+                        <div className="group-info" onClick={action(() => (GroupManager.Instance.currentGroup = group))}>
+                            <FontAwesomeIcon icon={'info-circle'} color={'#e8e8e8'} size={'sm'} style={{ backgroundColor: '#1e89d7', borderRadius: '100%', border: '1px solid #1e89d7' }} />
+                        </div>
+                    ) : null}
                     <div className="edit-actions">
                         {admin || this.myDocAcls ? (
-                            <select
-                                className={"permissions-dropdown"}
-                                value={permissions}
-                                onChange={e => this.setInternalGroupSharing(group, e.currentTarget.value)}
-                            >
-                                {this.sharingOptions(uniform, group.title === "Override")}
+                            <select className={'permissions-dropdown'} value={permissions} onChange={e => this.setInternalGroupSharing(group, e.currentTarget.value)}>
+                                {this.sharingOptions(uniform, group.title === 'Override')}
                             </select>
                         ) : (
-                            <div className={"permissions-dropdown"}>
-                                {permissions}
-                            </div>
+                            <div className={'permissions-dropdown'}>{permissions}</div>
                         )}
                     </div>
                 </div>
@@ -632,102 +609,95 @@ export class SharingManager extends React.Component<{}> {
         });
 
         return (
-            <div className={"sharing-interface"}>
-                {GroupManager.Instance?.currentGroup ?
-                    <GroupMemberView
-                        group={GroupManager.Instance.currentGroup}
-                        onCloseButtonClick={action(() => GroupManager.Instance.currentGroup = undefined)}
-                    /> :
-                    null}
+            <div className={'sharing-interface'}>
+                {GroupManager.Instance?.currentGroup ? <GroupMemberView group={GroupManager.Instance.currentGroup} onCloseButtonClick={action(() => (GroupManager.Instance.currentGroup = undefined))} /> : null}
                 <div className="sharing-contents">
-                    <p className={"share-title"}><b>Share </b>{this.focusOn(docs.length < 2 ? StrCast(targetDoc?.title, "this document") : "-multiple-")}</p>
-                    <div className={"close-button"} onClick={this.close}>
-                        <FontAwesomeIcon icon={"times"} color={"black"} size={"lg"} />
+                    <p className={'share-title'}>
+                        <b>Share </b>
+                        {this.focusOn(docs.length < 2 ? StrCast(targetDoc?.title, 'this document') : '-multiple-')}
+                    </p>
+                    <div className={'close-button'} onClick={this.close}>
+                        <FontAwesomeIcon icon={'times'} color={'black'} size={'lg'} />
                     </div>
                     {/* {this.linkVisible ?
                         <div>
                             {this.sharingUrl}
                         </div> :
                         (null)} */}
-                    {<div className="share-container">
-                        <div className="share-setup">
-                            <Select
-                                className="user-search"
-                                placeholder="Enter user or group name..."
-                                isMulti
-                                isSearchable
-                                closeMenuOnSelect={false}
-                                options={options}
-                                onKeyDown={e => e.stopPropagation()}
-                                onChange={this.handleUsersChange}
-                                value={this.selectedUsers}
-                                styles={{
-                                    indicatorSeparator: () => ({
-                                        visibility: "hidden"
-                                    })
-                                }}
-                            />
-                            <select className="permissions-select" onChange={this.handlePermissionsChange} value={this.permissions}>
-                                {this.sharingOptions(true)}
-                            </select>
-                            <button ref={this.shareDocumentButtonRef} className="share-button" onClick={this.share}>
-                                Share
-                            </button>
-                        </div>
-                        <div className="sort-checkboxes">
-                            <input type="checkbox" onChange={action(() => this.showUserOptions = !this.showUserOptions)} /> <label style={{ marginRight: 10 }}>Individuals</label>
-                            <input type="checkbox" onChange={action(() => this.showGroupOptions = !this.showGroupOptions)} /> <label>Groups</label>
-                        </div>
+                    {
+                        <div className="share-container">
+                            <div className="share-setup">
+                                <Select
+                                    className="user-search"
+                                    placeholder="Enter user or group name..."
+                                    isMulti
+                                    isSearchable
+                                    closeMenuOnSelect={false}
+                                    options={options}
+                                    onKeyDown={e => e.stopPropagation()}
+                                    onChange={this.handleUsersChange}
+                                    value={this.selectedUsers}
+                                    styles={{
+                                        indicatorSeparator: () => ({
+                                            visibility: 'hidden',
+                                        }),
+                                    }}
+                                />
+                                <select className="permissions-select" onChange={this.handlePermissionsChange} value={this.permissions}>
+                                    {this.sharingOptions(true)}
+                                </select>
+                                <button ref={this.shareDocumentButtonRef} className="share-button" onClick={this.share}>
+                                    Share
+                                </button>
+                            </div>
+                            <div className="sort-checkboxes">
+                                <input type="checkbox" onChange={action(() => (this.showUserOptions = !this.showUserOptions))} /> <label style={{ marginRight: 10 }}>Individuals</label>
+                                <input type="checkbox" onChange={action(() => (this.showGroupOptions = !this.showGroupOptions))} /> <label>Groups</label>
+                            </div>
 
-                        <div className="acl-container">
-                            {Doc.noviceMode ? (null) :
-                                <div className="layoutDoc-acls">
-                                    <input type="checkbox" onChange={action(() => this.layoutDocAcls = !this.layoutDocAcls)} checked={this.layoutDocAcls} /> <label>Layout</label>
-                                </div>}
+                            <div className="acl-container">
+                                {Doc.noviceMode ? null : (
+                                    <div className="layoutDoc-acls">
+                                        <input type="checkbox" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} /> <label>Layout</label>
+                                    </div>
+                                )}
+                            </div>
                         </div>
-                    </div>
                     }
                     <div className="main-container">
-                        <div className={"individual-container"}>
-                            <div
-                                className="user-sort"
-                                onClick={action(() => this.individualSort = this.individualSort === "ascending" ? "descending" : this.individualSort === "descending" ? "none" : "ascending")}>
-                                Individuals {this.individualSort === "ascending" ? <FontAwesomeIcon icon={"caret-up"} size={"xs"} />
-                                    : this.individualSort === "descending" ? <FontAwesomeIcon icon={"caret-down"} size={"xs"} />
-                                        : <FontAwesomeIcon icon={"caret-right"} size={"xs"} />}
-                            </div>
-                            <div className={"users-list"}>
-                                {userListContents}
+                        <div className={'individual-container'}>
+                            <div className="user-sort" onClick={action(() => (this.individualSort = this.individualSort === 'ascending' ? 'descending' : this.individualSort === 'descending' ? 'none' : 'ascending'))}>
+                                Individuals{' '}
+                                {this.individualSort === 'ascending' ? (
+                                    <FontAwesomeIcon icon={'caret-up'} size={'xs'} />
+                                ) : this.individualSort === 'descending' ? (
+                                    <FontAwesomeIcon icon={'caret-down'} size={'xs'} />
+                                ) : (
+                                    <FontAwesomeIcon icon={'caret-right'} size={'xs'} />
+                                )}
                             </div>
+                            <div className={'users-list'}>{userListContents}</div>
                         </div>
-                        <div className={"group-container"}>
-                            <div
-                                className="user-sort"
-                                onClick={action(() => this.groupSort = this.groupSort === "ascending" ? "descending" : this.groupSort === "descending" ? "none" : "ascending")}>
-                                Groups {this.groupSort === "ascending" ? <FontAwesomeIcon icon={"caret-up"} size={"xs"} />
-                                    : this.groupSort === "descending" ? <FontAwesomeIcon icon={"caret-down"} size={"xs"} />
-                                        : <FontAwesomeIcon icon={"caret-right"} size={"xs"} />}
-
-                            </div>
-                            <div className={"groups-list"}>
-                                {groupListContents}
+                        <div className={'group-container'}>
+                            <div className="user-sort" onClick={action(() => (this.groupSort = this.groupSort === 'ascending' ? 'descending' : this.groupSort === 'descending' ? 'none' : 'ascending'))}>
+                                Groups{' '}
+                                {this.groupSort === 'ascending' ? (
+                                    <FontAwesomeIcon icon={'caret-up'} size={'xs'} />
+                                ) : this.groupSort === 'descending' ? (
+                                    <FontAwesomeIcon icon={'caret-down'} size={'xs'} />
+                                ) : (
+                                    <FontAwesomeIcon icon={'caret-right'} size={'xs'} />
+                                )}
                             </div>
+                            <div className={'groups-list'}>{groupListContents}</div>
                         </div>
                     </div>
-
                 </div>
             </div>
         );
     }
 
     render() {
-        return <MainViewModal
-            contents={this.sharingInterface}
-            isDisplayed={this.isOpen}
-            interactive={true}
-            dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity}
-            overlayDisplayedOpacity={this.overlayOpacity}
-            closeOnExternalClick={this.close}
-        />;
+        return <MainViewModal contents={this.sharingInterface} isDisplayed={this.isOpen} interactive={true} dialogueBoxDisplayedOpacity={this.dialogueBoxOpacity} overlayDisplayedOpacity={this.overlayOpacity} closeOnExternalClick={this.close} />;
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 31fa5b157..542f85228 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -4,42 +4,46 @@
 
 import * as React from 'react';
 import * as ReactDOM from 'react-dom';
-import { AssignAllExtensions } from "../../extensions/General/Extensions";
-import { Docs } from "../documents/Documents";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { LinkManager } from "../util/LinkManager";
+import { AssignAllExtensions } from '../../extensions/General/Extensions';
+import { Docs } from '../documents/Documents';
+import { CurrentUserUtils } from '../util/CurrentUserUtils';
+import { LinkManager } from '../util/LinkManager';
 import { ReplayMovements } from '../util/ReplayMovements';
-import { TrackMovements } from "../util/TrackMovements";
-import { CollectionView } from "./collections/CollectionView";
-import { MainView } from "./MainView";
+import { TrackMovements } from '../util/TrackMovements';
+import { CollectionView } from './collections/CollectionView';
+import { MainView } from './MainView';
 
 AssignAllExtensions();
 
 (async () => {
-    MainView.Live = window.location.search.includes("live");
-    window.location.search.includes("safe") && CollectionView.SetSafeMode(true);
+    MainView.Live = window.location.search.includes('live');
+    window.location.search.includes('safe') && CollectionView.SetSafeMode(true);
     const info = await CurrentUserUtils.loadCurrentUser();
-    if (info.id !== "__guest__") {
+    if (info.id !== '__guest__') {
         // a guest will not have an id registered
         await CurrentUserUtils.loadUserDocument(info.id);
     } else {
         await Docs.Prototypes.initialize();
         new LinkManager();
     }
-    document.getElementById('root')!.addEventListener('wheel', event => {
-        if (event.ctrlKey) {
-            event.preventDefault();
-        }
-    }, true);
+    document.getElementById('root')!.addEventListener(
+        'wheel',
+        event => {
+            if (event.ctrlKey) {
+                event.preventDefault();
+            }
+        },
+        true
+    );
     const startload = (document as any).startLoad;
-    const loading = Date.now() - (startload ? Number(startload) : (Date.now() - 3000));
-    console.log("Load Time = " + loading);
+    const loading = Date.now() - (startload ? Number(startload) : Date.now() - 3000);
+    console.log('Loading Time = ' + loading);
     const d = new Date();
-    d.setTime(d.getTime() + (100 * 24 * 60 * 60 * 1000));
-    const expires = "expires=" + d.toUTCString();
+    d.setTime(d.getTime() + 100 * 24 * 60 * 60 * 1000);
+    const expires = 'expires=' + d.toUTCString();
     document.cookie = `loadtime=${loading};${expires};path=/`;
     new LinkManager();
     new TrackMovements();
     new ReplayMovements();
     ReactDOM.render(<MainView />, document.getElementById('root'));
-})();
\ No newline at end of file
+})();
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 4940c5f9d..6c0a67de2 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -32,7 +32,7 @@ import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOpti
 import { CollectionLinearView } from './collections/collectionLinear';
 import { CollectionMenu } from './collections/CollectionMenu';
 import { CollectionViewType } from './collections/CollectionView';
-import "./collections/TreeView.scss";
+import './collections/TreeView.scss';
 import { ComponentDecorations } from './ComponentDecorations';
 import { ContextMenu } from './ContextMenu';
 import { DashboardView } from './DashboardView';
@@ -45,7 +45,7 @@ import { KeyManager } from './GlobalKeyHandler';
 import { InkTranscription } from './InkTranscription';
 import { LightboxView } from './LightboxView';
 import { LinkMenu } from './linking/LinkMenu';
-import "./MainView.scss";
+import './MainView.scss';
 import { AudioBox } from './nodes/AudioBox';
 import { DocumentLinksButton } from './nodes/DocumentLinksButton';
 import { DocumentView } from './nodes/DocumentView';
@@ -63,7 +63,7 @@ import { PreviewCursor } from './PreviewCursor';
 import { PropertiesView } from './PropertiesView';
 import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider';
 import { TopBar } from './topbar/TopBar';
-const _global = (window /* browser */ || global /* node */) as any;
+const _global = (window /* browser */ || global) /* node */ as any;
 
 @observer
 export class MainView extends React.Component {
@@ -73,30 +73,54 @@ export class MainView extends React.Component {
     @observable public LastButton: Opt<Doc>;
     @observable private _windowWidth: number = 0;
     @observable private _windowHeight: number = 0;
-    @observable private _dashUIWidth: number = 0;   // width of entire main dashboard region including left menu buttons and properties panel (but not including the dashboard selector button row)
-    @observable private _dashUIHeight: number = 0;  // height of entire main dashboard region including top menu buttons
-    @observable private _panelContent: string = "none";
+    @observable private _dashUIWidth: number = 0; // width of entire main dashboard region including left menu buttons and properties panel (but not including the dashboard selector button row)
+    @observable private _dashUIHeight: number = 0; // height of entire main dashboard region including top menu buttons
+    @observable private _panelContent: string = 'none';
     @observable private _sidebarContent: any = CurrentUserUtils.MyLeftSidebarPanel;
     @observable private _leftMenuFlyoutWidth: number = 0;
 
-    @computed private get dashboardTabHeight() { return 27; } //  27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js
-    @computed private get topOfDashUI() { return Number(DASHBOARD_SELECTOR_HEIGHT.replace("px", "")); }
-    @computed private get topOfHeaderBarDoc() { return this.topOfDashUI; }
-    @computed private get topOfSidebarDoc() { return this.topOfDashUI + this.topMenuHeight(); }
-    @computed private get topOfMainDoc() { return this.topOfDashUI + this.topMenuHeight() + this.headerBarDocHeight(); }
-    @computed private get topOfMainDocContent() { return this.topOfMainDoc + this.dashboardTabHeight; }
-    @computed private get leftScreenOffsetOfMainDocView() { return this.leftMenuWidth() - 2; }
-    @computed private get userDoc() { return Doc.UserDoc(); }
-    @computed private get colorScheme() { return StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme); }
-    @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; }
-    @computed private get headerBarDoc() { return CurrentUserUtils.MyHeaderBar; }
-    @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs?.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); }
+    @computed private get dashboardTabHeight() {
+        return 27;
+    } //  27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js
+    @computed private get topOfDashUI() {
+        return Number(DASHBOARD_SELECTOR_HEIGHT.replace('px', ''));
+    }
+    @computed private get topOfHeaderBarDoc() {
+        return this.topOfDashUI;
+    }
+    @computed private get topOfSidebarDoc() {
+        return this.topOfDashUI + this.topMenuHeight();
+    }
+    @computed private get topOfMainDoc() {
+        return this.topOfDashUI + this.topMenuHeight() + this.headerBarDocHeight();
+    }
+    @computed private get topOfMainDocContent() {
+        return this.topOfMainDoc + this.dashboardTabHeight;
+    }
+    @computed private get leftScreenOffsetOfMainDocView() {
+        return this.leftMenuWidth() - 2;
+    }
+    @computed private get userDoc() {
+        return Doc.UserDoc();
+    }
+    @computed private get colorScheme() {
+        return StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme);
+    }
+    @computed private get mainContainer() {
+        return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard;
+    }
+    @computed private get headerBarDoc() {
+        return CurrentUserUtils.MyHeaderBar;
+    }
+    @computed public get mainFreeform(): Opt<Doc> {
+        return (docs => (docs?.length > 1 ? docs[1] : undefined))(DocListCast(this.mainContainer!.data));
+    }
 
     headerBarDocWidth = () => this.mainDocViewWidth();
     headerBarDocHeight = () => CurrentUserUtils.headerBarHeight ?? 0;
     topMenuHeight = () => 35;
     topMenuWidth = returnZero; // value is ignored ...
-    leftMenuWidth = () => Number(LEFT_MENU_WIDTH.replace("px", ""));
+    leftMenuWidth = () => Number(LEFT_MENU_WIDTH.replace('px', ''));
     leftMenuHeight = () => this._dashUIHeight;
     leftMenuFlyoutWidth = () => this._leftMenuFlyoutWidth;
     leftMenuFlyoutHeight = () => this._dashUIHeight;
@@ -106,89 +130,325 @@ export class MainView extends React.Component {
     mainDocViewHeight = () => this._dashUIHeight - this.headerBarDocHeight();
 
     componentDidMount() {
-        document.getElementById("root")?.addEventListener("scroll", e => ((ele) => ele.scrollLeft = ele.scrollTop = 0)(document.getElementById("root")!));
-        const ele = document.getElementById("loader");
-        const prog = document.getElementById("dash-progress");
+        document.getElementById('root')?.addEventListener('scroll', e => (ele => (ele.scrollLeft = ele.scrollTop = 0))(document.getElementById('root')!));
+        const ele = document.getElementById('loader');
+        const prog = document.getElementById('dash-progress');
         if (ele && prog) {
             // remove from DOM
             setTimeout(() => {
                 clearTimeout();
-                prog.style.transition = "1s";
-                prog.style.width = "100%";
+                prog.style.transition = '1s';
+                prog.style.width = '100%';
             }, 0);
-            setTimeout(() => ele.outerHTML = '', 1000);
+            setTimeout(() => (ele.outerHTML = ''), 1000);
         }
         this._sidebarContent.proto = undefined;
         if (!MainView.Live) {
-            DocServer.setPlaygroundFields(["dataTransition", "treeViewOpen", "showSidebar", "sidebarWidthPercent", "viewTransition",
-                "panX", "panY", "nativeWidth", "nativeHeight", "text-scrollHeight", "text-height", "hideMinimap",
-                "viewScale", "scrollTop", "hidden", "curPage", "viewType", "chromeHidden", "nativeWidth"]); // can play with these fields on someone else's
+            DocServer.setPlaygroundFields([
+                'dataTransition',
+                'treeViewOpen',
+                'showSidebar',
+                'sidebarWidthPercent',
+                'viewTransition',
+                'panX',
+                'panY',
+                'nativeWidth',
+                'nativeHeight',
+                'text-scrollHeight',
+                'text-height',
+                'hideMinimap',
+                'viewScale',
+                'scrollTop',
+                'hidden',
+                'curPage',
+                'viewType',
+                'chromeHidden',
+                'nativeWidth',
+            ]); // can play with these fields on someone else's
         }
-        DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg)));
+        DocServer.GetRefField('rtfProto').then(
+            proto =>
+                proto instanceof Doc &&
+                reaction(
+                    () => StrCast(proto.BROADCAST_MESSAGE),
+                    msg => msg && alert(msg)
+                )
+        );
 
         const tag = document.createElement('script');
-        tag.src = "https://www.youtube.com/iframe_api";
+        tag.src = 'https://www.youtube.com/iframe_api';
         const firstScriptTag = document.getElementsByTagName('script')[0];
         firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag);
-        window.removeEventListener("keydown", KeyManager.Instance.handle);
-        window.addEventListener("keydown", KeyManager.Instance.handle);
-        window.removeEventListener("keyup", KeyManager.Instance.unhandle);
-        window.addEventListener("keyup", KeyManager.Instance.unhandle);
-        window.addEventListener("paste", KeyManager.Instance.paste as any);
-        document.addEventListener("dash", (e: any) => {  // event used by chrome plugin to tell Dash which document to focus on
+        window.removeEventListener('keydown', KeyManager.Instance.handle);
+        window.addEventListener('keydown', KeyManager.Instance.handle);
+        window.removeEventListener('keyup', KeyManager.Instance.unhandle);
+        window.addEventListener('keyup', KeyManager.Instance.unhandle);
+        window.addEventListener('paste', KeyManager.Instance.paste as any);
+        document.addEventListener('dash', (e: any) => {
+            // event used by chrome plugin to tell Dash which document to focus on
             const id = FormattedTextBox.GetDocFromUrl(e.detail);
-            DocServer.GetRefField(id).then(doc => (doc instanceof Doc) ? DocumentManager.Instance.jumpToDocument(doc, false, undefined, []) : (null));
+            DocServer.GetRefField(id).then(doc => (doc instanceof Doc ? DocumentManager.Instance.jumpToDocument(doc, false, undefined, []) : null));
         });
-        document.addEventListener("linkAnnotationToDash", Hypothesis.linkListener);
+        document.addEventListener('linkAnnotationToDash', Hypothesis.linkListener);
         this.initEventListeners();
     }
 
     componentWillUnMount() {
-        window.removeEventListener("keyup", KeyManager.Instance.unhandle);
-        window.removeEventListener("keydown", KeyManager.Instance.handle);
-        window.removeEventListener("pointerdown", this.globalPointerDown);
-        window.removeEventListener("paste", KeyManager.Instance.paste as any);
-        document.removeEventListener("linkAnnotationToDash", Hypothesis.linkListener);
+        window.removeEventListener('keyup', KeyManager.Instance.unhandle);
+        window.removeEventListener('keydown', KeyManager.Instance.handle);
+        window.removeEventListener('pointerdown', this.globalPointerDown);
+        window.removeEventListener('paste', KeyManager.Instance.paste as any);
+        document.removeEventListener('linkAnnotationToDash', Hypothesis.linkListener);
     }
 
     constructor(props: Readonly<{}>) {
         super(props);
         MainView.Instance = this;
-        CurrentUserUtils._urlState = HistoryUtil.parseUrl(window.location) || {} as any;
+        CurrentUserUtils._urlState = HistoryUtil.parseUrl(window.location) || ({} as any);
 
         // causes errors to be generated when modifying an observable outside of an action
-        configure({ enforceActions: "observed" });
+        configure({ enforceActions: 'observed' });
 
-        if (window.location.pathname !== "/home") {
-            const pathname = window.location.pathname.substr(1).split("/");
-            if (pathname.length > 1 && pathname[0] === "doc") {
+        if (window.location.pathname !== '/home') {
+            const pathname = window.location.pathname.substr(1).split('/');
+            if (pathname.length > 1 && pathname[0] === 'doc') {
                 CurrentUserUtils.MainDocId = pathname[1];
                 !this.userDoc && DocServer.GetRefField(pathname[1]).then(action(field => field instanceof Doc && (CurrentUserUtils.GuestTarget = field)));
             }
         }
 
-        library.add(...[fa.faEdit, fa.faTrash, fa.faTrashAlt, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faCalendar,
-        fa.faSquare, far.faSquare as any, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faMapMarker, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock,
-        fa.faLock, fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointLeft, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard,
-        fa.faQuestion, fa.faTasks, fa.faPalette, fa.faAngleLeft, fa.faAngleRight, fa.faBell, fa.faCamera, fa.faExpand, fa.faCaretDown, fa.faCaretLeft, fa.faCaretRight,
-        fa.faCaretSquareDown, fa.faCaretSquareRight, fa.faArrowsAltH, fa.faPlus, fa.faMinus, fa.faTerminal, fa.faToggleOn, fa.faFile, fa.faLocationArrow,
-        fa.faSearch, fa.faFileDownload, fa.faFileUpload, fa.faStop, fa.faCalculator, fa.faWindowMaximize, fa.faAddressCard, fa.faQuestionCircle, fa.faArrowLeft,
-        fa.faArrowRight, fa.faArrowDown, fa.faArrowUp, fa.faBolt, fa.faBullseye, fa.faCaretUp, fa.faCat, fa.faCheck, fa.faChevronRight, fa.faChevronLeft, fa.faChevronDown, fa.faChevronUp,
-        fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt,
-        fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer,
-        fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV,
-        fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideoSlash, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes, fa.faFlag,
-        fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined,
-        fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript,
-        fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any,
-        fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle as any,
-        fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, fa.faAngleDoubleDown, fa.faAngleDoubleLeft, fa.faAngleDoubleUp, faBuffer as any, fa.faExpand, fa.faUndo,
-        fa.faSlidersH, fa.faAngleUp, fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl,
-        fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo,
-        fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical,
-        fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll,
-        fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines,
-        fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt, fa.faSearchPlus, fa.faVolumeUp, fa.faVolumeDown, fa.faSquareRootAlt, fa.faVolumeMute]);
+        library.add(
+            ...[
+                fa.faEdit,
+                fa.faTrash,
+                fa.faTrashAlt,
+                fa.faShare,
+                fa.faDownload,
+                fa.faExpandArrowsAlt,
+                fa.faLayerGroup,
+                fa.faExternalLinkAlt,
+                fa.faCalendar,
+                fa.faSquare,
+                far.faSquare as any,
+                fa.faConciergeBell,
+                fa.faWindowRestore,
+                fa.faFolder,
+                fa.faMapPin,
+                fa.faMapMarker,
+                fa.faFingerprint,
+                fa.faCrosshairs,
+                fa.faDesktop,
+                fa.faUnlock,
+                fa.faLock,
+                fa.faLaptopCode,
+                fa.faMale,
+                fa.faCopy,
+                fa.faHandPointLeft,
+                fa.faHandPointRight,
+                fa.faCompass,
+                fa.faSnowflake,
+                fa.faMicrophone,
+                fa.faKeyboard,
+                fa.faQuestion,
+                fa.faTasks,
+                fa.faPalette,
+                fa.faAngleLeft,
+                fa.faAngleRight,
+                fa.faBell,
+                fa.faCamera,
+                fa.faExpand,
+                fa.faCaretDown,
+                fa.faCaretLeft,
+                fa.faCaretRight,
+                fa.faCaretSquareDown,
+                fa.faCaretSquareRight,
+                fa.faArrowsAltH,
+                fa.faPlus,
+                fa.faMinus,
+                fa.faTerminal,
+                fa.faToggleOn,
+                fa.faFile,
+                fa.faLocationArrow,
+                fa.faSearch,
+                fa.faFileDownload,
+                fa.faFileUpload,
+                fa.faStop,
+                fa.faCalculator,
+                fa.faWindowMaximize,
+                fa.faAddressCard,
+                fa.faQuestionCircle,
+                fa.faArrowLeft,
+                fa.faArrowRight,
+                fa.faArrowDown,
+                fa.faArrowUp,
+                fa.faBolt,
+                fa.faBullseye,
+                fa.faCaretUp,
+                fa.faCat,
+                fa.faCheck,
+                fa.faChevronRight,
+                fa.faChevronLeft,
+                fa.faChevronDown,
+                fa.faChevronUp,
+                fa.faClone,
+                fa.faCloudUploadAlt,
+                fa.faCommentAlt,
+                fa.faCompressArrowsAlt,
+                fa.faCut,
+                fa.faEllipsisV,
+                fa.faEraser,
+                fa.faExclamation,
+                fa.faFileAlt,
+                fa.faFileAudio,
+                fa.faFileVideo,
+                fa.faFilePdf,
+                fa.faFilm,
+                fa.faFilter,
+                fa.faFont,
+                fa.faGlobeAmericas,
+                fa.faGlobeAsia,
+                fa.faHighlighter,
+                fa.faLongArrowAltRight,
+                fa.faMousePointer,
+                fa.faMusic,
+                fa.faObjectGroup,
+                fa.faPause,
+                fa.faPen,
+                fa.faPenNib,
+                fa.faPhone,
+                fa.faPlay,
+                fa.faPortrait,
+                fa.faRedoAlt,
+                fa.faStamp,
+                fa.faStickyNote,
+                fa.faArrowsAltV,
+                fa.faTimesCircle,
+                fa.faThumbtack,
+                fa.faTree,
+                fa.faTv,
+                fa.faUndoAlt,
+                fa.faVideoSlash,
+                fa.faVideo,
+                fa.faAsterisk,
+                fa.faBrain,
+                fa.faImage,
+                fa.faPaintBrush,
+                fa.faTimes,
+                fa.faFlag,
+                fa.faEye,
+                fa.faArrowsAlt,
+                fa.faQuoteLeft,
+                fa.faSortAmountDown,
+                fa.faAlignLeft,
+                fa.faAlignCenter,
+                fa.faAlignRight,
+                fa.faHeading,
+                fa.faRulerCombined,
+                fa.faFillDrip,
+                fa.faLink,
+                fa.faUnlink,
+                fa.faBold,
+                fa.faItalic,
+                fa.faClipboard,
+                fa.faUnderline,
+                fa.faStrikethrough,
+                fa.faSuperscript,
+                fa.faSubscript,
+                fa.faIndent,
+                fa.faEyeDropper,
+                fa.faPaintRoller,
+                fa.faBars,
+                fa.faBrush,
+                fa.faShapes,
+                fa.faEllipsisH,
+                fa.faHandPaper,
+                fa.faMap,
+                fa.faUser,
+                faHireAHelper as any,
+                fa.faTrashRestore,
+                fa.faUsers,
+                fa.faWrench,
+                fa.faCog,
+                fa.faMap,
+                fa.faBellSlash,
+                fa.faExpandAlt,
+                fa.faArchive,
+                fa.faBezierCurve,
+                fa.faCircle,
+                far.faCircle as any,
+                fa.faLongArrowAltRight,
+                fa.faPenFancy,
+                fa.faAngleDoubleRight,
+                fa.faAngleDoubleDown,
+                fa.faAngleDoubleLeft,
+                fa.faAngleDoubleUp,
+                faBuffer as any,
+                fa.faExpand,
+                fa.faUndo,
+                fa.faSlidersH,
+                fa.faAngleUp,
+                fa.faAngleDown,
+                fa.faPlayCircle,
+                fa.faClock,
+                fa.faRocket,
+                fa.faExchangeAlt,
+                fa.faHashtag,
+                fa.faAlignJustify,
+                fa.faCheckSquare,
+                fa.faListUl,
+                fa.faWindowMinimize,
+                fa.faWindowRestore,
+                fa.faTextWidth,
+                fa.faTextHeight,
+                fa.faClosedCaptioning,
+                fa.faInfoCircle,
+                fa.faTag,
+                fa.faSyncAlt,
+                fa.faPhotoVideo,
+                fa.faArrowAltCircleDown,
+                fa.faArrowAltCircleUp,
+                fa.faArrowAltCircleLeft,
+                fa.faArrowAltCircleRight,
+                fa.faStopCircle,
+                fa.faCheckCircle,
+                fa.faGripVertical,
+                fa.faSortUp,
+                fa.faSortDown,
+                fa.faTable,
+                fa.faTh,
+                fa.faThList,
+                fa.faProjectDiagram,
+                fa.faSignature,
+                fa.faColumns,
+                fa.faChevronCircleUp,
+                fa.faUpload,
+                fa.faBorderAll,
+                fa.faBraille,
+                fa.faChalkboard,
+                fa.faPencilAlt,
+                fa.faEyeSlash,
+                fa.faSmile,
+                fa.faIndent,
+                fa.faOutdent,
+                fa.faChartBar,
+                fa.faBan,
+                fa.faPhoneSlash,
+                fa.faGripLines,
+                fa.faSave,
+                fa.faBookmark,
+                fa.faList,
+                fa.faListOl,
+                fa.faFolderPlus,
+                fa.faLightbulb,
+                fa.faBookOpen,
+                fa.faMapMarkerAlt,
+                fa.faSearchPlus,
+                fa.faVolumeUp,
+                fa.faVolumeDown,
+                fa.faSquareRootAlt,
+                fa.faVolumeMute,
+            ]
+        );
         this.initAuthenticationRouters();
     }
 
@@ -197,175 +457,211 @@ export class MainView extends React.Component {
         const targets = document.elementsFromPoint(e.x, e.y);
         if (targets.length) {
             const targClass = targets[0].className.toString();
-            !targClass.includes("contextMenu") && ContextMenu.Instance.closeMenu();
-            !["timeline-menu-desc", "timeline-menu-item", "timeline-menu-input"].includes(targClass) && TimelineMenu.Instance.closeMenu();
+            !targClass.includes('contextMenu') && ContextMenu.Instance.closeMenu();
+            !['timeline-menu-desc', 'timeline-menu-item', 'timeline-menu-input'].includes(targClass) && TimelineMenu.Instance.closeMenu();
         }
     });
 
     initEventListeners = () => {
-        window.addEventListener("drop", e => e.preventDefault(), false);  // prevent default behavior of navigating to a new web page
-        window.addEventListener("dragover", e => e.preventDefault(), false);
+        window.addEventListener('drop', e => e.preventDefault(), false); // prevent default behavior of navigating to a new web page
+        window.addEventListener('dragover', e => e.preventDefault(), false);
         // document.addEventListener("pointermove", action(e => SearchBox.Instance._undoBackground = UndoManager.batchCounter ? "#000000a8" : undefined));
-        document.addEventListener("pointerdown", this.globalPointerDown);
-        document.addEventListener("click", (e: MouseEvent) => {
-            if (!e.cancelBubble) {
-                const pathstr = (e as any)?.path?.map((p: any) => p.classList?.toString()).join();
-                if (pathstr?.includes("libraryFlyout")) {
-                    SelectionManager.DeselectAll();
+        document.addEventListener('pointerdown', this.globalPointerDown);
+        document.addEventListener(
+            'click',
+            (e: MouseEvent) => {
+                if (!e.cancelBubble) {
+                    const pathstr = (e as any)?.path?.map((p: any) => p.classList?.toString()).join();
+                    if (pathstr?.includes('libraryFlyout')) {
+                        SelectionManager.DeselectAll();
+                    }
                 }
-            }
-        }, false);
+            },
+            false
+        );
         document.oncontextmenu = () => false;
-    }
+    };
 
     initAuthenticationRouters = async () => {
         const received = CurrentUserUtils.MainDocId;
         if (received && !this.userDoc) {
-            reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(), { fireImmediately: true });
-        } 
+            reaction(
+                () => CurrentUserUtils.GuestTarget,
+                target => target && CurrentUserUtils.createNewDashboard(),
+                { fireImmediately: true }
+            );
+        }
         // else {
         //     PromiseValue(this.userDoc.activeDashboard).then(dash => {
         //         if (dash instanceof Doc) CurrentUserUtils.openDashboard(dash);
         //         else CurrentUserUtils.createNewDashboard();
         //     });
         // }
-    }
+    };
 
     @action
     createNewPresentation = async () => {
-        const pres = Docs.Create.PresDocument({ title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" });
-        CollectionDockingView.AddSplit(pres, "left");
+        const pres = Docs.Create.PresDocument({ title: 'Untitled Trail', _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: 'alias', _chromeHidden: true, boxShadow: '0 0' });
+        CollectionDockingView.AddSplit(pres, 'left');
         CurrentUserUtils.ActivePresentation = pres;
-        Doc.AddDocToList(CurrentUserUtils.MyTrails, "data", pres);
-    }
+        Doc.AddDocToList(CurrentUserUtils.MyTrails, 'data', pres);
+    };
 
     @action
     createNewFolder = async () => {
-        const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true });
-        Doc.AddDocToList(CurrentUserUtils.MyFilesystem, "data", folder);
-    }
+        const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _stayInCollection: true, isFolder: true });
+        Doc.AddDocToList(CurrentUserUtils.MyFilesystem, 'data', folder);
+    };
 
     @observable _exploreMode = false;
     @computed get exploreMode() {
-        return () => this._exploreMode ? ScriptField.MakeScript("CollectionBrowseClick(documentView, clientX, clientY)",
-            { documentView: "any", clientX: "number", clientY: "number" })! : undefined;
+        return () => (this._exploreMode ? ScriptField.MakeScript('CollectionBrowseClick(documentView, clientX, clientY)', { documentView: 'any', clientX: 'number', clientY: 'number' })! : undefined);
     }
     headerBarScreenXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.headerBarDocHeight(), 1);
 
     @computed get headerBarDocView() {
-        return <div className="mainView-headerBar" style={{ height: this.headerBarDocHeight() }}>
-            <DocumentView key="headerBarDoc"
-                Document={this.headerBarDoc}
-                DataDoc={undefined}
-                addDocument={undefined}
-                addDocTab={this.addDocTabFunc}
-                pinToPres={emptyFunction}
-                docViewPath={returnEmptyDoclist}
-                styleProvider={DefaultStyleProvider}
-                rootSelected={returnTrue}
-                removeDocument={returnFalse}
-                fitContentsToBox={returnTrue}
-                isDocumentActive={returnTrue} // headerBar is always documentActive (ie, the docView gets pointer events)
-                isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive
-                ScreenToLocalTransform={this.headerBarScreenXf}
-                childHideResizeHandles={returnTrue}
-                dontRegisterView={true}
-                hideResizeHandles={true}
-                PanelWidth={this.headerBarDocWidth}
-                PanelHeight={this.headerBarDocHeight}
-                renderDepth={0}
-                focus={DocUtils.DefaultFocus}
-                whenChildContentsActiveChanged={emptyFunction}
-                bringToFront={emptyFunction}
-                docFilters={returnEmptyFilter}
-                docRangeFilters={returnEmptyFilter}
-                searchFilterDocs={returnEmptyDoclist}
-                ContainingCollectionView={undefined}
-                ContainingCollectionDoc={undefined}
-            /></div>;
+        return (
+            <div className="mainView-headerBar" style={{ height: this.headerBarDocHeight() }}>
+                <DocumentView
+                    key="headerBarDoc"
+                    Document={this.headerBarDoc}
+                    DataDoc={undefined}
+                    addDocument={undefined}
+                    addDocTab={this.addDocTabFunc}
+                    pinToPres={emptyFunction}
+                    docViewPath={returnEmptyDoclist}
+                    styleProvider={DefaultStyleProvider}
+                    rootSelected={returnTrue}
+                    removeDocument={returnFalse}
+                    fitContentsToBox={returnTrue}
+                    isDocumentActive={returnTrue} // headerBar is always documentActive (ie, the docView gets pointer events)
+                    isContentActive={returnTrue} // headerBar is awlays contentActive which means its items are always documentActive
+                    ScreenToLocalTransform={this.headerBarScreenXf}
+                    childHideResizeHandles={returnTrue}
+                    dontRegisterView={true}
+                    hideResizeHandles={true}
+                    PanelWidth={this.headerBarDocWidth}
+                    PanelHeight={this.headerBarDocHeight}
+                    renderDepth={0}
+                    focus={DocUtils.DefaultFocus}
+                    whenChildContentsActiveChanged={emptyFunction}
+                    bringToFront={emptyFunction}
+                    docFilters={returnEmptyFilter}
+                    docRangeFilters={returnEmptyFilter}
+                    searchFilterDocs={returnEmptyDoclist}
+                    ContainingCollectionView={undefined}
+                    ContainingCollectionDoc={undefined}
+                />
+            </div>
+        );
     }
     @computed get mainDocView() {
-        return <>
-            {this.headerBarDocView}
-            <DocumentView key="main"
-                Document={this.mainContainer!}
-                DataDoc={undefined}
-                addDocument={undefined}
-                addDocTab={this.addDocTabFunc}
-                pinToPres={emptyFunction}
-                docViewPath={returnEmptyDoclist}
-                styleProvider={undefined}
-                rootSelected={returnTrue}
-                isContentActive={returnTrue}
-                removeDocument={undefined}
-                ScreenToLocalTransform={Transform.Identity}
-                PanelWidth={this.mainDocViewWidth}
-                PanelHeight={this.mainDocViewHeight}
-                focus={DocUtils.DefaultFocus}
-                whenChildContentsActiveChanged={emptyFunction}
-                bringToFront={emptyFunction}
-                docFilters={returnEmptyFilter}
-                docRangeFilters={returnEmptyFilter}
-                searchFilterDocs={returnEmptyDoclist}
-                ContainingCollectionView={undefined}
-                ContainingCollectionDoc={undefined}
-                suppressSetHeight={true}
-                renderDepth={-1}
-            /></>;
+        return (
+            <>
+                {this.headerBarDocView}
+                <DocumentView
+                    key="main"
+                    Document={this.mainContainer!}
+                    DataDoc={undefined}
+                    addDocument={undefined}
+                    addDocTab={this.addDocTabFunc}
+                    pinToPres={emptyFunction}
+                    docViewPath={returnEmptyDoclist}
+                    styleProvider={undefined}
+                    rootSelected={returnTrue}
+                    isContentActive={returnTrue}
+                    removeDocument={undefined}
+                    ScreenToLocalTransform={Transform.Identity}
+                    PanelWidth={this.mainDocViewWidth}
+                    PanelHeight={this.mainDocViewHeight}
+                    focus={DocUtils.DefaultFocus}
+                    whenChildContentsActiveChanged={emptyFunction}
+                    bringToFront={emptyFunction}
+                    docFilters={returnEmptyFilter}
+                    docRangeFilters={returnEmptyFilter}
+                    searchFilterDocs={returnEmptyDoclist}
+                    ContainingCollectionView={undefined}
+                    ContainingCollectionDoc={undefined}
+                    suppressSetHeight={true}
+                    renderDepth={-1}
+                />
+            </>
+        );
     }
 
     @computed get dockingContent() {
-        return <div key="docking" className={`mainView-dockingContent${this._leftMenuFlyoutWidth ? "-flyout" : ""}`} onDrop={e => { e.stopPropagation(); e.preventDefault(); }}
-            style={{
-                minWidth: `calc(100% - ${this._leftMenuFlyoutWidth + this.leftMenuWidth() + this.propertiesWidth()}px)`,
-                transform: LightboxView.LightboxDoc ? "scale(0.0001)" : undefined,
-            }}>
-            {!this.mainContainer ? (null) : this.mainDocView}
-        </div>;
+        return (
+            <div
+                key="docking"
+                className={`mainView-dockingContent${this._leftMenuFlyoutWidth ? '-flyout' : ''}`}
+                onDrop={e => {
+                    e.stopPropagation();
+                    e.preventDefault();
+                }}
+                style={{
+                    minWidth: `calc(100% - ${this._leftMenuFlyoutWidth + this.leftMenuWidth() + this.propertiesWidth()}px)`,
+                    transform: LightboxView.LightboxDoc ? 'scale(0.0001)' : undefined,
+                }}>
+                {!this.mainContainer ? null : this.mainDocView}
+            </div>
+        );
     }
 
     @action
     onPropertiesPointerDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e,
-            action(e => (CurrentUserUtils.propertiesWidth = Math.max(0, this._dashUIWidth - e.clientX)) ? false : false),
+        setupMoveUpEvents(
+            this,
+            e,
+            action(e => ((CurrentUserUtils.propertiesWidth = Math.max(0, this._dashUIWidth - e.clientX)) ? false : false)),
             action(() => CurrentUserUtils.propertiesWidth < 5 && (CurrentUserUtils.propertiesWidth = 0)),
-            action(() => CurrentUserUtils.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._dashUIWidth - 50, 250) : 0), false);
-    }
+            action(() => (CurrentUserUtils.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._dashUIWidth - 50, 250) : 0)),
+            false
+        );
+    };
 
     @action
     onFlyoutPointerDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e,
-            action(e => (this._leftMenuFlyoutWidth = Math.max(e.clientX - 58, 0)) ? false : false),
+        setupMoveUpEvents(
+            this,
+            e,
+            action(e => ((this._leftMenuFlyoutWidth = Math.max(e.clientX - 58, 0)) ? false : false)),
             () => this._leftMenuFlyoutWidth < 5 && this.closeFlyout(),
-            this.closeFlyout);
-    }
+            this.closeFlyout
+        );
+    };
 
     sidebarScreenToLocal = () => new Transform(0, -this.topOfSidebarDoc, 1);
     mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftScreenOffsetOfMainDocView, 0);
     addDocTabFunc = (doc: Doc, location: string): boolean => {
-        const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":");
-        const locationParams = locationFields.length > 1 ? locationFields[1] : "";
+        const locationFields = doc._viewType === CollectionViewType.Docking ? ['dashboard'] : location.split(':');
+        const locationParams = locationFields.length > 1 ? locationFields[1] : '';
         if (doc.dockingConfig) return CurrentUserUtils.openDashboard(doc);
         switch (locationFields[0]) {
-            case "dashboard": return CurrentUserUtils.openDashboard(doc);
-            case "close": return CollectionDockingView.CloseSplit(doc, locationParams);
-            case "fullScreen": return CollectionDockingView.OpenFullScreen(doc);
-            case "lightbox": return LightboxView.AddDocTab(doc, location);
-            case "toggle": return CollectionDockingView.ToggleSplit(doc, locationParams);
-            case "inPlace":
-            case "add":
+            case 'dashboard':
+                return CurrentUserUtils.openDashboard(doc);
+            case 'close':
+                return CollectionDockingView.CloseSplit(doc, locationParams);
+            case 'fullScreen':
+                return CollectionDockingView.OpenFullScreen(doc);
+            case 'lightbox':
+                return LightboxView.AddDocTab(doc, location);
+            case 'toggle':
+                return CollectionDockingView.ToggleSplit(doc, locationParams);
+            case 'inPlace':
+            case 'add':
             default:
                 return CollectionDockingView.AddSplit(doc, locationParams);
         }
-    }
-
+    };
 
     @computed get flyout() {
-        return !this._leftMenuFlyoutWidth ? <div key="flyout" className={`mainView-libraryFlyout-out`}>
-            {this.docButtons}
-        </div> :
-            <div key="libFlyout" className="mainView-libraryFlyout" style={{ minWidth: this._leftMenuFlyoutWidth, width: this._leftMenuFlyoutWidth }} >
-                <div className="mainView-contentArea" >
+        return !this._leftMenuFlyoutWidth ? (
+            <div key="flyout" className={`mainView-libraryFlyout-out`}>
+                {this.docButtons}
+            </div>
+        ) : (
+            <div key="libFlyout" className="mainView-libraryFlyout" style={{ minWidth: this._leftMenuFlyoutWidth, width: this._leftMenuFlyoutWidth }}>
+                <div className="mainView-contentArea">
                     <DocumentView
                         Document={this._sidebarContent.proto || this._sidebarContent}
                         DataDoc={undefined}
@@ -393,37 +689,40 @@ export class MainView extends React.Component {
                     />
                 </div>
                 {this.docButtons}
-            </div>;
+            </div>
+        );
     }
 
     @computed get leftMenuPanel() {
-        return <div key="menu" className="mainView-leftMenuPanel">
-            <DocumentView
-                Document={CurrentUserUtils.MyLeftSidebarMenu}
-                DataDoc={undefined}
-                addDocument={undefined}
-                addDocTab={this.addDocTabFunc}
-                pinToPres={emptyFunction}
-                rootSelected={returnTrue}
-                removeDocument={returnFalse}
-                ScreenToLocalTransform={this.sidebarScreenToLocal}
-                PanelWidth={this.leftMenuWidth}
-                PanelHeight={this.leftMenuHeight}
-                renderDepth={0}
-                docViewPath={returnEmptyDoclist}
-                focus={DocUtils.DefaultFocus}
-                styleProvider={DefaultStyleProvider}
-                isContentActive={returnTrue}
-                whenChildContentsActiveChanged={emptyFunction}
-                bringToFront={emptyFunction}
-                docFilters={returnEmptyFilter}
-                docRangeFilters={returnEmptyFilter}
-                searchFilterDocs={returnEmptyDoclist}
-                ContainingCollectionView={undefined}
-                ContainingCollectionDoc={undefined}
-                scriptContext={this}
-            />
-        </div>;
+        return (
+            <div key="menu" className="mainView-leftMenuPanel">
+                <DocumentView
+                    Document={CurrentUserUtils.MyLeftSidebarMenu}
+                    DataDoc={undefined}
+                    addDocument={undefined}
+                    addDocTab={this.addDocTabFunc}
+                    pinToPres={emptyFunction}
+                    rootSelected={returnTrue}
+                    removeDocument={returnFalse}
+                    ScreenToLocalTransform={this.sidebarScreenToLocal}
+                    PanelWidth={this.leftMenuWidth}
+                    PanelHeight={this.leftMenuHeight}
+                    renderDepth={0}
+                    docViewPath={returnEmptyDoclist}
+                    focus={DocUtils.DefaultFocus}
+                    styleProvider={DefaultStyleProvider}
+                    isContentActive={returnTrue}
+                    whenChildContentsActiveChanged={emptyFunction}
+                    bringToFront={emptyFunction}
+                    docFilters={returnEmptyFilter}
+                    docRangeFilters={returnEmptyFilter}
+                    searchFilterDocs={returnEmptyDoclist}
+                    ContainingCollectionView={undefined}
+                    ContainingCollectionDoc={undefined}
+                    scriptContext={this}
+                />
+            </div>
+        );
     }
 
     @action
@@ -432,69 +731,78 @@ export class MainView extends React.Component {
         const willOpen = !this._leftMenuFlyoutWidth || this._panelContent !== title;
         this.closeFlyout();
         if (willOpen) {
-            switch (this._panelContent = title) {
-                case "Settings":
+            switch ((this._panelContent = title)) {
+                case 'Settings':
                     SettingsManager.Instance.open();
                     break;
-                case "Help":
+                case 'Help':
                     break;
                 default:
                     this.expandFlyout(button);
             }
         }
         return true;
-    }
+    };
 
     @computed get mainInnerContent() {
         const leftMenuFlyoutWidth = this._leftMenuFlyoutWidth + this.leftMenuWidth();
         const width = this.propertiesWidth() + leftMenuFlyoutWidth;
-        return <>
-            {this.leftMenuPanel}
-            <div key="inner" className={`mainView-innerContent${this.colorScheme}`}>
-                {this.flyout}
-                <div className="mainView-libraryHandle" style={{ left: leftMenuFlyoutWidth - 10 /* ~half width of handle */, display: !this._leftMenuFlyoutWidth ? "none" : undefined }} onPointerDown={this.onFlyoutPointerDown} >
-                    <FontAwesomeIcon icon="chevron-left" color={this.colorScheme === ColorScheme.Dark ? "white" : "black"} style={{ opacity: "50%" }} size="sm" />
-                </div>
-                <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)` }}>
-
-                    {this.dockingContent}
-
-                    <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}>
-                        <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.colorScheme === ColorScheme.Dark ? Colors.WHITE : Colors.BLACK} size="sm" />
+        return (
+            <>
+                {this.leftMenuPanel}
+                <div key="inner" className={`mainView-innerContent${this.colorScheme}`}>
+                    {this.flyout}
+                    <div className="mainView-libraryHandle" style={{ left: leftMenuFlyoutWidth - 10 /* ~half width of handle */, display: !this._leftMenuFlyoutWidth ? 'none' : undefined }} onPointerDown={this.onFlyoutPointerDown}>
+                        <FontAwesomeIcon icon="chevron-left" color={this.colorScheme === ColorScheme.Dark ? 'white' : 'black'} style={{ opacity: '50%' }} size="sm" />
                     </div>
-                    <div className="properties-container" style={{ width: this.propertiesWidth() }}>
-                        {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} addDocTab={this.addDocTabFunc} width={this.propertiesWidth()} height={this.propertiesHeight()} />}
+                    <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)` }}>
+                        {this.dockingContent}
+
+                        <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}>
+                            <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? 'chevron-left' : 'chevron-right'} color={this.colorScheme === ColorScheme.Dark ? Colors.WHITE : Colors.BLACK} size="sm" />
+                        </div>
+                        <div className="properties-container" style={{ width: this.propertiesWidth() }}>
+                            {this.propertiesWidth() < 10 ? null : <PropertiesView styleProvider={DefaultStyleProvider} addDocTab={this.addDocTabFunc} width={this.propertiesWidth()} height={this.propertiesHeight()} />}
+                        </div>
                     </div>
                 </div>
-            </div>
-        </>;
+            </>
+        );
     }
 
-
     @computed get mainDashboardArea() {
-        return !this.userDoc ? (null) :
-            <div className="mainView-dashboardArea" ref={r => {
-                r && new _global.ResizeObserver(action(() => {
-                    this._dashUIWidth = r.getBoundingClientRect().width;
-                    this._dashUIHeight = r.getBoundingClientRect().height;
-                })).observe(r);
-            }} style={{
-                color: this.colorScheme === ColorScheme.Dark ? "rgb(205,205,205)" : "black",
-                height: `calc(100% - ${this.topOfDashUI + this.topMenuHeight()}px)`,
-                width: "100%",
-            }} >
+        return !this.userDoc ? null : (
+            <div
+                className="mainView-dashboardArea"
+                ref={r => {
+                    r &&
+                        new _global.ResizeObserver(
+                            action(() => {
+                                this._dashUIWidth = r.getBoundingClientRect().width;
+                                this._dashUIHeight = r.getBoundingClientRect().height;
+                            })
+                        ).observe(r);
+                }}
+                style={{
+                    color: this.colorScheme === ColorScheme.Dark ? 'rgb(205,205,205)' : 'black',
+                    height: `calc(100% - ${this.topOfDashUI + this.topMenuHeight()}px)`,
+                    width: '100%',
+                }}>
                 {this.mainInnerContent}
-            </div>;
+            </div>
+        );
     }
 
-
     expandFlyout = action((button: Doc) => {
-        // bcz: What's going on here!?  
+        // bcz: What's going on here!?
         // Chrome(not firefox) seems to have a bug when the flyout expands and there's a zoomed freeform tab.  All of the div below the CollectionFreeFormView's main div
         // generate the wrong value from getClientRectangle() -- specifically they return an 'x' that is the flyout's width greater than it should be.
         // interactively adjusting the flyout fixes the problem.  So does programmatically changing the value after a timeout to something *fractionally* different (ie, 1.5, not 1);)
-        this._leftMenuFlyoutWidth = (this._leftMenuFlyoutWidth || 250);
-        setTimeout(action(() => this._leftMenuFlyoutWidth += 0.5), 0);
+        this._leftMenuFlyoutWidth = this._leftMenuFlyoutWidth || 250;
+        setTimeout(
+            action(() => (this._leftMenuFlyoutWidth += 0.5)),
+            0
+        );
 
         this._sidebarContent.proto = button.target as any;
         this.LastButton = button;
@@ -502,29 +810,29 @@ export class MainView extends React.Component {
 
     closeFlyout = action(() => {
         this.LastButton = undefined;
-        this._panelContent = "none";
+        this._panelContent = 'none';
         this._sidebarContent.proto = undefined;
         this._leftMenuFlyoutWidth = 0;
     });
 
-    remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(CurrentUserUtils.MyDockedBtns, "data", doc), true);
+    remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(CurrentUserUtils.MyDockedBtns, 'data', doc), true);
     moveButtonDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => this.remButtonDoc(doc) && addDocument(doc);
-    addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(CurrentUserUtils.MyDockedBtns, "data", doc), true);
+    addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(CurrentUserUtils.MyDockedBtns, 'data', doc), true);
 
     buttonBarXf = () => {
         if (!this._docBtnRef.current) return Transform.Identity();
         const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current);
         return new Transform(-translateX, -translateY, 1 / scale);
-    }
+    };
 
     @computed get docButtons() {
-        return !CurrentUserUtils.MyDockedBtns ? (null) :
-            <div className="mainView-docButtons" ref={this._docBtnRef} style={{ height: !CurrentUserUtils.MyDockedBtns.linearViewIsExpanded ? "42px" : undefined }} >
+        return !CurrentUserUtils.MyDockedBtns ? null : (
+            <div className="mainView-docButtons" ref={this._docBtnRef} style={{ height: !CurrentUserUtils.MyDockedBtns.linearViewIsExpanded ? '42px' : undefined }}>
                 <CollectionLinearView
                     Document={CurrentUserUtils.MyDockedBtns}
                     DataDoc={undefined}
-                    fieldKey={"data"}
-                    dropAction={"alias"}
+                    fieldKey={'data'}
+                    dropAction={'alias'}
                     setHeight={returnFalse}
                     styleProvider={DefaultStyleProvider}
                     rootSelected={returnTrue}
@@ -550,51 +858,62 @@ export class MainView extends React.Component {
                     docRangeFilters={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                     ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined} />
-                {['watching', 'recording'].includes(String(this.userDoc?.presentationMode) ?? '') ? <div style={{ border: '.5rem solid green', padding: '5px' }}>{this.userDoc?.presentationMode}</div> : <></>}
-            </div>;
+                    ContainingCollectionDoc={undefined}
+                />
+                {['watching', 'recording'].includes(String(this.userDoc?.presentationMode) ?? '') ? <div style={{ border: '.5rem solid green', padding: '5px' }}>{StrCast(this.userDoc?.presentationMode)}</div> : <></>}
+            </div>
+        );
     }
     @computed get snapLines() {
-        return !SnappingManager.GetShowSnapLines() ? (null) : <div className="mainView-snapLines">
-            <svg style={{ width: "100%", height: "100%" }}>
-                {SnappingManager.horizSnapLines().map(l => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
-                {SnappingManager.vertSnapLines().map(l => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)}
-            </svg>
-        </div>;
+        return !SnappingManager.GetShowSnapLines() ? null : (
+            <div className="mainView-snapLines">
+                <svg style={{ width: '100%', height: '100%' }}>
+                    {SnappingManager.horizSnapLines().map(l => (
+                        <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={'1 1'} />
+                    ))}
+                    {SnappingManager.vertSnapLines().map(l => (
+                        <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={'1 1'} />
+                    ))}
+                </svg>
+            </div>
+        );
     }
 
     @computed get inkResources() {
-        return <svg width={0} height={0}>
-            <defs>
-                <filter id="inkSelectionHalo">
-                    <feColorMatrix type="matrix"
-                        result="color"
-                        values="1 0 0 0 0
+        return (
+            <svg width={0} height={0}>
+                <defs>
+                    <filter id="inkSelectionHalo">
+                        <feColorMatrix
+                            type="matrix"
+                            result="color"
+                            values="1 0 0 0 0
                 0 0 0 0 0
                 0 0 0 0 0
-                0 0 0 1 0">
-                    </feColorMatrix>
-                    <feGaussianBlur in="color" stdDeviation="4" result="blur"></feGaussianBlur>
-                    <feOffset in="blur" dx="0" dy="0" result="offset"></feOffset>
-                    <feMerge>
-                        <feMergeNode in="bg"></feMergeNode>
-                        <feMergeNode in="offset"></feMergeNode>
-                        <feMergeNode in="SourceGraphic"></feMergeNode>
-                    </feMerge>
-                </filter>
-            </defs>
-        </svg>;
+                0 0 0 1 0"></feColorMatrix>
+                        <feGaussianBlur in="color" stdDeviation="4" result="blur"></feGaussianBlur>
+                        <feOffset in="blur" dx="0" dy="0" result="offset"></feOffset>
+                        <feMerge>
+                            <feMergeNode in="bg"></feMergeNode>
+                            <feMergeNode in="offset"></feMergeNode>
+                            <feMergeNode in="SourceGraphic"></feMergeNode>
+                        </feMerge>
+                    </filter>
+                </defs>
+            </svg>
+        );
     }
 
-    @computed get invisibleWebBox() { // see note under the makeLink method in HypothesisUtils.ts
-        return !DocumentLinksButton.invisibleWebDoc ? null :
+    @computed get invisibleWebBox() {
+        // see note under the makeLink method in HypothesisUtils.ts
+        return !DocumentLinksButton.invisibleWebDoc ? null : (
             <div className="mainView-invisibleWebRef" ref={DocumentLinksButton.invisibleWebRef}>
                 <WebBox
-                    fieldKey={"data"}
+                    fieldKey={'data'}
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
                     Document={DocumentLinksButton.invisibleWebDoc}
-                    dropAction={"move"}
+                    dropAction={'move'}
                     styleProvider={undefined}
                     isSelected={returnFalse}
                     select={returnFalse}
@@ -615,108 +934,131 @@ export class MainView extends React.Component {
                     docRangeFilters={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                 />
-            </div>;
+            </div>
+        );
     }
 
     render() {
-        return (<div className={`mainView-container${this.colorScheme}`}
-            onScroll={() => ((ele) => ele.scrollTop = ele.scrollLeft = 0)(document.getElementById("root")!)}
-            ref={r => {
-                r && new _global.ResizeObserver(action(() => { this._windowWidth = r.getBoundingClientRect().width; this._windowHeight = r.getBoundingClientRect().height; })).observe(r);
-            }}>
-            {this.inkResources}
-            <DictationOverlay />
-            <SharingManager />
-            <SettingsManager />
-            <CaptureManager />
-            <GroupManager />
-            <GoogleAuthenticationManager />
-            <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfHeaderBarDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} />
-            <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} />
-            <TopBar />
-            {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
-            {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => DocumentLinksButton.LinkEditorDocView = undefined)} docView={DocumentLinksButton.LinkEditorDocView} /> : (null)}
-            {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)}
-            
-            {((page:string) => {
-                switch (page) {
-                    case "dashboard":
-                    default:return <>
-                            <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 1999 }} >
-                                    <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
-                            </div> 
-                            <GestureOverlay> {this.mainDashboardArea}   </GestureOverlay>
-                        </>;
-                    case "home":  return <DashboardView/>;
-                } })(CurrentUserUtils.ActivePage)
-            }
-            
-            <PreviewCursor />
-            <TaskCompletionBox />
-            <ContextMenu />
-            <RadialMenu />
-            <AnchorMenu />
-            <DashFieldViewMenu />
-            <MarqueeOptionsMenu />
-            <OverlayView />
-            <TimelineMenu />
-            <RichTextMenu />
-            <InkTranscription />
-            {this.snapLines}
-            <div className="mainView-webRef" ref={this.makeWebRef} />
-            <LightboxView PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} />
-        </div >);
+        return (
+            <div
+                className={`mainView-container${this.colorScheme}`}
+                onScroll={() => (ele => (ele.scrollTop = ele.scrollLeft = 0))(document.getElementById('root')!)}
+                ref={r => {
+                    r &&
+                        new _global.ResizeObserver(
+                            action(() => {
+                                this._windowWidth = r.getBoundingClientRect().width;
+                                this._windowHeight = r.getBoundingClientRect().height;
+                            })
+                        ).observe(r);
+                }}>
+                {this.inkResources}
+                <DictationOverlay />
+                <SharingManager />
+                <SettingsManager />
+                <CaptureManager />
+                <GroupManager />
+                <GoogleAuthenticationManager />
+                <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfHeaderBarDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} />
+                <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} />
+                <TopBar />
+                {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
+                {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => (DocumentLinksButton.LinkEditorDocView = undefined))} docView={DocumentLinksButton.LinkEditorDocView} /> : null}
+                {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : null}
+
+                {((page: string) => {
+                    switch (page) {
+                        case 'dashboard':
+                        default:
+                            return (
+                                <>
+                                    <div style={{ position: 'relative', display: LightboxView.LightboxDoc ? 'none' : undefined, zIndex: 1999 }}>
+                                        <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
+                                    </div>
+                                    <GestureOverlay> {this.mainDashboardArea} </GestureOverlay>
+                                </>
+                            );
+                        case 'home':
+                            return <DashboardView />;
+                    }
+                })(CurrentUserUtils.ActivePage)}
+
+                <PreviewCursor />
+                <TaskCompletionBox />
+                <ContextMenu />
+                <RadialMenu />
+                <AnchorMenu />
+                <DashFieldViewMenu />
+                <MarqueeOptionsMenu />
+                <OverlayView />
+                <TimelineMenu />
+                <RichTextMenu />
+                <InkTranscription />
+                {this.snapLines}
+                <div className="mainView-webRef" ref={this.makeWebRef} />
+                <LightboxView PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} />
+            </div>
+        );
     }
 
     makeWebRef = (ele: HTMLDivElement) => {
-        reaction(() => DocumentLinksButton.invisibleWebDoc,
+        reaction(
+            () => DocumentLinksButton.invisibleWebDoc,
             invisibleDoc => {
                 ReactDOM.unmountComponentAtNode(ele);
-                invisibleDoc && ReactDOM.render(<span title="Drag as document" className="invisible-webbox" >
-                    <div className="mainView-webRef" ref={DocumentLinksButton.invisibleWebRef}>
-                        <WebBox
-                            fieldKey={"data"}
-                            ContainingCollectionView={undefined}
-                            ContainingCollectionDoc={undefined}
-                            Document={invisibleDoc}
-                            dropAction={"move"}
-                            isSelected={returnFalse}
-                            docViewPath={returnEmptyDoclist}
-                            select={returnFalse}
-                            rootSelected={returnFalse}
-                            renderDepth={0}
-                            setHeight={returnFalse}
-                            styleProvider={undefined}
-                            addDocTab={returnFalse}
-                            pinToPres={returnFalse}
-                            ScreenToLocalTransform={Transform.Identity}
-                            bringToFront={returnFalse}
-                            isContentActive={emptyFunction}
-                            whenChildContentsActiveChanged={returnFalse}
-                            focus={returnFalse}
-                            PanelWidth={() => 500}
-                            PanelHeight={() => 800}
-                            docFilters={returnEmptyFilter}
-                            docRangeFilters={returnEmptyFilter}
-                            searchFilterDocs={returnEmptyDoclist}
-                        />
-                    </div>;
-                </span>, ele);
+                invisibleDoc &&
+                    ReactDOM.render(
+                        <span title="Drag as document" className="invisible-webbox">
+                            <div className="mainView-webRef" ref={DocumentLinksButton.invisibleWebRef}>
+                                <WebBox
+                                    fieldKey={'data'}
+                                    ContainingCollectionView={undefined}
+                                    ContainingCollectionDoc={undefined}
+                                    Document={invisibleDoc}
+                                    dropAction={'move'}
+                                    isSelected={returnFalse}
+                                    docViewPath={returnEmptyDoclist}
+                                    select={returnFalse}
+                                    rootSelected={returnFalse}
+                                    renderDepth={0}
+                                    setHeight={returnFalse}
+                                    styleProvider={undefined}
+                                    addDocTab={returnFalse}
+                                    pinToPres={returnFalse}
+                                    ScreenToLocalTransform={Transform.Identity}
+                                    bringToFront={returnFalse}
+                                    isContentActive={emptyFunction}
+                                    whenChildContentsActiveChanged={returnFalse}
+                                    focus={returnFalse}
+                                    PanelWidth={() => 500}
+                                    PanelHeight={() => 800}
+                                    docFilters={returnEmptyFilter}
+                                    docRangeFilters={returnEmptyFilter}
+                                    searchFilterDocs={returnEmptyDoclist}
+                                />
+                            </div>
+                            ;
+                        </span>,
+                        ele
+                    );
 
                 let success = false;
                 const onSuccess = () => {
                     success = true;
                     clearTimeout(interval);
-                    document.removeEventListener("editSuccess", onSuccess);
+                    document.removeEventListener('editSuccess', onSuccess);
                 };
 
                 // For some reason, Hypothes.is annotations don't load until a click is registered on the page,
                 // so we keep simulating clicks until annotations have loaded and editing is successful
                 const interval = setInterval(() => !success && simulateMouseClick(ele, 50, 50, 50, 50), 500);
                 setTimeout(() => !success && clearInterval(interval), 10000); // give up if no success after 10s
-                document.addEventListener("editSuccess", onSuccess);
-            });
-    }
+                document.addEventListener('editSuccess', onSuccess);
+            }
+        );
+    };
 }
 
-ScriptingGlobals.add(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); });
\ No newline at end of file
+ScriptingGlobals.add(function selectMainMenu(doc: Doc, title: string) {
+    MainView.Instance.selectMenu(doc);
+});
diff --git a/src/client/views/Touchable.tsx b/src/client/views/Touchable.tsx
index 789756a78..436cb688f 100644
--- a/src/client/views/Touchable.tsx
+++ b/src/client/views/Touchable.tsx
@@ -4,7 +4,7 @@ import { InteractionUtils } from '../util/InteractionUtils';
 
 const HOLD_DURATION = 1000;
 
-export abstract class Touchable<T = {}> extends React.Component<T> {
+export abstract class Touchable<T = {}> extends React.Component<React.PropsWithChildren<T>> {
     //private holdTimer: NodeJS.Timeout | undefined;
     private moveDisposer?: InteractionUtils.MultiTouchEventDisposer;
     private endDisposer?: InteractionUtils.MultiTouchEventDisposer;
@@ -25,7 +25,6 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
      */
     @action
     protected onTouchStart = (e: Event, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): void => {
-
         const actualPts: React.Touch[] = [];
         const te = me.touchEvent;
         // loop through all touches on screen
@@ -60,7 +59,7 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
                 case 1:
                     this.handle1PointerDown(te, me);
                     te.persist();
-                    // -- code for radial menu -- 
+                    // -- code for radial menu --
                     // if (this.holdTimer) {
                     //     clearTimeout(this.holdTimer)
                     //     this.holdTimer = undefined;
@@ -71,11 +70,11 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
                     break;
             }
         }
-    }
+    };
 
     /**
-    * Handle touch move event
-    */
+     * Handle touch move event
+     */
     @action
     protected onTouch = (e: Event, me: InteractionUtils.MultiTouchEvent<TouchEvent>): void => {
         const te = me.touchEvent;
@@ -85,8 +84,12 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
         if (!InteractionUtils.IsDragging(this.prevPoints, myTouches, 5) && !this._touchDrag) return;
         this._touchDrag = true;
         switch (myTouches.length) {
-            case 1: this.handle1PointerMove(te, me); break;
-            case 2: this.handle2PointersMove(te, me); break;
+            case 1:
+                this.handle1PointerMove(te, me);
+                break;
+            case 2:
+                this.handle2PointersMove(te, me);
+                break;
         }
 
         for (const pt of me.touches) {
@@ -94,7 +97,7 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
                 this.prevPoints.set(pt.identifier, pt);
             }
         }
-    }
+    };
 
     @action
     protected onTouchEnd = (e: Event, me: InteractionUtils.MultiTouchEvent<TouchEvent>): void => {
@@ -110,7 +113,6 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
         this._touchDrag = false;
         te.stopPropagation();
 
-
         // if (e.targetTouches.length === 0) {
         //     this.prevPoints.clear();
         // }
@@ -119,36 +121,36 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
             this.cleanUpInteractions();
         }
         e.stopPropagation();
-    }
+    };
 
     cleanUpInteractions = (): void => {
         this.removeMoveListeners();
         this.removeEndListeners();
-    }
+    };
 
     handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>): any => {
         e.stopPropagation();
         e.preventDefault();
-    }
+    };
 
     handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>): any => {
         e.stopPropagation();
         e.preventDefault();
-    }
+    };
 
     handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): any => {
         this.removeMoveListeners();
         this.addMoveListeners();
         this.removeEndListeners();
         this.addEndListeners();
-    }
+    };
 
     handle2PointersDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): any => {
         this.removeMoveListeners();
         this.addMoveListeners();
         this.removeEndListeners();
         this.addEndListeners();
-    }
+    };
 
     handle1PointerHoldStart = (e: Event, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): any => {
         e.stopPropagation();
@@ -159,30 +161,30 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
         this.removeHoldEndListeners();
         this.addHoldMoveListeners();
         this.addHoldEndListeners();
-    }
+    };
 
     addMoveListeners = () => {
         const handler = (e: Event) => this.onTouch(e, (e as CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>).detail);
-        document.addEventListener("dashOnTouchMove", handler);
-        this.moveDisposer = () => document.removeEventListener("dashOnTouchMove", handler);
-    }
+        document.addEventListener('dashOnTouchMove', handler);
+        this.moveDisposer = () => document.removeEventListener('dashOnTouchMove', handler);
+    };
     addEndListeners = () => {
         const handler = (e: Event) => this.onTouchEnd(e, (e as CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>).detail);
-        document.addEventListener("dashOnTouchEnd", handler);
-        this.endDisposer = () => document.removeEventListener("dashOnTouchEnd", handler);
-    }
+        document.addEventListener('dashOnTouchEnd', handler);
+        this.endDisposer = () => document.removeEventListener('dashOnTouchEnd', handler);
+    };
 
     addHoldMoveListeners = () => {
         const handler = (e: Event) => this.handle1PointerHoldMove(e, (e as CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>).detail);
-        document.addEventListener("dashOnTouchHoldMove", handler);
-        this.holdMoveDisposer = () => document.removeEventListener("dashOnTouchHoldMove", handler);
-    }
+        document.addEventListener('dashOnTouchHoldMove', handler);
+        this.holdMoveDisposer = () => document.removeEventListener('dashOnTouchHoldMove', handler);
+    };
 
     addHoldEndListeners = () => {
         const handler = (e: Event) => this.handle1PointerHoldEnd(e, (e as CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>).detail);
-        document.addEventListener("dashOnTouchHoldEnd", handler);
-        this.holdEndDisposer = () => document.removeEventListener("dashOnTouchHoldEnd", handler);
-    }
+        document.addEventListener('dashOnTouchHoldEnd', handler);
+        this.holdEndDisposer = () => document.removeEventListener('dashOnTouchHoldEnd', handler);
+    };
 
     removeMoveListeners = () => this.moveDisposer?.();
     removeEndListeners = () => this.endDisposer?.();
@@ -192,7 +194,7 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
     handle1PointerHoldMove = (e: Event, me: InteractionUtils.MultiTouchEvent<TouchEvent>): void => {
         // e.stopPropagation();
         // me.touchEvent.stopPropagation();
-    }
+    };
 
     handle1PointerHoldEnd = (e: Event, me: InteractionUtils.MultiTouchEvent<TouchEvent>): void => {
         e.stopPropagation();
@@ -202,10 +204,10 @@ export abstract class Touchable<T = {}> extends React.Component<T> {
 
         me.touchEvent.stopPropagation();
         me.touchEvent.preventDefault();
-    }
+    };
 
     handleHandDown = (e: React.TouchEvent) => {
         // e.stopPropagation();
         // e.preventDefault();
-    }
-}
\ No newline at end of file
+    };
+}
diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx
index e80ba6f36..7a393a4f7 100644
--- a/src/client/views/animationtimeline/Timeline.tsx
+++ b/src/client/views/animationtimeline/Timeline.tsx
@@ -1,19 +1,19 @@
-import { faBackward, faForward, faGripLines, faPauseCircle, faPlayCircle } from "@fortawesome/free-solid-svg-icons";
+import { faBackward, faForward, faGripLines, faPauseCircle, faPlayCircle } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable, runInAction, trace } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import { Doc, DocListCast } from "../../../fields/Doc";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
-import { Utils, setupMoveUpEvents, emptyFunction, returnFalse } from "../../../Utils";
-import { FieldViewProps } from "../nodes/FieldView";
-import { KeyframeFunc } from "./Keyframe";
-import "./Timeline.scss";
-import { TimelineOverview } from "./TimelineOverview";
-import { Track } from "./Track";
-import clamp from "../../util/clamp";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { IconLookup } from "@fortawesome/fontawesome-svg-core";
+import { action, computed, observable, runInAction, trace } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
+import { Utils, setupMoveUpEvents, emptyFunction, returnFalse } from '../../../Utils';
+import { FieldViewProps } from '../nodes/FieldView';
+import { KeyframeFunc } from './Keyframe';
+import './Timeline.scss';
+import { TimelineOverview } from './TimelineOverview';
+import { Track } from './Track';
+import clamp from '../../util/clamp';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { IconLookup } from '@fortawesome/fontawesome-svg-core';
 
 /**
  * Timeline class controls most of timeline functions besides individual keyframe and track mechanism. Main functions are 
@@ -35,10 +35,8 @@ import { IconLookup } from "@fortawesome/fontawesome-svg-core";
     @author Andrew Kim 
  */
 
-
 @observer
 export class Timeline extends React.Component<FieldViewProps> {
-
     //readonly constants
     private readonly DEFAULT_TICK_SPACING: number = 50;
     private readonly MAX_TITLE_HEIGHT = 75;
@@ -57,8 +55,7 @@ export class Timeline extends React.Component<FieldViewProps> {
     @observable private _roundToggleRef = React.createRef<HTMLDivElement>();
     @observable private _roundToggleContainerRef = React.createRef<HTMLDivElement>();
 
-
-    //boolean vars and instance vars 
+    //boolean vars and instance vars
     @observable private _currentBarX: number = 0;
     @observable private _windSpeed: number = 1;
     @observable private _isPlaying: boolean = false; //scrubber playing
@@ -73,13 +70,13 @@ export class Timeline extends React.Component<FieldViewProps> {
     @observable private _titleHeight = 0;
 
     /**
-     * collection get method. Basically defines what defines collection's children. These will be tracked in the timeline. Do not edit. 
+     * collection get method. Basically defines what defines collection's children. These will be tracked in the timeline. Do not edit.
      */
     @computed
     private get children(): Doc[] {
         const annotatedDoc = [DocumentType.IMG, DocumentType.VID, DocumentType.PDF, DocumentType.MAP].includes(StrCast(this.props.Document.type) as any);
         if (annotatedDoc) {
-            return DocListCast(this.props.Document[Doc.LayoutFieldKey(this.props.Document) + "-annotations"]);
+            return DocListCast(this.props.Document[Doc.LayoutFieldKey(this.props.Document) + '-annotations']);
         }
         return DocListCast(this.props.Document[this.props.fieldKey]);
     }
@@ -91,7 +88,8 @@ export class Timeline extends React.Component<FieldViewProps> {
         this._titleHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; //check if relHeight is less than Maxheight. Else, just set relheight to max
         this.MIN_CONTAINER_HEIGHT = this._titleHeight + 130; //offset
         this.DEFAULT_CONTAINER_HEIGHT = this._titleHeight * 2 + 130; //twice the titleheight + offset
-        if (!this.props.Document.AnimationLength) { //if animation length did not exist
+        if (!this.props.Document.AnimationLength) {
+            //if animation length did not exist
             this.props.Document.AnimationLength = this._time; //set it to default time
         } else {
             this._time = NumCast(this.props.Document.AnimationLength); //else, set time to animationlength stored from before
@@ -116,24 +114,29 @@ export class Timeline extends React.Component<FieldViewProps> {
     drawTicks = () => {
         const ticks = [];
         for (let i = 0; i < this._time / this._tickIncrement; i++) {
-            ticks.push(<div key={Utils.GenerateGuid()} className="tick" style={{ transform: `translate(${i * this._tickSpacing}px)`, position: "absolute", pointerEvents: "none" }}> <p className="number-label">{this.toReadTime(i * this._tickIncrement)}</p></div>);
+            ticks.push(
+                <div key={Utils.GenerateGuid()} className="tick" style={{ transform: `translate(${i * this._tickSpacing}px)`, position: 'absolute', pointerEvents: 'none' }}>
+                    {' '}
+                    <p className="number-label">{this.toReadTime(i * this._tickIncrement)}</p>
+                </div>
+            );
         }
         return ticks;
-    }
+    };
 
     /**
      * changes the scrubber to actual pixel position
      */
     @action
     changeCurrentBarX = (pixel: number) => {
-        pixel <= 0 ? this._currentBarX = 0 : pixel >= this._totalLength ? this._currentBarX = this._totalLength : this._currentBarX = pixel;
-    }
+        pixel <= 0 ? (this._currentBarX = 0) : pixel >= this._totalLength ? (this._currentBarX = this._totalLength) : (this._currentBarX = pixel);
+    };
 
     //for playing
     onPlay = (e: React.MouseEvent) => {
         e.stopPropagation();
         this.play();
-    }
+    };
 
     /**
      * when playbutton is clicked
@@ -149,8 +152,7 @@ export class Timeline extends React.Component<FieldViewProps> {
         this._isPlaying = !this._isPlaying;
         this._playButton = this._isPlaying ? faPauseCircle : faPlayCircle;
         this._isPlaying && playTimeline();
-    }
-
+    };
 
     /**
      * fast forward the timeline scrubbing
@@ -159,30 +161,32 @@ export class Timeline extends React.Component<FieldViewProps> {
     windForward = (e: React.MouseEvent) => {
         e.preventDefault();
         e.stopPropagation();
-        if (this._windSpeed < 64) { //max speed is 32 
+        if (this._windSpeed < 64) {
+            //max speed is 32
             this._windSpeed = this._windSpeed * 2;
         }
-    }
+    };
 
     /**
-     * rewind the timeline scrubbing 
+     * rewind the timeline scrubbing
      */
     @action
     windBackward = (e: React.MouseEvent) => {
         e.preventDefault();
         e.stopPropagation();
-        if (this._windSpeed > 1 / 16) { // min speed is 1/8
+        if (this._windSpeed > 1 / 16) {
+            // min speed is 1/8
             this._windSpeed = this._windSpeed / 2;
         }
-    }
+    };
 
     /**
-     * scrubber down 
+     * scrubber down
      */
     @action
     onScrubberDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, this.onScrubberMove, emptyFunction, emptyFunction);
-    }
+    };
 
     /**
      * when there is any scrubber movement
@@ -194,16 +198,15 @@ export class Timeline extends React.Component<FieldViewProps> {
         const offsetX = Math.round(e.clientX - left) * this.props.ScreenToLocalTransform().Scale;
         this.changeCurrentBarX(offsetX + this._visibleStart); //changes scrubber to clicked scrubber position
         return false;
-    }
+    };
 
     /**
      * when panning the timeline (in editing mode)
      */
     @action
     onPanDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, this.onPanMove, emptyFunction, (e) =>
-            this.changeCurrentBarX(this._trackbox.current!.scrollLeft + e.clientX - this._trackbox.current!.getBoundingClientRect().left));
-    }
+        setupMoveUpEvents(this, e, this.onPanMove, emptyFunction, e => this.changeCurrentBarX(this._trackbox.current!.scrollLeft + e.clientX - this._trackbox.current!.getBoundingClientRect().left));
+    };
 
     /**
      * when moving the timeline (in editing mode)
@@ -218,29 +221,34 @@ export class Timeline extends React.Component<FieldViewProps> {
         if (this._visibleStart + this._visibleLength + 20 >= this._totalLength) {
             this._visibleStart -= e.movementX;
             this._totalLength -= e.movementX;
-            this._time -= KeyframeFunc.convertPixelTime(e.movementX, "mili", "time", this._tickSpacing, this._tickIncrement);
+            this._time -= KeyframeFunc.convertPixelTime(e.movementX, 'mili', 'time', this._tickSpacing, this._tickIncrement);
             this.props.Document.AnimationLength = this._time;
         }
         return false;
-    }
-
+    };
 
     @action
     movePanX = (pixel: number) => {
         this._infoContainer.current!.scrollLeft = pixel;
         this._visibleStart = this._infoContainer.current!.scrollLeft;
-    }
+    };
 
     /**
      * resizing timeline (in editing mode) (the hamburger drag icon)
      */
     onResizeDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, action((e) => {
-            const offset = e.clientY - this._timelineContainer.current!.getBoundingClientRect().bottom;
-            this._containerHeight = clamp(this.MIN_CONTAINER_HEIGHT, this._containerHeight + offset, this.MAX_CONTAINER_HEIGHT);
-            return false;
-        }), emptyFunction, emptyFunction);
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            action(e => {
+                const offset = e.clientY - this._timelineContainer.current!.getBoundingClientRect().bottom;
+                this._containerHeight = clamp(this.MIN_CONTAINER_HEIGHT, this._containerHeight + offset, this.MAX_CONTAINER_HEIGHT);
+                return false;
+            }),
+            emptyFunction,
+            emptyFunction
+        );
+    };
 
     /**
      * for displaying time to standard min:sec
@@ -251,19 +259,18 @@ export class Timeline extends React.Component<FieldViewProps> {
         const inSeconds = Math.round(time * 100) / 100;
 
         const min = Math.floor(inSeconds / 60);
-        const sec = (Math.round((inSeconds % 60) * 100) / 100);
+        const sec = Math.round((inSeconds % 60) * 100) / 100;
         let secString = sec.toFixed(2);
 
         if (Math.floor(sec / 10) === 0) {
-            secString = "0" + secString;
+            secString = '0' + secString;
         }
 
         return `${min}:${secString}`;
-    }
-
+    };
 
     /**
-     * timeline zoom function 
+     * timeline zoom function
      * use mouse middle button to zoom in/out the timeline
      */
     @action
@@ -271,17 +278,16 @@ export class Timeline extends React.Component<FieldViewProps> {
         e.preventDefault();
         e.stopPropagation();
         const offset = e.clientX - this._infoContainer.current!.getBoundingClientRect().left;
-        const prevTime = KeyframeFunc.convertPixelTime(this._visibleStart + offset, "mili", "time", this._tickSpacing, this._tickIncrement);
-        const prevCurrent = KeyframeFunc.convertPixelTime(this._currentBarX, "mili", "time", this._tickSpacing, this._tickIncrement);
+        const prevTime = KeyframeFunc.convertPixelTime(this._visibleStart + offset, 'mili', 'time', this._tickSpacing, this._tickIncrement);
+        const prevCurrent = KeyframeFunc.convertPixelTime(this._currentBarX, 'mili', 'time', this._tickSpacing, this._tickIncrement);
         this.zoom(e.deltaY < 0);
-        const currPixel = KeyframeFunc.convertPixelTime(prevTime, "mili", "pixel", this._tickSpacing, this._tickIncrement);
-        const currCurrent = KeyframeFunc.convertPixelTime(prevCurrent, "mili", "pixel", this._tickSpacing, this._tickIncrement);
+        const currPixel = KeyframeFunc.convertPixelTime(prevTime, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
+        const currCurrent = KeyframeFunc.convertPixelTime(prevCurrent, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
         this._infoContainer.current!.scrollLeft = currPixel - offset;
         this._visibleStart = currPixel - offset > 0 ? currPixel - offset : 0;
         this._visibleStart += this._visibleLength + this._visibleStart > this._totalLength ? this._totalLength - (this._visibleStart + this._visibleLength) : 0;
         this.changeCurrentBarX(currCurrent);
-    }
-
+    };
 
     resetView(doc: Doc) {
         doc._panX = doc._customOriginX ?? 0;
@@ -324,7 +330,7 @@ export class Timeline extends React.Component<FieldViewProps> {
             this._tickSpacing = spacingChange;
             this._tickIncrement = incrementChange;
         }
-    }
+    };
 
     /**
      * tool box includes the toggle buttons at the top of the timeline (both editing mode and play mode)
@@ -333,60 +339,90 @@ export class Timeline extends React.Component<FieldViewProps> {
         const size = 40 * scale; //50 is default
         const iconSize = 25;
         const width: number = this.props.PanelWidth();
-        const modeType = this.props.Document.isATOn ? "Author" : "Play";
+        const modeType = this.props.Document.isATOn ? 'Author' : 'Play';
 
         //decides if information should be omitted because the timeline is very small
         // if its less than 950 pixels then it's going to be overlapping
-        let modeString = modeType, overviewString = "", lengthString = "";
+        let modeString = modeType,
+            overviewString = '',
+            lengthString = '';
         if (width < 850) {
-            modeString = "Mode: " + modeType;
-            overviewString = "Overview:";
-            lengthString = "Length: ";
+            modeString = 'Mode: ' + modeType;
+            overviewString = 'Overview:';
+            lengthString = 'Length: ';
         }
 
         return (
             <div key="timeline_toolbox" className="timeline-toolbox" style={{ height: `${size}px` }}>
                 <div className="playbackControls">
-                    <div className="timeline-icon" key="timeline_windBack" onClick={this.windBackward} title="Slow Down Animation"> <FontAwesomeIcon icon={faBackward as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div>
-                    <div className="timeline-icon" key=" timeline_play" onClick={this.onPlay} title="Play/Pause"> <FontAwesomeIcon icon={this._playButton as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div>
-                    <div className="timeline-icon" key="timeline_windForward" onClick={this.windForward} title="Speed Up Animation"> <FontAwesomeIcon icon={faForward as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} /> </div>
+                    <div className="timeline-icon" key="timeline_windBack" onClick={this.windBackward} title="Slow Down Animation">
+                        {' '}
+                        <FontAwesomeIcon icon={faBackward as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} />{' '}
+                    </div>
+                    <div className="timeline-icon" key=" timeline_play" onClick={this.onPlay} title="Play/Pause">
+                        {' '}
+                        <FontAwesomeIcon icon={this._playButton as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} />{' '}
+                    </div>
+                    <div className="timeline-icon" key="timeline_windForward" onClick={this.windForward} title="Speed Up Animation">
+                        {' '}
+                        <FontAwesomeIcon icon={faForward as IconLookup} style={{ height: `${iconSize}px`, width: `${iconSize}px` }} />{' '}
+                    </div>
                 </div>
                 <div className="grid-box overview-tool">
                     <div className="overview-box">
-                        <div key="overview-text" className="animation-text">{overviewString}</div>
-                        <TimelineOverview tickSpacing={this._tickSpacing} tickIncrement={this._tickIncrement} time={this._time} parent={this} isAuthoring={BoolCast(this.props.Document.isATOn)} currentBarX={this._currentBarX} totalLength={this._totalLength} visibleLength={this._visibleLength} visibleStart={this._visibleStart} changeCurrentBarX={this.changeCurrentBarX} movePanX={this.movePanX} />
+                        <div key="overview-text" className="animation-text">
+                            {overviewString}
+                        </div>
+                        <TimelineOverview
+                            tickSpacing={this._tickSpacing}
+                            tickIncrement={this._tickIncrement}
+                            time={this._time}
+                            parent={this}
+                            isAuthoring={BoolCast(this.props.Document.isATOn)}
+                            currentBarX={this._currentBarX}
+                            totalLength={this._totalLength}
+                            visibleLength={this._visibleLength}
+                            visibleStart={this._visibleStart}
+                            changeCurrentBarX={this.changeCurrentBarX}
+                            movePanX={this.movePanX}
+                        />
                     </div>
                     <div className="mode-box overview-tool">
-                        <div key="animation-text" className="animation-text">{modeString}</div>
+                        <div key="animation-text" className="animation-text">
+                            {modeString}
+                        </div>
                         <div key="round-toggle" ref={this._roundToggleContainerRef} className="round-toggle">
-                            <div key="round-toggle-slider" ref={this._roundToggleRef} className="round-toggle-slider" onPointerDown={this.toggleChecked}> </div>
+                            <div key="round-toggle-slider" ref={this._roundToggleRef} className="round-toggle-slider" onPointerDown={this.toggleChecked}>
+                                {' '}
+                            </div>
                         </div>
                     </div>
-                    <div className="time-box overview-tool" style={{ display: "flex" }}>
+                    <div className="time-box overview-tool" style={{ display: 'flex' }}>
                         {this.timeIndicator(lengthString, totalTime)}
-                        <div className="resetView-tool" title="Return to Default View" onClick={() => this.resetView(this.props.Document)}><FontAwesomeIcon icon="compress-arrows-alt" size="lg" /></div>
-                        <div className="resetView-tool" style={{ display: this.props.Document.isATOn ? "flex" : "none" }} title="Set Default View" onClick={() => this.setView(this.props.Document)}><FontAwesomeIcon icon="expand-arrows-alt" size="lg" /></div>
+                        <div className="resetView-tool" title="Return to Default View" onClick={() => this.resetView(this.props.Document)}>
+                            <FontAwesomeIcon icon="compress-arrows-alt" size="lg" />
+                        </div>
+                        <div className="resetView-tool" style={{ display: this.props.Document.isATOn ? 'flex' : 'none' }} title="Set Default View" onClick={() => this.setView(this.props.Document)}>
+                            <FontAwesomeIcon icon="expand-arrows-alt" size="lg" />
+                        </div>
                     </div>
                 </div>
             </div>
         );
-    }
+    };
 
     timeIndicator(lengthString: string, totalTime: number) {
         if (this.props.Document.isATOn) {
-            return (
-                <div key="time-text" className="animation-text" style={{ visibility: this.props.Document.isATOn ? "visible" : "hidden", display: this.props.Document.isATOn ? "flex" : "none" }}>{`Total: ${this.toReadTime(totalTime)}`}</div>
-            );
-        }
-        else {
+            return <div key="time-text" className="animation-text" style={{ visibility: this.props.Document.isATOn ? 'visible' : 'hidden', display: this.props.Document.isATOn ? 'flex' : 'none' }}>{`Total: ${this.toReadTime(totalTime)}`}</div>;
+        } else {
             const ctime = `Current: ${this.getCurrentTime()}`;
             const ttime = `Total: ${this.toReadTime(this._time)}`;
             return (
-                <div style={{ flexDirection: "column" }}>
-                    <div className="animation-text" style={{ fontSize: "10px", width: "100%", display: !this.props.Document.isATOn ? "block" : "none" }}>
+                <div style={{ flexDirection: 'column' }}>
+                    <div className="animation-text" style={{ fontSize: '10px', width: '100%', display: !this.props.Document.isATOn ? 'block' : 'none' }}>
                         {ctime}
                     </div>
-                    <div className="animation-text" style={{ fontSize: "10px", width: "100%", display: !this.props.Document.isATOn ? "block" : "none" }}>
+                    <div className="animation-text" style={{ fontSize: '10px', width: '100%', display: !this.props.Document.isATOn ? 'block' : 'none' }}>
                         {ttime}
                     </div>
                 </div>
@@ -402,7 +438,7 @@ export class Timeline extends React.Component<FieldViewProps> {
         e.preventDefault();
         e.stopPropagation();
         this.toggleHandle();
-    }
+    };
 
     /**
      * turns on the toggle button (the purple slide button that changes from editing mode and play mode
@@ -415,23 +451,22 @@ export class Timeline extends React.Component<FieldViewProps> {
         this.props.Document.isATOn = !this.props.Document.isATOn;
         if (!BoolCast(this.props.Document.isATOn)) {
             //turning on playmode...
-            roundToggle.style.transform = "translate(0px, 0px)";
-            roundToggle.style.animationName = "turnoff";
-            roundToggleContainer.style.animationName = "turnoff";
-            roundToggleContainer.style.backgroundColor = "white";
+            roundToggle.style.transform = 'translate(0px, 0px)';
+            roundToggle.style.animationName = 'turnoff';
+            roundToggleContainer.style.animationName = 'turnoff';
+            roundToggleContainer.style.backgroundColor = 'white';
             timelineContainer.style.top = `${-this._containerHeight}px`;
             this.toPlay();
         } else {
             //turning on authoring mode...
-            roundToggle.style.transform = "translate(20px, 0px)";
-            roundToggle.style.animationName = "turnon";
-            roundToggleContainer.style.animationName = "turnon";
-            roundToggleContainer.style.backgroundColor = "#9acedf";
-            timelineContainer.style.top = "0px";
+            roundToggle.style.transform = 'translate(20px, 0px)';
+            roundToggle.style.animationName = 'turnon';
+            roundToggleContainer.style.animationName = 'turnon';
+            roundToggleContainer.style.backgroundColor = '#9acedf';
+            timelineContainer.style.top = '0px';
             this.toAuthoring();
         }
-    }
-
+    };
 
     @action.bound
     changeLengths() {
@@ -443,9 +478,9 @@ export class Timeline extends React.Component<FieldViewProps> {
 
     // @computed
     getCurrentTime = () => {
-        const current = KeyframeFunc.convertPixelTime(this._currentBarX, "mili", "time", this._tickSpacing, this._tickIncrement);
+        const current = KeyframeFunc.convertPixelTime(this._currentBarX, 'mili', 'time', this._tickSpacing, this._tickIncrement);
         return this.toReadTime(current > this._time ? this._time : current);
-    }
+    };
 
     @observable private mapOfTracks: (Track | null)[] = [];
 
@@ -465,22 +500,22 @@ export class Timeline extends React.Component<FieldViewProps> {
             }
         });
         return longestTime;
-    }
+    };
 
     @action
     toAuthoring = () => {
         this._time = Math.ceil((this.findLongestTime() ?? 1) / 100000) * 100000;
-        this._totalLength = KeyframeFunc.convertPixelTime(this._time, "mili", "pixel", this._tickSpacing, this._tickIncrement);
-    }
+        this._totalLength = KeyframeFunc.convertPixelTime(this._time, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
+    };
 
     @action
     toPlay = () => {
         this._time = this.findLongestTime();
-        this._totalLength = KeyframeFunc.convertPixelTime(this._time, "mili", "pixel", this._tickSpacing, this._tickIncrement);
-    }
+        this._totalLength = KeyframeFunc.convertPixelTime(this._time, 'mili', 'pixel', this._tickSpacing, this._tickIncrement);
+    };
 
     /**
-     * if you have any question here, just shoot me an email or text. 
+     * if you have any question here, just shoot me an email or text.
      * basically the only thing you need to edit besides render methods in track (individual track lines) and keyframe (green region)
      */
     render() {
@@ -488,23 +523,46 @@ export class Timeline extends React.Component<FieldViewProps> {
 
         // change visible and total width
         return (
-            <div style={{ visibility: "visible" }}>
-                <div key="timeline_wrapper" style={{ visibility: this.props.Document.isATOn ? "visible" : "hidden", left: "0px", top: "0px", position: "absolute", width: "100%", transform: "translate(0px, 0px)" }}>
+            <div style={{ visibility: 'visible' }}>
+                <div key="timeline_wrapper" style={{ visibility: this.props.Document.isATOn ? 'visible' : 'hidden', left: '0px', top: '0px', position: 'absolute', width: '100%', transform: 'translate(0px, 0px)' }}>
                     <div key="timeline_container" className="timeline-container" ref={this._timelineContainer} style={{ height: `${this._containerHeight}px`, top: `0px` }}>
                         <div key="timeline_info" className="info-container" onPointerDown={this.onPanDown} ref={this._infoContainer} onWheel={this.onWheelZoom}>
                             {this.drawTicks()}
                             <div key="timeline_scrubber" className="scrubber" style={{ transform: `translate(${this._currentBarX}px)` }}>
-                                <div key="timeline_scrubberhead" className="scrubberhead" onPointerDown={this.onScrubberDown} ></div>
+                                <div key="timeline_scrubberhead" className="scrubberhead" onPointerDown={this.onScrubberDown}></div>
                             </div>
                             <div key="timeline_trackbox" className="trackbox" ref={this._trackbox} style={{ width: `${this._totalLength}px` }}>
-                                {this.children.map(doc =>
-                                    <Track ref={ref => this.mapOfTracks.push(ref)} node={doc} currentBarX={this._currentBarX} changeCurrentBarX={this.changeCurrentBarX} transform={this.props.ScreenToLocalTransform()} time={this._time} tickSpacing={this._tickSpacing} tickIncrement={this._tickIncrement} collection={this.props.Document} timelineVisible={true} />
-                                )}
+                                {this.children.map(doc => (
+                                    <Track
+                                        ref={ref => this.mapOfTracks.push(ref)}
+                                        node={doc}
+                                        currentBarX={this._currentBarX}
+                                        changeCurrentBarX={this.changeCurrentBarX}
+                                        transform={this.props.ScreenToLocalTransform()}
+                                        time={this._time}
+                                        tickSpacing={this._tickSpacing}
+                                        tickIncrement={this._tickIncrement}
+                                        collection={this.props.Document}
+                                        timelineVisible={true}
+                                    />
+                                ))}
                             </div>
                         </div>
                         <div className="currentTime">Current: {this.getCurrentTime()}</div>
                         <div key="timeline_title" className="title-container" ref={this._titleContainer}>
-                            {this.children.map(doc => <div style={{ height: `${(this._titleHeight)}px` }} className="datapane" onPointerOver={() => { Doc.BrushDoc(doc); }} onPointerOut={() => { Doc.UnBrushDoc(doc); }}><p>{doc.title}</p></div>)}
+                            {this.children.map(doc => (
+                                <div
+                                    style={{ height: `${this._titleHeight}px` }}
+                                    className="datapane"
+                                    onPointerOver={() => {
+                                        Doc.BrushDoc(doc);
+                                    }}
+                                    onPointerOut={() => {
+                                        Doc.UnBrushDoc(doc);
+                                    }}>
+                                    <p>{StrCast(doc.title)}</p>
+                                </div>
+                            ))}
                         </div>
                         <div key="timeline_resize" onPointerDown={this.onResizeDown}>
                             <FontAwesomeIcon className="resize" icon={faGripLines as IconLookup} />
@@ -515,4 +573,4 @@ export class Timeline extends React.Component<FieldViewProps> {
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 37589974b..7d40cab8c 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -1,37 +1,36 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { CursorProperty } from "csstype";
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { DataSym, Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
-import { List } from "../../../fields/List";
-import { listSpec } from "../../../fields/Schema";
-import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
-import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
-import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils";
-import { Docs, DocUtils } from "../../documents/Documents";
-import { DragManager, dropActionType } from "../../util/DragManager";
-import { SnappingManager } from "../../util/SnappingManager";
-import { Transform } from "../../util/Transform";
-import { undoBatch } from "../../util/UndoManager";
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import { EditableView } from "../EditableView";
-import { LightboxView } from "../LightboxView";
-import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
-import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from "../nodes/DocumentView";
-import { StyleProp } from "../StyleProvider";
-import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow";
-import "./CollectionStackingView.scss";
-import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
-import { CollectionSubView } from "./CollectionSubView";
-import { CollectionViewType } from "./CollectionView";
-import { FieldViewProps } from "../nodes/FieldView";
-import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
-const _global = (window /* browser */ || global /* node */) as any;
-
+import React = require('react');
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { CursorProperty } from 'csstype';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { DataSym, Doc, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { listSpec } from '../../../fields/Schema';
+import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
+import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { TraceMobx } from '../../../fields/util';
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
+import { Docs, DocUtils } from '../../documents/Documents';
+import { DragManager, dropActionType } from '../../util/DragManager';
+import { SnappingManager } from '../../util/SnappingManager';
+import { Transform } from '../../util/Transform';
+import { undoBatch } from '../../util/UndoManager';
+import { ContextMenu } from '../ContextMenu';
+import { ContextMenuProps } from '../ContextMenuItem';
+import { EditableView } from '../EditableView';
+import { LightboxView } from '../LightboxView';
+import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
+import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from '../nodes/DocumentView';
+import { StyleProp } from '../StyleProvider';
+import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow';
+import './CollectionStackingView.scss';
+import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn';
+import { CollectionSubView } from './CollectionSubView';
+import { CollectionViewType } from './CollectionView';
+import { FieldViewProps } from '../nodes/FieldView';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+const _global = (window /* browser */ || global) /* node */ as any;
 
 export type collectionStackingViewProps = {
     chromeHidden?: boolean;
@@ -46,27 +45,50 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     _draggerRef = React.createRef<HTMLDivElement>();
     _pivotFieldDisposer?: IReactionDisposer;
     _autoHeightDisposer?: IReactionDisposer;
-    _docXfs: { height: () => number, width: () => number, stackedDocTransform: () => Transform }[] = [];
+    _docXfs: { height: () => number; width: () => number; stackedDocTransform: () => Transform }[] = [];
     _columnStart: number = 0;
     @observable _heightMap = new Map<string, number>();
-    @observable _cursor: CursorProperty = "grab";
+    @observable _cursor: CursorProperty = 'grab';
     @observable _scroll = 0; // used to force the document decoration to update when scrolling
-    @computed get chromeHidden() { return this.props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden); }
-    @computed get columnHeaders() { return Cast(this.layoutDoc._columnHeaders, listSpec(SchemaHeaderField), null); }
-    @computed get pivotField() { return StrCast(this.layoutDoc._pivotField); }
-    @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => (pair.layout instanceof Doc) && !pair.layout.hidden).map(pair => pair.layout); }
-    @computed get headerMargin() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin); }
-    @computed get xMargin() { return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); }
-    @computed get yMargin() { return this.props.yPadding || NumCast(this.layoutDoc._yMargin, 5); } // 2 * this.gridGap)); }
-    @computed get gridGap() { return NumCast(this.layoutDoc._gridGap, 10); }
-    @computed get isStackingView() { return (this.props.viewType ?? this.layoutDoc._viewType) === CollectionViewType.Stacking; }
-    @computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; }
-    @computed get showAddAGroup() { return this.pivotField && !this.chromeHidden; }
+    @computed get chromeHidden() {
+        return this.props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden);
+    }
+    @computed get columnHeaders() {
+        return Cast(this.layoutDoc._columnHeaders, listSpec(SchemaHeaderField), null);
+    }
+    @computed get pivotField() {
+        return StrCast(this.layoutDoc._pivotField);
+    }
+    @computed get filteredChildren() {
+        return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc && !pair.layout.hidden).map(pair => pair.layout);
+    }
+    @computed get headerMargin() {
+        return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin);
+    }
+    @computed get xMargin() {
+        return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, 0.05 * this.props.PanelWidth()));
+    }
+    @computed get yMargin() {
+        return this.props.yPadding || NumCast(this.layoutDoc._yMargin, 5);
+    } // 2 * this.gridGap)); }
+    @computed get gridGap() {
+        return NumCast(this.layoutDoc._gridGap, 10);
+    }
+    @computed get isStackingView() {
+        return (this.props.viewType ?? this.layoutDoc._viewType) === CollectionViewType.Stacking;
+    }
+    @computed get numGroupColumns() {
+        return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1;
+    }
+    @computed get showAddAGroup() {
+        return this.pivotField && !this.chromeHidden;
+    }
     @computed get columnWidth() {
-        return Math.min(this.props.PanelWidth() - 2 * this.xMargin,
-            this.isStackingView ? Number.MAX_VALUE : this.layoutDoc._columnWidth === -1 ? this.props.PanelWidth() - 2 * this.xMargin : NumCast(this.layoutDoc._columnWidth, 250));
+        return Math.min(this.props.PanelWidth() - 2 * this.xMargin, this.isStackingView ? Number.MAX_VALUE : this.layoutDoc._columnWidth === -1 ? this.props.PanelWidth() - 2 * this.xMargin : NumCast(this.layoutDoc._columnWidth, 250));
+    }
+    @computed get NodeWidth() {
+        return this.props.PanelWidth() - this.gridGap;
     }
-    @computed get NodeWidth() { return this.props.PanelWidth() - this.gridGap; }
 
     constructor(props: any) {
         super(props);
@@ -84,21 +106,23 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             const width = () => this.getDocWidth(d);
             const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
             const style = this.isStackingView ? { width: width(), marginTop: i ? this.gridGap : 0, height: height() } : { gridRowEnd: `span ${rowSpan}` };
-            return <div className={`collectionStackingView-${this.isStackingView ? "columnDoc" : "masonryDoc"}`} key={d[Id]} style={style} >
-                {this.getDisplayDoc(d, width)}
-            </div>;
+            return (
+                <div className={`collectionStackingView-${this.isStackingView ? 'columnDoc' : 'masonryDoc'}`} key={d[Id]} style={style}>
+                    {this.getDisplayDoc(d, width)}
+                </div>
+            );
         });
-    }
+    };
     @action
     setDocHeight = (key: string, sectionHeight: number) => {
         this._heightMap.set(key, sectionHeight);
-    }
+    };
 
     get Sections() {
         if (!this.pivotField || this.columnHeaders instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
 
         if (this.columnHeaders === undefined) {
-            setTimeout(() => this.layoutDoc._columnHeaders = new List<SchemaHeaderField>(), 0);
+            setTimeout(() => (this.layoutDoc._columnHeaders = new List<SchemaHeaderField>()), 0);
             return new Map<SchemaHeaderField, Doc[]>();
         }
         const columnHeaders = Array.from(this.columnHeaders);
@@ -114,8 +138,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             const existingHeader = columnHeaders.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`));
             if (existingHeader) {
                 fields.get(existingHeader)!.push(d);
-            }
-            else {
+            } else {
                 const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`);
                 fields.set(newSchemaHeader, [d]);
                 columnHeaders.push(newSchemaHeader);
@@ -124,13 +147,19 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         });
         // remove all empty columns if hideHeadings is set
         if (this.layoutDoc._columnsHideIfEmpty) {
-            Array.from(fields.keys()).filter(key => !fields.get(key)!.length).map(header => {
-                fields.delete(header);
-                columnHeaders.splice(columnHeaders.indexOf(header), 1);
-                changed = true;
-            });
+            Array.from(fields.keys())
+                .filter(key => !fields.get(key)!.length)
+                .map(header => {
+                    fields.delete(header);
+                    columnHeaders.splice(columnHeaders.indexOf(header), 1);
+                    changed = true;
+                });
         }
-        changed && setTimeout(action(() => this.columnHeaders?.splice(0, this.columnHeaders.length, ...columnHeaders)), 0);
+        changed &&
+            setTimeout(
+                action(() => this.columnHeaders?.splice(0, this.columnHeaders.length, ...columnHeaders)),
+                0
+            );
         return fields;
     }
 
@@ -140,13 +169,19 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         // reset section headers when a new filter is inputted
         this._pivotFieldDisposer = reaction(
             () => this.pivotField,
-            () => this.layoutDoc._columnHeaders = new List()
+            () => (this.layoutDoc._columnHeaders = new List())
+        );
+        this._autoHeightDisposer = reaction(
+            () => this.layoutDoc._autoHeight,
+            autoHeight =>
+                autoHeight &&
+                this.props.setHeight?.(
+                    Math.min(
+                        NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
+                        this.headerMargin + (this.isStackingView ? Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', '')))) : this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), 0))
+                    )
+                )
         );
-        this._autoHeightDisposer = reaction(() => this.layoutDoc._autoHeight,
-            autoHeight => autoHeight && this.props.setHeight?.(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
-                this.headerMargin + (this.isStackingView ?
-                    Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))) :
-                    this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0)))));
     }
 
     componentWillUnmount() {
@@ -158,45 +193,50 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     @action
     moveDocument = (doc: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean): boolean => {
         return this.props.removeDocument?.(doc) && addDocument?.(doc) ? true : false;
-    }
+    };
     createRef = (ele: HTMLDivElement | null) => {
         this._masonryGridRef = ele;
         this.createDashEventsTarget(ele!); //so the whole grid is the drop target?
-    }
+    };
 
-    @computed get onChildClickHandler() { return () => this.props.childClickScript || ScriptCast(this.Document.onChildClick); }
-    @computed get onChildDoubleClickHandler() { return () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); }
+    @computed get onChildClickHandler() {
+        return () => this.props.childClickScript || ScriptCast(this.Document.onChildClick);
+    }
+    @computed get onChildDoubleClickHandler() {
+        return () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick);
+    }
 
     addDocTab = (doc: Doc, where: string) => {
-        if (where === "inPlace" && this.layoutDoc.isInPlaceContainer) {
+        if (where === 'inPlace' && this.layoutDoc.isInPlaceContainer) {
             this.dataDoc[this.props.fieldKey] = new List<Doc>([doc]);
             return true;
         }
         return this.props.addDocTab(doc, where);
-    }
+    };
 
     scrollToBottom = () => {
         smoothScroll(500, this._mainCont!, this._mainCont!.scrollHeight);
-    }
+    };
 
     focusDocument = (doc: Doc, options?: DocFocusOptions) => {
         Doc.BrushDoc(doc);
 
         let focusSpeed = 0;
-        const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName("documentView-node")).find((node: any) => node.id === doc[Id]);
+        const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]);
         if (found) {
             const top = found.getBoundingClientRect().top;
             const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top);
             if (Math.floor(localTop[1]) !== 0) {
-                smoothScroll(focusSpeed = doc.presTransition || doc.presTransition === 0 ? NumCast(doc.presTransition) : 500, this._mainCont!, localTop[1] + this._mainCont!.scrollTop);
+                smoothScroll((focusSpeed = doc.presTransition || doc.presTransition === 0 ? NumCast(doc.presTransition) : 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop);
             }
         }
         const endFocus = async (moved: boolean) => options?.afterFocus?.(moved) ?? ViewAdjustment.doNothing;
         this.props.focus(this.rootDoc, {
-            willZoom: options?.willZoom, scale: options?.scale, afterFocus: (didFocus: boolean) =>
-                new Promise<ViewAdjustment>(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed))
+            willZoom: options?.willZoom,
+            scale: options?.scale,
+            afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => setTimeout(async () => res(await endFocus(didFocus)), focusSpeed)),
         });
-    }
+    };
 
     styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string) => {
         if (property === StyleProp.Opacity && doc) {
@@ -208,85 +248,88 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             }
         }
         return this.props.styleProvider?.(doc, props, property);
-    }
+    };
     @undoBatch
     @action
     onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
         const docView = fieldProps.DocumentView?.();
-        if (docView && ["Enter"].includes(e.key) && e.ctrlKey) {
+        if (docView && ['Enter'].includes(e.key) && e.ctrlKey) {
             e.stopPropagation?.();
-            const below = !e.altKey && e.key !== "Tab";
+            const below = !e.altKey && e.key !== 'Tab';
             const layoutKey = StrCast(docView.LayoutFieldKey);
             const newDoc = Doc.MakeCopy(docView.rootDoc, true);
             const dataField = docView.rootDoc[Doc.LayoutFieldKey(newDoc)];
             newDoc[DataSym][Doc.LayoutFieldKey(newDoc)] = dataField === undefined || Cast(dataField, listSpec(Doc), null)?.length !== undefined ? new List<Doc>([]) : undefined;
-            if (layoutKey !== "layout" && docView.rootDoc[layoutKey] instanceof Doc) {
+            if (layoutKey !== 'layout' && docView.rootDoc[layoutKey] instanceof Doc) {
                 newDoc[layoutKey] = docView.rootDoc[layoutKey];
             }
             Doc.GetProto(newDoc).text = undefined;
             FormattedTextBox.SelectOnLoad = newDoc[Id];
             return this.addDocument?.(newDoc);
         }
-    }
+    };
     isContentActive = () => this.props.isSelected() || this.props.isContentActive();
-    isChildContentActive = () => this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : undefined;
+    isChildContentActive = () => (this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : undefined);
     getDisplayDoc(doc: Doc, width: () => number) {
-        const dataDoc = (!doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS) ? undefined : this.props.DataDoc;
+        const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField && !doc.PARAMS ? undefined : this.props.DataDoc;
         const height = () => this.getDocHeight(doc);
 
         let dref: Opt<DocumentView>;
         const stackedDocTransform = () => this.getDocTransform(doc, dref);
         this._docXfs.push({ stackedDocTransform, width, height });
-        return <DocumentView ref={r => dref = r || undefined}
-            Document={doc}
-            DataDoc={dataDoc || (!Doc.AreProtosEqual(doc[DataSym], doc) && doc[DataSym])}
-            renderDepth={this.props.renderDepth + 1}
-            PanelWidth={width}
-            PanelHeight={height}
-            styleProvider={this.styleProvider}
-            docViewPath={this.props.docViewPath}
-            fitWidth={this.props.childFitWidth}
-            isContentActive={this.isChildContentActive}
-            onKey={this.onKeyDown}
-            isDocumentActive={this.isContentActive}
-            LayoutTemplate={this.props.childLayoutTemplate}
-            LayoutTemplateString={this.props.childLayoutString}
-            NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || doc._fitWidth && !Doc.NativeWidth(doc) ? width : undefined}  // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
-            NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || doc._fitWidth && !Doc.NativeHeight(doc) ? height : undefined}
-            dontCenter={this.props.childIgnoreNativeSize ? "xy" : undefined}
-            dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)}
-            rootSelected={this.rootSelected}
-            showTitle={this.props.childShowTitle}
-            dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
-            onClick={this.onChildClickHandler}
-            onDoubleClick={this.onChildDoubleClickHandler}
-            ScreenToLocalTransform={stackedDocTransform}
-            focus={this.focusDocument}
-            docFilters={this.childDocFilters}
-            hideDecorationTitle={this.props.childHideDecorationTitle?.()}
-            hideResizeHandles={this.props.childHideResizeHandles?.()}
-            hideTitle={this.props.childHideTitle?.()}
-            docRangeFilters={this.childDocRangeFilters}
-            searchFilterDocs={this.searchFilterDocs}
-            ContainingCollectionDoc={this.props.CollectionView?.props.Document}
-            ContainingCollectionView={this.props.CollectionView}
-            addDocument={this.props.addDocument}
-            moveDocument={this.props.moveDocument}
-            removeDocument={this.props.removeDocument}
-            contentPointerEvents={StrCast(this.layoutDoc.contentPointerEvents)}
-            whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
-            addDocTab={this.addDocTab}
-            bringToFront={returnFalse}
-            scriptContext={this.props.scriptContext}
-            pinToPres={this.props.pinToPres}
-        />;
+        return (
+            <DocumentView
+                ref={r => (dref = r || undefined)}
+                Document={doc}
+                DataDoc={dataDoc || (!Doc.AreProtosEqual(doc[DataSym], doc) && doc[DataSym])}
+                renderDepth={this.props.renderDepth + 1}
+                PanelWidth={width}
+                PanelHeight={height}
+                styleProvider={this.styleProvider}
+                docViewPath={this.props.docViewPath}
+                fitWidth={this.props.childFitWidth}
+                isContentActive={this.isChildContentActive}
+                onKey={this.onKeyDown}
+                isDocumentActive={this.isContentActive}
+                LayoutTemplate={this.props.childLayoutTemplate}
+                LayoutTemplateString={this.props.childLayoutString}
+                NativeWidth={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || (doc._fitWidth && !Doc.NativeWidth(doc)) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
+                NativeHeight={this.props.childIgnoreNativeSize ? returnZero : this.props.childFitWidth?.(doc) || (doc._fitWidth && !Doc.NativeHeight(doc)) ? height : undefined}
+                dontCenter={this.props.childIgnoreNativeSize ? 'xy' : undefined}
+                dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)}
+                rootSelected={this.rootSelected}
+                showTitle={this.props.childShowTitle}
+                dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
+                onClick={this.onChildClickHandler}
+                onDoubleClick={this.onChildDoubleClickHandler}
+                ScreenToLocalTransform={stackedDocTransform}
+                focus={this.focusDocument}
+                docFilters={this.childDocFilters}
+                hideDecorationTitle={this.props.childHideDecorationTitle?.()}
+                hideResizeHandles={this.props.childHideResizeHandles?.()}
+                hideTitle={this.props.childHideTitle?.()}
+                docRangeFilters={this.childDocRangeFilters}
+                searchFilterDocs={this.searchFilterDocs}
+                ContainingCollectionDoc={this.props.CollectionView?.props.Document}
+                ContainingCollectionView={this.props.CollectionView}
+                addDocument={this.props.addDocument}
+                moveDocument={this.props.moveDocument}
+                removeDocument={this.props.removeDocument}
+                contentPointerEvents={StrCast(this.layoutDoc.contentPointerEvents)}
+                whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+                addDocTab={this.addDocTab}
+                bringToFront={returnFalse}
+                scriptContext={this.props.scriptContext}
+                pinToPres={this.props.pinToPres}
+            />
+        );
     }
 
     getDocTransform(doc: Doc, dref?: DocumentView) {
         const y = this._scroll; // required for document decorations to update when the text box container is scrolled
         const { scale, translateX, translateY } = Utils.GetScreenTransform(dref?.ContentDiv || undefined);
-        // the document view may center its contents and if so, will prepend that onto the screenToLocalTansform.  so we have to subtract that off 
-        return new Transform(- translateX + (dref?.centeringX || 0), - translateY + (dref?.centeringY || 0), 1).scale(this.props.ScreenToLocalTransform().Scale);
+        // the document view may center its contents and if so, will prepend that onto the screenToLocalTansform.  so we have to subtract that off
+        return new Transform(-translateX + (dref?.centeringX || 0), -translateY + (dref?.centeringY || 0), 1).scale(this.props.ScreenToLocalTransform().Scale);
     }
     getDocWidth(d?: Doc) {
         if (!d) return 0;
@@ -300,37 +343,42 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
     getDocHeight(d?: Doc) {
         if (!d || d.hidden) return 0;
         const childLayoutDoc = Doc.Layout(d, this.props.childLayoutTemplate?.());
-        const childDataDoc = (!d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS) ? undefined : this.props.DataDoc;
-        const maxHeight = (lim => lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim)(NumCast(this.layoutDoc.childLimitHeight, -1));
+        const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField && !d.PARAMS ? undefined : this.props.DataDoc;
+        const maxHeight = (lim => (lim === 0 ? this.props.PanelWidth() : lim === -1 ? 10000 : lim))(NumCast(this.layoutDoc.childLimitHeight, -1));
         const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[WidthSym]() : 0);
         const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[HeightSym]() : 0);
         if (nw && nh) {
             const colWid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
             const docWid = this.layoutDoc._columnsFill ? colWid : Math.min(this.getDocWidth(d), colWid);
-            return Math.min(
-                maxHeight,
-                docWid * nh / nw);
+            return Math.min(maxHeight, (docWid * nh) / nw);
         }
         const childHeight = NumCast(childLayoutDoc._height);
-        const panelHeight = (childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
+        const panelHeight = childLayoutDoc._fitWidth || this.props.childFitWidth?.(d) ? Number.MAX_SAFE_INTEGER : this.props.PanelHeight() - 2 * this.yMargin;
         return Math.min(childHeight, maxHeight, panelHeight);
     }
 
     columnDividerDown = (e: React.PointerEvent) => {
-        runInAction(() => this._cursor = "grabbing");
-        setupMoveUpEvents(this, e, this.onDividerMove, action(() => this._cursor = "grab"), emptyFunction);
-    }
+        runInAction(() => (this._cursor = 'grabbing'));
+        setupMoveUpEvents(
+            this,
+            e,
+            this.onDividerMove,
+            action(() => (this._cursor = 'grab')),
+            emptyFunction
+        );
+    };
     @action
     onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
         this.layoutDoc._columnWidth = Math.max(10, this.columnWidth + delta[0]);
         return false;
-    }
+    };
 
     @computed get columnDragger() {
-        return <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef}
-            style={{ cursor: this._cursor, left: `${this.columnWidth + this.xMargin}px`, top: `${Math.max(0, this.yMargin - 9)}px` }} >
-            <FontAwesomeIcon icon={"arrows-alt-h"} />
-        </div>;
+        return (
+            <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef} style={{ cursor: this._cursor, left: `${this.columnWidth + this.xMargin}px`, top: `${Math.max(0, this.yMargin - 9)}px` }}>
+                <FontAwesomeIcon icon={'arrows-alt-h'} />
+            </div>
+        );
     }
 
     @undoBatch
@@ -341,7 +389,10 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         let dropAfter = 0;
         if (de.complete.docDragData) {
             this._docXfs.map((cd, i) => {
-                const pos = cd.stackedDocTransform().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
+                const pos = cd
+                    .stackedDocTransform()
+                    .inverse()
+                    .transformPoint(-2 * this.gridGap, -2 * this.gridGap);
                 const pos1 = cd.stackedDocTransform().inverse().transformPoint(cd.width(), cd.height());
                 if (where[0] > pos[0] && where[0] < pos1[0] && where[1] > pos[1] && (i === this._docXfs.length - 1 || where[1] < pos1[1])) {
                     dropInd = i;
@@ -358,21 +409,19 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 DragManager.docsBeingDragged = [];
                 if (docs && newDocs.length) {
                     const insertInd = dropInd === -1 ? docs.length : dropInd + dropAfter;
-                    const offset = newDocs.reduce((off, ndoc) => this.filteredChildren.find((fdoc, i) => ndoc === fdoc && i < insertInd) ? off + 1 : off, 0);
+                    const offset = newDocs.reduce((off, ndoc) => (this.filteredChildren.find((fdoc, i) => ndoc === fdoc && i < insertInd) ? off + 1 : off), 0);
                     newDocs.filter(ndoc => docs.indexOf(ndoc) !== -1).forEach(ndoc => docs.splice(docs.indexOf(ndoc), 1));
                     docs.splice(insertInd - offset, 0, ...newDocs);
                 }
             }
-        }
-        else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
-            const source = Docs.Create.TextDocument("", { _width: 200, _height: 75, _fitWidth: true, title: "dropped annotation" });
+        } else if (de.complete.linkDragData?.dragDocument.context === this.props.Document && de.complete.linkDragData?.linkDragView?.props.CollectionFreeFormDocumentView?.()) {
+            const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, _fitWidth: true, title: 'dropped annotation' });
             this.props.addDocument?.(source);
-            de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: de.complete.linkDragData.linkSourceGetAnchor() }, "doc annotation", ""); // TODODO this is where in text links get passed
+            de.complete.linkDocument = DocUtils.MakeLink({ doc: source }, { doc: de.complete.linkDragData.linkSourceGetAnchor() }, 'doc annotation', ''); // TODODO this is where in text links get passed
             e.stopPropagation();
-        }
-        else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData);
+        } else if (de.complete.annoDragData?.dragDocument && super.onInternalDrop(e, de)) return this.internalAnchorAnnoDrop(e, de.complete.annoDragData);
         return false;
-    }
+    };
 
     @undoBatch
     internalAnchorAnnoDrop(e: Event, annoDragData: DragManager.AnchorAnnoDragData) {
@@ -390,7 +439,10 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
         const where = [e.clientX, e.clientY];
         let targInd = -1;
         this._docXfs.map((cd, i) => {
-            const pos = cd.stackedDocTransform().inverse().transformPoint(-2 * this.gridGap, -2 * this.gridGap);
+            const pos = cd
+                .stackedDocTransform()
+                .inverse()
+                .transformPoint(-2 * this.gridGap, -2 * this.gridGap);
             const pos1 = cd.stackedDocTransform().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;
@@ -406,98 +458,103 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                 }
             }
         });
-    }
+    };
     headings = () => Array.from(this.Sections);
     refList: any[] = [];
     sectionStacking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
         const key = this.pivotField;
-        let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
+        let type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | undefined = undefined;
         if (this.pivotField) {
             const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]);
             if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
                 type = types[0];
             }
         }
-        return <CollectionStackingViewFieldColumn
-            unobserveHeight={ref => this.refList.splice(this.refList.indexOf(ref), 1)}
-            observeHeight={ref => {
-                if (ref) {
-                    this.refList.push(ref);
-                    this.observer = new _global.ResizeObserver(action((entries: any) => {
-                        if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
-                            const height = this.headerMargin +
-                                Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
-                                    Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))));
-                            if (!LightboxView.IsLightboxDocView(this.props.docViewPath())) {
-                                this.props.setHeight?.(height);
-                            }
-                        }
-                    }));
-                    this.observer.observe(ref);
-                }
-            }}
-            addDocument={this.addDocument}
-            chromeHidden={this.chromeHidden}
-            columnHeaders={this.columnHeaders}
-            Document={this.props.Document}
-            DataDoc={this.props.DataDoc}
-            renderChildren={this.children}
-            columnWidth={this.columnWidth}
-            numGroupColumns={this.numGroupColumns}
-            gridGap={this.gridGap}
-            pivotField={this.pivotField}
-            key={heading?.heading ?? ""}
-            headings={this.headings}
-            heading={heading?.heading ?? ""}
-            headingObject={heading}
-            docList={docList}
-            yMargin={this.yMargin}
-            type={type}
-            createDropTarget={this.createDashEventsTarget}
-            screenToLocalTransform={this.props.ScreenToLocalTransform}
-        />;
-    }
+        return (
+            <CollectionStackingViewFieldColumn
+                unobserveHeight={ref => this.refList.splice(this.refList.indexOf(ref), 1)}
+                observeHeight={ref => {
+                    if (ref) {
+                        this.refList.push(ref);
+                        this.observer = new _global.ResizeObserver(
+                            action((entries: any) => {
+                                if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+                                    const height = this.headerMargin + Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace('px', '')))));
+                                    if (!LightboxView.IsLightboxDocView(this.props.docViewPath())) {
+                                        this.props.setHeight?.(height);
+                                    }
+                                }
+                            })
+                        );
+                        this.observer.observe(ref);
+                    }
+                }}
+                addDocument={this.addDocument}
+                chromeHidden={this.chromeHidden}
+                columnHeaders={this.columnHeaders}
+                Document={this.props.Document}
+                DataDoc={this.props.DataDoc}
+                renderChildren={this.children}
+                columnWidth={this.columnWidth}
+                numGroupColumns={this.numGroupColumns}
+                gridGap={this.gridGap}
+                pivotField={this.pivotField}
+                key={heading?.heading ?? ''}
+                headings={this.headings}
+                heading={heading?.heading ?? ''}
+                headingObject={heading}
+                docList={docList}
+                yMargin={this.yMargin}
+                type={type}
+                createDropTarget={this.createDashEventsTarget}
+                screenToLocalTransform={this.props.ScreenToLocalTransform}
+            />
+        );
+    };
 
     sectionMasonry = (heading: SchemaHeaderField | undefined, docList: Doc[], first: boolean) => {
         const key = this.pivotField;
-        let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
+        let type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | undefined = undefined;
         const types = docList.length ? docList.map(d => typeof d[key]) : this.filteredChildren.map(d => typeof d[key]);
         if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
             type = types[0];
         }
-        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
-            showHandle={first}
-            Document={this.props.Document}
-            chromeHidden={this.chromeHidden}
-            pivotField={this.pivotField}
-            unobserveHeight={(ref) => this.refList.splice(this.refList.indexOf(ref), 1)}
-            observeHeight={(ref) => {
-                if (ref) {
-                    this.refList.push(ref);
-                    this.observer = new _global.ResizeObserver(action((entries: any) => {
-                        if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
-                            const height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0);
-                            this.props.setHeight?.(this.headerMargin + height);
-                        }
-                    }));
-                    this.observer.observe(ref);
-                }
-            }}
-            key={heading ? heading.heading : ""}
-            rows={rows}
-            headings={this.headings}
-            heading={heading ? heading.heading : ""}
-            headingObject={heading}
-            docList={docList}
-            parent={this}
-            type={type}
-            createDropTarget={this.createDashEventsTarget}
-            screenToLocalTransform={this.props.ScreenToLocalTransform}
-            setDocHeight={this.setDocHeight}
-        />;
-    }
+        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
+                showHandle={first}
+                Document={this.props.Document}
+                chromeHidden={this.chromeHidden}
+                pivotField={this.pivotField}
+                unobserveHeight={ref => this.refList.splice(this.refList.indexOf(ref), 1)}
+                observeHeight={ref => {
+                    if (ref) {
+                        this.refList.push(ref);
+                        this.observer = new _global.ResizeObserver(
+                            action((entries: any) => {
+                                if (this.layoutDoc._autoHeight && ref && this.refList.length && !SnappingManager.GetIsDragging()) {
+                                    const height = this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), 0);
+                                    this.props.setHeight?.(this.headerMargin + height);
+                                }
+                            })
+                        );
+                        this.observer.observe(ref);
+                    }
+                }}
+                key={heading ? heading.heading : ''}
+                rows={rows}
+                headings={this.headings}
+                heading={heading ? heading.heading : ''}
+                headingObject={heading}
+                docList={docList}
+                parent={this}
+                type={type}
+                createDropTarget={this.createDashEventsTarget}
+                screenToLocalTransform={this.props.ScreenToLocalTransform}
+                setDocHeight={this.setDocHeight}
+            />
+        );
+    };
 
     @action
     addGroup = (value: string) => {
@@ -507,25 +564,25 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             return true;
         }
         return false;
-    }
+    };
 
     sortFunc = (a: [SchemaHeaderField, Doc[]], b: [SchemaHeaderField, Doc[]]): 1 | -1 => {
-        const descending = StrCast(this.layoutDoc._columnsSort) === "descending";
+        const descending = StrCast(this.layoutDoc._columnsSort) === 'descending';
         const firstEntry = descending ? b : a;
         const secondEntry = descending ? a : b;
         return firstEntry[0].heading > secondEntry[0].heading ? 1 : -1;
-    }
+    };
 
     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()) {
             const subItems: ContextMenuProps[] = [];
-            subItems.push({ description: `${this.layoutDoc._columnsFill ? "Variable Size" : "Autosize"} Column`, event: () => this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill, icon: "plus" });
-            subItems.push({ description: `${this.layoutDoc._autoHeight ? "Variable Height" : "Auto Height"}`, event: () => this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight, icon: "plus" });
-            subItems.push({ description: "Clear All", event: () => this.dataDoc.data = new List([]), icon: "times" });
-            ContextMenu.Instance.addItem({ description: "Options...", subitems: subItems, icon: "eye" });
+            subItems.push({ description: `${this.layoutDoc._columnsFill ? 'Variable Size' : 'Autosize'} Column`, event: () => (this.layoutDoc._columnsFill = !this.layoutDoc._columnsFill), icon: 'plus' });
+            subItems.push({ description: `${this.layoutDoc._autoHeight ? 'Variable Height' : 'Auto Height'}`, event: () => (this.layoutDoc._autoHeight = !this.layoutDoc._autoHeight), icon: 'plus' });
+            subItems.push({ description: 'Clear All', event: () => (this.dataDoc.data = new List([])), icon: 'times' });
+            ContextMenu.Instance.addItem({ description: 'Options...', subitems: subItems, icon: 'eye' });
         }
-    }
+    };
 
     @computed get renderedSections() {
         TraceMobx();
@@ -534,7 +591,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             const entries = Array.from(this.Sections.entries());
             sections = this.layoutDoc._columnsSort ? entries.sort(this.sortFunc) : entries;
         }
-        return sections.map((section, i) => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1], i === 0));
+        return sections.map((section, i) => (this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1], i === 0)));
     }
 
     @computed get buttonMenu() {
@@ -544,85 +601,90 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
             const width: number = NumCast(menuDoc._width, 30);
             const height: number = NumCast(menuDoc._height, 30);
             console.log(menuDoc.title, width, height);
-            return (<div className="buttonMenu-docBtn"
-                style={{ width: width, height: height }}>
-                <DocumentView
-                    Document={menuDoc}
-                    DataDoc={menuDoc}
-                    isContentActive={this.props.isContentActive}
-                    isDocumentActive={returnTrue}
-                    addDocument={this.props.addDocument}
-                    moveDocument={this.props.moveDocument}
-                    addDocTab={this.props.addDocTab}
-                    pinToPres={emptyFunction}
-                    rootSelected={this.props.isSelected}
-                    removeDocument={this.props.removeDocument}
-                    ScreenToLocalTransform={Transform.Identity}
-                    PanelWidth={() => 35}
-                    PanelHeight={() => 35}
-                    renderDepth={this.props.renderDepth}
-                    focus={emptyFunction}
-                    styleProvider={this.props.styleProvider}
-                    docViewPath={returnEmptyDoclist}
-                    whenChildContentsActiveChanged={emptyFunction}
-                    bringToFront={emptyFunction}
-                    docFilters={this.props.docFilters}
-                    docRangeFilters={this.props.docRangeFilters}
-                    searchFilterDocs={this.props.searchFilterDocs}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined}
-                />
-            </div>
+            return (
+                <div className="buttonMenu-docBtn" style={{ width: width, height: height }}>
+                    <DocumentView
+                        Document={menuDoc}
+                        DataDoc={menuDoc}
+                        isContentActive={this.props.isContentActive}
+                        isDocumentActive={returnTrue}
+                        addDocument={this.props.addDocument}
+                        moveDocument={this.props.moveDocument}
+                        addDocTab={this.props.addDocTab}
+                        pinToPres={emptyFunction}
+                        rootSelected={this.props.isSelected}
+                        removeDocument={this.props.removeDocument}
+                        ScreenToLocalTransform={Transform.Identity}
+                        PanelWidth={() => 35}
+                        PanelHeight={() => 35}
+                        renderDepth={this.props.renderDepth}
+                        focus={emptyFunction}
+                        styleProvider={this.props.styleProvider}
+                        docViewPath={returnEmptyDoclist}
+                        whenChildContentsActiveChanged={emptyFunction}
+                        bringToFront={emptyFunction}
+                        docFilters={this.props.docFilters}
+                        docRangeFilters={this.props.docRangeFilters}
+                        searchFilterDocs={this.props.searchFilterDocs}
+                        ContainingCollectionView={undefined}
+                        ContainingCollectionDoc={undefined}
+                    />
+                </div>
             );
         }
     }
 
+    @computed get nativeWidth() {
+        return this.props.NativeWidth?.() ?? Doc.NativeWidth(this.layoutDoc);
+    }
+    @computed get nativeHeight() {
+        return this.props.NativeHeight?.() ?? Doc.NativeHeight(this.layoutDoc);
+    }
 
-    @computed get nativeWidth() { return this.props.NativeWidth?.() ?? Doc.NativeWidth(this.layoutDoc); }
-    @computed get nativeHeight() { return this.props.NativeHeight?.() ?? Doc.NativeHeight(this.layoutDoc); }
-
-    @computed get scaling() { return !this.nativeWidth ? 1 : this.props.PanelHeight() / this.nativeHeight; }
+    @computed get scaling() {
+        return !this.nativeWidth ? 1 : this.props.PanelHeight() / this.nativeHeight;
+    }
 
-    @computed get backgroundEvents() { return SnappingManager.GetIsDragging(); }
+    @computed get backgroundEvents() {
+        return SnappingManager.GetIsDragging();
+    }
     observer: any;
     render() {
         TraceMobx();
         const editableViewProps = {
-            GetValue: () => "",
+            GetValue: () => '',
             SetValue: this.addGroup,
-            contents: "+ ADD A GROUP"
+            contents: '+ ADD A GROUP',
         };
         const buttonMenu = this.rootDoc.buttonMenu;
         const noviceExplainer = this.rootDoc.explainer;
         return (
             <>
-                {buttonMenu || noviceExplainer ? <div className="documentButtonMenu">
-                    {buttonMenu ? this.buttonMenu : null}
-                    {Doc.noviceMode && noviceExplainer ?
-                        <div className="documentExplanation">
-                            {noviceExplainer}
-                        </div>
-                        : null
-                    }
-                </div> : null}
-                <div className="collectionStackingMasonry-cont" >
-                    <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"}
+                {buttonMenu || noviceExplainer ? (
+                    <div className="documentButtonMenu">
+                        {buttonMenu ? this.buttonMenu : null}
+                        {Doc.noviceMode && noviceExplainer ? <div className="documentExplanation">{StrCast(noviceExplainer)}</div> : null}
+                    </div>
+                ) : null}
+                <div className="collectionStackingMasonry-cont">
+                    <div
+                        className={this.isStackingView ? 'collectionStackingView' : 'collectionMasonryView'}
                         ref={this.createRef}
                         style={{
-                            overflowY: this.props.isContentActive() ? "auto" : "hidden",
+                            overflowY: this.props.isContentActive() ? 'auto' : 'hidden',
                             background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor),
-                            pointerEvents: this.backgroundEvents ? "all" : undefined
+                            pointerEvents: this.backgroundEvents ? 'all' : undefined,
                         }}
-                        onScroll={action(e => this._scroll = e.currentTarget.scrollTop)}
+                        onScroll={action(e => (this._scroll = e.currentTarget.scrollTop))}
                         onDrop={this.onExternalDrop.bind(this)}
                         onContextMenu={this.onContextMenu}
-                        onWheel={e => this.props.isContentActive(true) && e.stopPropagation()} >
+                        onWheel={e => this.props.isContentActive(true) && 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 }}>
+                        {!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 }}>
                                 <EditableView {...editableViewProps} />
-                            </div>}
+                            </div>
+                        )}
                         {/* {this.chromeHidden || !this.props.isSelected() ? (null) :
                             <Switch
                                 onChange={this.onToggle}
@@ -634,7 +696,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
                     </div>
                 </div>
             </>
-
         );
     }
 }
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index ba72fb7b9..f5b9162d3 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,5 +1,5 @@
-import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
-import { observer } from "mobx-react";
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
 import { DataSym, Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
@@ -11,27 +11,27 @@ import { emptyFunction, OmitKeys, returnEmptyDoclist, returnEmptyFilter, returnF
 import { DocUtils } from '../../documents/Documents';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { DocumentManager } from '../../util/DocumentManager';
-import { DragManager, dropActionType } from "../../util/DragManager";
+import { DragManager, dropActionType } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
-import { EditableView } from "../EditableView";
+import { EditableView } from '../EditableView';
 import { DocumentView } from '../nodes/DocumentView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { StyleProp } from '../StyleProvider';
 import { CollectionFreeFormView } from './collectionFreeForm';
-import { CollectionSubView } from "./CollectionSubView";
-import "./CollectionTreeView.scss";
-import { TreeView } from "./TreeView";
-import React = require("react");
-import { FieldViewProps } from "../nodes/FieldView";
-const _global = (window /* browser */ || global /* node */) as any;
+import { CollectionSubView } from './CollectionSubView';
+import './CollectionTreeView.scss';
+import { TreeView } from './TreeView';
+import React = require('react');
+import { FieldViewProps } from '../nodes/FieldView';
+const _global = (window /* browser */ || global) /* node */ as any;
 
 export type collectionTreeViewProps = {
-    treeViewExpandedView?: "fields" | "layout" | "links" | "data";
+    treeViewExpandedView?: 'fields' | 'layout' | 'links' | 'data';
     treeViewOpen?: boolean;
     treeViewHideTitle?: boolean;
     treeViewHideHeaderFields?: boolean;
@@ -45,9 +45,9 @@ export type collectionTreeViewProps = {
 };
 
 export enum TreeViewType {
-    outline = "outline",
-    fileSystem = "fileSystem",
-    default = "default"
+    outline = 'outline',
+    fileSystem = 'fileSystem',
+    default = 'default',
 }
 
 @observer
@@ -61,13 +61,28 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     private observer: any; // observer for monitoring tree view items.
     private static expandViewLabelSize = 20;
 
-    @computed get doc() { return this.props.Document; }
-    @computed get dataDoc() { return this.props.DataDoc || this.doc; }
-    @computed get treeViewtruncateTitleWidth() { return NumCast(this.doc.treeViewTruncateTitleWidth, this.panelWidth()); }
-    @computed get treeChildren() { TraceMobx(); return this.props.childDocuments || this.childDocs; }
-    @computed get outlineMode() { return this.doc.treeViewType === TreeViewType.outline; }
-    @computed get fileSysMode() { return this.doc.treeViewType === TreeViewType.fileSystem; }
-    @computed get dashboardMode() { return this.doc === CurrentUserUtils.MyDashboards; }
+    @computed get doc() {
+        return this.props.Document;
+    }
+    @computed get dataDoc() {
+        return this.props.DataDoc || this.doc;
+    }
+    @computed get treeViewtruncateTitleWidth() {
+        return NumCast(this.doc.treeViewTruncateTitleWidth, this.panelWidth());
+    }
+    @computed get treeChildren() {
+        TraceMobx();
+        return this.props.childDocuments || this.childDocs;
+    }
+    @computed get outlineMode() {
+        return this.doc.treeViewType === TreeViewType.outline;
+    }
+    @computed get fileSysMode() {
+        return this.doc.treeViewType === TreeViewType.fileSystem;
+    }
+    @computed get dashboardMode() {
+        return this.doc === CurrentUserUtils.MyDashboards;
+    }
 
     @observable _explainerHeight = 0; // height of the description of the tree view
 
@@ -75,11 +90,11 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
 
     // these should stay in synch with counterparts in DocComponent.ts ViewBoxAnnotatableComponent
     @observable _isAnyChildContentActive = false;
-    whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive));
-    isContentActive = (outsideReaction?: boolean) => (CurrentUserUtils.ActiveTool !== InkTool.None ||
-        (this.props.isContentActive?.() || this.props.Document.forceActive ||
-            this.props.isSelected(outsideReaction) || this._isAnyChildContentActive ||
-            this.props.rootSelected(outsideReaction)) ? true : false)
+    whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive)));
+    isContentActive = (outsideReaction?: boolean) =>
+        CurrentUserUtils.ActiveTool !== InkTool.None || this.props.isContentActive?.() || this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isAnyChildContentActive || this.props.rootSelected(outsideReaction)
+            ? true
+            : false;
 
     componentWillUnmount() {
         this._isDisposing = true;
@@ -89,47 +104,51 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     }
 
     componentDidMount() {
-        this._disposers.autoheight = reaction(() => this.rootDoc.autoHeight,
+        this._disposers.autoheight = reaction(
+            () => this.rootDoc.autoHeight,
             auto => auto && this.computeHeight(),
-            { fireImmediately: true });
+            { fireImmediately: true }
+        );
     }
 
     computeHeight = () => {
         if (!this._isDisposing) {
-            const titleHeight = !this._titleRef ? this.marginTop() : Number(getComputedStyle(this._titleRef).height.replace("px", ""));
-            const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), this.marginBot());
+            const titleHeight = !this._titleRef ? this.marginTop() : Number(getComputedStyle(this._titleRef).height.replace('px', ''));
+            const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), this.marginBot());
             this.layoutDoc._autoHeightMargins = bodyHeight;
             this.props.setHeight?.(bodyHeight + titleHeight);
         }
-    }
+    };
     unobserveHeight = (ref: any) => {
         this.refList.delete(ref);
         this.rootDoc.autoHeight && this.computeHeight();
-    }
+    };
     observeHeight = (ref: any) => {
         if (ref) {
             this.refList.add(ref);
-            this.observer = new _global.ResizeObserver(action((entries: any) => {
-                if (this.rootDoc.autoHeight && ref && this.refList.size && !SnappingManager.GetIsDragging()) {
-                    this.computeHeight();
-                }
-            }));
+            this.observer = new _global.ResizeObserver(
+                action((entries: any) => {
+                    if (this.rootDoc.autoHeight && ref && this.refList.size && !SnappingManager.GetIsDragging()) {
+                        this.computeHeight();
+                    }
+                })
+            );
             this.rootDoc.autoHeight && this.computeHeight();
             this.observer.observe(ref);
         }
-    }
+    };
     protected createTreeDropTarget = (ele: HTMLDivElement) => {
         this._treedropDisposer?.();
-        if (this._mainEle = ele) this._treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.doc, this.onInternalPreDrop.bind(this));
-    }
+        if ((this._mainEle = ele)) this._treedropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.doc, this.onInternalPreDrop.bind(this));
+    };
 
     protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
         const dragData = de.complete.docDragData;
         if (dragData) {
             const isInTree = () => Doc.AreProtosEqual(dragData.treeViewDoc, this.props.Document) || dragData.draggedDocuments.some(d => d.context === this.doc && this.childDocs.includes(d));
-            dragData.dropAction = targetAction && !isInTree() ? targetAction : this.doc === dragData?.treeViewDoc ? "same" : dragData.dropAction;
+            dragData.dropAction = targetAction && !isInTree() ? targetAction : this.doc === dragData?.treeViewDoc ? 'same' : dragData.dropAction;
         }
-    }
+    };
 
     @action
     remove = (doc: Doc | Doc[]): boolean => {
@@ -149,7 +168,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             return true;
         }
         return false;
-    }
+    };
 
     @action
     addDoc = (docs: Doc | Doc[], relativeTo: Opt<Doc>, before?: boolean): boolean => {
@@ -161,81 +180,85 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             }, true);
         if (this.doc.resolvedDataDoc instanceof Promise) return false;
         return relativeTo === undefined ? this.props.addDocument?.(docs) || false : doAddDoc(docs);
-    }
+    };
     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 (!Doc.noviceMode) {
             const layoutItems: ContextMenuProps[] = [];
-            layoutItems.push({ description: "Make tree state " + (this.doc.treeViewOpenIsTransient ? "persistent" : "transient"), event: () => this.doc.treeViewOpenIsTransient = !this.doc.treeViewOpenIsTransient, icon: "paint-brush" });
-            layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? "Show" : "Hide") + " Header Fields", event: () => this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields, icon: "paint-brush" });
-            layoutItems.push({ description: (this.doc.treeViewHideTitle ? "Show" : "Hide") + " Title", event: () => this.doc.treeViewHideTitle = !this.doc.treeViewHideTitle, icon: "paint-brush" });
-            ContextMenu.Instance.addItem({ description: "Options...", subitems: layoutItems, icon: "eye" });
-            const existingOnClick = ContextMenu.Instance.findByDescription("OnClick...");
-            const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
-            onClicks.push({ description: "Edit onChecked Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.doc, undefined, "onCheckedClick"), "edit onCheckedClick"), icon: "edit" });
-            !existingOnClick && ContextMenu.Instance.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
+            layoutItems.push({ description: 'Make tree state ' + (this.doc.treeViewOpenIsTransient ? 'persistent' : 'transient'), event: () => (this.doc.treeViewOpenIsTransient = !this.doc.treeViewOpenIsTransient), icon: 'paint-brush' });
+            layoutItems.push({ description: (this.doc.treeViewHideHeaderFields ? 'Show' : 'Hide') + ' Header Fields', event: () => (this.doc.treeViewHideHeaderFields = !this.doc.treeViewHideHeaderFields), icon: 'paint-brush' });
+            layoutItems.push({ description: (this.doc.treeViewHideTitle ? 'Show' : 'Hide') + ' Title', event: () => (this.doc.treeViewHideTitle = !this.doc.treeViewHideTitle), icon: 'paint-brush' });
+            ContextMenu.Instance.addItem({ description: 'Options...', subitems: layoutItems, icon: 'eye' });
+            const existingOnClick = ContextMenu.Instance.findByDescription('OnClick...');
+            const onClicks: ContextMenuProps[] = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : [];
+            onClicks.push({ description: 'Edit onChecked Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.doc, undefined, 'onCheckedClick'), 'edit onCheckedClick'), icon: 'edit' });
+            !existingOnClick && ContextMenu.Instance.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
         }
-    }
+    };
     onTreeDrop = (e: React.DragEvent, addDocs?: (docs: Doc[]) => void) => this.onExternalDrop(e, {}, addDocs);
 
     @undoBatch
     makeTextCollection = (childDocs: Doc[]) => {
         this.addDoc(TreeView.makeTextBullet(), childDocs.length ? childDocs[0] : undefined, true);
-    }
+    };
 
     get editableTitle() {
-        return <EditableView
-            contents={this.dataDoc.title}
-            display={"block"}
-            maxHeight={72}
-            height={"auto"}
-            GetValue={() => StrCast(this.dataDoc.title)}
-            SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => {
-                if (enter && this.props.Document.treeViewType === TreeViewType.outline) this.makeTextCollection(this.treeChildren);
-                this.dataDoc.title = value;
-                return true;
-            })} />;
+        return (
+            <EditableView
+                contents={this.dataDoc.title}
+                display={'block'}
+                maxHeight={72}
+                height={'auto'}
+                GetValue={() => StrCast(this.dataDoc.title)}
+                SetValue={undoBatch((value: string, shift: boolean, enter: boolean) => {
+                    if (enter && this.props.Document.treeViewType === TreeViewType.outline) this.makeTextCollection(this.treeChildren);
+                    this.dataDoc.title = value;
+                    return true;
+                })}
+            />
+        );
     }
 
-
     onKey = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
-        if (this.outlineMode && e.key === "Enter") {
+        if (this.outlineMode && e.key === 'Enter') {
             e.stopPropagation();
             this.makeTextCollection(this.treeChildren);
             return true;
         }
-    }
+    };
     get documentTitle() {
-        return <FormattedTextBox
-            {...this.props}
-            fieldKey={"text"}
-            renderDepth={this.props.renderDepth + 1}
-            isContentActive={this.isContentActive}
-            isDocumentActive={this.isContentActive}
-            rootSelected={returnTrue}
-            forceAutoHeight={true} // needed to make the title resize even if the rest of the tree view is not autoHeight
-            PanelWidth={this.documentTitleWidth}
-            PanelHeight={this.documentTitleHeight}
-            scaling={returnOne}
-            onKey={this.onKey}
-            docFilters={returnEmptyFilter}
-            docRangeFilters={returnEmptyFilter}
-            searchFilterDocs={returnEmptyDoclist}
-            ContainingCollectionDoc={this.doc}
-            ContainingCollectionView={this.props.CollectionView}
-            addDocument={returnFalse}
-            moveDocument={returnFalse}
-            removeDocument={returnFalse}
-            whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-            bringToFront={returnFalse}
-        />;
+        return (
+            <FormattedTextBox
+                {...this.props}
+                fieldKey={'text'}
+                renderDepth={this.props.renderDepth + 1}
+                isContentActive={this.isContentActive}
+                isDocumentActive={this.isContentActive}
+                rootSelected={returnTrue}
+                forceAutoHeight={true} // needed to make the title resize even if the rest of the tree view is not autoHeight
+                PanelWidth={this.documentTitleWidth}
+                PanelHeight={this.documentTitleHeight}
+                scaling={returnOne}
+                onKey={this.onKey}
+                docFilters={returnEmptyFilter}
+                docRangeFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
+                ContainingCollectionDoc={this.doc}
+                ContainingCollectionView={this.props.CollectionView}
+                addDocument={returnFalse}
+                moveDocument={returnFalse}
+                removeDocument={returnFalse}
+                whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                bringToFront={returnFalse}
+            />
+        );
     }
     childContextMenuItems = () => {
         const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []);
         const customFilters = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []);
         const icons = StrListCast(this.doc.childContextMenuIcons);
         return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
-    }
+    };
     @computed get treeViewElements() {
         TraceMobx();
         const dropAction = StrCast(this.doc.childDropAction) as dropActionType;
@@ -266,36 +289,34 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
             this.props.treeViewSkipFields,
             true,
             this.whenChildContentsActiveChanged,
-            this.props.dontRegisterView || Cast(this.props.Document.childDontRegisterViews, "boolean", null),
+            this.props.dontRegisterView || Cast(this.props.Document.childDontRegisterViews, 'boolean', null),
             this.observeHeight,
             this.unobserveHeight,
             this.childContextMenuItems(),
             //TODO: [AL] add these
             this.props.AddToMap,
             this.props.RemFromMap,
-            this.props.hierarchyIndex,
+            this.props.hierarchyIndex
         );
     }
     @computed get titleBar() {
-        return this.dataDoc === null ? (null) :
-            <div className="collectionTreeView-titleBar" key={this.doc[Id]}
-                style={!this.outlineMode ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}}
-                ref={r => this._titleRef = r}>
+        return this.dataDoc === null ? null : (
+            <div className="collectionTreeView-titleBar" key={this.doc[Id]} style={!this.outlineMode ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}} ref={r => (this._titleRef = r)}>
                 {this.outlineMode ? this.documentTitle : this.editableTitle}
-            </div>;
+            </div>
+        );
     }
 
     @computed get noviceExplainer() {
-        return !Doc.noviceMode || !this.rootDoc.explainer ? (null) :
-            <div className="documentExplanation"> {this.rootDoc.explainer} </div>;
+        return !Doc.noviceMode || !this.rootDoc.explainer ? null : <div className="documentExplanation"> {StrCast(this.rootDoc.explainer)} </div>;
     }
 
     return35 = () => 35;
     @computed get buttonMenu() {
         const menuDoc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
         // To create a multibutton menu add a CollectionLinearView
-        return !menuDoc ? null :
-            (<div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}>
+        return !menuDoc ? null : (
+            <div className="buttonMenu-docBtn" style={{ width: NumCast(menuDoc._width, 30), height: NumCast(menuDoc._height, 30) }}>
                 <DocumentView
                     Document={menuDoc}
                     DataDoc={menuDoc}
@@ -322,11 +343,16 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
                 />
-            </div>);
+            </div>
+        );
     }
 
-    @computed get nativeWidth() { return Doc.NativeWidth(this.Document, undefined, true); }
-    @computed get nativeHeight() { return Doc.NativeHeight(this.Document, undefined, true); }
+    @computed get nativeWidth() {
+        return Doc.NativeWidth(this.Document, undefined, true);
+    }
+    @computed get nativeHeight() {
+        return Doc.NativeHeight(this.Document, undefined, true);
+    }
 
     @computed get contentScaling() {
         const nw = this.nativeWidth;
@@ -347,68 +373,73 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     addAnnotationDocument = (doc: Doc | Doc[]) => this.props.CollectionView?.addDocument(doc, `${this.props.fieldKey}-annotations`) || false;
     remAnnotationDocument = (doc: Doc | Doc[]) => this.props.CollectionView?.removeDocument(doc, `${this.props.fieldKey}-annotations`) || false;
     moveAnnotationDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[], annotationKey?: string) => boolean) =>
-        this.props.CollectionView?.moveDocument(doc, targetCollection, addDocument, `${this.props.fieldKey}-annotations`) || false
+        this.props.CollectionView?.moveDocument(doc, targetCollection, addDocument, `${this.props.fieldKey}-annotations`) || false;
 
     contentFunc = () => {
         const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor);
-        const pointerEvents = () => !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined;
-        const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? (null) : this.titleBar;
+        const pointerEvents = () => (!this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined);
+        const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? null : this.titleBar;
         return [
-            <div className="collectionTreeView-contents" key="tree" style={{
-                ...(!titleBar ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}),
-                overflow: "auto",
-                height: "100%"//this.layoutDoc._autoHeight ? "max-content" : "100%"
-            }} >
+            <div
+                className="collectionTreeView-contents"
+                key="tree"
+                style={{
+                    ...(!titleBar ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}),
+                    overflow: 'auto',
+                    height: '100%', //this.layoutDoc._autoHeight ? "max-content" : "100%"
+                }}>
                 {titleBar}
-                <div className="collectionTreeView-container"
+                <div
+                    className="collectionTreeView-container"
                     style={{
-                        transform: this.outlineMode ? `scale(${this.contentScaling})` : "",
+                        transform: this.outlineMode ? `scale(${this.contentScaling})` : '',
                         paddingLeft: `${this.marginX()}px`,
-                        width: this.outlineMode ? `calc(${100 / this.contentScaling}%)` : ""
+                        width: this.outlineMode ? `calc(${100 / this.contentScaling}%)` : '',
                     }}
                     onContextMenu={this.onContextMenu}>
-                    {!this.buttonMenu && !this.noviceExplainer ? (null) :
+                    {!this.buttonMenu && !this.noviceExplainer ? null : (
                         <div className="documentButtonMenu" ref={action((r: HTMLDivElement) => r && (this._explainerHeight = r.getBoundingClientRect().height))}>
                             {this.buttonMenu}
                             {this.noviceExplainer}
                         </div>
-                    }
-                    <div className="collectionTreeView-dropTarget"
+                    )}
+                    <div
+                        className="collectionTreeView-dropTarget"
                         style={{
                             background: background(),
                             height: `calc(100% - ${this._explainerHeight}px)`,
-                            pointerEvents: pointerEvents()
+                            pointerEvents: pointerEvents(),
                         }}
                         onWheel={e => e.stopPropagation()}
                         onDrop={this.onTreeDrop}
                         ref={r => !this.doc.treeViewHasOverlay && r && this.createTreeDropTarget(r)}>
-                        <ul className={`no-indent${this.outlineMode ? "-outline" : ""}`} >
-                            {this.treeViewElements}
-                        </ul>
-                    </div >
+                        <ul className={`no-indent${this.outlineMode ? '-outline' : ''}`}>{this.treeViewElements}</ul>
+                    </div>
                 </div>
-            </div>
+            </div>,
         ];
-    }
+    };
     render() {
         TraceMobx();
 
-        return !(this.doc instanceof Doc) || !this.treeChildren ? (null) :
-            this.doc.treeViewHasOverlay ?
-                <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
-                    isAnnotationOverlay={true}
-                    isAnnotationOverlayScrollable={true}
-                    childDocumentsActive={this.props.isDocumentActive}
-                    fieldKey={this.props.fieldKey + "-annotations"}
-                    dropAction={"move"}
-                    select={emptyFunction}
-                    addDocument={this.addAnnotationDocument}
-                    removeDocument={this.remAnnotationDocument}
-                    moveDocument={this.moveAnnotationDocument}
-                    bringToFront={emptyFunction}
-                    renderDepth={this.props.renderDepth + 1} >
-                    {this.contentFunc}
-                </CollectionFreeFormView> :
-                this.contentFunc();
+        return !(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeViewHasOverlay ? (
+            <CollectionFreeFormView
+                {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                isAnnotationOverlay={true}
+                isAnnotationOverlayScrollable={true}
+                childDocumentsActive={this.props.isDocumentActive}
+                fieldKey={this.props.fieldKey + '-annotations'}
+                dropAction={'move'}
+                select={emptyFunction}
+                addDocument={this.addAnnotationDocument}
+                removeDocument={this.remAnnotationDocument}
+                moveDocument={this.moveAnnotationDocument}
+                bringToFront={emptyFunction}
+                renderDepth={this.props.renderDepth + 1}>
+                {this.contentFunc}
+            </CollectionFreeFormView>
+        ) : (
+            this.contentFunc()
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 2ae0c01ef..d788f9a77 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,5 +1,5 @@
 import { computed, observable, runInAction } from 'mobx';
-import { observer } from "mobx-react";
+import { observer } from 'mobx-react';
 import * as React from 'react';
 import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app
 import { Doc, DocListCast } from '../../../fields/Doc';
@@ -14,66 +14,65 @@ import { BranchCreate, BranchTask } from '../../documents/Gitlike';
 import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { ImageUtils } from '../../util/Import & Export/ImageUtils';
 import { InteractionUtils } from '../../util/InteractionUtils';
-import { ContextMenu } from "../ContextMenu";
+import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
 import { CollectionCarousel3DView } from './CollectionCarousel3DView';
 import { CollectionCarouselView } from './CollectionCarouselView';
-import { CollectionDockingView } from "./CollectionDockingView";
+import { CollectionDockingView } from './CollectionDockingView';
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
 import { CollectionGridView } from './collectionGrid/CollectionGridView';
 import { CollectionLinearView } from './collectionLinear';
 import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView';
 import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
 import { CollectionPileView } from './CollectionPileView';
-import { CollectionSchemaView } from "./collectionSchema/CollectionSchemaView";
+import { CollectionSchemaView } from './collectionSchema/CollectionSchemaView';
 import { CollectionStackingView } from './CollectionStackingView';
 import { SubCollectionViewProps } from './CollectionSubView';
 import { CollectionTimeView } from './CollectionTimeView';
-import { CollectionTreeView } from "./CollectionTreeView";
+import { CollectionTreeView } from './CollectionTreeView';
 import './CollectionView.scss';
 export const COLLECTION_BORDER_WIDTH = 2;
 const path = require('path');
 
 export enum CollectionViewType {
-    Invalid = "invalid",
-    Freeform = "freeform",
-    Schema = "schema",
-    Docking = "docking",
+    Invalid = 'invalid',
+    Freeform = 'freeform',
+    Schema = 'schema',
+    Docking = 'docking',
     Tree = 'tree',
-    Stacking = "stacking",
-    Masonry = "masonry",
-    Multicolumn = "multicolumn",
-    Multirow = "multirow",
-    Time = "time",
-    Carousel = "carousel",
-    Carousel3D = "3D Carousel",
-    Linear = "linear",
+    Stacking = 'stacking',
+    Masonry = 'masonry',
+    Multicolumn = 'multicolumn',
+    Multirow = 'multirow',
+    Time = 'time',
+    Carousel = 'carousel',
+    Carousel3D = '3D Carousel',
+    Linear = 'linear',
     //Staff = "staff",
-    Map = "map",
-    Grid = "grid",
-    Pile = "pileup",
-    StackedTimeline = "stacked timeline"
+    Map = 'map',
+    Grid = 'grid',
+    Pile = 'pileup',
+    StackedTimeline = 'stacked timeline',
 }
-export interface CollectionViewProps extends FieldViewProps {
-    isAnnotationOverlay?: boolean;  // is the collection an annotation overlay (eg an overlay on an image/video/etc)
+interface CollectionViewProps_ extends FieldViewProps {
+    isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc)
     isAnnotationOverlayScrollable?: boolean; // whether the annotation overlay can be vertically scrolled (just for tree views, currently)
     layoutEngine?: () => string;
     setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void;
 
     // property overrides for child documents
-    children?: never | (() => JSX.Element[]) | React.ReactNode;
     childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox)
-    childDocumentsActive?: () => boolean;// whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode)
+    childDocumentsActive?: () => boolean; // whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode)
     childFitWidth?: (child: Doc) => boolean;
     childShowTitle?: () => string;
     childOpacity?: () => number;
-    childContextMenuItems?: () => { script: ScriptField, label: string }[];
+    childContextMenuItems?: () => { script: ScriptField; label: string }[];
     childHideTitle?: () => boolean; // whether to hide the documentdecorations title for children
     childHideDecorationTitle?: () => boolean;
     childHideResizeHandles?: () => boolean;
-    childLayoutTemplate?: () => (Doc | undefined);// specify a layout Doc template to use for children of the collection
+    childLayoutTemplate?: () => Doc | undefined; // specify a layout Doc template to use for children of the collection
     childLayoutString?: string;
     childIgnoreNativeSize?: boolean;
     childClickScript?: ScriptField;
@@ -81,20 +80,25 @@ export interface CollectionViewProps extends FieldViewProps {
     //TODO: [AL] add these fields
     AddToMap?: (treeViewDoc: Doc, index: number[]) => Doc[];
     RemFromMap?: (treeViewDoc: Doc, index: number[]) => Doc[];
-    hierarchyIndex?: number[]; // hierarchical index of a document up to the rendering root (primarily used for tree views) 
+    hierarchyIndex?: number[]; // hierarchical index of a document up to the rendering root (primarily used for tree views)
 }
+export interface CollectionViewProps extends React.PropsWithChildren<CollectionViewProps_> {}
 @observer
 export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & CollectionViewProps>() {
-    public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); }
+    public static LayoutString(fieldStr: string) {
+        return FieldView.LayoutString(CollectionView, fieldStr);
+    }
 
     @observable private static _safeMode = false;
-    public static SetSafeMode(safeMode: boolean) { this._safeMode = safeMode; }
+    public static SetSafeMode(safeMode: boolean) {
+        this._safeMode = safeMode;
+    }
 
     protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
 
     constructor(props: any) {
         super(props);
-        runInAction(() => this._annotationKeySuffix = returnEmptyString);
+        runInAction(() => (this._annotationKeySuffix = returnEmptyString));
     }
 
     get collectionViewType(): CollectionViewType | undefined {
@@ -102,15 +106,17 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
         if (CollectionView._safeMode) {
             switch (viewField) {
                 case CollectionViewType.Freeform:
-                case CollectionViewType.Schema: return CollectionViewType.Tree;
-                case CollectionViewType.Invalid: return CollectionViewType.Freeform;
+                case CollectionViewType.Schema:
+                    return CollectionViewType.Tree;
+                case CollectionViewType.Invalid:
+                    return CollectionViewType.Freeform;
             }
         }
         return viewField as any as CollectionViewType;
     }
 
     showIsTagged = () => {
-        return (null);
+        return null;
         // this section would display an icon in the bototm right of a collection to indicate that all
         // photos had been processed through Google's content analysis API and Google's tags had been
         // assigned to the documents googlePhotosTags field.
@@ -119,136 +125,171 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
         // const allTagged = imageProtos.length > 0 && imageProtos.every(image => image.googlePhotosTags);
         // return !allTagged ? (null) : <img id={"google-tags"} src={"/assets/google_tags.png"} />;
         //this.isContentActive();
-    }
+    };
 
-    screenToLocalTransform = () => this.props.renderDepth ? this.props.ScreenToLocalTransform() : this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth());
+    screenToLocalTransform = () => (this.props.renderDepth ? this.props.ScreenToLocalTransform() : this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth()));
     private renderSubView = (type: CollectionViewType | undefined, props: SubCollectionViewProps) => {
         TraceMobx();
         if (type === undefined) return null;
         switch (type) {
             default:
-            case CollectionViewType.Freeform: return <CollectionFreeFormView key="collview" {...props} />;
-            case CollectionViewType.Schema: return <CollectionSchemaView key="collview" {...props} />;
-            case CollectionViewType.Docking: return <CollectionDockingView key="collview" {...props} />;
-            case CollectionViewType.Tree: return <CollectionTreeView key="collview" {...props} />;
-            case CollectionViewType.Multicolumn: return <CollectionMulticolumnView key="collview" {...props} />;
-            case CollectionViewType.Multirow: return <CollectionMultirowView key="collview" {...props} />;
-            case CollectionViewType.Linear: return <CollectionLinearView key="collview" {...props} />;
-            case CollectionViewType.Pile: return <CollectionPileView key="collview" {...props} />;
-            case CollectionViewType.Carousel: return <CollectionCarouselView key="collview" {...props} />;
-            case CollectionViewType.Carousel3D: return <CollectionCarousel3DView key="collview" {...props} />;
-            case CollectionViewType.Stacking: return <CollectionStackingView key="collview" {...props} />;
-            case CollectionViewType.Masonry: return <CollectionStackingView key="collview" {...props} />;
-            case CollectionViewType.Time: return <CollectionTimeView key="collview" {...props} />;
-            case CollectionViewType.Grid: return <CollectionGridView key="collview" {...props} />;
+            case CollectionViewType.Freeform:
+                return <CollectionFreeFormView key="collview" {...props} />;
+            case CollectionViewType.Schema:
+                return <CollectionSchemaView key="collview" {...props} />;
+            case CollectionViewType.Docking:
+                return <CollectionDockingView key="collview" {...props} />;
+            case CollectionViewType.Tree:
+                return <CollectionTreeView key="collview" {...props} />;
+            case CollectionViewType.Multicolumn:
+                return <CollectionMulticolumnView key="collview" {...props} />;
+            case CollectionViewType.Multirow:
+                return <CollectionMultirowView key="collview" {...props} />;
+            case CollectionViewType.Linear:
+                return <CollectionLinearView key="collview" {...props} />;
+            case CollectionViewType.Pile:
+                return <CollectionPileView key="collview" {...props} />;
+            case CollectionViewType.Carousel:
+                return <CollectionCarouselView key="collview" {...props} />;
+            case CollectionViewType.Carousel3D:
+                return <CollectionCarousel3DView key="collview" {...props} />;
+            case CollectionViewType.Stacking:
+                return <CollectionStackingView key="collview" {...props} />;
+            case CollectionViewType.Masonry:
+                return <CollectionStackingView key="collview" {...props} />;
+            case CollectionViewType.Time:
+                return <CollectionTimeView key="collview" {...props} />;
+            case CollectionViewType.Grid:
+                return <CollectionGridView key="collview" {...props} />;
             //case CollectionViewType.Staff: return <CollectionStaffView key="collview" {...props} />;
         }
-    }
+    };
 
     setupViewTypes(category: string, func: (viewType: CollectionViewType) => Doc, addExtras: boolean) {
         const subItems: ContextMenuProps[] = [];
-        subItems.push({ description: "Freeform", event: () => func(CollectionViewType.Freeform), icon: "signature" });
+        subItems.push({ description: 'Freeform', event: () => func(CollectionViewType.Freeform), icon: 'signature' });
         if (addExtras && CollectionView._safeMode) {
-            ContextMenu.Instance.addItem({ description: "Test Freeform", event: () => func(CollectionViewType.Invalid), icon: "project-diagram" });
+            ContextMenu.Instance.addItem({ description: 'Test Freeform', event: () => func(CollectionViewType.Invalid), icon: 'project-diagram' });
         }
-        subItems.push({ description: "Schema", event: () => func(CollectionViewType.Schema), icon: "th-list" });
-        subItems.push({ description: "Tree", event: () => func(CollectionViewType.Tree), icon: "tree" });
-        subItems.push({ description: "Stacking", event: () => func(CollectionViewType.Stacking)._autoHeight = true, icon: "ellipsis-v" });
-        subItems.push({ description: "Multicolumn", event: () => func(CollectionViewType.Multicolumn), icon: "columns" });
-        subItems.push({ description: "Multirow", event: () => func(CollectionViewType.Multirow), icon: "columns" });
-        subItems.push({ description: "Masonry", event: () => func(CollectionViewType.Masonry), icon: "columns" });
-        subItems.push({ description: "Carousel", event: () => func(CollectionViewType.Carousel), icon: "columns" });
-        subItems.push({ description: "3D Carousel", event: () => func(CollectionViewType.Carousel3D), icon: "columns" });
-        !Doc.noviceMode && subItems.push({ description: "Pivot/Time", event: () => func(CollectionViewType.Time), icon: "columns" });
-        !Doc.noviceMode && subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
-        subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" });
+        subItems.push({ description: 'Schema', event: () => func(CollectionViewType.Schema), icon: 'th-list' });
+        subItems.push({ description: 'Tree', event: () => func(CollectionViewType.Tree), icon: 'tree' });
+        subItems.push({ description: 'Stacking', event: () => (func(CollectionViewType.Stacking)._autoHeight = true), icon: 'ellipsis-v' });
+        subItems.push({ description: 'Multicolumn', event: () => func(CollectionViewType.Multicolumn), icon: 'columns' });
+        subItems.push({ description: 'Multirow', event: () => func(CollectionViewType.Multirow), icon: 'columns' });
+        subItems.push({ description: 'Masonry', event: () => func(CollectionViewType.Masonry), icon: 'columns' });
+        subItems.push({ description: 'Carousel', event: () => func(CollectionViewType.Carousel), icon: 'columns' });
+        subItems.push({ description: '3D Carousel', event: () => func(CollectionViewType.Carousel3D), icon: 'columns' });
+        !Doc.noviceMode && subItems.push({ description: 'Pivot/Time', event: () => func(CollectionViewType.Time), icon: 'columns' });
+        !Doc.noviceMode && subItems.push({ description: 'Map', event: () => func(CollectionViewType.Map), icon: 'globe-americas' });
+        subItems.push({ description: 'Grid', event: () => func(CollectionViewType.Grid), icon: 'th-list' });
 
         if (!Doc.IsSystem(this.rootDoc) && !this.rootDoc.isGroup && !this.rootDoc.annotationOn) {
             const existingVm = ContextMenu.Instance.findByDescription(category);
-            const catItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
-            catItems.push({ description: "Add a Perspective...", addDivider: true, noexpand: true, subitems: subItems, icon: "eye" });
-            !existingVm && ContextMenu.Instance.addItem({ description: category, subitems: catItems, icon: "eye" });
+            const catItems = existingVm && 'subitems' in existingVm ? existingVm.subitems : [];
+            catItems.push({ description: 'Add a Perspective...', addDivider: true, noexpand: true, subitems: subItems, icon: 'eye' });
+            !existingVm && ContextMenu.Instance.addItem({ description: category, subitems: catItems, icon: 'eye' });
         }
     }
 
     onContextMenu = (e: React.MouseEvent): void => {
         const cm = ContextMenu.Instance;
         if (e.nativeEvent.cancelBubble) return; // nested calls to React to render can cause the same event to trigger in the outer view even if the inner view has handled it.  This avoid CollectionDockingView menu options from being added when the event has been handled by a sub-document.
-        if (cm && !e.isPropagationStopped() && this.rootDoc[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
-            this.setupViewTypes("UI Controls...", vtype => {
-                const newRendition = Doc.MakeAlias(this.rootDoc);
-                newRendition._viewType = vtype;
-                this.props.addDocTab(newRendition, "add:right");
-                return newRendition;
-            }, false);
+        if (cm && !e.isPropagationStopped() && this.rootDoc[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
+            this.setupViewTypes(
+                'UI Controls...',
+                vtype => {
+                    const newRendition = Doc.MakeAlias(this.rootDoc);
+                    newRendition._viewType = vtype;
+                    this.props.addDocTab(newRendition, 'add:right');
+                    return newRendition;
+                },
+                false
+            );
 
-            const options = cm.findByDescription("Options...");
-            const optionItems = options && "subitems" in options ? options.subitems : [];
-            !Doc.noviceMode ? optionItems.splice(0, 0, { description: `${this.rootDoc.forceActive ? "Select" : "Force"} Contents Active`, event: () => this.rootDoc.forceActive = !this.rootDoc.forceActive, icon: "project-diagram" }) : null;
+            const options = cm.findByDescription('Options...');
+            const optionItems = options && 'subitems' in options ? options.subitems : [];
+            !Doc.noviceMode ? optionItems.splice(0, 0, { description: `${this.rootDoc.forceActive ? 'Select' : 'Force'} Contents Active`, event: () => (this.rootDoc.forceActive = !this.rootDoc.forceActive), icon: 'project-diagram' }) : null;
             if (this.rootDoc.childLayout instanceof Doc) {
-                optionItems.push({ description: "View Child Layout", event: () => this.props.addDocTab(this.rootDoc.childLayout as Doc, "add:right"), icon: "project-diagram" });
+                optionItems.push({ description: 'View Child Layout', event: () => this.props.addDocTab(this.rootDoc.childLayout as Doc, 'add:right'), icon: 'project-diagram' });
             }
             if (this.rootDoc.childClickedOpenTemplateView instanceof Doc) {
-                optionItems.push({ description: "View Child Detailed Layout", event: () => this.props.addDocTab(this.rootDoc.childClickedOpenTemplateView as Doc, "add:right"), icon: "project-diagram" });
+                optionItems.push({ description: 'View Child Detailed Layout', event: () => this.props.addDocTab(this.rootDoc.childClickedOpenTemplateView as Doc, 'add:right'), icon: 'project-diagram' });
             }
-            !Doc.noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? "Unset" : "Set"} inPlace Container`, event: () => this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer, icon: "project-diagram" });
+            !Doc.noviceMode && optionItems.push({ description: `${this.rootDoc.isInPlaceContainer ? 'Unset' : 'Set'} inPlace Container`, event: () => (this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer), icon: 'project-diagram' });
 
             if (!Doc.noviceMode) {
                 optionItems.push({
-                    description: "Create Branch", event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), "add:right"), icon: "project-diagram"
+                    description: 'Create Branch',
+                    event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), 'add:right'),
+                    icon: 'project-diagram',
                 });
                 optionItems.push({
-                    description: "Pull Master", event: () => BranchTask(this.rootDoc, "pull"), icon: "project-diagram"
+                    description: 'Pull Master',
+                    event: () => BranchTask(this.rootDoc, 'pull'),
+                    icon: 'project-diagram',
                 });
                 optionItems.push({
-                    description: "Merge Branches", event: () => BranchTask(this.rootDoc, "merge"), icon: "project-diagram"
+                    description: 'Merge Branches',
+                    event: () => BranchTask(this.rootDoc, 'merge'),
+                    icon: 'project-diagram',
                 });
             }
             if (this.Document._viewType === CollectionViewType.Docking) {
-                optionItems.push({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(), icon: "project-diagram" });
+                optionItems.push({ description: 'Create Dashboard', event: () => CurrentUserUtils.createNewDashboard(), icon: 'project-diagram' });
             }
 
-            !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "hand-point-right" });
+            !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'hand-point-right' });
 
             if (!Doc.noviceMode && !this.rootDoc.annotationOn) {
-                const existingOnClick = cm.findByDescription("OnClick...");
-                const onClicks = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
-                const funcs = [{ key: "onChildClick", name: "On Child Clicked" }, { key: "onChildDoubleClick", name: "On Child Double Clicked" }];
-                funcs.map(func => onClicks.push({
-                    description: `Edit ${func.name} script`, icon: "edit", event: (obj: any) => {
-                        const alias = Doc.MakeAlias(this.rootDoc);
-                        DocUtils.makeCustomViewClicked(alias, undefined, func.key);
-                        this.props.addDocTab(alias, "add:right");
-                    }
-                }));
-                DocListCast(Cast(Doc.UserDoc()["clickFuncs-child"], Doc, null).data).forEach(childClick =>
+                const existingOnClick = cm.findByDescription('OnClick...');
+                const onClicks = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : [];
+                const funcs = [
+                    { key: 'onChildClick', name: 'On Child Clicked' },
+                    { key: 'onChildDoubleClick', name: 'On Child Double Clicked' },
+                ];
+                funcs.map(func =>
+                    onClicks.push({
+                        description: `Edit ${func.name} script`,
+                        icon: 'edit',
+                        event: (obj: any) => {
+                            const alias = Doc.MakeAlias(this.rootDoc);
+                            DocUtils.makeCustomViewClicked(alias, undefined, func.key);
+                            this.props.addDocTab(alias, 'add:right');
+                        },
+                    })
+                );
+                DocListCast(Cast(Doc.UserDoc()['clickFuncs-child'], Doc, null).data).forEach(childClick =>
                     onClicks.push({
                         description: `Set child ${childClick.title}`,
-                        icon: "edit",
-                        event: () => Doc.GetProto(this.rootDoc)[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data)),
-                    }));
-                !Doc.IsSystem(this.rootDoc) && !existingOnClick && cm.addItem({ description: "OnClick...", noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
+                        icon: 'edit',
+                        event: () => (Doc.GetProto(this.rootDoc)[StrCast(childClick.targetScriptKey)] = ObjectField.MakeCopy(ScriptCast(childClick.data))),
+                    })
+                );
+                !Doc.IsSystem(this.rootDoc) && !existingOnClick && cm.addItem({ description: 'OnClick...', noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
             }
 
             if (!Doc.noviceMode) {
-                const more = cm.findByDescription("More...");
-                const moreItems = more && "subitems" in more ? more.subitems : [];
-                moreItems.push({ description: "Export Image Hierarchy", icon: "columns", event: () => ImageUtils.ExportHierarchyToFileSystem(this.rootDoc) });
-                !more && cm.addItem({ description: "More...", subitems: moreItems, icon: "hand-point-right" });
+                const more = cm.findByDescription('More...');
+                const moreItems = more && 'subitems' in more ? more.subitems : [];
+                moreItems.push({ description: 'Export Image Hierarchy', icon: 'columns', event: () => ImageUtils.ExportHierarchyToFileSystem(this.rootDoc) });
+                !more && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'hand-point-right' });
             }
         }
-    }
+    };
 
     bodyPanelWidth = () => this.props.PanelWidth();
 
     childHideResizeHandles = () => this.props.childHideResizeHandles?.() ?? BoolCast(this.Document.childHideResizeHandles);
     childHideDecorationTitle = () => this.props.childHideDecorationTitle?.() ?? BoolCast(this.Document.childHideDecorationTitle);
     childLayoutTemplate = () => this.props.childLayoutTemplate?.() || Cast(this.rootDoc.childLayoutTemplate, Doc, null);
-    @computed get childLayoutString() { return StrCast(this.rootDoc.childLayoutString); }
+    @computed get childLayoutString() {
+        return StrCast(this.rootDoc.childLayoutString);
+    }
 
     isContentActive = (outsideReaction?: boolean) => {
         return this.props.isContentActive();
-    }
+    };
     render() {
         TraceMobx();
         const props: SubCollectionViewProps = {
@@ -268,10 +309,11 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
             childHideDecorationTitle: this.childHideDecorationTitle,
             CollectionView: this,
         };
-        return (<div className={"collectionView"} onContextMenu={this.onContextMenu}
-            style={{ pointerEvents: this.props.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform && this.rootDoc._lockedPosition ? "none" : undefined }}>
-            {this.showIsTagged()}
-            {this.renderSubView(this.collectionViewType, props)}
-        </div>);
+        return (
+            <div className={'collectionView'} onContextMenu={this.onContextMenu} style={{ pointerEvents: this.props.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform && this.rootDoc._lockedPosition ? 'none' : undefined }}>
+                {this.showIsTagged()}
+                {this.renderSubView(this.collectionViewType, props)}
+            </div>
+        );
     }
 }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index dacbb3508..8720c9097 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,25 +1,23 @@
-import { computed } from "mobx";
-import { observer } from "mobx-react";
-import { Id } from "../../../../fields/FieldSymbols";
-import { DocumentManager } from "../../../util/DocumentManager";
-import "./CollectionFreeFormLinksView.scss";
-import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
-import React = require("react");
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
+import { Id } from '../../../../fields/FieldSymbols';
+import { DocumentManager } from '../../../util/DocumentManager';
+import './CollectionFreeFormLinksView.scss';
+import { CollectionFreeFormLinkView } from './CollectionFreeFormLinkView';
+import React = require('react');
 
 @observer
-export class CollectionFreeFormLinksView extends React.Component {
+export class CollectionFreeFormLinksView extends React.Component<React.PropsWithChildren<{}>> {
     @computed get uniqueConnections() {
-        return Array.from(new Set(DocumentManager.Instance.LinkedDocumentViews)).map(c =>
-            <CollectionFreeFormLinkView key={c.l[Id]} A={c.a} B={c.b} LinkDocs={[c.l]} />
-        );
+        return Array.from(new Set(DocumentManager.Instance.LinkedDocumentViews)).map(c => <CollectionFreeFormLinkView key={c.l[Id]} A={c.a} B={c.b} LinkDocs={[c.l]} />);
     }
 
     render() {
-        return <div className="collectionfreeformlinksview-container">
-            <svg className="collectionfreeformlinksview-svgCanvas">
-                {this.uniqueConnections}
-            </svg>
-            {this.props.children}
-        </div>;
+        return (
+            <div className="collectionfreeformlinksview-container">
+                <svg className="collectionfreeformlinksview-svgCanvas">{this.uniqueConnections}</svg>
+                {this.props.children}
+            </div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionGrid/Grid.tsx b/src/client/views/collections/collectionGrid/Grid.tsx
index 3d2ed0cf9..3d1d87aa0 100644
--- a/src/client/views/collections/collectionGrid/Grid.tsx
+++ b/src/client/views/collections/collectionGrid/Grid.tsx
@@ -1,14 +1,13 @@
 import * as React from 'react';
-import { observer } from "mobx-react";
+import { observer } from 'mobx-react';
 
-import "../../../../../node_modules/react-grid-layout/css/styles.css";
-import "../../../../../node_modules/react-resizable/css/styles.css";
+import '../../../../../node_modules/react-grid-layout/css/styles.css';
+import '../../../../../node_modules/react-resizable/css/styles.css';
 
 import * as GridLayout from 'react-grid-layout';
 import { Layout } from 'react-grid-layout';
 export { Layout } from 'react-grid-layout';
 
-
 interface GridProps {
     width: number;
     nodeList: JSX.Element[] | null;
@@ -29,9 +28,10 @@ interface GridProps {
 @observer
 export default class Grid extends React.Component<GridProps> {
     render() {
-        const compactType = this.props.compactType === "vertical" || this.props.compactType === "horizontal" ? this.props.compactType : null;
+        const compactType = this.props.compactType === 'vertical' || this.props.compactType === 'horizontal' ? this.props.compactType : null;
         return (
-            <GridLayout className="layout"
+            <GridLayout
+                className="layout"
                 layout={this.props.layout}
                 cols={this.props.numCols}
                 rowHeight={this.props.rowHeight}
@@ -43,9 +43,8 @@ export default class Grid extends React.Component<GridProps> {
                 useCSSTransforms={true}
                 onLayoutChange={this.props.setLayout}
                 preventCollision={this.props.preventCollision}
-                transformScale={1 / this.props.transformScale} // still doesn't work :(
-                margin={[this.props.margin, this.props.margin]}
-            >
+                transformScale={1 / this.props.transformScale} // still doesn't work :(  ??
+                margin={[this.props.margin, this.props.margin]}>
                 {this.props.nodeList}
             </GridLayout>
         );
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index c31267e87..8adfdc70b 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -9,7 +9,7 @@ import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types
 import { emptyFunction, returnEmptyDoclist, returnTrue, Utils } from '../../../../Utils';
 import { DocUtils } from '../../../documents/Documents';
 import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
-import { DocumentManager } from "../../../util/DocumentManager";
+import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
 import { Colors, Shadows } from '../../global/globalEnums';
@@ -21,13 +21,12 @@ import { StyleProp } from '../../StyleProvider';
 import { CollectionStackedTimeline } from '../CollectionStackedTimeline';
 import { CollectionSubView } from '../CollectionSubView';
 import { CollectionViewType } from '../CollectionView';
-import "./CollectionLinearView.scss";
-
+import './CollectionLinearView.scss';
 
 /**
- * CollectionLinearView is the class for rendering the horizontal collection 
+ * CollectionLinearView is the class for rendering the horizontal collection
  * of documents, it useful for horizontal menus. It can either be expandable
- * or not using the linearViewExpandable field. 
+ * or not using the linearViewExpandable field.
  * It is used in the following locations:
  * - It is used in the popup menu on the bottom left (see docButtons() in MainView.tsx)
  * - It is used for the context sensitive toolbar at the top (see contMenuButtons() in CollectionMenu.tsx)
@@ -48,45 +47,47 @@ export class CollectionLinearView extends CollectionSubView() {
     }
 
     componentDidMount() {
-        this._widthDisposer = reaction(() => 5 + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.length * (this.rootDoc[HeightSym]()) : 10),
+        this._widthDisposer = reaction(
+            () => 5 + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.length * this.rootDoc[HeightSym]() : 10),
             width => this.childDocs.length && (this.layoutDoc._width = width),
             { fireImmediately: true }
         );
 
         this._selectedDisposer = reaction(
             () => NumCast(this.layoutDoc.selectedIndex),
-            (i) => runInAction(() => {
-                this._selectedIndex = i;
-                let selected: any = undefined;
-                this.childLayoutPairs.map(async (pair, ind) => {
-                    const isSelected = this._selectedIndex === ind;
-                    if (isSelected) {
-                        selected = pair;
-                    }
-                    else {
-                        ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log);
+            i =>
+                runInAction(() => {
+                    this._selectedIndex = i;
+                    let selected: any = undefined;
+                    this.childLayoutPairs.map(async (pair, ind) => {
+                        const isSelected = this._selectedIndex === ind;
+                        if (isSelected) {
+                            selected = pair;
+                        } else {
+                            ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log);
+                        }
+                    });
+                    if (selected && selected.layout) {
+                        ScriptCast(selected.layout.proto?.onPointerDown)?.script.run({ this: selected.layout.proto }, console.log);
                     }
-                });
-                if (selected && selected.layout) {
-                    ScriptCast(selected.layout.proto?.onPointerDown)?.script.run({ this: selected.layout.proto }, console.log);
-                }
-            }),
+                }),
             { fireImmediately: true }
         );
     }
-    protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view
+    protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
+        //used for stacking and masonry view
         this._dropDisposer && this._dropDisposer();
         if (ele) {
             this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
         }
-    }
+    };
 
     dimension = () => NumCast(this.rootDoc._height); // 2 * the padding
     getTransform = (ele: Opt<HTMLDivElement>) => {
         if (!ele) return Transform.Identity();
         const { scale, translateX, translateY } = Utils.GetScreenTransform(ele);
         return new Transform(-translateX, -translateY, 1);
-    }
+    };
 
     @action
     exitLongLinks = () => {
@@ -99,28 +100,27 @@ export class CollectionLinearView extends CollectionSubView() {
         }
         DocumentLinksButton.StartLink = undefined;
         DocumentLinksButton.StartLinkView = undefined;
-    }
+    };
 
     @action
     changeDescriptionSetting = () => {
         if (LinkDescriptionPopup.showDescriptions) {
-            if (LinkDescriptionPopup.showDescriptions === "ON") {
-                LinkDescriptionPopup.showDescriptions = "OFF";
+            if (LinkDescriptionPopup.showDescriptions === 'ON') {
+                LinkDescriptionPopup.showDescriptions = 'OFF';
                 LinkDescriptionPopup.descriptionPopup = false;
             } else {
-                LinkDescriptionPopup.showDescriptions = "ON";
+                LinkDescriptionPopup.showDescriptions = 'ON';
             }
         } else {
-            LinkDescriptionPopup.showDescriptions = "OFF";
+            LinkDescriptionPopup.showDescriptions = 'OFF';
             LinkDescriptionPopup.descriptionPopup = false;
         }
-    }
+    };
 
     myContextMenu = (e: React.MouseEvent) => {
         e.stopPropagation();
         e.preventDefault();
-    }
-
+    };
 
     getDisplayDoc = (doc: Doc, preview: boolean = false) => {
         const nested = doc._viewType === CollectionViewType.Linear;
@@ -129,44 +129,50 @@ export class CollectionLinearView extends CollectionSubView() {
         let dref: Opt<HTMLDivElement>;
         const docXf = () => this.getTransform(dref);
         // const scalable = pair.layout.onClick || pair.layout.onDragStart;
-        return hidden ? (null) : <div className={preview ? "preview" : `collectionLinearView-docBtn`} key={doc[Id]} ref={r => dref = r || undefined}
-            style={{
-                pointerEvents: "all",
-                width: nested ? undefined : NumCast(doc._width),
-                height: nested ? undefined : NumCast(doc._height),
-                marginLeft: !nested ? 2.5 : 0,
-                marginRight: !nested ? 2.5 : 0,
-                // width: NumCast(pair.layout._width),
-                // height: NumCast(pair.layout._height),
-            }}  >
-            <DocumentView
-                Document={doc}
-                isContentActive={this.props.isContentActive}
-                isDocumentActive={returnTrue}
-                addDocument={this.props.addDocument}
-                moveDocument={this.props.moveDocument}
-                addDocTab={this.props.addDocTab}
-                pinToPres={emptyFunction}
-                rootSelected={this.props.isSelected}
-                removeDocument={this.props.removeDocument}
-                ScreenToLocalTransform={docXf}
-                PanelWidth={nested ? doc[WidthSym] : this.dimension}
-                PanelHeight={nested || doc._height ? doc[HeightSym] : this.dimension}
-                renderDepth={this.props.renderDepth + 1}
-                dontRegisterView={BoolCast(this.rootDoc.childDontRegisterViews)}
-                focus={emptyFunction}
-                styleProvider={this.props.styleProvider}
-                docViewPath={returnEmptyDoclist}
-                whenChildContentsActiveChanged={emptyFunction}
-                bringToFront={emptyFunction}
-                docFilters={this.props.docFilters}
-                docRangeFilters={this.props.docRangeFilters}
-                searchFilterDocs={this.props.searchFilterDocs}
-                ContainingCollectionView={undefined}
-                ContainingCollectionDoc={undefined}
-                hideResizeHandles={true} />
-        </div>;
-    }
+        return hidden ? null : (
+            <div
+                className={preview ? 'preview' : `collectionLinearView-docBtn`}
+                key={doc[Id]}
+                ref={r => (dref = r || undefined)}
+                style={{
+                    pointerEvents: 'all',
+                    width: nested ? undefined : NumCast(doc._width),
+                    height: nested ? undefined : NumCast(doc._height),
+                    marginLeft: !nested ? 2.5 : 0,
+                    marginRight: !nested ? 2.5 : 0,
+                    // width: NumCast(pair.layout._width),
+                    // height: NumCast(pair.layout._height),
+                }}>
+                <DocumentView
+                    Document={doc}
+                    isContentActive={this.props.isContentActive}
+                    isDocumentActive={returnTrue}
+                    addDocument={this.props.addDocument}
+                    moveDocument={this.props.moveDocument}
+                    addDocTab={this.props.addDocTab}
+                    pinToPres={emptyFunction}
+                    rootSelected={this.props.isSelected}
+                    removeDocument={this.props.removeDocument}
+                    ScreenToLocalTransform={docXf}
+                    PanelWidth={nested ? doc[WidthSym] : this.dimension}
+                    PanelHeight={nested || doc._height ? doc[HeightSym] : this.dimension}
+                    renderDepth={this.props.renderDepth + 1}
+                    dontRegisterView={BoolCast(this.rootDoc.childDontRegisterViews)}
+                    focus={emptyFunction}
+                    styleProvider={this.props.styleProvider}
+                    docViewPath={returnEmptyDoclist}
+                    whenChildContentsActiveChanged={emptyFunction}
+                    bringToFront={emptyFunction}
+                    docFilters={this.props.docFilters}
+                    docRangeFilters={this.props.docRangeFilters}
+                    searchFilterDocs={this.props.searchFilterDocs}
+                    ContainingCollectionView={undefined}
+                    ContainingCollectionDoc={undefined}
+                    hideResizeHandles={true}
+                />
+            </div>
+        );
+    };
 
     render() {
         const guid = Utils.GenerateGuid(); // Generate a unique ID to use as the label
@@ -178,66 +184,99 @@ export class CollectionLinearView extends CollectionSubView() {
         const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
         const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
         const icon: string = StrCast(this.props.Document.icon); // Menu opener toggle
-        const menuOpener = <label htmlFor={`${guid}`}
-            style={{
-                boxShadow: floating ? Shadows.STANDARD_SHADOW : undefined,
-                color: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.BLACK,
-                backgroundColor: backgroundColor === color ? "black" : BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.LIGHT_GRAY
-            }}
-            onPointerDown={e => e.stopPropagation()} >
-            <div className="collectionLinearView-menuOpener">
-                {BoolCast(this.layoutDoc.linearViewIsExpanded) ? icon ? icon : <FontAwesomeIcon icon={"minus"} /> : icon ? icon : <FontAwesomeIcon icon={"plus"} />}
-            </div>
-        </label>;
-
-        return <div className={`collectionLinearView-outer ${this.layoutDoc.linearViewSubMenu}`} style={{ backgroundColor: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : "transparent" }}>
-            <div className="collectionLinearView" ref={this.createDashEventsTarget}
-                onContextMenu={this.myContextMenu} >
-                {!expandable ? (null) : <Tooltip title={<><div className="dash-tooltip">{BoolCast(this.props.Document.linearViewIsExpanded) ? "Close" : "Open"}</div></>} placement="top">
-                    {menuOpener}
-                </Tooltip>}
-                <input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.linearViewIsExpanded)} ref={this.addMenuToggle}
-                    onChange={action(() => this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked)} />
-
-                <div className="collectionLinearView-content"
-                    style={{
-                        height: this.dimension(),
-                        flexDirection: flexDir,
-                        gap: flexGap
-                    }}>
-                    {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
-                </div>
-                {!DocumentLinksButton.StartLink || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? null :
-                    <span className="bottomPopup-background" style={{ pointerEvents: "all" }}
-                        onPointerDown={e => e.stopPropagation()} >
-                        <span className="bottomPopup-text" >
-                            Creating link from: <b>{DocumentLinksButton.AnnotationId ? "Annotation in " : " "} {StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...'}</b>
-                        </span>
+        const menuOpener = (
+            <label
+                htmlFor={`${guid}`}
+                style={{
+                    boxShadow: floating ? Shadows.STANDARD_SHADOW : undefined,
+                    color: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.BLACK,
+                    backgroundColor: backgroundColor === color ? 'black' : BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.LIGHT_GRAY,
+                }}
+                onPointerDown={e => e.stopPropagation()}>
+                <div className="collectionLinearView-menuOpener">{BoolCast(this.layoutDoc.linearViewIsExpanded) ? icon ? icon : <FontAwesomeIcon icon={'minus'} /> : icon ? icon : <FontAwesomeIcon icon={'plus'} />}</div>
+            </label>
+        );
 
-                        <Tooltip title={<><div className="dash-tooltip">{"Toggle description pop-up"} </div></>} placement="top">
-                            <span className="bottomPopup-descriptions" onClick={this.changeDescriptionSetting}>
-                                Labels: {LinkDescriptionPopup.showDescriptions ? LinkDescriptionPopup.showDescriptions : "ON"}
-                            </span>
+        return (
+            <div className={`collectionLinearView-outer ${this.layoutDoc.linearViewSubMenu}`} style={{ backgroundColor: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : 'transparent' }}>
+                <div className="collectionLinearView" ref={this.createDashEventsTarget} onContextMenu={this.myContextMenu}>
+                    {!expandable ? null : (
+                        <Tooltip
+                            title={
+                                <>
+                                    <div className="dash-tooltip">{BoolCast(this.props.Document.linearViewIsExpanded) ? 'Close' : 'Open'}</div>
+                                </>
+                            }
+                            placement="top">
+                            {menuOpener}
                         </Tooltip>
+                    )}
+                    <input
+                        id={`${guid}`}
+                        type="checkbox"
+                        checked={BoolCast(this.props.Document.linearViewIsExpanded)}
+                        ref={this.addMenuToggle}
+                        onChange={action(() => (this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked))}
+                    />
 
-                        <Tooltip title={<><div className="dash-tooltip">Exit linking mode</div></>} placement="top">
-                            <span className="bottomPopup-exit" onClick={this.exitLongLinks}>
-                                Stop
+                    <div
+                        className="collectionLinearView-content"
+                        style={{
+                            height: this.dimension(),
+                            flexDirection: flexDir,
+                            gap: flexGap,
+                        }}>
+                        {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
+                    </div>
+                    {!DocumentLinksButton.StartLink || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? null : (
+                        <span className="bottomPopup-background" style={{ pointerEvents: 'all' }} onPointerDown={e => e.stopPropagation()}>
+                            <span className="bottomPopup-text">
+                                Creating link from:{' '}
+                                <b>
+                                    {(DocumentLinksButton.AnnotationId ? 'Annotation in ' : ' ') +
+                                        (StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...')}
+                                </b>
                             </span>
-                        </Tooltip>
-                    </span>}
-                {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? (null) :
-                    <span className="bottomPopup-background">
-                        <span className="bottomPopup-text">
-                            Currently playing:
-                            {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) =>
-                                <span className="audio-title" onPointerDown={() => DocumentManager.Instance.jumpToDocument(clip, true, undefined, [])}>
-                                    {clip.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? "" : ",")}
-                                </span>)}
-                        </span>
-                    </span>}
 
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">{'Toggle description pop-up'} </div>
+                                    </>
+                                }
+                                placement="top">
+                                <span className="bottomPopup-descriptions" onClick={this.changeDescriptionSetting}>
+                                    Labels: {LinkDescriptionPopup.showDescriptions ? LinkDescriptionPopup.showDescriptions : 'ON'}
+                                </span>
+                            </Tooltip>
+
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">Exit linking mode</div>
+                                    </>
+                                }
+                                placement="top">
+                                <span className="bottomPopup-exit" onClick={this.exitLongLinks}>
+                                    Stop
+                                </span>
+                            </Tooltip>
+                        </span>
+                    )}
+                    {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? null : (
+                        <span className="bottomPopup-background">
+                            <span className="bottomPopup-text">
+                                Currently playing:
+                                {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) => (
+                                    <span className="audio-title" onPointerDown={() => DocumentManager.Instance.jumpToDocument(clip, true, undefined, [])}>
+                                        {clip.title + (i === CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? '' : ',')}
+                                    </span>
+                                ))}
+                            </span>
+                        </span>
+                    )}
+                </div>
             </div>
-        </div>;
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx
index 1414bfdf7..ba301962b 100644
--- a/src/client/views/linking/LinkEditor.tsx
+++ b/src/client/views/linking/LinkEditor.tsx
@@ -1,15 +1,14 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Tooltip } from "@material-ui/core";
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, NumListCast, StrListCast, Field } from "../../../fields/Doc";
-import { DateCast, StrCast, Cast } from "../../../fields/Types";
-import { LinkManager } from "../../util/LinkManager";
-import { undoBatch } from "../../util/UndoManager";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, NumListCast, StrListCast, Field } from '../../../fields/Doc';
+import { DateCast, StrCast, Cast } from '../../../fields/Types';
+import { LinkManager } from '../../util/LinkManager';
+import { undoBatch } from '../../util/UndoManager';
 import './LinkEditor.scss';
-import { LinkRelationshipSearch } from "./LinkRelationshipSearch";
-import React = require("react");
-
+import { LinkRelationshipSearch } from './LinkRelationshipSearch';
+import React = require('react');
 
 interface LinkEditorProps {
     sourceDoc: Doc;
@@ -19,16 +18,20 @@ interface LinkEditorProps {
 }
 @observer
 export class LinkEditor extends React.Component<LinkEditorProps> {
-
     @observable description = Field.toString(LinkManager.currentLink?.description as any as Field);
     @observable relationship = StrCast(LinkManager.currentLink?.linkRelationship);
     @observable zoomFollow = StrCast(this.props.sourceDoc.followLinkZoom);
     @observable openDropdown: boolean = false;
     @observable showInfo: boolean = false;
-    @computed get infoIcon() { if (this.showInfo) { return "chevron-up"; } return "chevron-down"; }
-    @observable private buttonColor: string = "";
-    @observable private relationshipButtonColor: string = "";
-    @observable private relationshipSearchVisibility: string = "none";
+    @computed get infoIcon() {
+        if (this.showInfo) {
+            return 'chevron-up';
+        }
+        return 'chevron-down';
+    }
+    @observable private buttonColor: string = '';
+    @observable private relationshipButtonColor: string = '';
+    @observable private relationshipSearchVisibility: string = 'none';
     @observable private searchIsActive: boolean = false;
 
     //@observable description = this.props.linkDoc.description ? StrCast(this.props.linkDoc.description) : "DESCRIPTION";
@@ -37,7 +40,7 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
     deleteLink = (): void => {
         LinkManager.Instance.deleteLink(this.props.linkDoc);
         this.props.showLinks();
-    }
+    };
 
     @undoBatch
     setRelationshipValue = action((value: string) => {
@@ -49,11 +52,11 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
             const linkRelationshipSizes = NumListCast(Doc.UserDoc().linkRelationshipSizes);
             const linkColorList = StrListCast(Doc.UserDoc().linkColorList);
 
-            // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color 
+            // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color
             if (!linkRelationshipList?.includes(value)) {
                 linkRelationshipList.push(value);
                 linkRelationshipSizes.push(1);
-                const randColor = "rgb(" + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + ")";
+                const randColor = 'rgb(' + Math.floor(Math.random() * 255) + ',' + Math.floor(Math.random() * 255) + ',' + Math.floor(Math.random() * 255) + ')';
                 linkColorList.push(randColor);
                 // if the relationship is already in the list AND the new rel is different from the prev rel, update the rel sizes
             } else if (linkRelationshipList && value !== prevRelationship) {
@@ -61,20 +64,22 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
                 //increment size of new relationship size
                 if (index !== -1 && index < linkRelationshipSizes.length) {
                     const pvalue = linkRelationshipSizes[index];
-                    linkRelationshipSizes[index] = (pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue + 1);
+                    linkRelationshipSizes[index] = pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue + 1;
                 }
                 //decrement the size of the previous relationship if it already exists (i.e. not default 'link' relationship upon link creation)
                 if (linkRelationshipList.includes(prevRelationship)) {
                     const pindex = linkRelationshipList.indexOf(prevRelationship);
                     if (pindex !== -1 && pindex < linkRelationshipSizes.length) {
                         const pvalue = linkRelationshipSizes[pindex];
-                        linkRelationshipSizes[pindex] = Math.max(0, (pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue - 1));
+                        linkRelationshipSizes[pindex] = Math.max(0, pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue - 1);
                     }
                 }
-
             }
-            this.relationshipButtonColor = "rgb(62, 133, 55)";
-            setTimeout(action(() => this.relationshipButtonColor = ""), 750);
+            this.relationshipButtonColor = 'rgb(62, 133, 55)';
+            setTimeout(
+                action(() => (this.relationshipButtonColor = '')),
+                750
+            );
             return true;
         }
     });
@@ -89,144 +94,143 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
         if (linkRelationshipList) {
             return linkRelationshipList.filter(rel => rel.includes(query));
         }
-    }
+    };
 
     /**
      * toggles visibility of the relationship search results when the input field is focused on
      */
     @action
     toggleRelationshipResults = () => {
-        this.relationshipSearchVisibility = this.relationshipSearchVisibility === "none" ? "block" : "none";
-    }
+        this.relationshipSearchVisibility = this.relationshipSearchVisibility === 'none' ? 'block' : 'none';
+    };
 
     @undoBatch
     setDescripValue = action((value: string) => {
         if (LinkManager.currentLink) {
             Doc.GetProto(LinkManager.currentLink).description = value;
-            this.buttonColor = "rgb(62, 133, 55)";
-            setTimeout(action(() => this.buttonColor = ""), 750);
+            this.buttonColor = 'rgb(62, 133, 55)';
+            setTimeout(
+                action(() => (this.buttonColor = '')),
+                750
+            );
             return true;
         }
     });
 
     onDescriptionKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
-        if (e.key === "Enter") {
+        if (e.key === 'Enter') {
             this.setDescripValue(this.description);
             document.getElementById('input')?.blur();
         }
         e.stopPropagation();
-    }
+    };
 
     onRelationshipKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
-        if (e.key === "Enter") {
+        if (e.key === 'Enter') {
             this.setRelationshipValue(this.relationship);
             document.getElementById('input')?.blur();
         }
         e.stopPropagation();
-    }
+    };
 
     onDescriptionDown = () => this.setDescripValue(this.description);
     onRelationshipDown = () => this.setRelationshipValue(this.relationship);
 
     onBlur = () => {
-        //only hide the search results if the user clicks out of the input AND not on any of the search results 
+        //only hide the search results if the user clicks out of the input AND not on any of the search results
         // i.e. if search is not active
         if (!this.searchIsActive) {
             this.toggleRelationshipResults();
         }
-    }
+    };
     onFocus = () => {
         this.toggleRelationshipResults();
-    }
+    };
     toggleSearchIsActive = () => {
         this.searchIsActive = !this.searchIsActive;
-    }
+    };
 
     @action
-    handleDescriptionChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.description = e.target.value; }
+    handleDescriptionChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this.description = e.target.value;
+    };
     @action
-    handleRelationshipChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.relationship = e.target.value; }
+    handleRelationshipChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this.relationship = e.target.value;
+    };
     @action
-    handleZoomFollowChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.props.sourceDoc.followLinkZoom = e.target.checked; }
+    handleZoomFollowChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+        this.props.sourceDoc.followLinkZoom = e.target.checked;
+    };
     @action
     handleRelationshipSearchChange = (result: string) => {
         this.setRelationshipValue(result);
         this.toggleRelationshipResults();
         this.relationship = result;
-    }
+    };
     @computed
     get editRelationship() {
         //NOTE: confusingly, the classnames for the following relationship JSX elements are the same as the for the description elements for shared CSS
-        return <div className="linkEditor-description">
-            <div className="linkEditor-description-label">Link Relationship:</div>
-            <div className="linkEditor-description-input">
-                <div className="linkEditor-description-editing">
-                    <input
-                        style={{ width: "100%" }}
-                        id="input"
-                        value={this.relationship}
-                        autoComplete={"off"}
-                        placeholder={"Enter link relationship"}
-                        onKeyDown={this.onRelationshipKey}
-                        onChange={this.handleRelationshipChange}
-                        onFocus={this.onFocus}
-                        onBlur={this.onBlur}
-                    ></input>
-                    <LinkRelationshipSearch
-                        results={this.getRelationshipResults()}
-                        display={this.relationshipSearchVisibility}
-                        handleRelationshipSearchChange={this.handleRelationshipSearchChange}
-                        toggleSearch={this.toggleSearchIsActive}
-                    />
+        return (
+            <div className="linkEditor-description">
+                <div className="linkEditor-description-label">Link Relationship:</div>
+                <div className="linkEditor-description-input">
+                    <div className="linkEditor-description-editing">
+                        <input
+                            style={{ width: '100%' }}
+                            id="input"
+                            value={this.relationship}
+                            autoComplete={'off'}
+                            placeholder={'Enter link relationship'}
+                            onKeyDown={this.onRelationshipKey}
+                            onChange={this.handleRelationshipChange}
+                            onFocus={this.onFocus}
+                            onBlur={this.onBlur}></input>
+                        <LinkRelationshipSearch results={this.getRelationshipResults()} display={this.relationshipSearchVisibility} handleRelationshipSearchChange={this.handleRelationshipSearchChange} toggleSearch={this.toggleSearchIsActive} />
+                    </div>
+                    <div className="linkEditor-description-add-button" style={{ background: this.relationshipButtonColor }} onPointerDown={this.onRelationshipDown}>
+                        Set
+                    </div>
                 </div>
-                <div className="linkEditor-description-add-button"
-                    style={{ background: this.relationshipButtonColor }}
-                    onPointerDown={this.onRelationshipDown}>Set</div>
             </div>
-        </div>;
+        );
     }
     @computed
     get editZoomFollow() {
         //NOTE: confusingly, the classnames for the following relationship JSX elements are the same as the for the description elements for shared CSS
-        return <div className="linkEditor-zoomFollow">
-            <div className="linkEditor-zoomFollow-label">Zoom To Link Target:</div>
-            <div className="linkEditor-zoomFollow-input">
-                <div className="linkEditor-zoomFollow-editing">
-                    <input
-                        style={{ width: "100%" }}
-                        type="checkbox"
-                        value={this.zoomFollow}
-                        onChange={this.handleZoomFollowChange} />
+        return (
+            <div className="linkEditor-zoomFollow">
+                <div className="linkEditor-zoomFollow-label">Zoom To Link Target:</div>
+                <div className="linkEditor-zoomFollow-input">
+                    <div className="linkEditor-zoomFollow-editing">
+                        <input style={{ width: '100%' }} type="checkbox" value={this.zoomFollow} onChange={this.handleZoomFollowChange} />
+                    </div>
                 </div>
             </div>
-        </div >;
+        );
     }
 
     @computed
     get editDescription() {
-        return <div className="linkEditor-description">
-            <div className="linkEditor-description-label">Link Description:</div>
-            <div className="linkEditor-description-input">
-                <div className="linkEditor-description-editing">
-                    <input
-                        style={{ width: "100%" }}
-                        autoComplete={"off"}
-                        id="input"
-                        value={this.description}
-                        placeholder={"Enter link description"}
-                        onKeyDown={this.onDescriptionKey}
-                        onChange={this.handleDescriptionChange}
-                    ></input>
+        return (
+            <div className="linkEditor-description">
+                <div className="linkEditor-description-label">Link Description:</div>
+                <div className="linkEditor-description-input">
+                    <div className="linkEditor-description-editing">
+                        <input style={{ width: '100%' }} autoComplete={'off'} id="input" value={this.description} placeholder={'Enter link description'} onKeyDown={this.onDescriptionKey} onChange={this.handleDescriptionChange}></input>
+                    </div>
+                    <div className="linkEditor-description-add-button" style={{ background: this.buttonColor }} onPointerDown={this.onDescriptionDown}>
+                        Set
+                    </div>
                 </div>
-                <div className="linkEditor-description-add-button"
-                    style={{ background: this.buttonColor }}
-                    onPointerDown={this.onDescriptionDown}>Set</div>
             </div>
-        </div>;
+        );
     }
 
     @action
-    changeDropdown = () => { this.openDropdown = !this.openDropdown; }
+    changeDropdown = () => {
+        this.openDropdown = !this.openDropdown;
+    };
 
     @undoBatch
     changeFollowBehavior = action((follow: string) => {
@@ -236,94 +240,114 @@ export class LinkEditor extends React.Component<LinkEditorProps> {
 
     @computed
     get followingDropdown() {
-        return <div className="linkEditor-followingDropdown">
-            <div className="linkEditor-followingDropdown-label">Follow Behavior:</div>
-            <div className="linkEditor-followingDropdown-dropdown">
-                <div className="linkEditor-followingDropdown-header"
-                    onPointerDown={this.changeDropdown}>
-                    {StrCast(this.props.linkDoc.followLinkLocation, "default")}
-                    <FontAwesomeIcon className="linkEditor-followingDropdown-icon"
-                        icon={this.openDropdown ? "chevron-up" : "chevron-down"}
-                        size={"lg"} />
-                </div>
-                <div className="linkEditor-followingDropdown-optionsList"
-                    style={{ display: this.openDropdown ? "" : "none" }}>
-                    <div className="linkEditor-followingDropdown-option"
-                        onPointerDown={() => this.changeFollowBehavior("default")}>
-                        Default
+        return (
+            <div className="linkEditor-followingDropdown">
+                <div className="linkEditor-followingDropdown-label">Follow Behavior:</div>
+                <div className="linkEditor-followingDropdown-dropdown">
+                    <div className="linkEditor-followingDropdown-header" onPointerDown={this.changeDropdown}>
+                        {StrCast(this.props.linkDoc.followLinkLocation, 'default')}
+                        <FontAwesomeIcon className="linkEditor-followingDropdown-icon" icon={this.openDropdown ? 'chevron-up' : 'chevron-down'} size={'lg'} />
                     </div>
-                    <div className="linkEditor-followingDropdown-option"
-                        onPointerDown={() => this.changeFollowBehavior("add:left")}>
-                        Always open in new left pane
-                    </div>
-                    <div className="linkEditor-followingDropdown-option"
-                        onPointerDown={() => this.changeFollowBehavior("add:right")}>
-                        Always open in new right pane
-                    </div>
-                    <div className="linkEditor-followingDropdown-option"
-                        onPointerDown={() => this.changeFollowBehavior("replace:right")}>
-                        Always replace right tab
-                    </div>
-                    <div className="linkEditor-followingDropdown-option"
-                        onPointerDown={() => this.changeFollowBehavior("replace:left")}>
-                        Always replace left tab
-                    </div>
-                    <div className="linkEditor-followingDropdown-option"
-                        onPointerDown={() => this.changeFollowBehavior("fullScreen")}>
-                        Always open full screen
-                    </div>
-                    <div className="linkEditor-followingDropdown-option"
-                        onPointerDown={() => this.changeFollowBehavior("add")}>
-                        Always open in a new tab
-                    </div>
-                    <div className="linkEditor-followingDropdown-option"
-                        onPointerDown={() => this.changeFollowBehavior("replace")}>
-                        Replace Tab
-                    </div>
-                    {this.props.linkDoc.linksToAnnotation ?
-                        <div className="linkEditor-followingDropdown-option"
-                            onPointerDown={() => this.changeFollowBehavior("openExternal")}>
-                            Always open in external page
+                    <div className="linkEditor-followingDropdown-optionsList" style={{ display: this.openDropdown ? '' : 'none' }}>
+                        <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('default')}>
+                            Default
+                        </div>
+                        <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('add:left')}>
+                            Always open in new left pane
+                        </div>
+                        <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('add:right')}>
+                            Always open in new right pane
+                        </div>
+                        <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('replace:right')}>
+                            Always replace right tab
+                        </div>
+                        <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('replace:left')}>
+                            Always replace left tab
                         </div>
-                        : null}
+                        <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('fullScreen')}>
+                            Always open full screen
+                        </div>
+                        <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('add')}>
+                            Always open in a new tab
+                        </div>
+                        <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('replace')}>
+                            Replace Tab
+                        </div>
+                        {this.props.linkDoc.linksToAnnotation ? (
+                            <div className="linkEditor-followingDropdown-option" onPointerDown={() => this.changeFollowBehavior('openExternal')}>
+                                Always open in external page
+                            </div>
+                        ) : null}
+                    </div>
                 </div>
             </div>
-        </div>;
+        );
     }
 
     @action
-    changeInfo = () => { this.showInfo = !this.showInfo; }
+    changeInfo = () => {
+        this.showInfo = !this.showInfo;
+    };
 
     render() {
         const destination = LinkManager.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc);
 
-        return !destination ? (null) : (
+        return !destination ? null : (
             <div className="linkEditor" tabIndex={0} onKeyDown={e => e.stopPropagation()}>
                 <div className="linkEditor-info">
-                    <Tooltip title={<><div className="dash-tooltip">Return to link menu</div></>} placement="top">
-                        <button className="linkEditor-button-back"
-                            style={{ display: this.props.hideback ? "none" : "" }}
-                            onClick={this.props.showLinks}>
-                            <FontAwesomeIcon icon="arrow-left" size="sm" /> </button>
+                    <Tooltip
+                        title={
+                            <>
+                                <div className="dash-tooltip">Return to link menu</div>
+                            </>
+                        }
+                        placement="top">
+                        <button className="linkEditor-button-back" style={{ display: this.props.hideback ? 'none' : '' }} onClick={this.props.showLinks}>
+                            <FontAwesomeIcon icon="arrow-left" size="sm" />{' '}
+                        </button>
                     </Tooltip>
-                    <p className="linkEditor-linkedTo">Editing Link to: <b>{
-                        destination.proto?.title ?? destination.title ?? "untitled"}</b></p>
-                    <Tooltip title={<><div className="dash-tooltip">Show more link information</div></>} placement="top">
-                        <div className="linkEditor-downArrow"><FontAwesomeIcon className="button" icon={this.infoIcon} size="lg" onPointerDown={this.changeInfo} /></div>
+                    <p className="linkEditor-linkedTo">
+                        Editing Link to: <b>{StrCast(destination.proto?.title, StrCast(destination.title, 'untitled'))}</b>
+                    </p>
+                    <Tooltip
+                        title={
+                            <>
+                                <div className="dash-tooltip">Show more link information</div>
+                            </>
+                        }
+                        placement="top">
+                        <div className="linkEditor-downArrow">
+                            <FontAwesomeIcon className="button" icon={this.infoIcon} size="lg" onPointerDown={this.changeInfo} />
+                        </div>
                     </Tooltip>
                 </div>
-                {this.showInfo ? <div className="linkEditor-moreInfo">
-                    <div>{this.props.linkDoc.author ? <div> <b>Author:</b> {this.props.linkDoc.author}</div> : null}</div>
-                    <div>{this.props.linkDoc.creationDate ? <div> <b>Creation Date:</b>
-                        {DateCast(this.props.linkDoc.creationDate).toString()}</div> : null}</div>
-                </div> : null}
+                {this.showInfo ? (
+                    <div className="linkEditor-moreInfo">
+                        <div>
+                            {this.props.linkDoc.author ? (
+                                <div>
+                                    {' '}
+                                    <b>Author:</b> {StrCast(this.props.linkDoc.author)}
+                                </div>
+                            ) : null}
+                        </div>
+                        <div>
+                            {this.props.linkDoc.creationDate ? (
+                                <div>
+                                    {' '}
+                                    <b>Creation Date:</b>
+                                    {DateCast(this.props.linkDoc.creationDate).toString()}
+                                </div>
+                            ) : null}
+                        </div>
+                    </div>
+                ) : null}
 
                 {this.editDescription}
                 {this.editRelationship}
                 {this.editZoomFollow}
                 {this.followingDropdown}
             </div>
-
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index a75e7a0c4..1e7f4f10b 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -2,7 +2,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
 import { action, observable } from 'mobx';
-import { observer } from "mobx-react";
+import { observer } from 'mobx-react';
 import { Doc, DocListCast } from '../../../fields/Doc';
 import { Cast, StrCast } from '../../../fields/Types';
 import { WebField } from '../../../fields/URLField';
@@ -16,8 +16,7 @@ import { undoBatch } from '../../util/UndoManager';
 import { DocumentView } from '../nodes/DocumentView';
 import { LinkDocPreview } from '../nodes/LinkDocPreview';
 import './LinkMenuItem.scss';
-import React = require("react");
-
+import React = require('react');
 
 interface LinkMenuItemProps {
     groupType: string;
@@ -50,22 +49,21 @@ export async function StartLinkTargetsDrag(dragEle: HTMLElement, docView: Docume
         };
         const containingView = docView.props.ContainingCollectionView;
         const finishDrag = (e: DragManager.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));
-                    }
-                    return droppedDocs;
-                }, [] as Doc[]));
+            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));
+                }
+                return droppedDocs;
+            }, [] as Doc[]));
 
         DragManager.StartDrag([dragEle], dragData, downX, downY, undefined, finishDrag);
     }
 }
 
-
 @observer
 export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
     private _drag = React.createRef<HTMLDivElement>();
@@ -74,20 +72,31 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
     _buttonRef = React.createRef<HTMLDivElement>();
 
     @observable private _showMore: boolean = false;
-    @action toggleShowMore(e: React.PointerEvent) { e.stopPropagation(); this._showMore = !this._showMore; }
+    @action toggleShowMore(e: React.PointerEvent) {
+        e.stopPropagation();
+        this._showMore = !this._showMore;
+    }
 
     onEdit = (e: React.PointerEvent): void => {
         LinkManager.currentLink = this.props.linkDoc;
-        setupMoveUpEvents(this, e, e => {
-            const dragData = new DragManager.DocumentDragData([this.props.linkDoc], "alias");
-            dragData.removeDropProperties = ["hidden"];
-            DragManager.StartDocumentDrag([this._editRef.current!], dragData, e.x, e.y);
-            return true;
-        }, emptyFunction, () => this.props.showEditor(this.props.linkDoc));
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            e => {
+                const dragData = new DragManager.DocumentDragData([this.props.linkDoc], 'alias');
+                dragData.removeDropProperties = ['hidden'];
+                DragManager.StartDocumentDrag([this._editRef.current!], dragData, e.x, e.y);
+                return true;
+            },
+            emptyFunction,
+            () => this.props.showEditor(this.props.linkDoc)
+        );
+    };
 
     onLinkButtonDown = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(this, e,
+        setupMoveUpEvents(
+            this,
+            e,
             e => {
                 const eleClone: any = this._drag.current!.cloneNode(true);
                 eleClone.style.transform = `translate(${e.x}px, ${e.y}px)`;
@@ -99,109 +108,170 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
             () => {
                 this.props.clearLinkEditor();
                 if (this.props.itemHandler) {
-
                     this.props.itemHandler?.(this.props.linkDoc);
                 } else {
-                    const focusDoc = Cast(this.props.linkDoc.anchor1, Doc, null)?.annotationOn === this.props.sourceDoc ? Cast(this.props.linkDoc.anchor1, Doc, null) :
-                        Cast(this.props.linkDoc.anchor2, Doc, null)?.annotationOn === this.props.sourceDoc ? Cast(this.props.linkDoc.anchor12, Doc, null) : undefined;
+                    const focusDoc =
+                        Cast(this.props.linkDoc.anchor1, Doc, null)?.annotationOn === this.props.sourceDoc
+                            ? Cast(this.props.linkDoc.anchor1, Doc, null)
+                            : Cast(this.props.linkDoc.anchor2, Doc, null)?.annotationOn === this.props.sourceDoc
+                            ? Cast(this.props.linkDoc.anchor12, Doc, null)
+                            : undefined;
 
                     if (focusDoc) this.props.docView.ComponentView?.scrollFocus?.(focusDoc, true);
                     LinkManager.FollowLink(this.props.linkDoc, this.props.sourceDoc, this.props.docView.props, false);
                 }
-            });
-    }
+            }
+        );
+    };
 
     deleteLink = (e: React.PointerEvent): void => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => {
-            this.props.linkDoc.linksToAnnotation && Hypothesis.deleteLink(this.props.linkDoc, this.props.sourceDoc, this.props.destinationDoc);
-            LinkManager.Instance.deleteLink(this.props.linkDoc);
-        })));
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            returnFalse,
+            emptyFunction,
+            undoBatch(
+                action(() => {
+                    this.props.linkDoc.linksToAnnotation && Hypothesis.deleteLink(this.props.linkDoc, this.props.sourceDoc, this.props.destinationDoc);
+                    LinkManager.Instance.deleteLink(this.props.linkDoc);
+                })
+            )
+        );
+    };
 
     autoMove = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.props.linkDoc.linkAutoMove = !this.props.linkDoc.linkAutoMove)));
-    }
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => (this.props.linkDoc.linkAutoMove = !this.props.linkDoc.linkAutoMove))));
+    };
 
     showLink = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.props.linkDoc.linkDisplay = !this.props.linkDoc.linkDisplay)));
-    }
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => (this.props.linkDoc.linkDisplay = !this.props.linkDoc.linkDisplay))));
+    };
 
     showAnchor = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.props.linkDoc.hidden = !this.props.linkDoc.hidden)));
-    }
+        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => (this.props.linkDoc.hidden = !this.props.linkDoc.hidden))));
+    };
 
     render() {
         const destinationIcon = Doc.toIcon(this.props.destinationDoc) as any as IconProp;
 
-        const title = StrCast(this.props.destinationDoc.title).length > 18 ?
-            StrCast(this.props.destinationDoc.title).substr(0, 14) + "..." : this.props.destinationDoc.title;
+        const title = StrCast(this.props.destinationDoc.title).length > 18 ? StrCast(this.props.destinationDoc.title).substr(0, 14) + '...' : this.props.destinationDoc.title;
 
         //  ...
         // from anika to bob: here's where the text that is specifically linked would show up (linkDoc.storedText)
         // ...
-        const source = this.props.sourceDoc.type === DocumentType.RTF ? this.props.linkDoc.storedText ?
-            StrCast(this.props.linkDoc.storedText).length > 17 ?
-                StrCast(this.props.linkDoc.storedText).substr(0, 18)
-                : this.props.linkDoc.storedText : undefined : undefined;
+        const source =
+            this.props.sourceDoc.type === DocumentType.RTF
+                ? this.props.linkDoc.storedText
+                    ? StrCast(this.props.linkDoc.storedText).length > 17
+                        ? StrCast(this.props.linkDoc.storedText).substr(0, 18)
+                        : this.props.linkDoc.storedText
+                    : undefined
+                : undefined;
 
         return (
             <div className="linkMenu-item">
-                <div className={"linkMenu-item-content expand-two"}>
-
-                    <div ref={this._drag} className="linkMenu-name" //title="drag to view target. click to customize."
+                <div className={'linkMenu-item-content expand-two'}>
+                    <div
+                        ref={this._drag}
+                        className="linkMenu-name" //title="drag to view target. click to customize."
                         onPointerLeave={LinkDocPreview.Clear}
-                        onPointerEnter={e => this.props.linkDoc && LinkDocPreview.SetLinkInfo({
-                            docProps: this.props.docView.props,
-                            linkSrc: this.props.sourceDoc,
-                            linkDoc: this.props.linkDoc,
-                            showHeader: false,
-                            location: [e.clientX, e.clientY + 20]
-                        })}
+                        onPointerEnter={e =>
+                            this.props.linkDoc &&
+                            LinkDocPreview.SetLinkInfo({
+                                docProps: this.props.docView.props,
+                                linkSrc: this.props.sourceDoc,
+                                linkDoc: this.props.linkDoc,
+                                showHeader: false,
+                                location: [e.clientX, e.clientY + 20],
+                            })
+                        }
                         onPointerDown={this.onLinkButtonDown}>
-
                         <div className="linkMenu-text">
-                            {source ? <p className="linkMenu-source-title"> <b>Source: {source}</b></p> : null}
+                            {source ? (
+                                <p className="linkMenu-source-title">
+                                    {' '}
+                                    <b>Source: {StrCast(source)}</b>
+                                </p>
+                            ) : null}
                             <div className="linkMenu-title-wrapper">
-                                <div className="destination-icon-wrapper" >
+                                <div className="destination-icon-wrapper">
                                     <FontAwesomeIcon className="destination-icon" icon={destinationIcon} size="sm" />
                                 </div>
                                 <p className="linkMenu-destination-title">
-                                    {this.props.linkDoc.linksToAnnotation && Cast(this.props.destinationDoc.data, WebField)?.url.href === this.props.linkDoc.annotationUri ? "Annotation in" : ""} {title}
+                                    {this.props.linkDoc.linksToAnnotation && Cast(this.props.destinationDoc.data, WebField)?.url.href === this.props.linkDoc.annotationUri ? 'Annotation in' : ''} {StrCast(title)}
                                 </p>
                             </div>
-                            {!this.props.linkDoc.description ? (null) : <p className="linkMenu-description">{StrCast(this.props.linkDoc.description)}</p>}
+                            {!this.props.linkDoc.description ? null : <p className="linkMenu-description">{StrCast(this.props.linkDoc.description)}</p>}
                         </div>
 
-                        <div className="linkMenu-item-buttons" ref={this._buttonRef} >
-
-                            <Tooltip title={<><div className="dash-tooltip">{this.props.linkDoc.hidden ? "Show Link Anchor" : "Hide Link Anchor"}</div></>}>
-                                <div className="button" ref={this._editRef} style={{ background: this.props.linkDoc.hidden ? "" : "#4476f7" /* $medium-blue */ }} onPointerDown={this.showAnchor} onClick={e => e.stopPropagation()}>
-                                    <FontAwesomeIcon className="fa-icon" icon={"eye"} size="sm" /></div>
+                        <div className="linkMenu-item-buttons" ref={this._buttonRef}>
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">{this.props.linkDoc.hidden ? 'Show Link Anchor' : 'Hide Link Anchor'}</div>
+                                    </>
+                                }>
+                                <div className="button" ref={this._editRef} style={{ background: this.props.linkDoc.hidden ? '' : '#4476f7' /* $medium-blue */ }} onPointerDown={this.showAnchor} onClick={e => e.stopPropagation()}>
+                                    <FontAwesomeIcon className="fa-icon" icon={'eye'} size="sm" />
+                                </div>
                             </Tooltip>
 
-                            <Tooltip title={<><div className="dash-tooltip">{this.props.linkDoc.linkDisplay ? "Hide Link Line" : "Show Link Line"}</div></>}>
-                                <div className="button" ref={this._editRef} style={{ background: this.props.linkDoc.hidden ? "gray" : this.props.linkDoc.linkDisplay ? "#4476f7"/* $medium-blue */ : "" }} onPointerDown={this.showLink} onClick={e => e.stopPropagation()}>
-                                    <FontAwesomeIcon className="fa-icon" icon={"project-diagram"} size="sm" /></div>
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">{this.props.linkDoc.linkDisplay ? 'Hide Link Line' : 'Show Link Line'}</div>
+                                    </>
+                                }>
+                                <div
+                                    className="button"
+                                    ref={this._editRef}
+                                    style={{ background: this.props.linkDoc.hidden ? 'gray' : this.props.linkDoc.linkDisplay ? '#4476f7' /* $medium-blue */ : '' }}
+                                    onPointerDown={this.showLink}
+                                    onClick={e => e.stopPropagation()}>
+                                    <FontAwesomeIcon className="fa-icon" icon={'project-diagram'} size="sm" />
+                                </div>
                             </Tooltip>
 
-                            <Tooltip title={<><div className="dash-tooltip">{this.props.linkDoc.linkAutoMove ? "Click to freeze link anchor position" : "Click to auto move link anchor"}</div></>}>
-                                <div className="button" ref={this._editRef} style={{ background: this.props.linkDoc.hidden ? "gray" : !this.props.linkDoc.linkAutoMove ? "" : "#4476f7" /* $medium-blue */ }} onPointerDown={this.autoMove} onClick={e => e.stopPropagation()}>
-                                    <FontAwesomeIcon className="fa-icon" icon={"play"} size="sm" /></div>
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">{this.props.linkDoc.linkAutoMove ? 'Click to freeze link anchor position' : 'Click to auto move link anchor'}</div>
+                                    </>
+                                }>
+                                <div
+                                    className="button"
+                                    ref={this._editRef}
+                                    style={{ background: this.props.linkDoc.hidden ? 'gray' : !this.props.linkDoc.linkAutoMove ? '' : '#4476f7' /* $medium-blue */ }}
+                                    onPointerDown={this.autoMove}
+                                    onClick={e => e.stopPropagation()}>
+                                    <FontAwesomeIcon className="fa-icon" icon={'play'} size="sm" />
+                                </div>
                             </Tooltip>
 
-                            <Tooltip title={<><div className="dash-tooltip">Edit Link</div></>}>
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">Edit Link</div>
+                                    </>
+                                }>
                                 <div className="button" ref={this._editRef} onPointerDown={this.onEdit} onClick={e => e.stopPropagation()}>
-                                    <FontAwesomeIcon className="fa-icon" icon="edit" size="sm" /></div>
+                                    <FontAwesomeIcon className="fa-icon" icon="edit" size="sm" />
+                                </div>
                             </Tooltip>
-                            <Tooltip title={<><div className="dash-tooltip">Delete Link</div></>}>
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">Delete Link</div>
+                                    </>
+                                }>
                                 <div className="button" onPointerDown={this.deleteLink} onClick={e => e.stopPropagation()}>
-                                    <FontAwesomeIcon className="fa-icon" icon="trash" size="sm" /></div>
+                                    <FontAwesomeIcon className="fa-icon" icon="trash" size="sm" />
+                                </div>
                             </Tooltip>
                         </div>
                     </div>
                 </div>
-
-            </div >
+            </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx
index 96ac3e332..f1d8123da 100644
--- a/src/client/views/nodes/DocumentContentsView.tsx
+++ b/src/client/views/nodes/DocumentContentsView.tsx
@@ -1,48 +1,48 @@
-import { computed } from "mobx";
-import { observer } from "mobx-react";
-import { AclPrivate, Doc, Opt } from "../../../fields/Doc";
-import { ScriptField } from "../../../fields/ScriptField";
-import { Cast, StrCast } from "../../../fields/Types";
-import { GetEffectiveAcl, TraceMobx } from "../../../fields/util";
-import { emptyPath, OmitKeys, Without } from "../../../Utils";
-import { DirectoryImportBox } from "../../util/Import & Export/DirectoryImportBox";
-import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
-import { CollectionSchemaView } from "../collections/collectionSchema/CollectionSchemaView";
-import { CollectionView } from "../collections/CollectionView";
-import { InkingStroke } from "../InkingStroke";
-import { PresElementBox } from "../nodes/trails/PresElementBox";
-import { SearchBox } from "../search/SearchBox";
-import { DashWebRTCVideo } from "../webcam/DashWebRTCVideo";
-import { YoutubeBox } from "./../../apis/youtube/YoutubeBox";
-import { AudioBox } from "./AudioBox";
-import { FontIconBox } from "./button/FontIconBox";
-import { ColorBox } from "./ColorBox";
-import { ComparisonBox } from "./ComparisonBox";
-import { DataVizBox } from "./DataVizBox/DataVizBox";
-import { DocumentViewProps } from "./DocumentView";
-import "./DocumentView.scss";
-import { EquationBox } from "./EquationBox";
-import { FieldView, FieldViewProps } from "./FieldView";
-import { FilterBox } from "./FilterBox";
-import { FormattedTextBox, FormattedTextBoxProps } from "./formattedText/FormattedTextBox";
-import { FunctionPlotBox } from "./FunctionPlotBox";
-import { ImageBox } from "./ImageBox";
-import { KeyValueBox } from "./KeyValueBox";
-import { LabelBox } from "./LabelBox";
-import { LinkAnchorBox } from "./LinkAnchorBox";
-import { LinkBox } from "./LinkBox";
-import { MapBox } from "./MapBox/MapBox";
-import { PDFBox } from "./PDFBox";
-import { RecordingBox } from "./RecordingBox";
-import { ScreenshotBox } from "./ScreenshotBox";
-import { ScriptingBox } from "./ScriptingBox";
-import { SliderBox } from "./SliderBox";
-import { PresBox } from "./trails/PresBox";
-import { VideoBox } from "./VideoBox";
-import { WebBox } from "./WebBox";
-import React = require("react");
-import XRegExp = require("xregexp");
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
+import { AclPrivate, Doc, Opt } from '../../../fields/Doc';
+import { ScriptField } from '../../../fields/ScriptField';
+import { Cast, StrCast } from '../../../fields/Types';
+import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
+import { emptyPath, OmitKeys, Without } from '../../../Utils';
+import { DirectoryImportBox } from '../../util/Import & Export/DirectoryImportBox';
+import { CollectionDockingView } from '../collections/CollectionDockingView';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
+import { CollectionSchemaView } from '../collections/collectionSchema/CollectionSchemaView';
+import { CollectionView } from '../collections/CollectionView';
+import { InkingStroke } from '../InkingStroke';
+import { PresElementBox } from '../nodes/trails/PresElementBox';
+import { SearchBox } from '../search/SearchBox';
+import { DashWebRTCVideo } from '../webcam/DashWebRTCVideo';
+import { YoutubeBox } from './../../apis/youtube/YoutubeBox';
+import { AudioBox } from './AudioBox';
+import { FontIconBox } from './button/FontIconBox';
+import { ColorBox } from './ColorBox';
+import { ComparisonBox } from './ComparisonBox';
+import { DataVizBox } from './DataVizBox/DataVizBox';
+import { DocumentViewProps } from './DocumentView';
+import './DocumentView.scss';
+import { EquationBox } from './EquationBox';
+import { FieldView, FieldViewProps } from './FieldView';
+import { FilterBox } from './FilterBox';
+import { FormattedTextBox, FormattedTextBoxProps } from './formattedText/FormattedTextBox';
+import { FunctionPlotBox } from './FunctionPlotBox';
+import { ImageBox } from './ImageBox';
+import { KeyValueBox } from './KeyValueBox';
+import { LabelBox } from './LabelBox';
+import { LinkAnchorBox } from './LinkAnchorBox';
+import { LinkBox } from './LinkBox';
+import { MapBox } from './MapBox/MapBox';
+import { PDFBox } from './PDFBox';
+import { RecordingBox } from './RecordingBox';
+import { ScreenshotBox } from './ScreenshotBox';
+import { ScriptingBox } from './ScriptingBox';
+import { SliderBox } from './SliderBox';
+import { PresBox } from './trails/PresBox';
+import { VideoBox } from './VideoBox';
+import { WebBox } from './WebBox';
+import React = require('react');
+import XRegExp = require('xregexp');
 
 const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this?
 
@@ -60,7 +60,6 @@ class ObserverJsxParser1 extends JsxParser {
 
 const ObserverJsxParser: typeof JsxParser = ObserverJsxParser1 as any;
 
-
 interface HTMLtagProps {
     Document: Doc;
     RootDoc: Doc;
@@ -68,16 +67,17 @@ interface HTMLtagProps {
     onClick?: ScriptField;
     onInput?: ScriptField;
     scaling: number;
+    children?: JSX.Element[];
 }
 
 //"<HTMLdiv borderRadius='100px' onClick={this.bannerColor=this.bannerColor==='red'?'green':'red'} overflow='hidden' position='absolute' width='100%' height='100%' transform='rotate({2*this.x+this.y}deg)'> <ImageBox {...props} fieldKey={'data'}/> <HTMLspan width='200px' top='0' height='35px' textAlign='center' paddingTop='10px' transform='translate(-40px, 45px) rotate(-45deg)' position='absolute' color='{this.bannerColor===`green`?`light`:`dark`}blue' backgroundColor='{this.bannerColor===`green`?`dark`:`light`}blue'> {this.title}</HTMLspan></HTMLdiv>"
-//"<HTMLdiv borderRadius='100px' overflow='hidden' position='absolute' width='100%' height='100%' 
-//          transform='rotate({2*this.x+this.y}deg)' 
+//"<HTMLdiv borderRadius='100px' overflow='hidden' position='absolute' width='100%' height='100%'
+//          transform='rotate({2*this.x+this.y}deg)'
 //          onClick = { this.bannerColor = this.bannerColor === 'red' ? 'green' : 'red' } >
 //    <ImageBox {...props} fieldKey={'data'}/>
-//    <HTMLspan width='200px' top='0' height='35px' textAlign='center' paddingTop='10px' 
-//              transform='translate(-40px, 45px) rotate(-45deg)' position='absolute' 
-//              color='{this.bannerColor===`green`?`light`:`dark`}blue' 
+//    <HTMLspan width='200px' top='0' height='35px' textAlign='center' paddingTop='10px'
+//              transform='translate(-40px, 45px) rotate(-45deg)' position='absolute'
+//              color='{this.bannerColor===`green`?`light`:`dark`}blue'
 //              backgroundColor='{this.bannerColor===`green`?`dark`:`light`}blue'>
 //       {this.title}
 //    </HTMLspan>
@@ -87,45 +87,51 @@ export class HTMLtag extends React.Component<HTMLtagProps> {
     click = (e: React.MouseEvent) => {
         const clickScript = (this.props as any).onClick as Opt<ScriptField>;
         clickScript?.script.run({ this: this.props.Document, self: this.props.RootDoc, scale: this.props.scaling });
-    }
+    };
     onInput = (e: React.FormEvent<HTMLDivElement>) => {
         const onInputScript = (this.props as any).onInput as Opt<ScriptField>;
         onInputScript?.script.run({ this: this.props.Document, self: this.props.RootDoc, value: (e.target as any).textContent });
-    }
+    };
     render() {
         const style: { [key: string]: any } = {};
-        const divKeys = OmitKeys(this.props, ["children", "htmltag", "RootDoc", "scaling", "Document", "key", "onInput", "onClick", "__proto__"]).omit;
-        const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a propery expression string:  { script }  into a value
-            return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.props.RootDoc, this: this.props.Document, scale: this.props.scaling }).result as string || "";
+        const divKeys = OmitKeys(this.props, ['children', 'htmltag', 'RootDoc', 'scaling', 'Document', 'key', 'onInput', 'onClick', '__proto__']).omit;
+        const replacer = (match: any, expr: string, offset: any, string: any) => {
+            // bcz: this executes a script to convert a propery expression string:  { script }  into a value
+            return (ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: 'number' })?.script.run({ self: this.props.RootDoc, this: this.props.Document, scale: this.props.scaling }).result as string) || '';
         };
         Object.keys(divKeys).map((prop: string) => {
             const p = (this.props as any)[prop] as string;
             style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer);
         });
         const Tag = this.props.htmltag as keyof JSX.IntrinsicElements;
-        return <Tag style={style} onClick={this.click} onInput={this.onInput as any}>
-            {this.props.children}
-        </Tag>;
+        return (
+            <Tag style={style} onClick={this.click} onInput={this.onInput as any}>
+                {this.props.children}
+            </Tag>
+        );
     }
 }
 
 @observer
-export class DocumentContentsView extends React.Component<DocumentViewProps & FormattedTextBoxProps & {
-    isSelected: (outsideReaction: boolean) => boolean,
-    select: (ctrl: boolean) => void,
-    scaling?: () => number,
-    setHeight?: (height: number) => void,
-    layoutKey: string,
-}> {
+export class DocumentContentsView extends React.Component<
+    DocumentViewProps &
+        FormattedTextBoxProps & {
+            isSelected: (outsideReaction: boolean) => boolean;
+            select: (ctrl: boolean) => void;
+            scaling?: () => number;
+            setHeight?: (height: number) => void;
+            layoutKey: string;
+        }
+> {
     @computed get layout(): string {
         TraceMobx();
         if (this.props.LayoutTemplateString) return this.props.LayoutTemplateString;
-        if (!this.layoutDoc) return "<p>awaiting layout</p>";
-        if (this.props.layoutKey === "layout_keyValue") return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString("data"));
-        const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layoutKey ? this.props.layoutKey : StrCast(this.layoutDoc.layoutKey, "layout")], "string");
-        if (layout === undefined) return this.props.Document.data ? "<FieldView {...props} fieldKey='data' />" : KeyValueBox.LayoutString(this.layoutDoc.proto ? "proto" : "");
-        if (typeof layout === "string") return layout;
-        return "<p>Loading layout</p>";
+        if (!this.layoutDoc) return '<p>awaiting layout</p>';
+        if (this.props.layoutKey === 'layout_keyValue') return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString('data'));
+        const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layoutKey ? this.props.layoutKey : StrCast(this.layoutDoc.layoutKey, 'layout')], 'string');
+        if (layout === undefined) return this.props.Document.data ? "<FieldView {...props} fieldKey='data' />" : KeyValueBox.LayoutString(this.layoutDoc.proto ? 'proto' : '');
+        if (typeof layout === 'string') return layout;
+        return '<p>Loading layout</p>';
     }
 
     get dataDoc() {
@@ -136,36 +142,39 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
         const params = StrCast(this.props.Document.PARAMS);
         // bcz: replaced this with below : is it correct?  change was made to accommodate passing fieldKey's from a layout script
         // const template: Doc = this.props.LayoutTemplate?.() || Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
-        const template: Doc = this.props.LayoutTemplate?.() || (this.props.LayoutTemplateString && this.props.Document) ||
+        const template: Doc =
+            this.props.LayoutTemplate?.() ||
+            (this.props.LayoutTemplateString && this.props.Document) ||
             (this.props.layoutKey && StrCast(this.props.Document[this.props.layoutKey]) && this.props.Document) ||
             Doc.Layout(this.props.Document, this.props.layoutKey ? Cast(this.props.Document[this.props.layoutKey], Doc, null) : undefined);
-        return Doc.expandTemplateLayout(template, this.props.Document, params ? "(" + params + ")" : this.props.layoutKey);
+        return Doc.expandTemplateLayout(template, this.props.Document, params ? '(' + params + ')' : this.props.layoutKey);
     }
 
     CreateBindings(onClick: Opt<ScriptField>, onInput: Opt<ScriptField>): JsxBindings {
-        const docOnlyProps = [  // these are the properties in  DocumentViewProps that need to be removed to pass on only DocumentSharedViewProps to the FieldViews
-            "hideResizeHandles",
-            "hideTitle",
-            "treeViewDoc",
-            "contentPointerEvents",
-            "radialMenu",
-            "LayoutTemplateString",
-            "LayoutTemplate",
-            "dontCenter",
-            "ContentScaling",
-            "contextMenuItems",
-            "onClick",
-            "onDoubleClick",
-            "onPointerDown",
-            "onPointerUp",
+        const docOnlyProps = [
+            // these are the properties in  DocumentViewProps that need to be removed to pass on only DocumentSharedViewProps to the FieldViews
+            'hideResizeHandles',
+            'hideTitle',
+            'treeViewDoc',
+            'contentPointerEvents',
+            'radialMenu',
+            'LayoutTemplateString',
+            'LayoutTemplate',
+            'dontCenter',
+            'ContentScaling',
+            'contextMenuItems',
+            'onClick',
+            'onDoubleClick',
+            'onPointerDown',
+            'onPointerUp',
         ];
         const list = {
-            ...OmitKeys(this.props, [...docOnlyProps], "").omit,
+            ...OmitKeys(this.props, [...docOnlyProps], '').omit,
             RootDoc: Cast(this.layoutDoc?.rootDocument, Doc, null) || this.layoutDoc,
             Document: this.layoutDoc,
             DataDoc: this.dataDoc,
             onClick: onClick,
-            onInput: onInput
+            onInput: onInput,
         };
         return { props: list };
     }
@@ -180,17 +189,17 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
 
         // replace code content with a script  >{content}<   as in  <HTMLdiv>{this.title}</HTMLdiv>
         const replacer = (match: any, prefix: string, expr: string, postfix: string, offset: any, string: any) => {
-            return prefix + (ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ this: this.props.Document }).result as string || "") + postfix;
+            return prefix + ((ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ this: this.props.Document }).result as string) || '') + postfix;
         };
         layoutFrame = layoutFrame.replace(/(>[^{]*)[^=]\{([^.'][^<}]+)\}([^}]*<)/g, replacer);
 
-        // replace HTML<tag> with corresponding HTML tag as in:  <HTMLdiv> becomes  <HTMLtag Document={props.Document} htmltag='div'> 
+        // replace HTML<tag> with corresponding HTML tag as in:  <HTMLdiv> becomes  <HTMLtag Document={props.Document} htmltag='div'>
         const replacer2 = (match: any, p1: string, offset: any, string: any) => {
             return `<HTMLtag RootDoc={props.RootDoc} Document={props.Document} scaling='${this.props.scaling?.() || 1}' htmltag='${p1}'`;
         };
         layoutFrame = layoutFrame.replace(/<HTML([a-zA-Z0-9_-]+)/g, replacer2);
 
-        // replace /HTML<tag> with </HTMLdiv>  as in:  </HTMLdiv> becomes  </HTMLtag> 
+        // replace /HTML<tag> with </HTMLdiv>  as in:  </HTMLdiv> becomes  </HTMLtag>
         const replacer3 = (match: any, p1: string, offset: any, string: any) => {
             return `</HTMLtag`;
         };
@@ -200,16 +209,16 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
         const makeFuncProp = (func: string) => {
             const splits = layoutFrame.split(`${func}=`);
             if (splits.length > 1) {
-                const code = XRegExp.matchRecursive(splits[1], "{", "}", "", { valueNames: ["between", "left", "match", "right", "between"] });
+                const code = XRegExp.matchRecursive(splits[1], '{', '}', '', { valueNames: ['between', 'left', 'match', 'right', 'between'] });
                 layoutFrame = splits[0] + ` ${func}={props.${func}} ` + splits[1].substring(code[1].end + 1);
-                const script = code[1].value.replace(/^‘/, "").replace(/’$/, "");  // ‘’ are not valid quotes in javascript so get rid of them -- they may be present to make it easier to write complex scripts - see headerTemplate in currentUserUtils.ts
-                return ScriptField.MakeScript(script, { this: Doc.name, self: Doc.name, scale: "number", value: "string" });
+                const script = code[1].value.replace(/^‘/, '').replace(/’$/, ''); // ‘’ are not valid quotes in javascript so get rid of them -- they may be present to make it easier to write complex scripts - see headerTemplate in currentUserUtils.ts
+                return ScriptField.MakeScript(script, { this: Doc.name, self: Doc.name, scale: 'number', value: 'string' });
             }
             return undefined;
             // add input function to props
         };
-        const onClick = makeFuncProp("onClick");
-        const onInput = makeFuncProp("onInput");
+        const onClick = makeFuncProp('onClick');
+        const onInput = makeFuncProp('onInput');
         const bindings = this.CreateBindings(onClick, onInput);
         return { bindings, layoutFrame };
     }
@@ -218,23 +227,55 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo
         TraceMobx();
         const { bindings, layoutFrame } = this.renderData;
 
-        return (this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc || GetEffectiveAcl(this.layoutDoc) === AclPrivate) ? (null) :
+        return this.props.renderDepth > 12 || !layoutFrame || !this.layoutDoc || GetEffectiveAcl(this.layoutDoc) === AclPrivate ? null : (
             <ObserverJsxParser
                 key={42}
                 blacklistedAttrs={emptyPath}
                 renderInWrapper={false}
                 components={{
-                    FormattedTextBox, ImageBox, DirectoryImportBox, FontIconBox, LabelBox, EquationBox, SliderBox, FieldView,
-                    CollectionFreeFormView, CollectionDockingView, CollectionSchemaView, CollectionView, WebBox, KeyValueBox,
-                    PDFBox, VideoBox, AudioBox, RecordingBox, PresBox, YoutubeBox, PresElementBox, SearchBox, FilterBox, FunctionPlotBox,
-                    ColorBox, DashWebRTCVideo, LinkAnchorBox, InkingStroke, LinkBox, ScriptingBox, MapBox,
-                    ScreenshotBox, DataVizBox, HTMLtag, ComparisonBox
+                    FormattedTextBox,
+                    ImageBox,
+                    DirectoryImportBox,
+                    FontIconBox,
+                    LabelBox,
+                    EquationBox,
+                    SliderBox,
+                    FieldView,
+                    CollectionFreeFormView,
+                    CollectionDockingView,
+                    CollectionSchemaView,
+                    CollectionView,
+                    WebBox,
+                    KeyValueBox,
+                    PDFBox,
+                    VideoBox,
+                    AudioBox,
+                    RecordingBox,
+                    PresBox,
+                    YoutubeBox,
+                    PresElementBox,
+                    SearchBox,
+                    FilterBox,
+                    FunctionPlotBox,
+                    ColorBox,
+                    DashWebRTCVideo,
+                    LinkAnchorBox,
+                    InkingStroke,
+                    LinkBox,
+                    ScriptingBox,
+                    MapBox,
+                    ScreenshotBox,
+                    DataVizBox,
+                    HTMLtag,
+                    ComparisonBox,
                 }}
                 bindings={bindings}
                 jsx={layoutFrame}
                 showWarnings={true}
-
-                onError={(test: any) => { console.log("DocumentContentsView:" + test); }}
-            />;
+                onError={(test: any) => {
+                    console.log('DocumentContentsView:' + test);
+                }}
+            />
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 6c7e174f7..47d5c662e 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -1,19 +1,19 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
 import { action, computed, observable } from 'mobx';
-import { observer } from "mobx-react";
-import wiki from "wikijs";
-import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocCastAsync } from "../../../fields/Doc";
-import { NumCast, StrCast, Cast } from "../../../fields/Types";
-import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, setupMoveUpEvents, Utils } from "../../../Utils";
+import { observer } from 'mobx-react';
+import wiki from 'wikijs';
+import { Doc, DocListCast, HeightSym, Opt, WidthSym, DocCastAsync } from '../../../fields/Doc';
+import { NumCast, StrCast, Cast } from '../../../fields/Types';
+import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, setupMoveUpEvents, Utils } from '../../../Utils';
 import { DocServer } from '../../DocServer';
-import { Docs, DocUtils } from "../../documents/Documents";
+import { Docs, DocUtils } from '../../documents/Documents';
 import { LinkManager } from '../../util/LinkManager';
-import { Transform } from "../../util/Transform";
+import { Transform } from '../../util/Transform';
 import { undoBatch } from '../../util/UndoManager';
-import { DocumentView, DocumentViewSharedProps } from "./DocumentView";
+import { DocumentView, DocumentViewSharedProps } from './DocumentView';
 import './LinkDocPreview.scss';
-import React = require("react");
+import React = require('react');
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
 
@@ -27,15 +27,19 @@ interface LinkDocPreviewProps {
 }
 @observer
 export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
-    @action public static Clear() { LinkDocPreview.LinkInfo = undefined; }
-    @action public static SetLinkInfo(info?: LinkDocPreviewProps) { LinkDocPreview.LinkInfo !== info && (LinkDocPreview.LinkInfo = info); }
+    @action public static Clear() {
+        LinkDocPreview.LinkInfo = undefined;
+    }
+    @action public static SetLinkInfo(info?: LinkDocPreviewProps) {
+        LinkDocPreview.LinkInfo !== info && (LinkDocPreview.LinkInfo = info);
+    }
 
     _infoRef = React.createRef<HTMLDivElement>();
     @observable public static LinkInfo: Opt<LinkDocPreviewProps>;
     @observable _targetDoc: Opt<Doc>;
     @observable _linkDoc: Opt<Doc>;
     @observable _linkSrc: Opt<Doc>;
-    @observable _toolTipText = "";
+    @observable _toolTipText = '';
     @observable _hrefInd = 0;
 
     @action init() {
@@ -47,113 +51,136 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
         if (anchor1 && anchor2) {
             linkTarget = Doc.AreProtosEqual(anchor1, this._linkSrc) || Doc.AreProtosEqual(anchor1?.annotationOn as Doc, this._linkSrc) ? anchor2 : anchor1;
         }
-        if (linkTarget?.annotationOn && linkTarget?.type !== DocumentType.RTF) { // want to show annotation context document if annotation is not text
-            linkTarget && DocCastAsync(linkTarget.annotationOn).then(action(anno => this._targetDoc = anno));
+        if (linkTarget?.annotationOn && linkTarget?.type !== DocumentType.RTF) {
+            // want to show annotation context document if annotation is not text
+            linkTarget && DocCastAsync(linkTarget.annotationOn).then(action(anno => (this._targetDoc = anno)));
         } else {
             this._targetDoc = linkTarget;
         }
-        this._toolTipText = "";
+        this._toolTipText = '';
     }
     componentDidUpdate(props: any) {
         if (props.linkSrc !== this.props.linkSrc || props.linkDoc !== this.props.linkDoc || props.hrefs !== this.props.hrefs) this.init();
     }
     componentDidMount() {
         this.init();
-        document.addEventListener("pointerdown", this.onPointerDown);
+        document.addEventListener('pointerdown', this.onPointerDown);
     }
 
     componentWillUnmount() {
         LinkDocPreview.SetLinkInfo(undefined);
-        document.removeEventListener("pointerdown", this.onPointerDown);
+        document.removeEventListener('pointerdown', this.onPointerDown);
     }
 
     onPointerDown = (e: PointerEvent) => {
         !this._infoRef.current?.contains(e.target as any) && LinkDocPreview.Clear(); // close preview when not clicking anywhere other than the info bar of the preview
-    }
+    };
 
     @computed get href() {
         if (this.props.hrefs?.length) {
             const href = this.props.hrefs[this._hrefInd];
-            if (href.indexOf(Doc.localServerPath()) !== 0) {  // link to a web page URL -- try to show a preview
-                if (href.startsWith("https://en.wikipedia.org/wiki/")) {
-                    wiki().page(href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(action(summary => this._toolTipText = summary.substring(0, 500))));
+            if (href.indexOf(Doc.localServerPath()) !== 0) {
+                // link to a web page URL -- try to show a preview
+                if (href.startsWith('https://en.wikipedia.org/wiki/')) {
+                    wiki()
+                        .page(href.replace('https://en.wikipedia.org/wiki/', ''))
+                        .then(page => page.summary().then(action(summary => (this._toolTipText = summary.substring(0, 500)))));
                 } else {
-                    setTimeout(action(() => this._toolTipText = "url => " + href));
+                    setTimeout(action(() => (this._toolTipText = 'url => ' + href)));
                 }
-            } else { // hyperlink to a document .. decode doc id and retrieve from the server. this will trigger vals() being invalidated
-                const anchorDoc = href.replace(Doc.localServerPath(), "").split("?")[0];
-                anchorDoc && DocServer.GetRefField(anchorDoc).then(action(anchor => {
-                    if (anchor instanceof Doc && DocListCast(anchor.links).length) {
-                        this._linkDoc = this._linkDoc ?? DocListCast(anchor.links)[0];
-                        const automaticLink = this._linkDoc.linkRelationship === LinkManager.AutoKeywords;
-                        if (automaticLink) { // automatic links specify the target in the link info, not the source
-                            const linkTarget = anchor;
-                            this._linkSrc = LinkManager.getOppositeAnchor(this._linkDoc, linkTarget);
-                            this._targetDoc = linkTarget;
-                        } else {
-                            this._linkSrc = anchor;
-                            const linkTarget = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc);
-                            this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget;
-                        }
-                        this._toolTipText = "";
-                    }
-                }));
+            } else {
+                // hyperlink to a document .. decode doc id and retrieve from the server. this will trigger vals() being invalidated
+                const anchorDoc = href.replace(Doc.localServerPath(), '').split('?')[0];
+                anchorDoc &&
+                    DocServer.GetRefField(anchorDoc).then(
+                        action(anchor => {
+                            if (anchor instanceof Doc && DocListCast(anchor.links).length) {
+                                this._linkDoc = this._linkDoc ?? DocListCast(anchor.links)[0];
+                                const automaticLink = this._linkDoc.linkRelationship === LinkManager.AutoKeywords;
+                                if (automaticLink) {
+                                    // automatic links specify the target in the link info, not the source
+                                    const linkTarget = anchor;
+                                    this._linkSrc = LinkManager.getOppositeAnchor(this._linkDoc, linkTarget);
+                                    this._targetDoc = linkTarget;
+                                } else {
+                                    this._linkSrc = anchor;
+                                    const linkTarget = LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc);
+                                    this._targetDoc = /*linkTarget?.type === DocumentType.MARKER &&*/ linkTarget?.annotationOn ? Cast(linkTarget.annotationOn, Doc, null) ?? linkTarget : linkTarget;
+                                }
+                                this._toolTipText = '';
+                            }
+                        })
+                    );
             }
             return href;
         }
         return undefined;
     }
     deleteLink = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(() => this._linkDoc && LinkManager.Instance.deleteLink(this._linkDoc)));
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            returnFalse,
+            emptyFunction,
+            undoBatch(() => this._linkDoc && LinkManager.Instance.deleteLink(this._linkDoc))
+        );
+    };
     nextHref = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, emptyFunction, action(() => {
-            const nextHrefInd = (this._hrefInd + 1) % (this.props.hrefs?.length || 1);
-            if (nextHrefInd !== this._hrefInd) {
-                this._linkDoc = undefined;
-                this._hrefInd = nextHrefInd;
-            }
-        }), true);
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            returnFalse,
+            emptyFunction,
+            action(() => {
+                const nextHrefInd = (this._hrefInd + 1) % (this.props.hrefs?.length || 1);
+                if (nextHrefInd !== this._hrefInd) {
+                    this._linkDoc = undefined;
+                    this._hrefInd = nextHrefInd;
+                }
+            }),
+            true
+        );
+    };
 
     followLink = (e: React.PointerEvent) => {
         if (this._linkDoc && this._linkSrc) {
             LinkDocPreview.Clear();
             LinkManager.FollowLink(this._linkDoc, this._linkSrc, this.props.docProps, false);
         } else if (this.props.hrefs?.length) {
-            this.props.docProps?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true }), "add:right");
+            this.props.docProps?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true }), 'add:right');
         }
-    }
+    };
     width = () => {
         if (!this._targetDoc) return 225;
         if (this._targetDoc[WidthSym]() < this._targetDoc?.[HeightSym]()) {
-            return Math.min(225, this._targetDoc[HeightSym]()) * this._targetDoc[WidthSym]() / this._targetDoc[HeightSym]();
+            return (Math.min(225, this._targetDoc[HeightSym]()) * this._targetDoc[WidthSym]()) / this._targetDoc[HeightSym]();
         }
         return Math.min(225, NumCast(this._targetDoc?.[WidthSym](), 225));
-    }
+    };
     height = () => {
         if (!this._targetDoc) return 225;
         if (this._targetDoc[WidthSym]() > this._targetDoc?.[HeightSym]()) {
-            return Math.min(225, this._targetDoc[WidthSym]()) * this._targetDoc[HeightSym]() / this._targetDoc[WidthSym]();
+            return (Math.min(225, this._targetDoc[WidthSym]()) * this._targetDoc[HeightSym]()) / this._targetDoc[WidthSym]();
         }
         return Math.min(225, NumCast(this._targetDoc?.[HeightSym](), 225));
-    }
+    };
     @computed get previewHeader() {
-        return !this._linkDoc || !this._targetDoc || !this._linkSrc ? (null) :
+        return !this._linkDoc || !this._targetDoc || !this._linkSrc ? null : (
             <div className="linkDocPreview-info" ref={this._infoRef}>
-                <div className="linkDocPreview-title" style={{ pointerEvents: "all" }}
+                <div
+                    className="linkDocPreview-title"
+                    style={{ pointerEvents: 'all' }}
                     onPointerDown={e => {
-                        DragManager.StartDocumentDrag([this._infoRef.current!],
-                            new DragManager.DocumentDragData([this._targetDoc!]), e.pageX, e.pageY);
+                        DragManager.StartDocumentDrag([this._infoRef.current!], new DragManager.DocumentDragData([this._targetDoc!]), e.pageX, e.pageY);
                         e.stopPropagation();
                         LinkDocPreview.Clear();
                     }}>
-                    {StrCast(this._targetDoc.title).length > 16 ? StrCast(this._targetDoc.title).substr(0, 16) + "..." : this._targetDoc.title}
+                    {StrCast(this._targetDoc.title).length > 16 ? StrCast(this._targetDoc.title).substr(0, 16) + '...' : StrCast(this._targetDoc.title)}
                     <p className="linkDocPreview-description"> {StrCast(this._linkDoc.description)}</p>
                 </div>
-                <div className="linkDocPreview-buttonBar" >
+                <div className="linkDocPreview-buttonBar">
                     <Tooltip title={<div className="dash-tooltip">Next Link</div>} placement="top">
-                        <div className="linkDocPreview-button" style={{ background: (this.props.hrefs?.length || 0) <= 1 ? "gray" : "green" }} onPointerDown={this.nextHref}>
+                        <div className="linkDocPreview-button" style={{ background: (this.props.hrefs?.length || 0) <= 1 ? 'gray' : 'green' }} onPointerDown={this.nextHref}>
                             <FontAwesomeIcon className="linkDocPreview-fa-icon" icon="chevron-right" color="white" size="sm" />
                         </div>
                     </Tooltip>
@@ -164,20 +191,24 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                         </div>
                     </Tooltip>
                 </div>
-            </div>;
+            </div>
+        );
     }
 
     @computed get docPreview() {
         const href = this.href; // needs to be here to trigger lookup of web pages and docs on server
-        return (!this._linkDoc || !this._targetDoc || !this._linkSrc) && !this._toolTipText ? (null) :
+        return (!this._linkDoc || !this._targetDoc || !this._linkSrc) && !this._toolTipText ? null : (
             <div className="linkDocPreview-inner">
-                {!this.props.showHeader ? (null) : this.previewHeader}
-                <div className="linkDocPreview-preview-wrapper" style={{ maxHeight: this._toolTipText ? "100%" : undefined, overflow: "auto" }}>
-                    {this._toolTipText ? this._toolTipText :
-                        <DocumentView ref={(r) => {
-                            const targetanchor = this._linkDoc && this._linkSrc && LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc);
-                            targetanchor && this._targetDoc !== targetanchor && r?.focus(targetanchor);
-                        }}
+                {!this.props.showHeader ? null : this.previewHeader}
+                <div className="linkDocPreview-preview-wrapper" style={{ maxHeight: this._toolTipText ? '100%' : undefined, overflow: 'auto' }}>
+                    {this._toolTipText ? (
+                        this._toolTipText
+                    ) : (
+                        <DocumentView
+                            ref={r => {
+                                const targetanchor = this._linkDoc && this._linkSrc && LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc);
+                                targetanchor && this._targetDoc !== targetanchor && r?.focus(targetanchor);
+                            }}
                             Document={this._targetDoc!}
                             moveDocument={returnFalse}
                             rootSelected={returnFalse}
@@ -206,16 +237,19 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                             bringToFront={returnFalse}
                             NativeWidth={Doc.NativeWidth(this._targetDoc) ? () => Doc.NativeWidth(this._targetDoc) : undefined}
                             NativeHeight={Doc.NativeHeight(this._targetDoc) ? () => Doc.NativeHeight(this._targetDoc) : undefined}
-                        />}
+                        />
+                    )}
                 </div>
-            </div>;
+            </div>
+        );
     }
 
     render() {
         const borders = 16; // 8px border on each side
-        return <div className="linkDocPreview" onPointerDown={this.followLink}
-            style={{ left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
-            {this.docPreview}
-        </div>;
+        return (
+            <div className="linkDocPreview" onPointerDown={this.followLink} style={{ left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
+                {this.docPreview}
+            </div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 0b7264d79..18a6b5453 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,8 +1,8 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
 import { action, computed, IReactionDisposer, observable, ObservableMap } from 'mobx';
-import { observer } from "mobx-react";
-import * as React from "react";
+import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Doc, DocListCast, Opt, WidthSym } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { InkTool } from '../../../../fields/InkField';
@@ -22,17 +22,17 @@ import { Annotation } from '../../pdf/Annotation';
 import { SidebarAnnos } from '../../SidebarAnnos';
 import { StyleProp } from '../../StyleProvider';
 import { FieldView, FieldViewProps } from '../FieldView';
-import "./MapBox.scss";
+import './MapBox.scss';
 import { MapBoxInfoWindow } from './MapBoxInfoWindow';
 
 /**
  * MapBox architecture:
  * Main component: MapBox.tsx
  * Supporting Components: SidebarAnnos, CollectionStackingView
- * 
- * MapBox is a node that extends the ViewBoxAnnotatableComponent. Similar to PDFBox and WebBox, it supports interaction between sidebar content and document content. 
- * The main body of MapBox uses Google Maps API to allow location retrieval, adding map markers, pan and zoom, and open street view. 
- * Dash Document architecture is integrated with Maps API: When drag and dropping documents with ExifData (gps Latitude and Longitude information) available, 
+ *
+ * MapBox is a node that extends the ViewBoxAnnotatableComponent. Similar to PDFBox and WebBox, it supports interaction between sidebar content and document content.
+ * The main body of MapBox uses Google Maps API to allow location retrieval, adding map markers, pan and zoom, and open street view.
+ * Dash Document architecture is integrated with Maps API: When drag and dropping documents with ExifData (gps Latitude and Longitude information) available,
  * sidebarAddDocument function checks if the document contains lat & lng information, if it does, then the document is added to both the sidebar and the infowindow (a pop up corresponding to a map marker--pin on map).
  * The lat and lng field of the document is filled when importing (spec see ConvertDMSToDD method and processFileUpload method in Documents.ts).
  * A map marker is considered a document that contains a collection with stacking view of documents, it has a lat, lng location, which is passed to Maps API's custom marker (red pin) to be rendered on the google maps
@@ -77,26 +77,30 @@ document.head.appendChild(script);
 //     },
 // });
 
-
 // options for searchbox in Google Maps Places Autocomplete API
 const options = {
-    fields: ["formatted_address", "geometry", "name"], // note: level of details is charged by item per retrieval, not recommended to return all fields
+    fields: ['formatted_address', 'geometry', 'name'], // note: level of details is charged by item per retrieval, not recommended to return all fields
     strictBounds: false,
-    types: ["establishment"], // type pf places, subject of change according to user need
+    types: ['establishment'], // type pf places, subject of change according to user need
 } as google.maps.places.AutocompleteOptions;
 
 @observer
 export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
-
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
     @observable private _overlayAnnoInfo: Opt<Doc>;
-    showInfo = action((anno: Opt<Doc>) => this._overlayAnnoInfo = anno);
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(MapBox, fieldKey); }
-    public get SidebarKey() { return this.fieldKey + "-sidebar"; }
+    showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(MapBox, fieldKey);
+    }
+    public get SidebarKey() {
+        return this.fieldKey + '-sidebar';
+    }
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
-    @computed get inlineTextAnnotations() { return this.allMapMarkers.filter(a => a.textInlineAnnotations); }
+    @computed get inlineTextAnnotations() {
+        return this.allMapMarkers.filter(a => a.textInlineAnnotations);
+    }
 
     @observable private _map: google.maps.Map = null as unknown as google.maps.Map;
     @observable private selectedPlace: Doc | undefined;
@@ -108,14 +112,19 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable private searchMarkers: google.maps.Marker[] = [];
     @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
     @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
-    @computed get allSidebarDocs() { return DocListCast(this.dataDoc[this.SidebarKey]); }
-    @computed get allMapMarkers() { return DocListCast(this.dataDoc[this.annotationKey]); }
+    @computed get allSidebarDocs() {
+        return DocListCast(this.dataDoc[this.SidebarKey]);
+    }
+    @computed get allMapMarkers() {
+        return DocListCast(this.dataDoc[this.annotationKey]);
+    }
     @observable private toggleAddMarker = false;
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
 
-
     @observable _showSidebar = false;
-    @computed get SidebarShown() { return this._showSidebar || this.layoutDoc._showSidebar ? true : false; }
+    @computed get SidebarShown() {
+        return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
+    }
 
     static _canAnnotate = true;
     static _hadSelection: boolean = false;
@@ -129,109 +138,106 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @action
     private setSearchBox = (searchBox: any) => {
         this.searchBox = searchBox;
-    }
+    };
 
     // iterate allMarkers to size, center, and zoom map to contain all markers
     private fitBounds = (map: google.maps.Map) => {
         const curBounds = map.getBounds() ?? new window.google.maps.LatLngBounds();
-        const isFitting = this.allMapMarkers.reduce((fits, place) =>
-            fits && curBounds?.contains({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), true as boolean);
-        !isFitting && map.fitBounds(this.allMapMarkers.reduce((bounds, place) =>
-            bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) }),
-            new window.google.maps.LatLngBounds()));
-    }
+        const isFitting = this.allMapMarkers.reduce((fits, place) => fits && curBounds?.contains({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), true as boolean);
+        !isFitting && map.fitBounds(this.allMapMarkers.reduce((bounds, place) => bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), new window.google.maps.LatLngBounds()));
+    };
 
     /**
      * Custom control for add marker button
-     * @param controlDiv 
-     * @param map 
+     * @param controlDiv
+     * @param map
      */
     private CenterControl = () => {
-        const controlDiv = document.createElement("div");
-        controlDiv.className = "mapBox-addMarker";
+        const controlDiv = document.createElement('div');
+        controlDiv.className = 'mapBox-addMarker';
         // Set CSS for the control border.
-        const controlUI = document.createElement("div");
-        controlUI.style.backgroundColor = "#fff";
-        controlUI.style.borderRadius = "3px";
-        controlUI.style.cursor = "pointer";
-        controlUI.style.marginTop = "10px";
-        controlUI.style.borderRadius = "4px";
-        controlUI.style.marginBottom = "22px";
-        controlUI.style.textAlign = "center";
-        controlUI.style.position = "absolute";
-        controlUI.style.width = "32px";
-        controlUI.style.height = "32px";
-        controlUI.title = "Click to toggle marker mode.  In marker mode, click on map to place a marker.";
-
-        const plIcon = document.createElement("img");
-        plIcon.src = "https://cdn4.iconfinder.com/data/icons/wirecons-free-vector-icons/32/add-256.png";
-        plIcon.style.color = "rgb(25,25,25)";
-        plIcon.style.fontFamily = "Roboto,Arial,sans-serif";
-        plIcon.style.fontSize = "16px";
-        plIcon.style.lineHeight = "32px";
-        plIcon.style.left = "18";
-        plIcon.style.top = "15";
-        plIcon.style.position = "absolute";
+        const controlUI = document.createElement('div');
+        controlUI.style.backgroundColor = '#fff';
+        controlUI.style.borderRadius = '3px';
+        controlUI.style.cursor = 'pointer';
+        controlUI.style.marginTop = '10px';
+        controlUI.style.borderRadius = '4px';
+        controlUI.style.marginBottom = '22px';
+        controlUI.style.textAlign = 'center';
+        controlUI.style.position = 'absolute';
+        controlUI.style.width = '32px';
+        controlUI.style.height = '32px';
+        controlUI.title = 'Click to toggle marker mode.  In marker mode, click on map to place a marker.';
+
+        const plIcon = document.createElement('img');
+        plIcon.src = 'https://cdn4.iconfinder.com/data/icons/wirecons-free-vector-icons/32/add-256.png';
+        plIcon.style.color = 'rgb(25,25,25)';
+        plIcon.style.fontFamily = 'Roboto,Arial,sans-serif';
+        plIcon.style.fontSize = '16px';
+        plIcon.style.lineHeight = '32px';
+        plIcon.style.left = '18';
+        plIcon.style.top = '15';
+        plIcon.style.position = 'absolute';
         plIcon.width = 14;
         plIcon.height = 14;
-        plIcon.innerHTML = "Add";
+        plIcon.innerHTML = 'Add';
         controlUI.appendChild(plIcon);
 
         // Set CSS for the control interior.
-        const markerIcon = document.createElement("img");
-        markerIcon.src = "https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-1024.png";
-        markerIcon.style.color = "rgb(25,25,25)";
-        markerIcon.style.fontFamily = "Roboto,Arial,sans-serif";
-        markerIcon.style.fontSize = "16px";
-        markerIcon.style.lineHeight = "32px";
-        markerIcon.style.left = "-2";
-        markerIcon.style.top = "1";
+        const markerIcon = document.createElement('img');
+        markerIcon.src = 'https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-1024.png';
+        markerIcon.style.color = 'rgb(25,25,25)';
+        markerIcon.style.fontFamily = 'Roboto,Arial,sans-serif';
+        markerIcon.style.fontSize = '16px';
+        markerIcon.style.lineHeight = '32px';
+        markerIcon.style.left = '-2';
+        markerIcon.style.top = '1';
         markerIcon.width = 30;
         markerIcon.height = 30;
-        markerIcon.style.position = "absolute";
-        markerIcon.innerHTML = "Add";
+        markerIcon.style.position = 'absolute';
+        markerIcon.innerHTML = 'Add';
         controlUI.appendChild(markerIcon);
 
         // Setup the click event listeners
-        controlUI.addEventListener("click", () => {
+        controlUI.addEventListener('click', () => {
             if (this.toggleAddMarker === true) {
                 this.toggleAddMarker = false;
-                console.log("add marker button status:" + this.toggleAddMarker);
-                controlUI.style.backgroundColor = "#fff";
-                markerIcon.style.color = "rgb(25,25,25)";
+                console.log('add marker button status:' + this.toggleAddMarker);
+                controlUI.style.backgroundColor = '#fff';
+                markerIcon.style.color = 'rgb(25,25,25)';
             } else {
                 this.toggleAddMarker = true;
-                console.log("add marker button status:" + this.toggleAddMarker);
-                controlUI.style.backgroundColor = "#4476f7";
-                markerIcon.style.color = "rgb(255,255,255)";
+                console.log('add marker button status:' + this.toggleAddMarker);
+                controlUI.style.backgroundColor = '#4476f7';
+                markerIcon.style.color = 'rgb(255,255,255)';
             }
         });
         controlDiv.appendChild(controlUI);
         return controlDiv;
-    }
+    };
 
     /**
      * Place the marker on google maps & store the empty marker as a MapMarker Document in allMarkers list
      * @param position - the LatLng position where the marker is placed
-     * @param map 
+     * @param map
      */
     @action
     private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => {
         const marker = new google.maps.Marker({
             position: position,
-            map: map
+            map: map,
         });
         map.panTo(position);
         const mapMarker = Docs.Create.MapMarkerDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
         this.addDocument(mapMarker, this.annotationKey);
-    }
+    };
 
     _loadPending = true;
     /**
      * store a reference to google map instance
      * setup the drawing manager on the top right corner of map
-     * fit map bounds to contain all markers 
-     * @param map 
+     * fit map bounds to contain all markers
+     * @param map
      */
     @action
     private loadHandler = (map: google.maps.Map) => {
@@ -256,7 +262,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         map.setZoom(NumCast(this.dataDoc.mapZoom, 2.5));
         map.setCenter(new google.maps.LatLng(NumCast(this.dataDoc.mapLat), NumCast(this.dataDoc.mapLng)));
         setTimeout(() => {
-
             if (this._loadPending && this._map.getBounds()) {
                 this._loadPending = false;
                 this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
@@ -268,7 +273,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this.placeMarker((e as any).latLng, map);
             }
         });
-    }
+    };
 
     @action
     centered = () => {
@@ -278,7 +283,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         }
         this.dataDoc.mapLat = this._map.getCenter()?.lat();
         this.dataDoc.mapLng = this._map.getCenter()?.lng();
-    }
+    };
 
     @action
     zoomChanged = () => {
@@ -287,64 +292,64 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this.layoutDoc.fitContentsToBox && this.fitBounds(this._map);
         }
         this.dataDoc.mapZoom = this._map.getZoom();
-    }
+    };
 
     /**
      * Load and render all map markers
-     * @param marker 
-     * @param place 
+     * @param marker
+     * @param place
      */
     @action
     private markerLoadHandler = (marker: google.maps.Marker, place: Doc) => {
-        place[Id] ? this.markerMap[place[Id]] = marker : null;
-    }
+        place[Id] ? (this.markerMap[place[Id]] = marker) : null;
+    };
 
     /**
      * on clicking the map marker, set the selected place to the marker document & set infowindowopen to be true
-     * @param e 
-     * @param place 
+     * @param e
+     * @param place
      */
     @action
     private markerClickHandler = (e: google.maps.MapMouseEvent, place: Doc) => {
         // set which place was clicked
         this.selectedPlace = place;
         place.infoWindowOpen = true;
-    }
+    };
 
     /**
      * Called when dragging documents into map sidebar or directly into infowindow; to create a map marker, ref to MapMarkerDocument in Documents.ts
-     * @param doc 
-     * @param sidebarKey 
-     * @returns 
+     * @param doc
+     * @param sidebarKey
+     * @returns
      */
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
-        console.log("print all sidebar Docs");
+        console.log('print all sidebar Docs');
         console.log(this.allSidebarDocs);
         if (!this.layoutDoc._showSidebar) this.toggleSidebar();
         const docs = doc instanceof Doc ? [doc] : doc;
         docs.forEach(doc => {
             if (doc.lat !== undefined && doc.lng !== undefined) {
                 const existingMarker = this.allMapMarkers.find(marker => marker.lat === doc.lat && marker.lng === doc.lng);
-                doc.onClickBehavior = "enterPortal";
+                doc.onClickBehavior = 'enterPortal';
                 if (existingMarker) {
-                    Doc.AddDocToList(existingMarker, "data", doc);
+                    Doc.AddDocToList(existingMarker, 'data', doc);
                 } else {
                     const marker = Docs.Create.MapMarkerDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {});
                     this.addDocument(marker, this.annotationKey);
                 }
             }
         }); //add to annotation list
-        console.log("sidebaraddDocument");
+        console.log('sidebaraddDocument');
         console.log(doc);
 
         return this.addDocument(doc, sidebarKey); // add to sidebar list
-    }
+    };
 
     /**
      * Removing documents from the sidebar
-     * @param doc 
-     * @param sidebarKey 
-     * @returns 
+     * @param doc
+     * @param sidebarKey
+     * @returns
      */
     sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
         if (this.layoutDoc._showSidebar) this.toggleSidebar();
@@ -354,31 +359,43 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             console.log(this.allSidebarDocs);
         });
         return this.removeDocument(doc, sidebarKey);
-    }
+    };
 
     /**
      * Toggle sidebar onclick the tiny comment button on the top right corner
-     * @param e 
+     * @param e
      */
     sidebarBtnDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, (e, down, delta) => {
-            const localDelta = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformDirection(delta[0], delta[1]);
-            const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
-            const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
-            const ratio = (curNativeWidth + localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
-            if (ratio >= 1) {
-                this.layoutDoc.nativeWidth = nativeWidth * ratio;
-                this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0];
-                this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
-            }
-            return false;
-        }, emptyFunction, this.toggleSidebar);
-    }
-
-    sidebarWidth = () => Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100 * this.props.PanelWidth();
-    @computed get sidebarWidthPercent() { return StrCast(this.layoutDoc._sidebarWidthPercent, "0%"); }
-    @computed get sidebarColor() { return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "#e4e4e4")); }
+        setupMoveUpEvents(
+            this,
+            e,
+            (e, down, delta) => {
+                const localDelta = this.props
+                    .ScreenToLocalTransform()
+                    .scale(this.props.scaling?.() || 1)
+                    .transformDirection(delta[0], delta[1]);
+                const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
+                const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
+                const ratio = (curNativeWidth + localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
+                if (ratio >= 1) {
+                    this.layoutDoc.nativeWidth = nativeWidth * ratio;
+                    this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0];
+                    this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
+                }
+                return false;
+            },
+            emptyFunction,
+            this.toggleSidebar
+        );
+    };
 
+    sidebarWidth = () => (Number(this.sidebarWidthPercent.substring(0, this.sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
+    @computed get sidebarWidthPercent() {
+        return StrCast(this.layoutDoc._sidebarWidthPercent, '0%');
+    }
+    @computed get sidebarColor() {
+        return StrCast(this.layoutDoc.sidebarColor, StrCast(this.layoutDoc[this.props.fieldKey + '-backgroundColor'], '#e4e4e4'));
+    }
 
     /**
      * function that reads the place inputed from searchbox, then zoom in on the location that's been autocompleted;
@@ -414,7 +431,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         };
 
         // put temporary cutomized marker on searched location
-        this.searchMarkers.forEach((marker) => {
+        this.searchMarkers.forEach(marker => {
             marker.setMap(null);
         });
         this.searchMarkers = [];
@@ -426,7 +443,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 position: place.geometry.location,
             })
         );
-    }
+    };
 
     /**
      * Handles toggle of sidebar on click the little comment button
@@ -435,95 +452,99 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         TraceMobx();
         const annotated = DocListCast(this.dataDoc[this.SidebarKey]).filter(d => d?.author).length;
         const color = !annotated ? Colors.WHITE : Colors.BLACK;
-        const backgroundColor = !annotated ? this.sidebarWidth() ? Colors.MEDIUM_BLUE : Colors.BLACK : this.props.styleProvider?.(this.rootDoc, this.props as any, StyleProp.WidgetColor + (annotated ? ":annotated" : ""));
-        return (!annotated) ? (null) :
-            <div className="formattedTextBox-sidebar-handle" onPointerDown={this.sidebarDown}
+        const backgroundColor = !annotated ? (this.sidebarWidth() ? Colors.MEDIUM_BLUE : Colors.BLACK) : this.props.styleProvider?.(this.rootDoc, this.props as any, StyleProp.WidgetColor + (annotated ? ':annotated' : ''));
+        return !annotated ? null : (
+            <div
+                className="formattedTextBox-sidebar-handle"
+                onPointerDown={this.sidebarDown}
                 style={{
                     left: `max(0px, calc(100% - ${this.sidebarWidthPercent} - 17px))`,
                     backgroundColor: backgroundColor,
                     color: color,
-                    opacity: annotated ? 1 : undefined
-                }} >
-                <FontAwesomeIcon icon={"comment-alt"} />
-            </div>;
+                    opacity: annotated ? 1 : undefined,
+                }}>
+                <FontAwesomeIcon icon={'comment-alt'} />
+            </div>
+        );
     }
 
     // TODO: Adding highlight box layer to Maps
     @action
     toggleSidebar = () => {
         const prevWidth = this.sidebarWidth();
-        this.layoutDoc._showSidebar = ((this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, "0%") === "0%" ? "50%" : "0%")) !== "0%";
+        this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? '50%' : '0%') !== '0%';
         this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
-    }
+    };
 
     sidebarDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), false);
-    }
+    };
     sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
         const bounds = this._ref.current!.getBoundingClientRect();
-        this.layoutDoc._sidebarWidthPercent = "" + 100 * Math.max(0, (1 - (e.clientX - bounds.left) / bounds.width)) + "%";
-        this.layoutDoc._showSidebar = this.layoutDoc._sidebarWidthPercent !== "0%";
+        this.layoutDoc._sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%';
+        this.layoutDoc._showSidebar = this.layoutDoc._sidebarWidthPercent !== '0%';
         e.preventDefault();
         return false;
-    }
+    };
 
-    setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => this._setPreviewCursor = func;
+    setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
 
     @action
     onMarqueeDown = (e: React.PointerEvent) => {
         if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
-            setupMoveUpEvents(this, e, action(e => {
-                MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
-                this._marqueeing = [e.clientX, e.clientY];
-                return true;
-            }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false);
+            setupMoveUpEvents(
+                this,
+                e,
+                action(e => {
+                    MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+                    this._marqueeing = [e.clientX, e.clientY];
+                    return true;
+                }),
+                returnFalse,
+                () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations),
+                false
+            );
         }
-    }
+    };
     @action finishMarquee = (x?: number, y?: number) => {
         this._marqueeing = undefined;
         this._isAnnotating = false;
         x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false);
-    }
+    };
 
     addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
         return this.addDocument(doc, annotationKey);
-    }
+    };
 
     pointerEvents = () => {
-        return this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && !MarqueeOptionsMenu.Instance.isShown() ?
-            "all" :
-            SnappingManager.GetIsDragging() ?
-                undefined : "none";
-    }
+        return this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none';
+    };
     @computed get annotationLayer() {
-        return <div className="mapBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
-            {this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
-                <Annotation key={`${anno[Id]}-annotation`} {...this.props}
-                    fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} />)}
-        </div>;
+        return (
+            <div className="mapBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
+                {this.inlineTextAnnotations
+                    .sort((a, b) => NumCast(a.y) - NumCast(b.y))
+                    .map(anno => (
+                        <Annotation key={`${anno[Id]}-annotation`} {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} />
+                    ))}
+            </div>
+        );
     }
 
     getAnchor = () => {
-        const anchor =
-            AnchorMenu.Instance?.GetAnchor(this._savedAnnotations) ??
-            this.rootDoc;
+        const anchor = AnchorMenu.Instance?.GetAnchor(this._savedAnnotations) ?? this.rootDoc;
         return anchor;
-    }
+    };
 
     /**
      * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker
-     * @returns 
+     * @returns
      */
     private renderMarkers = () => {
         return this.allMapMarkers.map(place => (
-            <Marker
-                key={place[Id]}
-                position={{ lat: NumCast(place.lat), lng: NumCast(place.lng) }}
-                onLoad={marker => this.markerLoadHandler(marker, place)}
-                onClick={(e: google.maps.MapMouseEvent) => this.markerClickHandler(e, place)}
-            />
+            <Marker key={place[Id]} position={{ lat: NumCast(place.lat), lng: NumCast(place.lng) }} onLoad={marker => this.markerLoadHandler(marker, place)} onClick={(e: google.maps.MapMouseEvent) => this.markerClickHandler(e, place)} />
         ));
-    }
+    };
 
     // TODO: auto center on select a document in the sidebar
     private handleMapCenter = (map: google.maps.Map) => {
@@ -531,7 +552,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         // if (SelectionManager.Views().lastElement()) {
         //     console.log(SelectionManager.Views().lastElement());
         // }
-    }
+    };
 
     panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
     panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
@@ -543,7 +564,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
     savedAnnotations = () => this._savedAnnotations;
     render() {
-        const renderAnnotations = (docFilters?: () => string[]) => (null);
+        const renderAnnotations = (docFilters?: () => string[]) => null;
         // bcz: commmented this out.  Otherwise, any documents that are rendered with an InfoWindow of a marker
         //      will also be rendered as freeform annotations on the map. However, it doesn't seem that rendering
         //      freeform documents on the map does anything anyway, so getting rid of it for now.  Also, since documents
@@ -571,88 +592,88 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         //     addDocument={this.sidebarAddDocument}
         //     childPointerEvents={true}
         //     pointerEvents={CurrentUserUtils.ActiveTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
-        return <div className="mapBox" ref={this._ref}>
-            {/*console.log(apiKey)*/}
-            {/* <LoadScript
+        return (
+            <div className="mapBox" ref={this._ref}>
+                {/*console.log(apiKey)*/}
+                {/* <LoadScript
                 googleMapsApiKey={apiKey!}
                 libraries={['places', 'drawing']}
             > */}
-            <div className="mapBox-wrapper"
-                onWheel={e => e.stopPropagation()}
-                onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()}
-                style={{ width: `calc(100% - ${this.sidebarWidthPercent})` }}>
-
-                <div style={{ mixBlendMode: "multiply" }}>
-                    {renderAnnotations(this.transparentFilter)}
+                <div className="mapBox-wrapper" onWheel={e => e.stopPropagation()} onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()} style={{ width: `calc(100% - ${this.sidebarWidthPercent})` }}>
+                    <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
+                    {renderAnnotations(this.opaqueFilter)}
+                    {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
+                    {this.annotationLayer}
+                    <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
+                        <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
+                            <input className="mapBox-input" ref={this.inputRef} type="text" placeholder="Enter location" />
+                        </Autocomplete>
+
+                        {this.renderMarkers()}
+                        {this.allMapMarkers
+                            .filter(marker => marker.infoWindowOpen)
+                            .map(marker => (
+                                <MapBoxInfoWindow
+                                    key={marker[Id]}
+                                    {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                                    place={marker}
+                                    markerMap={this.markerMap}
+                                    PanelWidth={this.infoWidth}
+                                    PanelHeight={this.infoHeight}
+                                    moveDocument={this.moveDocument}
+                                    isAnyChildContentActive={this.isAnyChildContentActive}
+                                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                                />
+                            ))}
+                        {/* {this.handleMapCenter(this._map)} */}
+                    </GoogleMap>
+                    {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
+                        <MarqueeAnnotator
+                            rootDoc={this.rootDoc}
+                            anchorMenuClick={this.anchorMenuClick}
+                            scrollTop={0}
+                            down={this._marqueeing}
+                            scaling={returnOne}
+                            addDocument={this.addDocumentWrapper}
+                            docView={this.props.docViewPath().lastElement()}
+                            finishMarquee={this.finishMarquee}
+                            savedAnnotations={this.savedAnnotations}
+                            annotationLayer={this._annotationLayer.current}
+                            mainCont={this._mainCont.current}
+                        />
+                    )}
                 </div>
-                {renderAnnotations(this.opaqueFilter)}
-                {SnappingManager.GetIsDragging() ? (null) : renderAnnotations()}
-                {this.annotationLayer}
-                <GoogleMap
-                    mapContainerStyle={mapContainerStyle}
-                    onZoomChanged={this.zoomChanged}
-                    onCenterChanged={this.centered}
-                    onLoad={this.loadHandler}
-                    options={mapOptions}
-                >
-                    <Autocomplete
-                        onLoad={this.setSearchBox}
-                        onPlaceChanged={this.handlePlaceChanged}>
-                        <input className="mapBox-input" ref={this.inputRef} type="text" placeholder="Enter location" />
-                    </Autocomplete>
-
-                    {this.renderMarkers()}
-                    {this.allMapMarkers.filter(marker => marker.infoWindowOpen).map(marker => <MapBoxInfoWindow key={marker[Id]}
-                        {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
-                        place={marker}
-                        markerMap={this.markerMap}
-                        PanelWidth={this.infoWidth}
-                        PanelHeight={this.infoHeight}
-                        moveDocument={this.moveDocument}
-                        isAnyChildContentActive={this.isAnyChildContentActive}
+                {/* </LoadScript > */}
+                <div className="mapBox-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+                    <SidebarAnnos
+                        ref={this._sidebarRef}
+                        {...this.props}
+                        fieldKey={this.fieldKey}
+                        rootDoc={this.rootDoc}
+                        layoutDoc={this.layoutDoc}
+                        dataDoc={this.dataDoc}
+                        showSidebar={this.SidebarShown}
+                        nativeWidth={NumCast(this.layoutDoc._nativeWidth)}
                         whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                    />)}
-                    {this.handleMapCenter(this._map)}
-                </GoogleMap>
-                {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
-                    <MarqueeAnnotator rootDoc={this.rootDoc}
-                        anchorMenuClick={this.anchorMenuClick}
-                        scrollTop={0}
-                        down={this._marqueeing} scaling={returnOne}
-                        addDocument={this.addDocumentWrapper}
-                        docView={this.props.docViewPath().lastElement()}
-                        finishMarquee={this.finishMarquee}
-                        savedAnnotations={this.savedAnnotations}
-                        annotationLayer={this._annotationLayer.current}
-                        mainCont={this._mainCont.current} />}
-            </div>
-            {/* </LoadScript > */}
-            <div className="mapBox-sidebar"
-                style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
-                <SidebarAnnos ref={this._sidebarRef}
-                    {...this.props}
-                    fieldKey={this.fieldKey}
-                    rootDoc={this.rootDoc}
-                    layoutDoc={this.layoutDoc}
-                    dataDoc={this.dataDoc}
-                    showSidebar={this.SidebarShown}
-                    nativeWidth={NumCast(this.layoutDoc._nativeWidth)}
-                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                    PanelWidth={this.sidebarWidth}
-                    sidebarAddDocument={this.sidebarAddDocument}
-                    moveDocument={this.moveDocument}
-                    removeDocument={this.sidebarRemoveDocument}
-                />
-            </div>
-            <div className="mapBox-overlayButton-sidebar" key="sidebar" title="Toggle Sidebar"
-                style={{
-                    display: !this.props.isContentActive() ? "none" : undefined,
-                    top: StrCast(this.rootDoc._showTitle) === "title" ? 20 : 5,
-                    backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK
-                }}
-                onPointerDown={this.sidebarBtnDown} >
-                <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
+                        PanelWidth={this.sidebarWidth}
+                        sidebarAddDocument={this.sidebarAddDocument}
+                        moveDocument={this.moveDocument}
+                        removeDocument={this.sidebarRemoveDocument}
+                    />
+                </div>
+                <div
+                    className="mapBox-overlayButton-sidebar"
+                    key="sidebar"
+                    title="Toggle Sidebar"
+                    style={{
+                        display: !this.props.isContentActive() ? 'none' : undefined,
+                        top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5,
+                        backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
+                    }}
+                    onPointerDown={this.sidebarBtnDown}>
+                    <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
+                </div>
             </div>
-        </div>;
+        );
     }
 }
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 1fb3cef2a..5b9d90780 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -1,12 +1,12 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
-import { observer } from "mobx-react";
-import * as Pdfjs from "pdfjs-dist";
-import "pdfjs-dist/web/pdf_viewer.css";
-import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+import { observer } from 'mobx-react';
+import * as Pdfjs from 'pdfjs-dist';
+import 'pdfjs-dist/web/pdf_viewer.css';
+import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
-import { ImageField, PdfField } from "../../../fields/URLField";
+import { ImageField, PdfField } from '../../../fields/URLField';
 import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
@@ -15,25 +15,27 @@ import { KeyCodes } from '../../util/KeyCodes';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
+import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
 import { Colors } from '../global/globalEnums';
-import { CreateImage } from "../nodes/WebBoxRenderer";
+import { CreateImage } from '../nodes/WebBoxRenderer';
 import { AnchorMenu } from '../pdf/AnchorMenu';
-import { PDFViewer } from "../pdf/PDFViewer";
+import { PDFViewer } from '../pdf/PDFViewer';
 import { SidebarAnnos } from '../SidebarAnnos';
 import { FieldView, FieldViewProps } from './FieldView';
 import { ImageBox } from './ImageBox';
-import "./PDFBox.scss";
+import './PDFBox.scss';
 import { VideoBox } from './VideoBox';
-import React = require("react");
+import React = require('react');
 import { CollectionFreeFormView } from '../collections/collectionFreeForm';
 
 @observer
 export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PDFBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(PDFBox, fieldKey);
+    }
     public static openSidebarWidth = 250;
     public static sidebarResizerWidth = 5;
-    private _searchString: string = "";
+    private _searchString: string = '';
     private _initialScrollTarget: Opt<Doc>;
     private _pdfViewer: PDFViewer | undefined;
     private _searchRef = React.createRef<HTMLInputElement>();
@@ -44,8 +46,12 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable private _pdf: Opt<Pdfjs.PDFDocumentProxy>;
     @observable private _pageControls = false;
 
-    @computed get pdfUrl() { return Cast(this.dataDoc[this.props.fieldKey], PdfField); }
-    @computed get pdfThumb() { return ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url; }
+    @computed get pdfUrl() {
+        return Cast(this.dataDoc[this.props.fieldKey], PdfField);
+    }
+    @computed get pdfThumb() {
+        return ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url;
+    }
 
     constructor(props: any) {
         super(props);
@@ -53,8 +59,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const nh = Doc.NativeHeight(this.Document, this.dataDoc) || 1200;
         !this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
         if (this.pdfUrl) {
-            if (PDFBox.pdfcache.get(this.pdfUrl.url.href)) runInAction(() => this._pdf = PDFBox.pdfcache.get(this.pdfUrl!.url.href));
-            else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action((pdf: any) => this._pdf = pdf));
+            if (PDFBox.pdfcache.get(this.pdfUrl.url.href)) runInAction(() => (this._pdf = PDFBox.pdfcache.get(this.pdfUrl!.url.href)));
+            else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action((pdf: any) => (this._pdf = pdf)));
         }
     }
 
@@ -65,15 +71,14 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         }
 
-        if (oldDiv.className === "pdfBox-ui" ||
-            oldDiv.className === "pdfViewerDash-overlay-inking") {
-            newDiv.style.display = "none";
+        if (oldDiv.className === 'pdfBox-ui' || oldDiv.className === 'pdfViewerDash-overlay-inking') {
+            newDiv.style.display = 'none';
         }
-        if (newDiv && newDiv.style) newDiv.style.overflow = "hidden";
+        if (newDiv && newDiv.style) newDiv.style.overflow = 'hidden';
         if (oldDiv instanceof HTMLCanvasElement) {
             const canvas = oldDiv;
             const img = document.createElement('img'); // create a Image Element
-            img.src = canvas.toDataURL();   //image sourcez
+            img.src = canvas.toDataURL(); //image sourcez
             img.style.width = canvas.style.width;
             img.style.height = canvas.style.height;
             const newCan = newDiv as HTMLCanvasElement;
@@ -81,20 +86,20 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             parEle.removeChild(newCan);
             parEle.appendChild(img);
         }
-    }
+    };
 
     crop = (region: Doc | undefined, addCrop?: boolean) => {
         if (!region) return;
         const cropping = Doc.MakeCopy(region, true);
         Doc.GetProto(region).lockedPosition = true;
-        Doc.GetProto(region).title = "region:" + this.rootDoc.title;
+        Doc.GetProto(region).title = 'region:' + this.rootDoc.title;
         Doc.GetProto(region).isPushpin = true;
         this.addDocument(region);
 
         const docViewContent = this.props.docViewPath().lastElement().ContentDiv!;
         const newDiv = docViewContent.cloneNode(true) as HTMLDivElement;
-        newDiv.style.width = (this.layoutDoc[WidthSym]()).toString();
-        newDiv.style.height = (this.layoutDoc[HeightSym]()).toString();
+        newDiv.style.width = this.layoutDoc[WidthSym]().toString();
+        newDiv.style.height = this.layoutDoc[HeightSym]().toString();
         this.replaceCanvases(docViewContent, newDiv);
         const htmlString = this._pdfViewer?._mainCont.current && new XMLSerializer().serializeToString(newDiv);
 
@@ -103,7 +108,7 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const anchw = cropping[WidthSym]() * (this.props.scaling?.() || 1);
         const anchh = cropping[HeightSym]() * (this.props.scaling?.() || 1);
         const viewScale = 1;
-        cropping.title = "crop: " + this.rootDoc.title;
+        cropping.title = 'crop: ' + this.rootDoc.title;
         cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
         cropping.y = NumCast(this.rootDoc.y);
         cropping._width = anchw;
@@ -114,100 +119,113 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         croppingProto.isPrototype = true;
         croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
         croppingProto.type = DocumentType.IMG;
-        croppingProto.layout = ImageBox.LayoutString("data");
-        croppingProto.data = new ImageField(Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png"));
-        croppingProto["data-nativeWidth"] = anchw;
-        croppingProto["data-nativeHeight"] = anchh;
+        croppingProto.layout = ImageBox.LayoutString('data');
+        croppingProto.data = new ImageField(Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png'));
+        croppingProto['data-nativeWidth'] = anchw;
+        croppingProto['data-nativeHeight'] = anchh;
         if (addCrop) {
-            DocUtils.MakeLink({ doc: region }, { doc: cropping }, "cropped image", "");
+            DocUtils.MakeLink({ doc: region }, { doc: cropping }, 'cropped image', '');
         }
         this.props.bringToFront(cropping);
 
         CreateImage(
-            "",
+            '',
             document.styleSheets,
             htmlString,
             anchw,
             anchh,
-            NumCast(region.y) * this.props.PanelWidth() / NumCast(this.rootDoc[this.fieldKey + "-nativeWidth"]),
-            NumCast(region.x) * this.props.PanelWidth() / NumCast(this.rootDoc[this.fieldKey + "-nativeWidth"]),
+            (NumCast(region.y) * this.props.PanelWidth()) / NumCast(this.rootDoc[this.fieldKey + '-nativeWidth']),
+            (NumCast(region.x) * this.props.PanelWidth()) / NumCast(this.rootDoc[this.fieldKey + '-nativeWidth']),
             4
-        ).then
-            ((data_url: any) => {
-                VideoBox.convertDataUri(data_url, region[Id]).then(
-                    returnedfilename => setTimeout(action(() => {
-                        croppingProto.data = new ImageField(returnedfilename);
-                    }), 500));
+        )
+            .then((data_url: any) => {
+                VideoBox.convertDataUri(data_url, region[Id]).then(returnedfilename =>
+                    setTimeout(
+                        action(() => {
+                            croppingProto.data = new ImageField(returnedfilename);
+                        }),
+                        500
+                    )
+                );
             })
             .catch(function (error: any) {
                 console.error('oops, something went wrong!', error);
             });
 
-
         return cropping;
-    }
+    };
 
     updateIcon = () => {
         // currently we render pdf icons as text labels
         const docViewContent = this.props.docViewPath().lastElement().ContentDiv!;
-        const filename = this.layoutDoc[Id] + "-icon" + (new Date()).getTime();
-        this._pdfViewer?._mainCont.current && CollectionFreeFormView.UpdateIcon(
-            filename, docViewContent, 
-            this.layoutDoc[WidthSym](), this.layoutDoc[HeightSym](), 
-            this.props.PanelWidth(), this.props.PanelHeight(),  
-            NumCast(this.layoutDoc._scrollTop),
-            NumCast(this.rootDoc[this.fieldKey + "-nativeHeight"], 1), 
-            true,  
-            this.layoutDoc[Id] + "-icon",
-            (iconFile:string, nativeWidth:number, nativeHeight:number) => {
-                setTimeout(() => {
-                    this.dataDoc.icon = new ImageField(iconFile);
-                    this.dataDoc["icon-nativeWidth"] = nativeWidth;
-                    this.dataDoc["icon-nativeHeight"] = nativeHeight;
-                }, 500);
-            });
-    }
+        const filename = this.layoutDoc[Id] + '-icon' + new Date().getTime();
+        this._pdfViewer?._mainCont.current &&
+            CollectionFreeFormView.UpdateIcon(
+                filename,
+                docViewContent,
+                this.layoutDoc[WidthSym](),
+                this.layoutDoc[HeightSym](),
+                this.props.PanelWidth(),
+                this.props.PanelHeight(),
+                NumCast(this.layoutDoc._scrollTop),
+                NumCast(this.rootDoc[this.fieldKey + '-nativeHeight'], 1),
+                true,
+                this.layoutDoc[Id] + '-icon',
+                (iconFile: string, nativeWidth: number, nativeHeight: number) => {
+                    setTimeout(() => {
+                        this.dataDoc.icon = new ImageField(iconFile);
+                        this.dataDoc['icon-nativeWidth'] = nativeWidth;
+                        this.dataDoc['icon-nativeHeight'] = nativeHeight;
+                    }, 500);
+                }
+            );
+    };
 
-    componentWillUnmount() { this._selectReactionDisposer?.(); }
+    componentWillUnmount() {
+        this._selectReactionDisposer?.();
+    }
     componentDidMount() {
         this.props.setContentView?.(this);
-        this._selectReactionDisposer = reaction(() => this.props.isSelected(),
+        this._selectReactionDisposer = reaction(
+            () => this.props.isSelected(),
             () => {
-                document.removeEventListener("keydown", this.onKeyDown);
-                this.props.isSelected(true) && document.addEventListener("keydown", this.onKeyDown);
-            }, { fireImmediately: true });
+                document.removeEventListener('keydown', this.onKeyDown);
+                this.props.isSelected(true) && document.addEventListener('keydown', this.onKeyDown);
+            },
+            { fireImmediately: true }
+        );
     }
 
     scrollFocus = (doc: Doc, smooth: boolean) => {
         let didToggle = false;
-        if (DocListCast(this.props.Document[this.fieldKey + "-sidebar"]).includes(doc) && !this.SidebarShown) {
+        if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) {
             this.toggleSidebar(!smooth);
             didToggle = true;
         }
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1;
         this._initialScrollTarget = doc;
         return this._pdfViewer?.scrollFocus(doc, smooth) ?? (didToggle ? 1 : undefined);
-    }
+    };
     getAnchor = () => {
         const anchor =
             this._pdfViewer?._getAnchor(this._pdfViewer.savedAnnotations()) ??
             Docs.Create.TextanchorDocument({
-                title: StrCast(this.rootDoc.title + "@" + NumCast(this.layoutDoc._scrollTop)?.toFixed(0)),
+                title: StrCast(this.rootDoc.title + '@' + NumCast(this.layoutDoc._scrollTop)?.toFixed(0)),
                 y: NumCast(this.layoutDoc._scrollTop),
-                unrendered: true
+                unrendered: true,
             });
         this.addDocument(anchor);
         return anchor;
-    }
+    };
 
     @action
     loaded = (nw: number, nh: number, np: number) => {
-        this.dataDoc[this.props.fieldKey + "-numPages"] = np;
-        Doc.SetNativeWidth(this.dataDoc, Math.max(Doc.NativeWidth(this.dataDoc), nw * 96 / 72));
-        Doc.SetNativeHeight(this.dataDoc, nh * 96 / 72);
+        this.dataDoc[this.props.fieldKey + '-numPages'] = np;
+        Doc.SetNativeWidth(this.dataDoc, Math.max(Doc.NativeWidth(this.dataDoc), (nw * 96) / 72));
+        Doc.SetNativeHeight(this.dataDoc, (nh * 96) / 72);
         this.layoutDoc._height = this.layoutDoc[WidthSym]() / (Doc.NativeAspect(this.dataDoc) || 1);
         !this.Document._fitWidth && (this.Document._height = this.Document[WidthSym]() * (nh / nw));
-    }
+    };
 
     public search = action((searchString: string, bwd?: boolean, clear: boolean = false) => {
         if (!this._searching && !clear) {
@@ -225,19 +243,23 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     public backPage = () => {
         this.Document._curPage = Math.max(1, (NumCast(this.Document._curPage) || 1) - 1);
         return true;
-    }
+    };
     public forwardPage = () => {
-        this.Document._curPage = Math.min(NumCast(this.dataDoc[this.props.fieldKey + "-numPages"]), (NumCast(this.Document._curPage) || 1) + 1);
+        this.Document._curPage = Math.min(NumCast(this.dataDoc[this.props.fieldKey + '-numPages']), (NumCast(this.Document._curPage) || 1) + 1);
         return true;
-    }
-    public gotoPage = (p: number) => this.Document._curPage = p;
+    };
+    public gotoPage = (p: number) => (this.Document._curPage = p);
 
     @undoBatch
     onKeyDown = action((e: KeyboardEvent) => {
         let processed = false;
         switch (e.key) {
-            case "PageDown": processed = this.forwardPage(); break;
-            case "PageUp": processed = this.backPage(); break;
+            case 'PageDown':
+                processed = this.forwardPage();
+                break;
+            case 'PageUp':
+                processed = this.backPage();
+                break;
         }
         if (processed) {
             e.stopImmediatePropagation();
@@ -251,131 +273,163 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this.scrollFocus(this._initialScrollTarget, false);
             this._initialScrollTarget = undefined;
         }
-    }
-    searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => this._searchString = e.currentTarget.value;
+    };
+    searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => (this._searchString = e.currentTarget.value);
 
     // adding external documents; to sidebar key
     // if (doc.Geolocation) this.addDocument(doc, this.fieldkey+"-annotation")
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
         if (!this.layoutDoc._showSidebar) this.toggleSidebar();
         return this.addDocument(doc, sidebarKey);
-    }
-    sidebarBtnDown = (e: React.PointerEvent, onButton: boolean) => { // onButton determines whether the width of the pdf box changes, or just the ratio of the sidebar to the pdf
-        const batch = UndoManager.StartBatch("sidebar");
-        setupMoveUpEvents(this, e, (e, down, delta) => {
-            const localDelta = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformDirection(delta[0], delta[1]);
-            const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
-            const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
-            const ratio = (curNativeWidth + (onButton ? 1 : -1) * localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
-            if (ratio >= 1) {
-                this.layoutDoc.nativeWidth = nativeWidth * ratio;
-                onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
-                this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
-            }
-            return false;
-        }, () => batch.end(), () => this.toggleSidebar());
-    }
+    };
+    sidebarBtnDown = (e: React.PointerEvent, onButton: boolean) => {
+        // onButton determines whether the width of the pdf box changes, or just the ratio of the sidebar to the pdf
+        const batch = UndoManager.StartBatch('sidebar');
+        setupMoveUpEvents(
+            this,
+            e,
+            (e, down, delta) => {
+                const localDelta = this.props
+                    .ScreenToLocalTransform()
+                    .scale(this.props.scaling?.() || 1)
+                    .transformDirection(delta[0], delta[1]);
+                const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
+                const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
+                const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.scaling?.() || 1)) / nativeWidth;
+                if (ratio >= 1) {
+                    this.layoutDoc.nativeWidth = nativeWidth * ratio;
+                    onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
+                    this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
+                }
+                return false;
+            },
+            () => batch.end(),
+            () => this.toggleSidebar()
+        );
+    };
     @observable _previewNativeWidth: Opt<number> = undefined;
     @observable _previewWidth: Opt<number> = undefined;
     toggleSidebar = action((preview: boolean = false) => {
-        const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
+        const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
         const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
         const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? PDFBox.openSidebarWidth + PDFBox.sidebarResizerWidth : 0) + nativeWidth) / nativeWidth;
         const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
         if (preview) {
             this._previewNativeWidth = nativeWidth * sideratio;
-            this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * sideratio / curNativeWidth;
+            this._previewWidth = (this.layoutDoc[WidthSym]() * nativeWidth * sideratio) / curNativeWidth;
             this._showSidebar = true;
-        }
-        else {
+        } else {
             this.layoutDoc.nativeWidth = nativeWidth * pdfratio;
-            this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * pdfratio / curNativeWidth;
+            this.layoutDoc._width = (this.layoutDoc[WidthSym]() * nativeWidth * pdfratio) / curNativeWidth;
             this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
         }
     });
     settingsPanel() {
-        const pageBtns = <>
-            <button className="pdfBox-backBtn" key="back" title="Page Back"
-                onPointerDown={e => e.stopPropagation()} onClick={this.backPage} >
-                <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-left"} size="sm" />
-            </button>
-            <button className="pdfBox-fwdBtn" key="fwd" title="Page Forward"
-                onPointerDown={e => e.stopPropagation()} onClick={this.forwardPage} >
-                <FontAwesomeIcon style={{ color: "white" }} icon={"arrow-right"} size="sm" />
-            </button>
-        </>;
-        const searchTitle = `${!this._searching ? "Open" : "Close"} Search Bar`;
+        const pageBtns = (
+            <>
+                <button className="pdfBox-backBtn" key="back" title="Page Back" onPointerDown={e => e.stopPropagation()} onClick={this.backPage}>
+                    <FontAwesomeIcon style={{ color: 'white' }} icon={'arrow-left'} size="sm" />
+                </button>
+                <button className="pdfBox-fwdBtn" key="fwd" title="Page Forward" onPointerDown={e => e.stopPropagation()} onClick={this.forwardPage}>
+                    <FontAwesomeIcon style={{ color: 'white' }} icon={'arrow-right'} size="sm" />
+                </button>
+            </>
+        );
+        const searchTitle = `${!this._searching ? 'Open' : 'Close'} Search Bar`;
         const curPage = NumCast(this.Document._curPage) || 1;
-        return !this.props.isContentActive() || this._pdfViewer?.isAnnotating ? (null) :
-            <div className="pdfBox-ui" onKeyDown={e => [KeyCodes.BACKSPACE, KeyCodes.DELETE].includes(e.keyCode) ? e.stopPropagation() : true}
-                onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? "flex" : "none" }}>
-                <div className="pdfBox-overlayCont" onPointerDown={(e) => e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}>
+        return !this.props.isContentActive() || this._pdfViewer?.isAnnotating ? null : (
+            <div
+                className="pdfBox-ui"
+                onKeyDown={e => ([KeyCodes.BACKSPACE, KeyCodes.DELETE].includes(e.keyCode) ? e.stopPropagation() : true)}
+                onPointerDown={e => e.stopPropagation()}
+                style={{ display: this.props.isContentActive() ? 'flex' : 'none' }}>
+                <div className="pdfBox-overlayCont" onPointerDown={e => e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}>
                     <button className="pdfBox-overlayButton" title={searchTitle} />
-                    <input className="pdfBox-searchBar" placeholder="Search" ref={this._searchRef} onChange={this.searchStringChanged}
+                    <input
+                        className="pdfBox-searchBar"
+                        placeholder="Search"
+                        ref={this._searchRef}
+                        onChange={this.searchStringChanged}
                         onKeyDown={e => {
                             e.stopPropagation();
                             e.keyCode === KeyCodes.ENTER && this.search(this._searchString, e.shiftKey);
-                        }} />
+                        }}
+                    />
                     <button className="pdfBox-search" title="Search" onClick={e => this.search(this._searchString, e.shiftKey)}>
                         <FontAwesomeIcon icon="search" size="sm" />
                     </button>
-                    <button className="pdfBox-prevIcon" title="Previous Annotation" onClick={this.prevAnnotation} >
-                        <FontAwesomeIcon icon={"arrow-up"} size="lg" />
+                    <button className="pdfBox-prevIcon" title="Previous Annotation" onClick={this.prevAnnotation}>
+                        <FontAwesomeIcon icon={'arrow-up'} size="lg" />
                     </button>
-                    <button className="pdfBox-nextIcon" title="Next Annotation" onClick={this.nextAnnotation} >
-                        <FontAwesomeIcon icon={"arrow-down"} size="lg" />
+                    <button className="pdfBox-nextIcon" title="Next Annotation" onClick={this.nextAnnotation}>
+                        <FontAwesomeIcon icon={'arrow-down'} size="lg" />
                     </button>
                 </div>
-                <button className="pdfBox-overlayButton" title={searchTitle}
-                    onClick={action(() => { this._searching = !this._searching; this.search("", true, true); })} >
-                    <div className="pdfBox-overlayButton-arrow" onPointerDown={(e) => e.stopPropagation()} />
-                    <div className="pdfBox-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}>
-                        <FontAwesomeIcon icon={this._searching ? "times" : "search"} size="lg" />
+                <button
+                    className="pdfBox-overlayButton"
+                    title={searchTitle}
+                    onClick={action(() => {
+                        this._searching = !this._searching;
+                        this.search('', true, true);
+                    })}>
+                    <div className="pdfBox-overlayButton-arrow" onPointerDown={e => e.stopPropagation()} />
+                    <div className="pdfBox-overlayButton-iconCont" onPointerDown={e => e.stopPropagation()}>
+                        <FontAwesomeIcon icon={this._searching ? 'times' : 'search'} size="lg" />
                     </div>
                 </button>
 
                 <div className="pdfBox-pageNums">
-                    <input value={curPage} style={{ width: `${curPage > 99 ? 4 : 3}ch`, pointerEvents: "all" }}
-                        onChange={e => this.Document._curPage = Number(e.currentTarget.value)}
+                    <input
+                        value={curPage}
+                        style={{ width: `${curPage > 99 ? 4 : 3}ch`, pointerEvents: 'all' }}
+                        onChange={e => (this.Document._curPage = Number(e.currentTarget.value))}
                         onKeyDown={e => e.stopPropagation()}
-                        onClick={action(() => this._pageControls = !this._pageControls)} />
-                    {this._pageControls ? pageBtns : (null)}
+                        onClick={action(() => (this._pageControls = !this._pageControls))}
+                    />
+                    {this._pageControls ? pageBtns : null}
                 </div>
-                <div className="pdfBox-sidebarBtn" key="sidebar" title="Toggle Sidebar"
+                <div
+                    className="pdfBox-sidebarBtn"
+                    key="sidebar"
+                    title="Toggle Sidebar"
                     style={{
-                        display: !this.props.isContentActive() ? "none" : undefined,
-                        top: StrCast(this.rootDoc._showTitle) === "title" ? 20 : 5,
-                        backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK
+                        display: !this.props.isContentActive() ? 'none' : undefined,
+                        top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5,
+                        backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
                     }}
-                    onPointerDown={e => this.sidebarBtnDown(e, true)} >
-                    <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
+                    onPointerDown={e => this.sidebarBtnDown(e, true)}>
+                    <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
                 </div>
-            </div>;
+            </div>
+        );
     }
-    sidebarWidth = () => !this.SidebarShown ? 0 :
-        PDFBox.sidebarResizerWidth + (this._previewWidth ? PDFBox.openSidebarWidth :
-            (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth))
+    sidebarWidth = () =>
+        !this.SidebarShown ? 0 : PDFBox.sidebarResizerWidth + (this._previewWidth ? PDFBox.openSidebarWidth : ((NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth()) / NumCast(this.layoutDoc.nativeWidth));
 
     specificContextMenu = (e: React.MouseEvent): void => {
         const funcs: ContextMenuProps[] = [];
-        funcs.push({ description: "Copy path", event: () => this.pdfUrl && Utils.CopyText(Utils.prepend("") + this.pdfUrl.url.pathname), icon: "expand-arrows-alt" });
-        funcs.push({ description: "update icon", event: () => this.pdfUrl && this.updateIcon(), icon: "expand-arrows-alt" });
+        funcs.push({ description: 'Copy path', event: () => this.pdfUrl && Utils.CopyText(Utils.prepend('') + this.pdfUrl.url.pathname), icon: 'expand-arrows-alt' });
+        funcs.push({ description: 'update icon', event: () => this.pdfUrl && this.updateIcon(), icon: 'expand-arrows-alt' });
         //funcs.push({ description: "Toggle Sidebar ", event: () => this.toggleSidebar(), icon: "expand-arrows-alt" });
-        ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
-    }
+        ContextMenu.Instance.addItem({ description: 'Options...', subitems: funcs, icon: 'asterisk' });
+    };
 
     @computed get renderTitleBox() {
-        const classname = "pdfBox" + (this.props.isContentActive() ? "-interactive" : "");
-        return <div className={classname} >
-            <div className="pdfBox-title-outer">
-                <strong className="pdfBox-title" >{this.props.Document.title}</strong>
+        const classname = 'pdfBox' + (this.props.isContentActive() ? '-interactive' : '');
+        return (
+            <div className={classname}>
+                <div className="pdfBox-title-outer">
+                    <strong className="pdfBox-title">{StrCast(this.props.Document.title)}</strong>
+                </div>
             </div>
-        </div>;
+        );
     }
 
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
     @observable _showSidebar = false;
-    @computed get SidebarShown() { return this._showSidebar || this.layoutDoc._showSidebar ? true : false; }
+    @computed get SidebarShown() {
+        return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
+    }
 
     contentScaling = () => 1;
     isPdfContentActive = () => this.isAnyChildContentActive() || this.props.isSelected();
@@ -383,54 +437,60 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         TraceMobx();
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
         const scale = previewScale * (this.props.scaling?.() || 1);
-        return <div className={"pdfBox"} onContextMenu={this.specificContextMenu}
-            style={{
-                display: this.props.thumbShown?.() ? "none" : undefined,
-                height: this.props.Document._scrollTop && !this.Document._fitWidth && (window.screen.width > 600) ?
-                    NumCast(this.Document._height) * this.props.PanelWidth() / NumCast(this.Document._width) : undefined
-            }}>
-            <div className="pdfBox-background" onPointerDown={e => this.sidebarBtnDown(e, false)} />
-            <div style={{
-                width: `calc(${100 / scale}% - ${this.sidebarWidth() / scale * (this._previewWidth ? scale : 1)}px)`,
-                height: `${100 / scale}%`,
-                transform: `scale(${scale})`,
-                position: "absolute",
-                transformOrigin: "top left",
-                top: 0,
-            }}>
-                <PDFViewer {...this.props}
+        return (
+            <div
+                className={'pdfBox'}
+                onContextMenu={this.specificContextMenu}
+                style={{
+                    display: this.props.thumbShown?.() ? 'none' : undefined,
+                    height: this.props.Document._scrollTop && !this.Document._fitWidth && window.screen.width > 600 ? (NumCast(this.Document._height) * this.props.PanelWidth()) / NumCast(this.Document._width) : undefined,
+                }}>
+                <div className="pdfBox-background" onPointerDown={e => this.sidebarBtnDown(e, false)} />
+                <div
+                    style={{
+                        width: `calc(${100 / scale}% - ${(this.sidebarWidth() / scale) * (this._previewWidth ? scale : 1)}px)`,
+                        height: `${100 / scale}%`,
+                        transform: `scale(${scale})`,
+                        position: 'absolute',
+                        transformOrigin: 'top left',
+                        top: 0,
+                    }}>
+                    <PDFViewer
+                        {...this.props}
+                        rootDoc={this.rootDoc}
+                        layoutDoc={this.layoutDoc}
+                        dataDoc={this.dataDoc}
+                        pdf={this._pdf!}
+                        url={this.pdfUrl!.url.pathname}
+                        isContentActive={this.isPdfContentActive}
+                        anchorMenuClick={this.anchorMenuClick}
+                        loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined}
+                        setPdfViewer={this.setPdfViewer}
+                        addDocument={this.addDocument}
+                        moveDocument={this.moveDocument}
+                        removeDocument={this.removeDocument}
+                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                        crop={this.crop}
+                        ContentScaling={returnOne}
+                    />
+                </div>
+                <SidebarAnnos
+                    ref={this._sidebarRef}
+                    {...this.props}
                     rootDoc={this.rootDoc}
                     layoutDoc={this.layoutDoc}
                     dataDoc={this.dataDoc}
-                    pdf={this._pdf!}
-                    url={this.pdfUrl!.url.pathname}
-                    isContentActive={this.isPdfContentActive}
-                    anchorMenuClick={this.anchorMenuClick}
-                    loaded={!Doc.NativeAspect(this.dataDoc) ? this.loaded : undefined}
-                    setPdfViewer={this.setPdfViewer}
-                    addDocument={this.addDocument}
+                    setHeight={emptyFunction}
+                    nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth)}
+                    showSidebar={this.SidebarShown}
+                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                    sidebarAddDocument={this.sidebarAddDocument}
                     moveDocument={this.moveDocument}
                     removeDocument={this.removeDocument}
-                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                    crop={this.crop}
-                    ContentScaling={returnOne}
                 />
+                {this.settingsPanel()}
             </div>
-            <SidebarAnnos ref={this._sidebarRef}
-                {...this.props}
-                rootDoc={this.rootDoc}
-                layoutDoc={this.layoutDoc}
-                dataDoc={this.dataDoc}
-                setHeight={emptyFunction}
-                nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth)}
-                showSidebar={this.SidebarShown}
-                whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                sidebarAddDocument={this.sidebarAddDocument}
-                moveDocument={this.moveDocument}
-                removeDocument={this.removeDocument}
-            />
-            {this.settingsPanel()}
-        </div>;
+        );
     }
 
     static pdfcache = new Map<string, Pdfjs.PDFDocumentProxy>();
@@ -446,13 +506,12 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
         const href = this.pdfUrl?.url.href;
         if (href) {
-            if (PDFBox.pdfcache.get(href)) setTimeout(action(() => this._pdf = PDFBox.pdfcache.get(href)));
+            if (PDFBox.pdfcache.get(href)) setTimeout(action(() => (this._pdf = PDFBox.pdfcache.get(href))));
             else {
                 if (!PDFBox.pdfpromise.get(href)) PDFBox.pdfpromise.set(href, Pdfjs.getDocument(href).promise);
-                PDFBox.pdfpromise.get(href)?.then(action((pdf: any) => PDFBox.pdfcache.set(href, this._pdf = pdf)));
+                PDFBox.pdfpromise.get(href)?.then(action((pdf: any) => PDFBox.pdfcache.set(href, (this._pdf = pdf))));
             }
         }
         return this.renderTitleBox;
     }
 }
-
diff --git a/src/client/views/nodes/formattedText/schema_rts.ts b/src/client/views/nodes/formattedText/schema_rts.ts
index 83561073c..d6e0e6002 100644
--- a/src/client/views/nodes/formattedText/schema_rts.ts
+++ b/src/client/views/nodes/formattedText/schema_rts.ts
@@ -1,8 +1,7 @@
-import { Schema, Slice } from "prosemirror-model";
-
-import { nodes } from "./nodes_rts";
-import { marks } from "./marks_rts";
+import { Schema, Slice } from 'prosemirror-model';
 
+import { nodes } from './nodes_rts';
+import { marks } from './marks_rts';
 
 // :: Schema
 // This schema rougly corresponds to the document schema used by
@@ -20,7 +19,9 @@ const fromJson = schema.nodeFromJSON;
 schema.nodeFromJSON = (json: any) => {
     const node = fromJson(json);
     if (json.type === schema.nodes.summary.name) {
-        node.attrs.text = Slice.fromJSON(schema, node.attrs.textslice);
+        // bcz: this is a hacky way to convert the JSON that's serialized for a summary node into the Slice that the summary node wants at run-time.
+        //      since attrs are readonly, assigning the text field like this violates the way prosemirror works, but I think we can get away with it.
+        (node.attrs.text as any) = Slice.fromJSON(schema, node.attrs.textslice);
     }
     return node;
-};
\ No newline at end of file
+};
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 7045d6d5d..91f8d1efc 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -1,55 +1,57 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Tooltip } from "@material-ui/core";
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { ColorState, SketchPicker } from "react-color";
+import React = require('react');
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { ColorState, SketchPicker } from 'react-color';
 import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
-import { Doc, DocListCast, DocListCastAsync, FieldResult } from "../../../../fields/Doc";
-import { InkTool } from "../../../../fields/InkField";
-import { List } from "../../../../fields/List";
-import { PrefetchProxy } from "../../../../fields/Proxy";
-import { listSpec } from "../../../../fields/Schema";
-import { BoolCast, Cast, DocCast, NumCast, StrCast } from "../../../../fields/Types";
+import { Doc, DocListCast, DocListCastAsync, FieldResult } from '../../../../fields/Doc';
+import { InkTool } from '../../../../fields/InkField';
+import { List } from '../../../../fields/List';
+import { PrefetchProxy } from '../../../../fields/Proxy';
+import { listSpec } from '../../../../fields/Schema';
+import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, returnFalse, returnOne, returnTrue, setupMoveUpEvents } from '../../../../Utils';
-import { Docs } from "../../../documents/Documents";
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { DocumentManager } from "../../../util/DocumentManager";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { undoBatch, UndoManager } from "../../../util/UndoManager";
-import { CollectionDockingView } from "../../collections/CollectionDockingView";
-import { MarqueeViewBounds } from "../../collections/collectionFreeForm";
-import { CollectionView, CollectionViewType } from "../../collections/CollectionView";
-import { TabDocView } from "../../collections/TabDocView";
-import { ViewBoxBaseComponent } from "../../DocComponent";
-import { Colors } from "../../global/globalEnums";
-import { LightboxView } from "../../LightboxView";
-import { CollectionFreeFormDocumentView } from "../CollectionFreeFormDocumentView";
+import { Docs } from '../../../documents/Documents';
+import { DocumentType } from '../../../documents/DocumentTypes';
+import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
+import { DocumentManager } from '../../../util/DocumentManager';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { undoBatch, UndoManager } from '../../../util/UndoManager';
+import { CollectionDockingView } from '../../collections/CollectionDockingView';
+import { MarqueeViewBounds } from '../../collections/collectionFreeForm';
+import { CollectionView, CollectionViewType } from '../../collections/CollectionView';
+import { TabDocView } from '../../collections/TabDocView';
+import { ViewBoxBaseComponent } from '../../DocComponent';
+import { Colors } from '../../global/globalEnums';
+import { LightboxView } from '../../LightboxView';
+import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentView';
 import { FieldView, FieldViewProps } from '../FieldView';
-import "./PresBox.scss";
-import { PresEffect, PresMovement, PresStatus } from "./PresEnums";
-import { ScriptingGlobals } from "../../../util/ScriptingGlobals";
-import { PresElementBox } from ".";
+import './PresBox.scss';
+import { PresEffect, PresMovement, PresStatus } from './PresEnums';
+import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
+import { PresElementBox } from '.';
 
 export interface PinProps {
     audioRange?: boolean;
     setPosition?: boolean;
     hidePresBox?: boolean;
     pinWithView?: PinViewProps;
-    pinDocView?: boolean;  // whether the current view specs of the document should be saved the pinned document
-    panelWidth?: number;  // panel width and height of the document (used to compute the bounds of the pinned view area)
-    panelHeight?: number
+    pinDocView?: boolean; // whether the current view specs of the document should be saved the pinned document
+    panelWidth?: number; // panel width and height of the document (used to compute the bounds of the pinned view area)
+    panelHeight?: number;
 }
 
 export interface PinViewProps {
-  bounds: MarqueeViewBounds;
-  scale: number;
+    bounds: MarqueeViewBounds;
+    scale: number;
 }
 
 @observer
 export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(PresBox, fieldKey);
+    }
 
     /**
      * transitions & effects for documents
@@ -67,22 +69,27 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             // when: this.layoutDoc === PresBox.Instance.childDocs[PresBox.Instance.itemIndex]?.presentationTargetDoc,
         };
         switch (presDoc.presEffect) {
-            case PresEffect.Zoom: return (<Zoom {...effectProps}>{renderDoc}</Zoom>);
-            case PresEffect.Fade: return (<Fade {...effectProps}>{renderDoc}</Fade>);
-            case PresEffect.Flip: return (<Flip {...effectProps}>{renderDoc}</Flip>);
-            case PresEffect.Rotate: return (<Rotate {...effectProps}>{renderDoc}</Rotate>);
-            case PresEffect.Bounce: return (<Bounce {...effectProps}>{renderDoc}</Bounce>);
-            case PresEffect.Roll: return (<Roll {...effectProps}>{renderDoc}</Roll>);
-            case PresEffect.Lightspeed: return (<LightSpeed {...effectProps}>{renderDoc}</LightSpeed>);
+            case PresEffect.Zoom:
+                return <Zoom {...effectProps}>{renderDoc}</Zoom>;
+            case PresEffect.Fade:
+                return <Fade {...effectProps}>{renderDoc}</Fade>;
+            case PresEffect.Flip:
+                return <Flip {...effectProps}>{renderDoc}</Flip>;
+            case PresEffect.Rotate:
+                return <Rotate {...effectProps}>{renderDoc}</Rotate>;
+            case PresEffect.Bounce:
+                return <Bounce {...effectProps}>{renderDoc}</Bounce>;
+            case PresEffect.Roll:
+                return <Roll {...effectProps}>{renderDoc}</Roll>;
+            case PresEffect.Lightspeed:
+                return <LightSpeed {...effectProps}>{renderDoc}</LightSpeed>;
             case PresEffect.None:
-            default: return renderDoc;
+            default:
+                return renderDoc;
         }
     }
     public static EffectsProvider(layoutDoc: Doc, renderDoc: any) {
-        return PresBox.Instance && layoutDoc === PresBox.Instance.childDocs[PresBox.Instance.itemIndex]?.presentationTargetDoc ?
-            PresBox.renderEffectsDoc(renderDoc, layoutDoc, PresBox.Instance.childDocs[PresBox.Instance.itemIndex])
-            :
-            renderDoc;
+        return PresBox.Instance && layoutDoc === PresBox.Instance.childDocs[PresBox.Instance.itemIndex]?.presentationTargetDoc ? PresBox.renderEffectsDoc(renderDoc, layoutDoc, PresBox.Instance.childDocs[PresBox.Instance.itemIndex]) : renderDoc;
     }
 
     @observable public static Instance: PresBox;
@@ -107,10 +114,18 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @observable private openMovementDropdown: boolean = false;
     @observable private openEffectDropdown: boolean = false;
     @observable private presentTools: boolean = false;
-    @computed get isTreeOrStack() {return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(StrCast(this.layoutDoc._viewType) as any) }
-    @computed get isTree() { return this.layoutDoc._viewType === CollectionViewType.Tree;}
-    @computed get presFieldKey() { return StrCast(this.layoutDoc.presFieldKey, "data"); }
-    @computed get childDocs() { return DocListCast(this.rootDoc[this.presFieldKey]); }
+    @computed get isTreeOrStack() {
+        return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(StrCast(this.layoutDoc._viewType) as any);
+    }
+    @computed get isTree() {
+        return this.layoutDoc._viewType === CollectionViewType.Tree;
+    }
+    @computed get presFieldKey() {
+        return StrCast(this.layoutDoc.presFieldKey, 'data');
+    }
+    @computed get childDocs() {
+        return DocListCast(this.rootDoc[this.presFieldKey]);
+    }
     @observable _treeViewMap: Map<Doc, number> = new Map();
 
     @computed get tagDocs() {
@@ -121,9 +136,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
         return tagDocs;
     }
-    @computed get itemIndex() { return NumCast(this.rootDoc._itemIndex); }
-    @computed get activeItem() { return Cast(this.childDocs[NumCast(this.rootDoc._itemIndex)], Doc, null); }
-    @computed get targetDoc() { return Cast(this.activeItem?.presentationTargetDoc, Doc, null); }
+    @computed get itemIndex() {
+        return NumCast(this.rootDoc._itemIndex);
+    }
+    @computed get activeItem() {
+        return Cast(this.childDocs[NumCast(this.rootDoc._itemIndex)], Doc, null);
+    }
+    @computed get targetDoc() {
+        return Cast(this.activeItem?.presentationTargetDoc, Doc, null);
+    }
     @computed get scrollable(): boolean {
         if (this.targetDoc.type === DocumentType.PDF || this.targetDoc.type === DocumentType.WEB || this.targetDoc.type === DocumentType.RTF || this.targetDoc._viewType === CollectionViewType.Stacking) return true;
         else return false;
@@ -134,7 +155,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
     constructor(props: any) {
         super(props);
-        if (CurrentUserUtils.ActivePresentation = this.rootDoc) runInAction(() => PresBox.Instance = this);
+        if ((CurrentUserUtils.ActivePresentation = this.rootDoc)) runInAction(() => (PresBox.Instance = this));
         this.props.Document.presentationFieldKey = this.fieldKey; // provide info to the presElement script so that it can look up rendering information about the presBox
     }
     @computed get selectedDocumentView() {
@@ -142,21 +163,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (this._selectedArray.size) return DocumentManager.Instance.getDocumentView(this.rootDoc);
     }
     @computed get isPres(): boolean {
-        document.removeEventListener("keydown", PresBox.keyEventsWrapper, true);
+        document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
         if (this.selectedDoc?.type === DocumentType.PRES) {
-            document.removeEventListener("keydown", PresBox.keyEventsWrapper, true);
-            document.addEventListener("keydown", PresBox.keyEventsWrapper, true);
+            document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
+            document.addEventListener('keydown', PresBox.keyEventsWrapper, true);
             return true;
         }
         return false;
     }
-    @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
+    @computed get selectedDoc() {
+        return this.selectedDocumentView?.rootDoc;
+    }
 
     _unmounting = false;
     @action
     componentWillUnmount() {
         this._unmounting = true;
-        document.removeEventListener("keydown", PresBox.keyEventsWrapper, true);
+        document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
         this._presKeyEventsActive = false;
         this.resetPresentation();
         // Turn of progressivize editors
@@ -167,37 +190,38 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     componentDidMount() {
         this._unmounting = false;
-        this.rootDoc._forceRenderEngine = "timeline";
+        this.rootDoc._forceRenderEngine = 'timeline';
         this.layoutDoc.presStatus = PresStatus.Edit;
         this.layoutDoc._gridGap = 0;
         this.layoutDoc._yMargin = 0;
         this.turnOffEdit(true);
-        DocListCastAsync(CurrentUserUtils.MyTrails.data).then(pres =>
-            !pres?.includes(this.rootDoc) && Doc.AddDocToList(CurrentUserUtils.MyTrails, "data", this.rootDoc));
-        this._disposers.selection = reaction(() => SelectionManager.Views(),
-            views => views.some(view => view.props.Document === this.rootDoc) && this.updateCurrentPresentation());
+        DocListCastAsync(CurrentUserUtils.MyTrails.data).then(pres => !pres?.includes(this.rootDoc) && Doc.AddDocToList(CurrentUserUtils.MyTrails, 'data', this.rootDoc));
+        this._disposers.selection = reaction(
+            () => SelectionManager.Views(),
+            views => views.some(view => view.props.Document === this.rootDoc) && this.updateCurrentPresentation()
+        );
     }
 
     @action
     updateCurrentPresentation = (pres?: Doc) => {
         if (pres) CurrentUserUtils.ActivePresentation = pres;
         else CurrentUserUtils.ActivePresentation = this.rootDoc;
-        document.removeEventListener("keydown", PresBox.keyEventsWrapper, true);
-        document.addEventListener("keydown", PresBox.keyEventsWrapper, true);
+        document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
+        document.addEventListener('keydown', PresBox.keyEventsWrapper, true);
         this._presKeyEventsActive = true;
         PresBox.Instance = this;
-    }
+    };
 
     // There are still other internal frames and should go through all frames before going to next slide
     nextInternalFrame = (targetDoc: Doc, activeItem: Doc) => {
-        const currentFrame = Cast(targetDoc?._currentFrame, "number", null);
+        const currentFrame = Cast(targetDoc?._currentFrame, 'number', null);
         const childDocs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
-        targetDoc._viewTransition = "all 1s";
-        setTimeout(() => targetDoc._viewTransition = undefined, 1010);
+        targetDoc._viewTransition = 'all 1s';
+        setTimeout(() => (targetDoc._viewTransition = undefined), 1010);
         this.nextKeyframe(targetDoc, activeItem);
         if (activeItem.presProgressivize) CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, targetDoc);
         else targetDoc.keyFrameEditing = true;
-    }
+    };
 
     _mediaTimer!: [NodeJS.Timeout, Doc];
     // 'Play on next' for audio or video therefore first navigate to the audio/video before it should be played
@@ -207,7 +231,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const targMedia = DocumentManager.Instance.getDocumentView(targetDoc);
             targMedia?.ComponentView?.playFrom?.(NumCast(activeItem.presStartTime), NumCast(activeItem.presStartTime) + duration);
         }
-    }
+    };
 
     stopTempMedia = (targetDocField: FieldResult) => {
         const targetDoc = Cast(targetDocField, Doc, null);
@@ -215,11 +239,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const targMedia = DocumentManager.Instance.getDocumentView(targetDoc);
             targMedia?.ComponentView?.Pause?.();
         }
-    }
+    };
 
-    //TODO: al: it seems currently that tempMedia doesn't stop onslidechange after clicking the button; the time the tempmedia stop depends on the start & end time 
+    //TODO: al: it seems currently that tempMedia doesn't stop onslidechange after clicking the button; the time the tempmedia stop depends on the start & end time
     // TODO: to handle child slides (entering into subtrail and exiting), also the next() and back() functions
-    // No more frames in current doc and next slide is defined, therefore move to next slide 
+    // No more frames in current doc and next slide is defined, therefore move to next slide
     nextSlide = (activeNext: Doc) => {
         const targetNext = Cast(activeNext.presentationTargetDoc, Doc, null);
         console.info('nextSlide', activeNext.title, targetNext?.title);
@@ -229,11 +253,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (!this.childDocs[nextSelected].groupWithUp) {
                 break;
             } else {
-                console.log("Title: " + this.childDocs[nextSelected].title);
+                console.log('Title: ' + this.childDocs[nextSelected].title);
                 this.gotoDocument(nextSelected, this.activeItem, true);
             }
         }
-    }
+    };
 
     // Called when the user activates 'next' - to move to the next part of the pres. trail
     @action
@@ -241,7 +265,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const activeNext = Cast(this.childDocs[this.itemIndex + 1], Doc, null);
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
-        const lastFrame = Cast(targetDoc?.lastFrame, "number", null);
+        const lastFrame = Cast(targetDoc?.lastFrame, 'number', null);
         const curFrame = NumCast(targetDoc?._currentFrame);
         let internalFrames: boolean = false;
         if (activeItem.presProgressivize || activeItem.zoomProgressivize || targetDoc.scrollProgressivize) internalFrames = true;
@@ -249,13 +273,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             // Case 1: There are still other frames and should go through all frames before going to next slide
             this.nextInternalFrame(targetDoc, activeItem);
         } else if (this.childDocs[this.itemIndex + 1] !== undefined) {
-            // Case 2: No more frames in current doc and next slide is defined, therefore move to next slide 
+            // Case 2: No more frames in current doc and next slide is defined, therefore move to next slide
             this.nextSlide(activeNext);
         } else if (this.childDocs[this.itemIndex + 1] === undefined && (this.layoutDoc.presLoop || this.layoutDoc.presStatus === PresStatus.Edit)) {
             // Case 3: Last slide and presLoop is toggled ON or it is in Edit mode
             this.gotoDocument(0, this.activeItem);
         }
-    }
+    };
 
     // Called when the user activates 'back' - to move to the previous part of the pres. trail
     @action
@@ -264,7 +288,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const targetDoc: Doc = this.targetDoc;
         const prevItem = Cast(this.childDocs[Math.max(0, this.itemIndex - 1)], Doc, null);
         const prevTargetDoc = Cast(prevItem.presentationTargetDoc, Doc, null);
-        const lastFrame = Cast(targetDoc.lastFrame, "number", null);
+        const lastFrame = Cast(targetDoc.lastFrame, 'number', null);
         const curFrame = NumCast(targetDoc._currentFrame);
         let prevSelected = this.itemIndex;
         // Functionality for group with up
@@ -284,7 +308,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             // Case 3: Pres loop is on so it should go to the last slide
             this.gotoDocument(this.childDocs.length - 1, activeItem);
         }
-    }
+    };
 
     //The function that is called when a document is clicked or reached through next or back.
     //it'll also execute the necessary actions if presentation is playing.
@@ -297,20 +321,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (from?.mediaStopTriggerList && this.layoutDoc.presStatus !== PresStatus.Edit) {
                 DocListCast(from.mediaStopTriggerList).forEach(this.stopTempMedia);
             }
-            if (from?.mediaStop === "auto" && this.layoutDoc.presStatus !== PresStatus.Edit) {
+            if (from?.mediaStop === 'auto' && this.layoutDoc.presStatus !== PresStatus.Edit) {
                 this.stopTempMedia(from.presentationTargetDoc);
             }
             // If next slide is audio / video 'Play automatically' then the next slide should be played
-            if (this.layoutDoc.presStatus !== PresStatus.Edit && (targetDoc.type === DocumentType.AUDIO || targetDoc.type === DocumentType.VID) && (activeItem.mediaStart === "auto")) {
+            if (this.layoutDoc.presStatus !== PresStatus.Edit && (targetDoc.type === DocumentType.AUDIO || targetDoc.type === DocumentType.VID) && activeItem.mediaStart === 'auto') {
                 this.startTempMedia(targetDoc, activeItem);
             }
             if (targetDoc) {
-                Doc.linkFollowHighlight((targetDoc.annotationOn instanceof Doc) ? [targetDoc, targetDoc.annotationOn] : targetDoc);
-                targetDoc && runInAction(() => {
-                    if (activeItem.presMovement === PresMovement.Jump) targetDoc.focusSpeed = 0;
-                    else targetDoc.focusSpeed = activeItem.presTransition ? activeItem.presTransition : 500;
-                });
-                setTimeout(() => targetDoc.focusSpeed = 500, this.activeItem.presTransition ? NumCast(this.activeItem.presTransition) + 10 : 510);
+                Doc.linkFollowHighlight(targetDoc.annotationOn instanceof Doc ? [targetDoc, targetDoc.annotationOn] : targetDoc);
+                targetDoc &&
+                    runInAction(() => {
+                        if (activeItem.presMovement === PresMovement.Jump) targetDoc.focusSpeed = 0;
+                        else targetDoc.focusSpeed = activeItem.presTransition ? activeItem.presTransition : 500;
+                    });
+                setTimeout(() => (targetDoc.focusSpeed = 500), this.activeItem.presTransition ? NumCast(this.activeItem.presTransition) + 10 : 510);
             }
             if (targetDoc?.lastFrame !== undefined) {
                 targetDoc._currentFrame = 0;
@@ -327,14 +352,14 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         clearTimeout(this._navTimer);
         const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document;
         if (bestTarget) console.log(bestTarget.title, bestTarget.type);
-        else console.log("no best target");
-        if (bestTarget)  this._navTimer = PresBox.navigateToDoc(bestTarget, activeItem, false);
-    }
+        else console.log('no best target');
+        if (bestTarget) this._navTimer = PresBox.navigateToDoc(bestTarget, activeItem, false);
+    };
 
     // navigates to the bestTarget document by making sure it is on screen,
-    // then it applies the view specs stored in activeItem to 
+    // then it applies the view specs stored in activeItem to
     @action
-    static navigateToDoc(bestTarget:Doc, activeItem:Doc, jumpToDoc:boolean)  {
+    static navigateToDoc(bestTarget: Doc, activeItem: Doc, jumpToDoc: boolean) {
         if (bestTarget.type === DocumentType.PDF || bestTarget.type === DocumentType.WEB || bestTarget.type === DocumentType.RTF || bestTarget._viewType === CollectionViewType.Stacking) {
             bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
             bestTarget._scrollTop = activeItem.presPinViewScroll;
@@ -343,32 +368,31 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         } else if ([DocumentType.AUDIO, DocumentType.VID].includes(bestTarget.type as any)) {
             bestTarget._currentTimecode = activeItem.presStartTime;
         } else {
-            const contentBounds= Cast(activeItem.contentBounds, listSpec("number"));
+            const contentBounds = Cast(activeItem.contentBounds, listSpec('number'));
             bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s';
             if (contentBounds) {
-                bestTarget._panX = (contentBounds[0] + contentBounds[2])/2;
-                bestTarget._panY = (contentBounds[1] + contentBounds[3])/2;
+                bestTarget._panX = (contentBounds[0] + contentBounds[2]) / 2;
+                bestTarget._panY = (contentBounds[1] + contentBounds[3]) / 2;
                 const dv = DocumentManager.Instance.getDocumentView(bestTarget);
                 if (dv) {
-                    bestTarget._viewScale =  Math.min(dv.props.PanelHeight() / (contentBounds[3] - contentBounds[1]),
-                            dv.props.PanelWidth() / (contentBounds[2]- contentBounds[0]));
-                };
+                    bestTarget._viewScale = Math.min(dv.props.PanelHeight() / (contentBounds[3] - contentBounds[1]), dv.props.PanelWidth() / (contentBounds[2] - contentBounds[0]));
+                }
             } else {
                 bestTarget._panX = activeItem.presPinViewX;
                 bestTarget._panY = activeItem.presPinViewY;
                 bestTarget._viewScale = activeItem.presPinViewScale;
             }
         }
-        return setTimeout(() => bestTarget._viewTransition = undefined, activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 510);
+        return setTimeout(() => (bestTarget._viewTransition = undefined), activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 510);
     }
 
     /**
      * This method makes sure that cursor navigates to the element that
-     * has the option open and last in the group. 
-     * Design choice: If the next document is not in presCollection or 
+     * has the option open and last in the group.
+     * Design choice: If the next document is not in presCollection or
      * presCollection itself then if there is a presCollection it will add
      * a new tab. If presCollection is undefined it will open the document
-     * on the right. 
+     * on the right.
      */
     navigateToElement = async (curDoc: Doc) => {
         const activeItem: Doc = this.activeItem;
@@ -401,7 +425,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             self._eleArray.splice(0, self._eleArray.length, ...eleViewCache);
         });
         const openInTab = (doc: Doc, finished?: () => void) => {
-            collectionDocView ? collectionDocView.props.addDocTab(doc, "") : this.props.addDocTab(doc, "");
+            collectionDocView ? collectionDocView.props.addDocTab(doc, '') : this.props.addDocTab(doc, '');
             this.layoutDoc.presCollection = targetDoc;
             // this still needs some fixing
             setTimeout(resetSelection, 500);
@@ -417,21 +441,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             // openInTab(targetDoc);
         } else if (curDoc.presMovement === PresMovement.Pan && targetDoc) {
             LightboxView.SetLightboxDoc(undefined);
-            await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext ? [srcContext]:[], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right
+            await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right
         } else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) {
             LightboxView.SetLightboxDoc(undefined);
-            //awaiting jump so that new scale can be found, since jumping is async     
-            await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext ? [srcContext]:[], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right    
+            //awaiting jump so that new scale can be found, since jumping is async
+            await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right
         }
         // After navigating to the document, if it is added as a presPinView then it will
         // adjust the pan and scale to that of the pinView when it was added.
         if (activeItem.presPinView) {
             console.log(targetDoc.title);
-            console.log("presPinView in PresBox.tsx:420");
+            console.log('presPinView in PresBox.tsx:420');
             // if targetDoc is not displayed but one of its aliases is, then we need to modify that alias, not the original target
             this.navigateToView(targetDoc, activeItem);
         }
-    }
+    };
 
     /**
      * Uses the viewfinder to progressivize through the different views of a single collection.
@@ -441,10 +465,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const targetDoc: Doc = this.targetDoc;
         const srcContext = Cast(targetDoc?.context, Doc, null);
         const docView = DocumentManager.Instance.getDocumentView(targetDoc);
-        const vfLeft = this.checkList(targetDoc, activeItem["viewfinder-left-indexed"]);
-        const vfWidth = this.checkList(targetDoc, activeItem["viewfinder-width-indexed"]);
-        const vfTop = this.checkList(targetDoc, activeItem["viewfinder-top-indexed"]);
-        const vfHeight = this.checkList(targetDoc, activeItem["viewfinder-height-indexed"]);
+        const vfLeft = this.checkList(targetDoc, activeItem['viewfinder-left-indexed']);
+        const vfWidth = this.checkList(targetDoc, activeItem['viewfinder-width-indexed']);
+        const vfTop = this.checkList(targetDoc, activeItem['viewfinder-top-indexed']);
+        const vfHeight = this.checkList(targetDoc, activeItem['viewfinder-height-indexed']);
         // Case 1: document that is not a Golden Layout tab
         if (srcContext) {
             const srcDocView = DocumentManager.Instance.getDocumentView(srcContext);
@@ -455,8 +479,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 const newPanX = NumCast(targetDoc.x) + NumCast(layoutdoc._width) / 2;
                 const newPanY = NumCast(targetDoc.y) + NumCast(layoutdoc._height) / 2;
                 const newScale = 0.9 * Math.min(Number(panelWidth) / vfWidth, Number(panelHeight) / vfHeight);
-                srcContext._panX = newPanX + (vfLeft + (vfWidth / 2));
-                srcContext._panY = newPanY + (vfTop + (vfHeight / 2));
+                srcContext._panX = newPanX + (vfLeft + vfWidth / 2);
+                srcContext._panY = newPanY + (vfTop + vfHeight / 2);
                 srcContext._viewScale = newScale;
             }
         }
@@ -465,8 +489,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const panelWidth: number = docView.props.PanelWidth();
             const panelHeight: number = docView.props.PanelHeight();
             const newScale = 0.9 * Math.min(Number(panelWidth) / vfWidth, Number(panelHeight) / vfHeight);
-            targetDoc._panX = vfLeft + (vfWidth / 2);
-            targetDoc._panY = vfTop + (vfWidth / 2);
+            targetDoc._panX = vfLeft + vfWidth / 2;
+            targetDoc._panY = vfTop + vfWidth / 2;
             targetDoc._viewScale = newScale;
         }
         const resize = document.getElementById('resizable');
@@ -476,7 +500,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             resize.style.top = vfTop + 'px';
             resize.style.left = vfLeft + 'px';
         }
-    }
+    };
 
     /**
      * For 'Hide Before' and 'Hide After' buttons making sure that
@@ -490,18 +514,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (tagDoc) tagDoc.opacity = 1;
             const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, tagDoc);
             const curInd: number = itemIndexes.indexOf(index);
-            if (tagDoc === this.layoutDoc.presCollection) { tagDoc.opacity = 1; }
-            else {
-                if (itemIndexes.length > 1 && curDoc.presHideBefore && curInd !== 0) { }
-                else if (curDoc.presHideBefore) {
+            if (tagDoc === this.layoutDoc.presCollection) {
+                tagDoc.opacity = 1;
+            } else {
+                if (itemIndexes.length > 1 && curDoc.presHideBefore && curInd !== 0) {
+                } else if (curDoc.presHideBefore) {
                     if (index > this.itemIndex) {
                         tagDoc.opacity = 0;
                     } else if (!curDoc.presHideAfter) {
                         tagDoc.opacity = 1;
                     }
                 }
-                if (itemIndexes.length > 1 && curDoc.presHideAfter && curInd !== (itemIndexes.length - 1)) { }
-                else if (curDoc.presHideAfter) {
+                if (itemIndexes.length > 1 && curDoc.presHideAfter && curInd !== itemIndexes.length - 1) {
+                } else if (curDoc.presHideAfter) {
                     if (index < this.itemIndex) {
                         tagDoc.opacity = 0;
                     } else if (!curDoc.presHideBefore) {
@@ -510,9 +535,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 }
             }
         });
-    }
-
-
+    };
 
     //The function that starts or resets presentaton functionally, depending on presStatus of the layoutDoc
     @action
@@ -521,13 +544,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         let activeItem: Doc = this.activeItem;
         let targetDoc: Doc = this.targetDoc;
         let duration = NumCast(activeItem.presDuration) + NumCast(activeItem.presTransition);
-        const timer = (ms: number) => new Promise(res => this._presTimer = setTimeout(res, ms));
-        const load = async () => { // Wrap the loop into an async function for this to work
+        const timer = (ms: number) => new Promise(res => (this._presTimer = setTimeout(res, ms)));
+        const load = async () => {
+            // Wrap the loop into an async function for this to work
             for (var i = startSlide; i < this.childDocs.length; i++) {
                 activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
                 targetDoc = Cast(activeItem.presentationTargetDoc, Doc, null);
                 duration = NumCast(activeItem.presDuration) + NumCast(activeItem.presTransition);
-                if (duration < 100) { duration = 2500; }
+                if (duration < 100) {
+                    duration = 2500;
+                }
                 if (NumCast(targetDoc.lastFrame) > 0) {
                     for (var f = 0; f < NumCast(targetDoc.lastFrame); f++) {
                         await timer(duration / NumCast(targetDoc.lastFrame));
@@ -535,7 +561,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     }
                 }
 
-                await timer(duration); this.next(); // then the created Promise can be awaited
+                await timer(duration);
+                this.next(); // then the created Promise can be awaited
                 if (i === this.childDocs.length - 1) {
                     setTimeout(() => {
                         clearTimeout(this._presTimer);
@@ -549,7 +576,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.startPresentation(startSlide);
         this.gotoDocument(startSlide, activeItem);
         load();
-    }
+    };
 
     // The function pauses the auto presentation
     @action
@@ -560,20 +587,23 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.layoutDoc.presLoop = false;
             this.childDocs.forEach(this.stopTempMedia);
         }
-    }
+    };
 
     //The function that resets the presentation by removing every action done by it. It also
     //stops the presentaton.
     resetPresentation = () => {
         this.rootDoc._itemIndex = 0;
-        this.childDocs.map(doc => Cast(doc.presentationTargetDoc, Doc, null)).filter(doc => doc instanceof Doc).forEach(doc => {
-            try {
-                doc.opacity = 1;
-            } catch (e) {
-                console.log("Reset presentation error: ", e);
-            }
-        });
-    }
+        this.childDocs
+            .map(doc => Cast(doc.presentationTargetDoc, Doc, null))
+            .filter(doc => doc instanceof Doc)
+            .forEach(doc => {
+                try {
+                    doc.opacity = 1;
+                } catch (e) {
+                    console.log('Reset presentation error: ', e);
+                }
+            });
+    };
 
     // The function allows for viewing the pres path on toggle
     @action togglePath = (srcContext: Doc, off?: boolean) => {
@@ -581,19 +611,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this._pathBoolean = false;
             srcContext.presPathView = false;
         } else {
-            runInAction(() => this._pathBoolean = !this._pathBoolean);
+            runInAction(() => (this._pathBoolean = !this._pathBoolean));
             srcContext.presPathView = this._pathBoolean;
         }
-    }
+    };
 
     // The function allows for expanding the view of pres on toggle
     @action toggleExpandMode = () => {
-        runInAction(() => this._expandBoolean = !this._expandBoolean);
+        runInAction(() => (this._expandBoolean = !this._expandBoolean));
         this.rootDoc.expandBoolean = this._expandBoolean;
-        this.childDocs.forEach((doc) => {
+        this.childDocs.forEach(doc => {
             doc.presExpandInlineButton = this._expandBoolean;
         });
-    }
+    };
 
     /**
      * The function that starts the presentation at the given index, also checking if actions should be applied
@@ -611,7 +641,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 tagDoc.opacity = 0;
             }
         });
-    }
+    };
 
     /**
      * The method called to open the presentation as a minimized view
@@ -621,7 +651,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
             this.layoutDoc.presStatus = PresStatus.Edit;
             Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, this.rootDoc);
-            CollectionDockingView.AddSplit(this.rootDoc, "right");
+            CollectionDockingView.AddSplit(this.rootDoc, 'right');
         } else {
             this.layoutDoc.presStatus = PresStatus.Edit;
             clearTimeout(this._presTimer);
@@ -633,7 +663,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, this.rootDoc);
             this.props.removeDocument?.(this.layoutDoc);
         }
-    }
+    };
 
     /**
      * Called when the user changes the view type
@@ -643,7 +673,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     viewChanged = action((e: React.ChangeEvent) => {
         //@ts-ignore
         const viewType = e.target.selectedOptions[0].value as CollectionViewType;
-        this.layoutDoc.presFieldKey = this.fieldKey+(viewType === CollectionViewType.Tree ?"-linearized":"");
+        this.layoutDoc.presFieldKey = this.fieldKey + (viewType === CollectionViewType.Tree ? '-linearized' : '');
         // pivot field may be set by the user in timeline view (or some other way) -- need to reset it here
         [CollectionViewType.Tree || CollectionViewType.Stacking].includes(viewType) && (this.rootDoc._pivotField = undefined);
         this.rootDoc._viewType = viewType;
@@ -677,20 +707,30 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
     });
 
-
     setMovementName = action((movement: any, activeItem: Doc): string => {
         let output: string = 'none';
         switch (movement) {
-            case PresMovement.Zoom: output = 'Pan & Zoom'; break; //Pan and zoom
-            case PresMovement.Pan: output = 'Pan'; break; //Pan
-            case PresMovement.Jump: output = 'Jump cut'; break; //Jump Cut
-            case PresMovement.None: output = 'None'; break; //None
-            default: output = 'Zoom'; activeItem.presMovement = 'zoom'; break; //default set as zoom
+            case PresMovement.Zoom:
+                output = 'Pan & Zoom';
+                break; //Pan and zoom
+            case PresMovement.Pan:
+                output = 'Pan';
+                break; //Pan
+            case PresMovement.Jump:
+                output = 'Jump cut';
+                break; //Jump Cut
+            case PresMovement.None:
+                output = 'None';
+                break; //None
+            default:
+                output = 'Zoom';
+                activeItem.presMovement = 'zoom';
+                break; //default set as zoom
         }
         return output;
     });
 
-    whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isChildActive = isActive));
+    whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isChildActive = isActive)));
     // For dragging documents into the presentation trail
     addDocumentFilter = (docs: Doc[]) => {
         docs.forEach((doc, i) => {
@@ -698,8 +738,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (doc.type === DocumentType.LABEL) {
                 const audio = Cast(doc.annotationOn, Doc, null);
                 if (audio) {
-                    audio.mediaStart = "manual";
-                    audio.mediaStop = "manual";
+                    audio.mediaStart = 'manual';
+                    audio.mediaStop = 'manual';
                     audio.presStartTime = NumCast(doc._timecodeToShow /* audioStart */, NumCast(doc._timecodeToShow /* videoStart */));
                     audio.presEndTime = NumCast(doc._timecodeToHide /* audioEnd */, NumCast(doc._timecodeToHide /* videoEnd */));
                     audio.presDuration = audio.presStartTime - audio.presEndTime;
@@ -714,7 +754,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     setTimeout(() => this.removeDocument(doc), 0);
                     return false;
                 } else {
-                    if (!doc.presentationTargetDoc) doc.title = doc.title + " - Slide";
+                    if (!doc.presentationTargetDoc) doc.title = doc.title + ' - Slide';
                     doc.aliasOf instanceof Doc && (doc.presentationTargetDoc = doc.aliasOf);
                     doc.presMovement = PresMovement.Zoom;
                     if (this._expandBoolean) doc.presExpandInlineButton = true;
@@ -722,13 +762,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
         });
         return true;
-    }
-    childLayoutTemplate = () => !this.isTreeOrStack ? undefined : DocCast(Doc.UserDoc().presElement);
+    };
+    childLayoutTemplate = () => (!this.isTreeOrStack ? undefined : DocCast(Doc.UserDoc().presElement));
     removeDocument = (doc: Doc) => Doc.RemoveDocFromList(this.rootDoc, this.fieldKey, doc);
-    getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65);// listBox padding-left and pres-box-cont minHeight
+    getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65); // listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
-    isContentActive = (outsideReaction?: boolean) => ((CurrentUserUtils.ActiveTool === InkTool.None && !this.layoutDoc._lockedPosition) &&
-        (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false)
+    isContentActive = (outsideReaction?: boolean) =>
+        CurrentUserUtils.ActiveTool === InkTool.None && !this.layoutDoc._lockedPosition && (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false;
 
     /**
      * For sorting the array so that the order is maintained when it is dropped.
@@ -736,7 +776,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     sortArray = (): Doc[] => {
         return this.childDocs.filter(doc => this._selectedArray.has(doc));
-    }
+    };
 
     /**
      * Method to get the list of selected items in the order in which they have been selected
@@ -745,9 +785,26 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const list = Array.from(this._selectedArray.keys()).map((doc: Doc, index: any) => {
             const curDoc = Cast(doc, Doc, null);
             const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
-            if (curDoc && curDoc === this.activeItem) return <div key={index} className="selectedList-items"><b>{index + 1}.  {curDoc.title}</b></div>;
-            else if (tagDoc) return <div key={index} className="selectedList-items">{index + 1}.  {curDoc.title}</div>;
-            else if (curDoc) return <div key={index} className="selectedList-items">{index + 1}.  {curDoc.title}</div>;
+            if (curDoc && curDoc === this.activeItem)
+                return (
+                    <div key={index} className="selectedList-items">
+                        <b>
+                            {index + 1}. {curDoc.title}
+                        </b>
+                    </div>
+                );
+            else if (tagDoc)
+                return (
+                    <div key={index} className="selectedList-items">
+                        {index + 1}. {curDoc.title}
+                    </div>
+                );
+            else if (curDoc)
+                return (
+                    <div key={index} className="selectedList-items">
+                        {index + 1}. {curDoc.title}
+                    </div>
+                );
         });
         return list;
     }
@@ -756,7 +813,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     selectPres = () => {
         const presDocView = DocumentManager.Instance.getDocumentView(this.rootDoc);
         presDocView && SelectionManager.SelectView(presDocView, false);
-    }
+    };
 
     //Regular click
     @action
@@ -766,12 +823,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (doc.presPinView || doc.presentationTargetDoc === this.layoutDoc.presCollection) setTimeout(() => this.updateCurrentPresentation(context), 0);
         else this.updateCurrentPresentation(context);
 
-        if (this.activeItem.setPosition &&
-            this.activeItem.y !== undefined &&
-            this.activeItem.x !== undefined &&
-            this.targetDoc.x !== undefined &&
-            this.targetDoc.y !== undefined) {
-            const timer = (ms: number) => new Promise(res => this._presTimer = setTimeout(res, ms));
+        if (this.activeItem.setPosition && this.activeItem.y !== undefined && this.activeItem.x !== undefined && this.targetDoc.x !== undefined && this.targetDoc.y !== undefined) {
+            const timer = (ms: number) => new Promise(res => (this._presTimer = setTimeout(res, ms)));
             const time = 10;
             const ydiff = NumCast(this.activeItem.y) - NumCast(this.targetDoc.y);
             const xdiff = NumCast(this.activeItem.x) - NumCast(this.targetDoc.x);
@@ -782,7 +835,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 await timer(0.1);
             }
         }
-    }
+    };
 
     //Command click
     @action
@@ -797,13 +850,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.removeFromArray(this._dragArray, doc);
         }
         this.selectPres();
-    }
+    };
 
     removeFromArray = (arr: any[], val: any) => {
         const index: number = arr.indexOf(val);
         const ret: any[] = arr.splice(index, 1);
         arr = ret;
-    }
+    };
 
     //Shift click
     @action
@@ -818,7 +871,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
         }
         this.selectPres();
-    }
+    };
 
     //regular click
     @action
@@ -829,17 +882,17 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this._dragArray.splice(0, this._dragArray.length, drag);
         focus && this.selectElement(doc);
         selectPres && this.selectPres();
-    }
+    };
 
     modifierSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, cmdClick: boolean, shiftClick: boolean) => {
         if (cmdClick) this.multiSelect(doc, ref, drag);
         else if (shiftClick) this.shiftSelect(doc, ref, drag);
         else this.regularSelect(doc, ref, drag, focus);
-    }
+    };
 
     static keyEventsWrapper = (e: KeyboardEvent) => {
         PresBox.Instance.keyEvents(e);
-    }
+    };
 
     // Key for when the presentaiton is active
     @action
@@ -847,57 +900,75 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (e.target instanceof HTMLInputElement) return;
         let handled = false;
         const anchorNode = document.activeElement as HTMLDivElement;
-        if (anchorNode && anchorNode.className?.includes("lm_title")) return;
+        if (anchorNode && anchorNode.className?.includes('lm_title')) return;
         switch (e.key) {
-            case "Backspace":
-                if (this.layoutDoc.presStatus === "edit") {
-                    undoBatch(action(() => {
-                        for (const doc of Array.from(this._selectedArray.keys())) {
-                            this.removeDocument(doc);
-                        }
-                        this._selectedArray.clear();
-                        this._eleArray.length = 0;
-                        this._dragArray.length = 0;
-                    }))();
+            case 'Backspace':
+                if (this.layoutDoc.presStatus === 'edit') {
+                    undoBatch(
+                        action(() => {
+                            for (const doc of Array.from(this._selectedArray.keys())) {
+                                this.removeDocument(doc);
+                            }
+                            this._selectedArray.clear();
+                            this._eleArray.length = 0;
+                            this._dragArray.length = 0;
+                        })
+                    )();
                     handled = true;
                 }
                 break;
-            case "Escape":
-                if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) { this.updateMinimize(); }
-                else if (this.layoutDoc.presStatus === "edit") { this._selectedArray.clear(); this._eleArray.length = this._dragArray.length = 0; }
-                else this.layoutDoc.presStatus = "edit";
+            case 'Escape':
+                if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
+                    this.updateMinimize();
+                } else if (this.layoutDoc.presStatus === 'edit') {
+                    this._selectedArray.clear();
+                    this._eleArray.length = this._dragArray.length = 0;
+                } else this.layoutDoc.presStatus = 'edit';
                 if (this._presTimer) clearTimeout(this._presTimer);
                 handled = true;
                 break;
-            case "Down": case "ArrowDown":
-            case "Right": case "ArrowRight":
-                if (e.shiftKey && this.itemIndex < this.childDocs.length - 1) { // TODO: update to work properly
+            case 'Down':
+            case 'ArrowDown':
+            case 'Right':
+            case 'ArrowRight':
+                if (e.shiftKey && this.itemIndex < this.childDocs.length - 1) {
+                    // TODO: update to work properly
                     this.rootDoc._itemIndex = NumCast(this.rootDoc._itemIndex) + 1;
                     this._selectedArray.set(this.childDocs[this.rootDoc._itemIndex], undefined);
                 } else {
                     this.next();
-                    if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; }
+                    if (this._presTimer) {
+                        clearTimeout(this._presTimer);
+                        this.layoutDoc.presStatus = PresStatus.Manual;
+                    }
                 }
                 handled = true;
                 break;
-            case "Up": case "ArrowUp":
-            case "Left": case "ArrowLeft":
-                if (e.shiftKey && this.itemIndex !== 0) { // TODO: update to work properly
+            case 'Up':
+            case 'ArrowUp':
+            case 'Left':
+            case 'ArrowLeft':
+                if (e.shiftKey && this.itemIndex !== 0) {
+                    // TODO: update to work properly
                     this.rootDoc._itemIndex = NumCast(this.rootDoc._itemIndex) - 1;
                     this._selectedArray.set(this.childDocs[this.rootDoc._itemIndex], undefined);
                 } else {
                     this.back();
-                    if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; }
+                    if (this._presTimer) {
+                        clearTimeout(this._presTimer);
+                        this.layoutDoc.presStatus = PresStatus.Manual;
+                    }
                 }
                 handled = true;
                 break;
-            case "Spacebar": case " ":
+            case 'Spacebar':
+            case ' ':
                 if (this.layoutDoc.presStatus === PresStatus.Manual) this.startAutoPres(this.itemIndex);
                 else if (this.layoutDoc.presStatus === PresStatus.Autoplay) if (this._presTimer) clearTimeout(this._presTimer);
                 handled = true;
                 break;
-            case "a":
-                if ((e.metaKey || e.altKey) && this.layoutDoc.presStatus === "edit") {
+            case 'a':
+                if ((e.metaKey || e.altKey) && this.layoutDoc.presStatus === 'edit') {
                     this._selectedArray.clear();
                     this.childDocs.forEach(doc => this._selectedArray.set(doc, undefined));
                     handled = true;
@@ -909,10 +980,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             e.stopPropagation();
             e.preventDefault();
         }
-    }
+    };
 
     /**
-     * 
+     *
      */
     @action
     viewPaths = () => {
@@ -920,7 +991,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (srcContext) {
             this.togglePath(srcContext);
         }
-    }
+    };
 
     getAllIndexes = (arr: Doc[], val: Doc): number[] => {
         const indexes = [];
@@ -928,7 +999,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             arr[i] === val && indexes.push(i);
         }
         return indexes;
-    }
+    };
 
     // Adds the index in the pres path graphically
     @computed get order() {
@@ -936,61 +1007,64 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const docs: Doc[] = [];
         const presCollection = Cast(this.rootDoc.presCollection, Doc, null);
         const dv = DocumentManager.Instance.getDocumentView(presCollection);
-        this.childDocs.filter(doc => Cast(doc.presentationTargetDoc, Doc, null)).forEach((doc, index) => {
-            const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
-            const srcContext = Cast(tagDoc.context, Doc, null);
-            const width = NumCast(tagDoc._width) / 10;
-            const height = Math.max(NumCast(tagDoc._height) / 10, 15);
-            const edge = Math.max(width, height);
-            const fontSize = edge * 0.8;
-            const gap = 2;
-            if (presCollection === srcContext) {
-                // Case A: Document is contained within the collection
-                if (docs.includes(tagDoc)) {
-                    const prevOccurances: number = this.getAllIndexes(docs, tagDoc).length;
-                    docs.push(tagDoc);
-                    order.push(
-                        <div className="pathOrder"
-                            key={tagDoc.id + 'pres' + index}
-                            style={{ top: NumCast(tagDoc.y) + (prevOccurances * (edge + gap) - (edge / 2)), left: NumCast(tagDoc.x) - (edge / 2), width: edge, height: edge, fontSize: fontSize }}
-                            onClick={() => this.selectElement(doc)}>
-                            <div className="pathOrder-frame">{index + 1}</div>
-                        </div>);
-                } else {
+        this.childDocs
+            .filter(doc => Cast(doc.presentationTargetDoc, Doc, null))
+            .forEach((doc, index) => {
+                const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
+                const srcContext = Cast(tagDoc.context, Doc, null);
+                const width = NumCast(tagDoc._width) / 10;
+                const height = Math.max(NumCast(tagDoc._height) / 10, 15);
+                const edge = Math.max(width, height);
+                const fontSize = edge * 0.8;
+                const gap = 2;
+                if (presCollection === srcContext) {
+                    // Case A: Document is contained within the collection
+                    if (docs.includes(tagDoc)) {
+                        const prevOccurances: number = this.getAllIndexes(docs, tagDoc).length;
+                        docs.push(tagDoc);
+                        order.push(
+                            <div
+                                className="pathOrder"
+                                key={tagDoc.id + 'pres' + index}
+                                style={{ top: NumCast(tagDoc.y) + (prevOccurances * (edge + gap) - edge / 2), left: NumCast(tagDoc.x) - edge / 2, width: edge, height: edge, fontSize: fontSize }}
+                                onClick={() => this.selectElement(doc)}>
+                                <div className="pathOrder-frame">{index + 1}</div>
+                            </div>
+                        );
+                    } else {
+                        docs.push(tagDoc);
+                        order.push(
+                            <div
+                                className="pathOrder"
+                                key={tagDoc.id + 'pres' + index}
+                                style={{ top: NumCast(tagDoc.y) - edge / 2, left: NumCast(tagDoc.x) - edge / 2, width: edge, height: edge, fontSize: fontSize }}
+                                onClick={() => this.selectElement(doc)}>
+                                <div className="pathOrder-frame">{index + 1}</div>
+                            </div>
+                        );
+                    }
+                } else if (doc.presPinView && presCollection === tagDoc && dv) {
+                    // Case B: Document is presPinView and is presCollection
+                    const scale: number = 1 / NumCast(doc.presPinViewScale);
+                    const height: number = dv.props.PanelHeight() * scale;
+                    const width: number = dv.props.PanelWidth() * scale;
+                    const indWidth = width / 10;
+                    const indHeight = Math.max(height / 10, 15);
+                    const indEdge = Math.max(indWidth, indHeight);
+                    const indFontSize = indEdge * 0.8;
+                    const xLoc: number = NumCast(doc.presPinViewX) - width / 2;
+                    const yLoc: number = NumCast(doc.presPinViewY) - height / 2;
                     docs.push(tagDoc);
                     order.push(
-                        <div className="pathOrder"
-                            key={tagDoc.id + 'pres' + index}
-                            style={{ top: NumCast(tagDoc.y) - (edge / 2), left: NumCast(tagDoc.x) - (edge / 2), width: edge, height: edge, fontSize: fontSize }}
-                            onClick={() => this.selectElement(doc)}>
-                            <div className="pathOrder-frame">{index + 1}</div>
-                        </div>);
+                        <>
+                            <div className="pathOrder" key={tagDoc.id + 'pres' + index} style={{ top: yLoc - indEdge / 2, left: xLoc - indEdge / 2, width: indEdge, height: indEdge, fontSize: indFontSize }} onClick={() => this.selectElement(doc)}>
+                                <div className="pathOrder-frame">{index + 1}</div>
+                            </div>
+                            <div className="pathOrder-presPinView" style={{ top: yLoc, left: xLoc, width: width, height: height, borderWidth: indEdge / 10 }}></div>
+                        </>
+                    );
                 }
-            } else if (doc.presPinView && presCollection === tagDoc && dv) {
-                // Case B: Document is presPinView and is presCollection
-                const scale: number = 1 / NumCast(doc.presPinViewScale);
-                const height: number = dv.props.PanelHeight() * scale;
-                const width: number = dv.props.PanelWidth() * scale;
-                const indWidth = width / 10;
-                const indHeight = Math.max(height / 10, 15);
-                const indEdge = Math.max(indWidth, indHeight);
-                const indFontSize = indEdge * 0.8;
-                const xLoc: number = NumCast(doc.presPinViewX) - (width / 2);
-                const yLoc: number = NumCast(doc.presPinViewY) - (height / 2);
-                docs.push(tagDoc);
-                order.push(
-                    <>
-                        <div className="pathOrder"
-                            key={tagDoc.id + 'pres' + index}
-                            style={{ top: yLoc - (indEdge / 2), left: xLoc - (indEdge / 2), width: indEdge, height: indEdge, fontSize: indFontSize }}
-                            onClick={() => this.selectElement(doc)}
-                        >
-                            <div className="pathOrder-frame">{index + 1}</div>
-                        </div>
-                        <div className="pathOrder-presPinView" style={{ top: yLoc, left: xLoc, width: width, height: height, borderWidth: indEdge / 10 }}></div>
-                    </>);
-            }
-        });
+            });
         return order;
     }
 
@@ -1003,37 +1077,39 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
      * collection)
      */
     @computed get paths() {
-        let pathPoints = "";
+        let pathPoints = '';
         const presCollection = Cast(this.rootDoc.presCollection, Doc, null);
         this.childDocs.forEach((doc, index) => {
             const tagDoc = Cast(doc.presentationTargetDoc, Doc, null);
             const srcContext = Cast(tagDoc?.context, Doc, null);
             if (tagDoc && presCollection === srcContext) {
-                const n1x = NumCast(tagDoc.x) + (NumCast(tagDoc._width) / 2);
-                const n1y = NumCast(tagDoc.y) + (NumCast(tagDoc._height) / 2);
-                if (index = 0) pathPoints = n1x + "," + n1y;
-                else pathPoints = pathPoints + " " + n1x + "," + n1y;
+                const n1x = NumCast(tagDoc.x) + NumCast(tagDoc._width) / 2;
+                const n1y = NumCast(tagDoc.y) + NumCast(tagDoc._height) / 2;
+                if ((index = 0)) pathPoints = n1x + ',' + n1y;
+                else pathPoints = pathPoints + ' ' + n1x + ',' + n1y;
             } else if (doc.presPinView && presCollection === tagDoc) {
                 const n1x = NumCast(doc.presPinViewX);
                 const n1y = NumCast(doc.presPinViewY);
-                if (index = 0) pathPoints = n1x + "," + n1y;
-                else pathPoints = pathPoints + " " + n1x + "," + n1y;
+                if ((index = 0)) pathPoints = n1x + ',' + n1y;
+                else pathPoints = pathPoints + ' ' + n1x + ',' + n1y;
             }
         });
-        return (<polyline
-            points={pathPoints}
-            style={{
-                opacity: 1,
-                stroke: "#69a6db",
-                strokeWidth: 5,
-                strokeDasharray: '10 5',
-                boxShadow: '0px 4px 4px rgba(0, 0, 0, 0.25)',
-            }}
-            fill="none"
-            markerStart="url(#markerArrow)"
-            markerMid="url(#markerSquare)"
-            markerEnd="url(#markerSquareFilled)"
-        />);
+        return (
+            <polyline
+                points={pathPoints}
+                style={{
+                    opacity: 1,
+                    stroke: '#69a6db',
+                    strokeWidth: 5,
+                    strokeDasharray: '10 5',
+                    boxShadow: '0px 4px 4px rgba(0, 0, 0, 0.25)',
+                }}
+                fill="none"
+                markerStart="url(#markerArrow)"
+                markerMid="url(#markerSquare)"
+                markerEnd="url(#markerSquareFilled)"
+            />
+        );
     }
 
     // Converts seconds to ms and updates presTransition
@@ -1042,8 +1118,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (change) timeInMS += change;
         if (timeInMS < 100) timeInMS = 100;
         if (timeInMS > 10000) timeInMS = 10000;
-        Array.from(this._selectedArray.keys()).forEach((doc) => doc.presTransition = timeInMS);
-    }
+        Array.from(this._selectedArray.keys()).forEach(doc => (doc.presTransition = timeInMS));
+    };
 
     // Converts seconds to ms and updates presTransition
     setZoom = (number: String, change?: number) => {
@@ -1051,8 +1127,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (change) scale += change;
         if (scale < 0.01) scale = 0.01;
         if (scale > 1.5) scale = 1.5;
-        Array.from(this._selectedArray.keys()).forEach((doc) => doc.presZoom = scale);
-    }
+        Array.from(this._selectedArray.keys()).forEach(doc => (doc.presZoom = scale));
+    };
 
     // Converts seconds to ms and updates presDuration
     setDurationTime = (number: String, change?: number) => {
@@ -1060,8 +1136,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (change) timeInMS += change;
         if (timeInMS < 100) timeInMS = 100;
         if (timeInMS > 20000) timeInMS = 20000;
-        Array.from(this._selectedArray.keys()).forEach((doc) => doc.presDuration = timeInMS);
-    }
+        Array.from(this._selectedArray.keys()).forEach(doc => (doc.presDuration = timeInMS));
+    };
 
     /**
      * When the movement dropdown is changes
@@ -1069,7 +1145,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @undoBatch
     updateMovement = action((movement: any, all?: boolean) => {
         const array: any[] = all ? this.childDocs : Array.from(this._selectedArray.keys());
-        array.forEach((doc) => {
+        array.forEach(doc => {
             switch (movement) {
                 case PresMovement.Zoom: //Pan and zoom
                     doc.presMovement = PresMovement.Zoom;
@@ -1081,7 +1157,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     doc.presJump = true;
                     doc.presMovement = PresMovement.Jump;
                     break;
-                case PresMovement.None: default:
+                case PresMovement.None:
+                default:
                     doc.presMovement = PresMovement.None;
                     break;
             }
@@ -1092,31 +1169,31 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     updateHideBefore = (activeItem: Doc) => {
         activeItem.presHideBefore = !activeItem.presHideBefore;
-        Array.from(this._selectedArray.keys()).forEach((doc) => doc.presHideBefore = activeItem.presHideBefore);
-    }
+        Array.from(this._selectedArray.keys()).forEach(doc => (doc.presHideBefore = activeItem.presHideBefore));
+    };
 
     @undoBatch
     @action
     updateHideAfter = (activeItem: Doc) => {
         activeItem.presHideAfter = !activeItem.presHideAfter;
-        Array.from(this._selectedArray.keys()).forEach((doc) => doc.presHideAfter = activeItem.presHideAfter);
-    }
+        Array.from(this._selectedArray.keys()).forEach(doc => (doc.presHideAfter = activeItem.presHideAfter));
+    };
 
     @undoBatch
     @action
     updateOpenDoc = (activeItem: Doc) => {
         activeItem.openDocument = !activeItem.openDocument;
-        Array.from(this._selectedArray.keys()).forEach((doc) => {
+        Array.from(this._selectedArray.keys()).forEach(doc => {
             doc.openDocument = activeItem.openDocument;
         });
-    }
+    };
 
     @undoBatch
     @action
     updateEffectDirection = (effect: any, all?: boolean) => {
         const array: any[] = all ? this.childDocs : Array.from(this._selectedArray.keys());
-        array.forEach((doc) => {
-            const tagDoc = doc;// Cast(doc.presentationTargetDoc, Doc, null);
+        array.forEach(doc => {
+            const tagDoc = doc; // Cast(doc.presentationTargetDoc, Doc, null);
             switch (effect) {
                 case PresEffect.Left:
                     tagDoc.presEffectDirection = PresEffect.Left;
@@ -1130,19 +1207,20 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 case PresEffect.Bottom:
                     tagDoc.presEffectDirection = PresEffect.Bottom;
                     break;
-                case PresEffect.Center: default:
+                case PresEffect.Center:
+                default:
                     tagDoc.presEffectDirection = PresEffect.Center;
                     break;
             }
         });
-    }
+    };
 
     @undoBatch
     @action
     updateEffect = (effect: any, all?: boolean) => {
         const array: any[] = all ? this.childDocs : Array.from(this._selectedArray.keys());
-        array.forEach((doc) => {
-            const tagDoc = doc;//Cast(doc.presentationTargetDoc, Doc, null);
+        array.forEach(doc => {
+            const tagDoc = doc; //Cast(doc.presentationTargetDoc, Doc, null);
             switch (effect) {
                 case PresEffect.Bounce:
                     tagDoc.presEffect = PresEffect.Bounce;
@@ -1159,19 +1237,20 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 case PresEffect.Rotate:
                     tagDoc.presEffect = PresEffect.Rotate;
                     break;
-                case PresEffect.None: default:
+                case PresEffect.None:
+                default:
                     tagDoc.presEffect = PresEffect.None;
                     break;
             }
         });
-    }
+    };
 
     _batch: UndoManager.Batch | undefined = undefined;
 
     @computed get transitionDropdown() {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
-        const isPresCollection: boolean = (targetDoc === this.layoutDoc.presCollection);
+        const isPresCollection: boolean = targetDoc === this.layoutDoc.presCollection;
         const isPinWithView: boolean = BoolCast(activeItem.presPinView);
         if (activeItem && targetDoc) {
             const type = targetDoc.type;
@@ -1182,155 +1261,301 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const effect = this.activeItem.presEffect ? this.activeItem.presEffect : 'None';
             activeItem.presMovement = activeItem.presMovement ? activeItem.presMovement : 'Zoom';
             return (
-                <div className={`presBox-ribbon ${this.transitionTools && this.layoutDoc.presStatus === PresStatus.Edit ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this.openMovementDropdown = false; this.openEffectDropdown = false; })}>
+                <div
+                    className={`presBox-ribbon ${this.transitionTools && this.layoutDoc.presStatus === PresStatus.Edit ? 'active' : ''}`}
+                    onPointerDown={e => e.stopPropagation()}
+                    onPointerUp={e => e.stopPropagation()}
+                    onClick={action(e => {
+                        e.stopPropagation();
+                        this.openMovementDropdown = false;
+                        this.openEffectDropdown = false;
+                    })}>
                     <div className="ribbon-box">
                         Movement
-                        {isPresCollection || (isPresCollection && isPinWithView) ?
+                        {isPresCollection || (isPresCollection && isPinWithView) ? (
                             <div className="ribbon-property" style={{ marginLeft: 0, height: 25, textAlign: 'left', paddingLeft: 5, paddingRight: 5, fontSize: 10 }}>
-                                {this.scrollable ? "Scroll to pinned view" : !isPinWithView ? "No movement" : "Pan & Zoom to pinned view"}
+                                {this.scrollable ? 'Scroll to pinned view' : !isPinWithView ? 'No movement' : 'Pan & Zoom to pinned view'}
                             </div>
-                            :
-                            <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openMovementDropdown = !this.openMovementDropdown; })} style={{ borderBottomLeftRadius: this.openMovementDropdown ? 0 : 5, border: this.openMovementDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
+                        ) : (
+                            <div
+                                className="presBox-dropdown"
+                                onClick={action(e => {
+                                    e.stopPropagation();
+                                    this.openMovementDropdown = !this.openMovementDropdown;
+                                })}
+                                style={{ borderBottomLeftRadius: this.openMovementDropdown ? 0 : 5, border: this.openMovementDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
                                 {this.setMovementName(activeItem.presMovement, activeItem)}
-                                <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openMovementDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={"angle-down"} />
-                                <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={e => e.stopPropagation()} style={{ display: this.openMovementDropdown ? "grid" : "none" }}>
-                                    <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.None ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.None)}>None</div>
-                                    <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Zoom ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Zoom)}>Pan {"&"} Zoom</div>
-                                    <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Pan ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Pan)}>Pan</div>
-                                    <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Jump ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Jump)}>Jump cut</div>
+                                <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this.openMovementDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
+                                <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={e => e.stopPropagation()} style={{ display: this.openMovementDropdown ? 'grid' : 'none' }}>
+                                    <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.None ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.None)}>
+                                        None
+                                    </div>
+                                    <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Zoom ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Zoom)}>
+                                        Pan {'&'} Zoom
+                                    </div>
+                                    <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Pan ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Pan)}>
+                                        Pan
+                                    </div>
+                                    <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Jump ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Jump)}>
+                                        Jump cut
+                                    </div>
                                 </div>
                             </div>
-                        }
-                        <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Zoom ? "inline-flex" : "none" }}>
+                        )}
+                        <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Zoom ? 'inline-flex' : 'none' }}>
                             <div className="presBox-subheading">Zoom (% screen filled)</div>
                             <div className="ribbon-property">
-                                <input className="presBox-input"
-                                    type="number" value={zoom}
-                                    onChange={action((e) => this.setZoom(e.target.value))} />%
+                                <input className="presBox-input" type="number" value={zoom} onChange={action(e => this.setZoom(e.target.value))} />%
                             </div>
                             <div className="ribbon-propertyUpDown">
                                 <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), 0.1))}>
-                                    <FontAwesomeIcon icon={"caret-up"} />
+                                    <FontAwesomeIcon icon={'caret-up'} />
                                 </div>
                                 <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setZoom(String(zoom), -0.1))}>
-                                    <FontAwesomeIcon icon={"caret-down"} />
+                                    <FontAwesomeIcon icon={'caret-down'} />
                                 </div>
                             </div>
                         </div>
-                        <input type="range" step="1" min="0" max="150" value={zoom}
-                            className={`toolbar-slider ${activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`}
+                        <input
+                            type="range"
+                            step="1"
+                            min="0"
+                            max="150"
+                            value={zoom}
+                            className={`toolbar-slider ${activeItem.presMovement === PresMovement.Zoom ? '' : 'none'}`}
                             id="toolbar-slider"
-                            onPointerDown={() => this._batch = UndoManager.StartBatch("presZoom")}
+                            onPointerDown={() => (this._batch = UndoManager.StartBatch('presZoom'))}
                             onPointerUp={() => this._batch?.end()}
                             onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                                 e.stopPropagation();
                                 this.setZoom(e.target.value);
-                            }} />
-                        <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "inline-flex" : "none" }}>
+                            }}
+                        />
+                        <div className="ribbon-doubleButton" style={{ display: activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? 'inline-flex' : 'none' }}>
                             <div className="presBox-subheading">Movement Speed</div>
                             <div className="ribbon-property">
-                                <input className="presBox-input"
-                                    type="number" value={transitionSpeed}
-                                    onKeyDown={e => e.stopPropagation()}
-                                    onChange={action((e) => this.setTransitionTime(e.target.value))} /> s
+                                <input className="presBox-input" type="number" value={transitionSpeed} onKeyDown={e => e.stopPropagation()} onChange={action(e => this.setTransitionTime(e.target.value))} /> s
                             </div>
                             <div className="ribbon-propertyUpDown">
                                 <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setTransitionTime(String(transitionSpeed), 1000))}>
-                                    <FontAwesomeIcon icon={"caret-up"} />
+                                    <FontAwesomeIcon icon={'caret-up'} />
                                 </div>
                                 <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setTransitionTime(String(transitionSpeed), -1000))}>
-                                    <FontAwesomeIcon icon={"caret-down"} />
+                                    <FontAwesomeIcon icon={'caret-down'} />
                                 </div>
                             </div>
                         </div>
-                        <input type="range" step="0.1" min="0.1" max="10" value={transitionSpeed}
-                            className={`toolbar-slider ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`}
+                        <input
+                            type="range"
+                            step="0.1"
+                            min="0.1"
+                            max="10"
+                            value={transitionSpeed}
+                            className={`toolbar-slider ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? '' : 'none'}`}
                             id="toolbar-slider"
-                            onPointerDown={() => this._batch = UndoManager.StartBatch("presTransition")}
+                            onPointerDown={() => (this._batch = UndoManager.StartBatch('presTransition'))}
                             onPointerUp={() => this._batch?.end()}
                             onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                                 e.stopPropagation();
                                 this.setTransitionTime(e.target.value);
-                            }} />
-                        <div className={`slider-headers ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`}>
+                            }}
+                        />
+                        <div className={`slider-headers ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? '' : 'none'}`}>
                             <div className="slider-text">Fast</div>
                             <div className="slider-text">Medium</div>
                             <div className="slider-text">Slow</div>
                         </div>
                     </div>
                     <div className="ribbon-box">
-                        Visibility {"&"} Duration
+                        Visibility {'&'} Duration
                         <div className="ribbon-doubleButton">
-                            {isPresCollection ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Hide before presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideBefore ? "active" : ""}`} onClick={() => this.updateHideBefore(activeItem)}>Hide before</div></Tooltip>}
-                            {isPresCollection ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Hide after presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideAfter ? "active" : ""}`} onClick={() => this.updateHideAfter(activeItem)}>Hide after</div></Tooltip>}
-                            <Tooltip title={<><div className="dash-tooltip">{"Open in lightbox view"}</div></>}><div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? Colors.LIGHT_BLUE : "" }} onClick={() => this.updateOpenDoc(activeItem)}>Lightbox</div></Tooltip>
+                            {isPresCollection ? null : (
+                                <Tooltip
+                                    title={
+                                        <>
+                                            <div className="dash-tooltip">{'Hide before presented'}</div>
+                                        </>
+                                    }>
+                                    <div className={`ribbon-toggle ${activeItem.presHideBefore ? 'active' : ''}`} onClick={() => this.updateHideBefore(activeItem)}>
+                                        Hide before
+                                    </div>
+                                </Tooltip>
+                            )}
+                            {isPresCollection ? null : (
+                                <Tooltip
+                                    title={
+                                        <>
+                                            <div className="dash-tooltip">{'Hide after presented'}</div>
+                                        </>
+                                    }>
+                                    <div className={`ribbon-toggle ${activeItem.presHideAfter ? 'active' : ''}`} onClick={() => this.updateHideAfter(activeItem)}>
+                                        Hide after
+                                    </div>
+                                </Tooltip>
+                            )}
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">{'Open in lightbox view'}</div>
+                                    </>
+                                }>
+                                <div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? Colors.LIGHT_BLUE : '' }} onClick={() => this.updateOpenDoc(activeItem)}>
+                                    Lightbox
+                                </div>
+                            </Tooltip>
                         </div>
-                        {(type === DocumentType.AUDIO || type === DocumentType.VID) ? (null) : <>
-                            <div className="ribbon-doubleButton" >
-                                <div className="presBox-subheading">Slide Duration</div>
-                                <div className="ribbon-property">
-                                    <input className="presBox-input"
-                                        type="number" value={duration}
-                                        onKeyDown={e => e.stopPropagation()}
-                                        onChange={action((e) => this.setDurationTime(e.target.value))} /> s
+                        {type === DocumentType.AUDIO || type === DocumentType.VID ? null : (
+                            <>
+                                <div className="ribbon-doubleButton">
+                                    <div className="presBox-subheading">Slide Duration</div>
+                                    <div className="ribbon-property">
+                                        <input className="presBox-input" type="number" value={duration} onKeyDown={e => e.stopPropagation()} onChange={action(e => this.setDurationTime(e.target.value))} /> s
+                                    </div>
+                                    <div className="ribbon-propertyUpDown">
+                                        <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setDurationTime(String(duration), 1000))}>
+                                            <FontAwesomeIcon icon={'caret-up'} />
+                                        </div>
+                                        <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setDurationTime(String(duration), -1000))}>
+                                            <FontAwesomeIcon icon={'caret-down'} />
+                                        </div>
+                                    </div>
+                                </div>
+                                <input
+                                    type="range"
+                                    step="0.1"
+                                    min="0.1"
+                                    max="20"
+                                    value={duration}
+                                    style={{ display: targetDoc.type === DocumentType.AUDIO ? 'none' : 'block' }}
+                                    className={'toolbar-slider'}
+                                    id="duration-slider"
+                                    onPointerDown={() => {
+                                        this._batch = UndoManager.StartBatch('presDuration');
+                                    }}
+                                    onPointerUp={() => {
+                                        if (this._batch) this._batch.end();
+                                    }}
+                                    onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
+                                        e.stopPropagation();
+                                        this.setDurationTime(e.target.value);
+                                    }}
+                                />
+                                <div className={'slider-headers'} style={{ display: targetDoc.type === DocumentType.AUDIO ? 'none' : 'grid' }}>
+                                    <div className="slider-text">Short</div>
+                                    <div className="slider-text">Medium</div>
+                                    <div className="slider-text">Long</div>
                                 </div>
-                                <div className="ribbon-propertyUpDown">
-                                    <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setDurationTime(String(duration), 1000))}>
-                                        <FontAwesomeIcon icon={"caret-up"} />
+                            </>
+                        )}
+                    </div>
+                    {isPresCollection ? null : (
+                        <div className="ribbon-box">
+                            Effects
+                            <div
+                                className="presBox-dropdown"
+                                onClick={action(e => {
+                                    e.stopPropagation();
+                                    this.openEffectDropdown = !this.openEffectDropdown;
+                                })}
+                                style={{ borderBottomLeftRadius: this.openEffectDropdown ? 0 : 5, border: this.openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
+                                {effect.toString()}
+                                <FontAwesomeIcon className="presBox-dropdownIcon" style={{ gridColumn: 2, color: this.openEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={'angle-down'} />
+                                <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this.openEffectDropdown ? 'grid' : 'none' }} onPointerDown={e => e.stopPropagation()}>
+                                    <div
+                                        className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.None || !this.activeItem.presEffect ? 'active' : ''}`}
+                                        onPointerDown={e => e.stopPropagation()}
+                                        onClick={() => this.updateEffect(PresEffect.None)}>
+                                        None
                                     </div>
-                                    <div className="ribbon-propertyUpDownItem" onClick={undoBatch(() => this.setDurationTime(String(duration), -1000))}>
-                                        <FontAwesomeIcon icon={"caret-down"} />
+                                    <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Fade ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Fade)}>
+                                        Fade In
+                                    </div>
+                                    <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Flip ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Flip)}>
+                                        Flip
+                                    </div>
+                                    <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Rotate ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Rotate)}>
+                                        Rotate
+                                    </div>
+                                    <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Bounce ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Bounce)}>
+                                        Bounce
+                                    </div>
+                                    <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Roll ? 'active' : ''}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Roll)}>
+                                        Roll
                                     </div>
                                 </div>
                             </div>
-                            <input type="range" step="0.1" min="0.1" max="20" value={duration}
-                                style={{ display: targetDoc.type === DocumentType.AUDIO ? "none" : "block" }}
-                                className={"toolbar-slider"} id="duration-slider"
-                                onPointerDown={() => { this._batch = UndoManager.StartBatch("presDuration"); }}
-                                onPointerUp={() => { if (this._batch) this._batch.end(); }}
-                                onChange={(e: React.ChangeEvent<HTMLInputElement>) => { e.stopPropagation(); this.setDurationTime(e.target.value); }}
-                            />
-                            <div className={"slider-headers"} style={{ display: targetDoc.type === DocumentType.AUDIO ? "none" : "grid" }}>
-                                <div className="slider-text">Short</div>
-                                <div className="slider-text">Medium</div>
-                                <div className="slider-text">Long</div>
+                            <div className="ribbon-doubleButton" style={{ display: effect === 'None' ? 'none' : 'inline-flex' }}>
+                                <div className="presBox-subheading">Effect direction</div>
+                                <div className="ribbon-property">{this.effectDirection}</div>
                             </div>
-                        </>}
-                    </div>
-                    {isPresCollection ? (null) : <div className="ribbon-box">
-                        Effects
-                        <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openEffectDropdown = !this.openEffectDropdown; })} style={{ borderBottomLeftRadius: this.openEffectDropdown ? 0 : 5, border: this.openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}>
-                            {effect}
-                            <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={"angle-down"} />
-                            <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this.openEffectDropdown ? "grid" : "none" }} onPointerDown={e => e.stopPropagation()}>
-                                <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.None || !this.activeItem.presEffect ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.None)}>None</div>
-                                <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Fade ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Fade)}>Fade In</div>
-                                <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Flip ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Flip)}>Flip</div>
-                                <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Rotate ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Rotate)}>Rotate</div>
-                                <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Bounce ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Bounce)}>Bounce</div>
-                                <div className={`presBox-dropdownOption ${this.activeItem.presEffect === PresEffect.Roll ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Roll)}>Roll</div>
-                            </div>
-                        </div>
-                        <div className="ribbon-doubleButton" style={{ display: effect === 'None' ? "none" : "inline-flex" }}>
-                            <div className="presBox-subheading" >Effect direction</div>
-                            <div className="ribbon-property">
-                                {this.effectDirection}
+                            <div className="effectDirection" style={{ display: effect === 'None' ? 'none' : 'grid', width: 40 }}>
+                                <Tooltip title={<div className="dash-tooltip">{'Enter from left'}</div>}>
+                                    <div
+                                        style={{ gridColumn: 1, gridRow: 2, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Left ? Colors.LIGHT_BLUE : 'black', cursor: 'pointer' }}
+                                        onClick={() => this.updateEffectDirection(PresEffect.Left)}>
+                                        <FontAwesomeIcon icon={'angle-right'} />
+                                    </div>
+                                </Tooltip>
+                                <Tooltip title={<div className="dash-tooltip">{'Enter from right'}</div>}>
+                                    <div
+                                        style={{ gridColumn: 3, gridRow: 2, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Right ? Colors.LIGHT_BLUE : 'black', cursor: 'pointer' }}
+                                        onClick={() => this.updateEffectDirection(PresEffect.Right)}>
+                                        <FontAwesomeIcon icon={'angle-left'} />
+                                    </div>
+                                </Tooltip>
+                                <Tooltip
+                                    title={
+                                        <>
+                                            <div className="dash-tooltip">{'Enter from top'}</div>
+                                        </>
+                                    }>
+                                    <div
+                                        style={{ gridColumn: 2, gridRow: 1, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Top ? Colors.LIGHT_BLUE : 'black', cursor: 'pointer' }}
+                                        onClick={() => this.updateEffectDirection(PresEffect.Top)}>
+                                        <FontAwesomeIcon icon={'angle-down'} />
+                                    </div>
+                                </Tooltip>
+                                <Tooltip
+                                    title={
+                                        <>
+                                            <div className="dash-tooltip">{'Enter from bottom'}</div>
+                                        </>
+                                    }>
+                                    <div
+                                        style={{ gridColumn: 2, gridRow: 3, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Bottom ? Colors.LIGHT_BLUE : 'black', cursor: 'pointer' }}
+                                        onClick={() => this.updateEffectDirection(PresEffect.Bottom)}>
+                                        <FontAwesomeIcon icon={'angle-up'} />
+                                    </div>
+                                </Tooltip>
+                                <Tooltip
+                                    title={
+                                        <>
+                                            <div className="dash-tooltip">{'Enter from center'}</div>
+                                        </>
+                                    }>
+                                    <div
+                                        style={{
+                                            gridColumn: 2,
+                                            gridRow: 2,
+                                            width: 10,
+                                            height: 10,
+                                            alignSelf: 'center',
+                                            justifySelf: 'center',
+                                            border: this.activeItem.presEffectDirection === PresEffect.Center || !this.activeItem.presEffectDirection ? `solid 2px ${Colors.LIGHT_BLUE}` : 'solid 2px black',
+                                            borderRadius: '100%',
+                                            cursor: 'pointer',
+                                        }}
+                                        onClick={() => this.updateEffectDirection(PresEffect.Center)}></div>
+                                </Tooltip>
                             </div>
                         </div>
-                        <div className="effectDirection" style={{ display: effect === 'None' ? "none" : "grid", width: 40 }}>
-                            <Tooltip title={<><div className="dash-tooltip">{"Enter from left"}</div></>}><div style={{ gridColumn: 1, gridRow: 2, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Left ? Colors.LIGHT_BLUE : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Left)}><FontAwesomeIcon icon={"angle-right"} /></div></Tooltip>
-                            <Tooltip title={<><div className="dash-tooltip">{"Enter from right"}</div></>}><div style={{ gridColumn: 3, gridRow: 2, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Right ? Colors.LIGHT_BLUE : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Right)}><FontAwesomeIcon icon={"angle-left"} /></div></Tooltip>
-                            <Tooltip title={<><div className="dash-tooltip">{"Enter from top"}</div></>}><div style={{ gridColumn: 2, gridRow: 1, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Top ? Colors.LIGHT_BLUE : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Top)}><FontAwesomeIcon icon={"angle-down"} /></div></Tooltip>
-                            <Tooltip title={<><div className="dash-tooltip">{"Enter from bottom"}</div></>}><div style={{ gridColumn: 2, gridRow: 3, justifySelf: 'center', color: this.activeItem.presEffectDirection === PresEffect.Bottom ? Colors.LIGHT_BLUE : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Bottom)}><FontAwesomeIcon icon={"angle-up"} /></div></Tooltip>
-                            <Tooltip title={<><div className="dash-tooltip">{"Enter from center"}</div></>}><div style={{ gridColumn: 2, gridRow: 2, width: 10, height: 10, alignSelf: 'center', justifySelf: 'center', border: this.activeItem.presEffectDirection === PresEffect.Center || !this.activeItem.presEffectDirection ? `solid 2px ${Colors.LIGHT_BLUE}` : "solid 2px black", borderRadius: "100%", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Center)}></div></Tooltip>
-                        </div>
-                    </div>}
+                    )}
                     <div className="ribbon-final-box">
                         <div className="ribbon-final-button-hidden" onClick={() => this.applyTo(this.childDocs)}>
                             Apply to all
                         </div>
                     </div>
-                </div >
+                </div>
             );
         }
     }
@@ -1338,11 +1563,21 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get effectDirection(): string {
         let effect = '';
         switch (this.activeItem.presEffectDirection) {
-            case 'left': effect = "Enter from left"; break;
-            case 'right': effect = "Enter from right"; break;
-            case 'top': effect = "Enter from top"; break;
-            case 'bottom': effect = "Enter from bottom"; break;
-            default: effect = "Enter from center"; break;
+            case 'left':
+                effect = 'Enter from left';
+                break;
+            case 'right':
+                effect = 'Enter from right';
+                break;
+            case 'top':
+                effect = 'Enter from top';
+                break;
+            case 'bottom':
+                effect = 'Enter from bottom';
+                break;
+            default:
+                effect = 'Enter from center';
+                break;
         }
         return effect;
     }
@@ -1355,7 +1590,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.updateMovement(activeItem.presMovement, true);
         this.updateEffect(activeItem.presEffect, true);
         this.updateEffectDirection(activeItem.presEffectDirection, true);
-        array.forEach((doc) => {
+        array.forEach(doc => {
             const curDoc = Cast(doc, Doc, null);
             const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null);
             if (tagDoc && targetDoc) {
@@ -1365,63 +1600,86 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 curDoc.presHideAfter = activeItem.presHideAfter;
             }
         });
-    }
+    };
 
     @computed get presPinViewOptionsDropdown() {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
-        const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: "auto", width: 16, filter: 'invert(1)' }} />;
+        const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ margin: 'auto', width: 16, filter: 'invert(1)' }} />;
         return (
             <>
-                {this.panable || this.scrollable || this.targetDoc.type === DocumentType.COMPARISON ? 'Pinned view' : (null)}
+                {this.panable || this.scrollable || this.targetDoc.type === DocumentType.COMPARISON ? 'Pinned view' : null}
                 <div className="ribbon-doubleButton">
-                    <Tooltip title={<><div className="dash-tooltip">{activeItem.presPinView ? "Turn off pin with view" : "Turn on pin with view"}</div></>}><div className="ribbon-toggle" style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? Colors.LIGHT_BLUE : "" }}
-                        onClick={() => {
-                            activeItem.presPinView = !activeItem.presPinView;
-                            targetDoc.presPinView = activeItem.presPinView;
-                            if (activeItem.presPinView) {
-                                if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.WEB || targetDoc._viewType === CollectionViewType.Stacking) {
-                                    const scroll = targetDoc._scrollTop;
-                                    activeItem.presPinView = true;
-                                    activeItem.presPinViewScroll = scroll;
-                                } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
-                                    activeItem.presStartTime = targetDoc._currentTimecode;
-                                    activeItem.presEndTime = NumCast(targetDoc._currentTimecode) + 0.1;
-                                } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG) {
-                                    const x = targetDoc._panX;
-                                    const y = targetDoc._panY;
-                                    const scale = targetDoc._viewScale;
-                                    activeItem.presPinView = true;
-                                    activeItem.presPinViewX = x;
-                                    activeItem.presPinViewY = y;
-                                    activeItem.presPinViewScale = scale;
-                                } else if (targetDoc.type === DocumentType.COMPARISON) {
-                                    const width = targetDoc._clipWidth;
-                                    activeItem.presPinClipWidth = width;
-                                    activeItem.presPinView = true;
+                    <Tooltip
+                        title={
+                            <>
+                                <div className="dash-tooltip">{activeItem.presPinView ? 'Turn off pin with view' : 'Turn on pin with view'}</div>
+                            </>
+                        }>
+                        <div
+                            className="ribbon-toggle"
+                            style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? Colors.LIGHT_BLUE : '' }}
+                            onClick={() => {
+                                activeItem.presPinView = !activeItem.presPinView;
+                                targetDoc.presPinView = activeItem.presPinView;
+                                if (activeItem.presPinView) {
+                                    if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.RTF || targetDoc.type === DocumentType.WEB || targetDoc._viewType === CollectionViewType.Stacking) {
+                                        const scroll = targetDoc._scrollTop;
+                                        activeItem.presPinView = true;
+                                        activeItem.presPinViewScroll = scroll;
+                                    } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
+                                        activeItem.presStartTime = targetDoc._currentTimecode;
+                                        activeItem.presEndTime = NumCast(targetDoc._currentTimecode) + 0.1;
+                                    } else if ((targetDoc.type === DocumentType.COL && targetDoc._viewType === CollectionViewType.Freeform) || targetDoc.type === DocumentType.IMG) {
+                                        const x = targetDoc._panX;
+                                        const y = targetDoc._panY;
+                                        const scale = targetDoc._viewScale;
+                                        activeItem.presPinView = true;
+                                        activeItem.presPinViewX = x;
+                                        activeItem.presPinViewY = y;
+                                        activeItem.presPinViewScale = scale;
+                                    } else if (targetDoc.type === DocumentType.COMPARISON) {
+                                        const width = targetDoc._clipWidth;
+                                        activeItem.presPinClipWidth = width;
+                                        activeItem.presPinView = true;
+                                    }
                                 }
-                            }
-                        }}>{presPinWithViewIcon}</div></Tooltip>
-                    {activeItem.presPinView ? <Tooltip title={<><div className="dash-tooltip">{"Update the pinned view with the view of the selected document"}</div></>}><div className="ribbon-button"
-                        onClick={() => {
-                            if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF) {
-                                const scroll = targetDoc._scrollTop;
-                                activeItem.presPinViewScroll = scroll;
-                            } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
-                                activeItem.presStartTime = targetDoc._currentTimecode;
-                                activeItem.presStartTime = NumCast(targetDoc._currentTimecode) + 0.1;
-                            } else if (targetDoc.type === DocumentType.COMPARISON) {
-                                const clipWidth = targetDoc._clipWidth;
-                                activeItem.presPinClipWidth = clipWidth;
-                            } else {
-                                const x = targetDoc._panX;
-                                const y = targetDoc._panY;
-                                const scale = targetDoc._viewScale;
-                                activeItem.presPinViewX = x;
-                                activeItem.presPinViewY = y;
-                                activeItem.presPinViewScale = scale;
-                            }
-                        }}>Update</div></Tooltip> : (null)}
+                            }}>
+                            {presPinWithViewIcon}
+                        </div>
+                    </Tooltip>
+                    {activeItem.presPinView ? (
+                        <Tooltip
+                            title={
+                                <>
+                                    <div className="dash-tooltip">{'Update the pinned view with the view of the selected document'}</div>
+                                </>
+                            }>
+                            <div
+                                className="ribbon-button"
+                                onClick={() => {
+                                    if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF) {
+                                        const scroll = targetDoc._scrollTop;
+                                        activeItem.presPinViewScroll = scroll;
+                                    } else if ([DocumentType.AUDIO, DocumentType.VID].includes(targetDoc.type as any)) {
+                                        activeItem.presStartTime = targetDoc._currentTimecode;
+                                        activeItem.presStartTime = NumCast(targetDoc._currentTimecode) + 0.1;
+                                    } else if (targetDoc.type === DocumentType.COMPARISON) {
+                                        const clipWidth = targetDoc._clipWidth;
+                                        activeItem.presPinClipWidth = clipWidth;
+                                    } else {
+                                        const x = targetDoc._panX;
+                                        const y = targetDoc._panY;
+                                        const scale = targetDoc._viewScale;
+                                        activeItem.presPinViewX = x;
+                                        activeItem.presPinViewY = y;
+                                        activeItem.presPinViewScale = scale;
+                                    }
+                                }}>
+                                Update
+                            </div>
+                        </Tooltip>
+                    ) : null}
                 </div>
             </>
         );
@@ -1432,38 +1690,58 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const targetDoc: Doc = this.targetDoc;
         return (
             <>
-                {this.panable ? <div style={{ display: activeItem.presPinView ? "block" : "none" }}>
-                    <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
-                        <div className="presBox-subheading">Pan X</div>
-                        <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
-                            <input className="presBox-input"
-                                style={{ textAlign: 'left', width: 50 }}
-                                type="number" value={NumCast(activeItem.presPinViewX)}
-                                onKeyDown={e => e.stopPropagation()}
-                                onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewX = Number(val); })} />
+                {this.panable ? (
+                    <div style={{ display: activeItem.presPinView ? 'block' : 'none' }}>
+                        <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
+                            <div className="presBox-subheading">Pan X</div>
+                            <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
+                                <input
+                                    className="presBox-input"
+                                    style={{ textAlign: 'left', width: 50 }}
+                                    type="number"
+                                    value={NumCast(activeItem.presPinViewX)}
+                                    onKeyDown={e => e.stopPropagation()}
+                                    onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
+                                        const val = e.target.value;
+                                        activeItem.presPinViewX = Number(val);
+                                    })}
+                                />
+                            </div>
                         </div>
-                    </div>
-                    <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
-                        <div className="presBox-subheading">Pan Y</div>
-                        <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
-                            <input className="presBox-input"
-                                style={{ textAlign: 'left', width: 50 }}
-                                type="number" value={NumCast(activeItem.presPinViewY)}
-                                onKeyDown={e => e.stopPropagation()}
-                                onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewY = Number(val); })} />
+                        <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
+                            <div className="presBox-subheading">Pan Y</div>
+                            <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
+                                <input
+                                    className="presBox-input"
+                                    style={{ textAlign: 'left', width: 50 }}
+                                    type="number"
+                                    value={NumCast(activeItem.presPinViewY)}
+                                    onKeyDown={e => e.stopPropagation()}
+                                    onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
+                                        const val = e.target.value;
+                                        activeItem.presPinViewY = Number(val);
+                                    })}
+                                />
+                            </div>
                         </div>
-                    </div>
-                    <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
-                        <div className="presBox-subheading">Scale</div>
-                        <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
-                            <input className="presBox-input"
-                                style={{ textAlign: 'left', width: 50 }}
-                                type="number" value={NumCast(activeItem.presPinViewScale)}
-                                onKeyDown={e => e.stopPropagation()}
-                                onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewScale = Number(val); })} />
+                        <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
+                            <div className="presBox-subheading">Scale</div>
+                            <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
+                                <input
+                                    className="presBox-input"
+                                    style={{ textAlign: 'left', width: 50 }}
+                                    type="number"
+                                    value={NumCast(activeItem.presPinViewScale)}
+                                    onKeyDown={e => e.stopPropagation()}
+                                    onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
+                                        const val = e.target.value;
+                                        activeItem.presPinViewScale = Number(val);
+                                    })}
+                                />
+                            </div>
                         </div>
                     </div>
-                </div> : (null)}
+                ) : null}
             </>
         );
     }
@@ -1473,18 +1751,26 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const targetDoc: Doc = this.targetDoc;
         return (
             <>
-                {this.scrollable ? <div style={{ display: activeItem.presPinView ? "block" : "none" }}>
-                    <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
-                        <div className="presBox-subheading">Scroll</div>
-                        <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
-                            <input className="presBox-input"
-                                style={{ textAlign: 'left', width: 50 }}
-                                type="number" value={NumCast(activeItem.presPinViewScroll)}
-                                onKeyDown={e => e.stopPropagation()}
-                                onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; activeItem.presPinViewScroll = Number(val); })} />
+                {this.scrollable ? (
+                    <div style={{ display: activeItem.presPinView ? 'block' : 'none' }}>
+                        <div className="ribbon-doubleButton" style={{ marginRight: 10 }}>
+                            <div className="presBox-subheading">Scroll</div>
+                            <div className="ribbon-property" style={{ paddingRight: 0, paddingLeft: 0 }}>
+                                <input
+                                    className="presBox-input"
+                                    style={{ textAlign: 'left', width: 50 }}
+                                    type="number"
+                                    value={NumCast(activeItem.presPinViewScroll)}
+                                    onKeyDown={e => e.stopPropagation()}
+                                    onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
+                                        const val = e.target.value;
+                                        activeItem.presPinViewScroll = Number(val);
+                                    })}
+                                />
+                            </div>
                         </div>
                     </div>
-                </div> : (null)}
+                ) : null}
             </>
         );
     }
@@ -1494,13 +1780,13 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const list = this.childDocs.map((doc, i) => {
             if (i > this.itemIndex) {
                 return (
-                    <option>{i + 1}. {doc.title}</option>
+                    <option>
+                        {i + 1}. {StrCast(doc.title)}
+                    </option>
                 );
             }
         });
-        return (
-            list
-        );
+        return list;
     }
 
     @computed get mediaOptionsDropdown() {
@@ -1510,25 +1796,29 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const clipEnd: number = NumCast(activeItem.clipEnd);
         const duration = Math.round(NumCast(activeItem[`${Doc.LayoutFieldKey(activeItem)}-duration`]) * 10);
         const mediaStopDocInd: number = NumCast(activeItem.mediaStopDoc);
-        const mediaStopDocStr: string = mediaStopDocInd ? mediaStopDocInd + ". " + this.childDocs[mediaStopDocInd - 1].title : "";
+        const mediaStopDocStr: string = mediaStopDocInd ? mediaStopDocInd + '. ' + this.childDocs[mediaStopDocInd - 1].title : '';
         if (activeItem && targetDoc) {
             return (
                 <div>
                     <div className={'presBox-ribbon'} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
                         <div>
                             <div className="ribbon-box">
-                                Start {"&"} End Time
-                                <div className={"slider-headers"}>
-                                    <div className="slider-block" >
+                                Start {'&'} End Time
+                                <div className={'slider-headers'}>
+                                    <div className="slider-block">
                                         <div className="slider-text" style={{ fontWeight: 500 }}>
                                             Start time (s)
                                         </div>
-                                        <div id={"startTime"} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}>
-                                            <input className="presBox-input"
+                                        <div id={'startTime'} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}>
+                                            <input
+                                                className="presBox-input"
                                                 style={{ textAlign: 'center', width: 30, height: 15, fontSize: 10 }}
-                                                type="number" value={NumCast(activeItem.presStartTime)}
+                                                type="number"
+                                                value={NumCast(activeItem.presStartTime)}
                                                 onKeyDown={e => e.stopPropagation()}
-                                                onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { activeItem.presStartTime = Number(e.target.value); })}
+                                                onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
+                                                    activeItem.presStartTime = Number(e.target.value);
+                                                })}
                                             />
                                         </div>
                                     </div>
@@ -1544,24 +1834,33 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                         <div className="slider-text" style={{ fontWeight: 500 }}>
                                             End time (s)
                                         </div>
-                                        <div id={"endTime"} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}>
-                                            <input className="presBox-input"
+                                        <div id={'endTime'} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}>
+                                            <input
+                                                className="presBox-input"
                                                 onKeyDown={e => e.stopPropagation()}
                                                 style={{ textAlign: 'center', width: 30, height: 15, fontSize: 10 }}
-                                                type="number" value={NumCast(activeItem.presEndTime)}
-                                                onChange={action((e: React.ChangeEvent<HTMLInputElement>) => { activeItem.presEndTime = Number(e.target.value); })}
+                                                type="number"
+                                                value={NumCast(activeItem.presEndTime)}
+                                                onChange={action((e: React.ChangeEvent<HTMLInputElement>) => {
+                                                    activeItem.presEndTime = Number(e.target.value);
+                                                })}
                                             />
                                         </div>
                                     </div>
                                 </div>
                                 <div className="multiThumb-slider">
-                                    <input type="range" step="0.1" min={clipStart} max={clipEnd} value={NumCast(activeItem.presEndTime)}
+                                    <input
+                                        type="range"
+                                        step="0.1"
+                                        min={clipStart}
+                                        max={clipEnd}
+                                        value={NumCast(activeItem.presEndTime)}
                                         style={{ gridColumn: 1, gridRow: 1 }}
-                                        className={`toolbar-slider ${"end"}`}
+                                        className={`toolbar-slider ${'end'}`}
                                         id="toolbar-slider"
                                         onPointerDown={() => {
-                                            this._batch = UndoManager.StartBatch("presEndTime");
-                                            const endBlock = document.getElementById("endTime");
+                                            this._batch = UndoManager.StartBatch('presEndTime');
+                                            const endBlock = document.getElementById('endTime');
                                             if (endBlock) {
                                                 endBlock.style.color = Colors.LIGHT_GRAY;
                                                 endBlock.style.backgroundColor = Colors.MEDIUM_BLUE;
@@ -1569,7 +1868,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                         }}
                                         onPointerUp={() => {
                                             this._batch?.end();
-                                            const endBlock = document.getElementById("endTime");
+                                            const endBlock = document.getElementById('endTime');
                                             if (endBlock) {
                                                 endBlock.style.color = Colors.BLACK;
                                                 endBlock.style.backgroundColor = Colors.LIGHT_GRAY;
@@ -1578,14 +1877,20 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                         onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                                             e.stopPropagation();
                                             activeItem.presEndTime = Number(e.target.value);
-                                        }} />
-                                    <input type="range" step="0.1" min={clipStart} max={clipEnd} value={NumCast(activeItem.presStartTime)}
+                                        }}
+                                    />
+                                    <input
+                                        type="range"
+                                        step="0.1"
+                                        min={clipStart}
+                                        max={clipEnd}
+                                        value={NumCast(activeItem.presStartTime)}
                                         style={{ gridColumn: 1, gridRow: 1 }}
-                                        className={`toolbar-slider ${"start"}`}
+                                        className={`toolbar-slider ${'start'}`}
                                         id="toolbar-slider"
                                         onPointerDown={() => {
-                                            this._batch = UndoManager.StartBatch("presStartTime");
-                                            const startBlock = document.getElementById("startTime");
+                                            this._batch = UndoManager.StartBatch('presStartTime');
+                                            const startBlock = document.getElementById('startTime');
                                             if (startBlock) {
                                                 startBlock.style.color = Colors.LIGHT_GRAY;
                                                 startBlock.style.backgroundColor = Colors.MEDIUM_BLUE;
@@ -1593,7 +1898,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                         }}
                                         onPointerUp={() => {
                                             this._batch?.end();
-                                            const startBlock = document.getElementById("startTime");
+                                            const startBlock = document.getElementById('startTime');
                                             if (startBlock) {
                                                 startBlock.style.color = Colors.BLACK;
                                                 startBlock.style.backgroundColor = Colors.LIGHT_GRAY;
@@ -1602,9 +1907,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                         onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                                             e.stopPropagation();
                                             activeItem.presStartTime = Number(e.target.value);
-                                        }} />
+                                        }}
+                                    />
                                 </div>
-                                <div className={`slider-headers ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? "" : "none"}`}>
+                                <div className={`slider-headers ${activeItem.presMovement === PresMovement.Pan || activeItem.presMovement === PresMovement.Zoom ? '' : 'none'}`}>
                                     <div className="slider-text">{clipStart} s</div>
                                     <div className="slider-text"></div>
                                     <div className="slider-text">{clipEnd} s</div>
@@ -1615,38 +1921,22 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 <div className="presBox-subheading">Start playing:</div>
                                 <div className="presBox-radioButtons">
                                     <div className="checkbox-container">
-                                        <input className="presBox-checkbox"
-                                            type="checkbox"
-                                            onChange={() => activeItem.mediaStart = "manual"}
-                                            checked={activeItem.mediaStart === "manual"}
-                                        />
+                                        <input className="presBox-checkbox" type="checkbox" onChange={() => (activeItem.mediaStart = 'manual')} checked={activeItem.mediaStart === 'manual'} />
                                         <div>On click</div>
                                     </div>
                                     <div className="checkbox-container">
-                                        <input className="presBox-checkbox"
-                                            type="checkbox"
-                                            onChange={() => activeItem.mediaStart = "auto"}
-                                            checked={activeItem.mediaStart === "auto"}
-                                        />
+                                        <input className="presBox-checkbox" type="checkbox" onChange={() => (activeItem.mediaStart = 'auto')} checked={activeItem.mediaStart === 'auto'} />
                                         <div>Automatically</div>
                                     </div>
                                 </div>
                                 <div className="presBox-subheading">Stop playing:</div>
                                 <div className="presBox-radioButtons">
                                     <div className="checkbox-container">
-                                        <input className="presBox-checkbox"
-                                            type="checkbox"
-                                            onChange={() => activeItem.mediaStop = "manual"}
-                                            checked={activeItem.mediaStop === "manual"}
-                                        />
+                                        <input className="presBox-checkbox" type="checkbox" onChange={() => (activeItem.mediaStop = 'manual')} checked={activeItem.mediaStop === 'manual'} />
                                         <div>At audio end time</div>
                                     </div>
                                     <div className="checkbox-container">
-                                        <input className="presBox-checkbox"
-                                            type="checkbox"
-                                            onChange={() => activeItem.mediaStop = "auto"}
-                                            checked={activeItem.mediaStop === "auto"}
-                                        />
+                                        <input className="presBox-checkbox" type="checkbox" onChange={() => (activeItem.mediaStop = 'auto')} checked={activeItem.mediaStop === 'auto'} />
                                         <div>On slide change</div>
                                     </div>
                                     {/* <div className="checkbox-container">
@@ -1670,7 +1960,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             </div>
                         </div>
                     </div>
-                </div >
+                </div>
             );
         }
     }
@@ -1678,84 +1968,137 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @computed get newDocumentToolbarDropdown() {
         return (
             <div>
-                <div className={'presBox-toolbar-dropdown'} style={{ display: this.newDocumentTools && this.layoutDoc.presStatus === "edit" ? "inline-flex" : "none" }} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
+                <div
+                    className={'presBox-toolbar-dropdown'}
+                    style={{ display: this.newDocumentTools && this.layoutDoc.presStatus === 'edit' ? 'inline-flex' : 'none' }}
+                    onClick={e => e.stopPropagation()}
+                    onPointerUp={e => e.stopPropagation()}
+                    onPointerDown={e => e.stopPropagation()}>
                     <div className="layout-container" style={{ height: 'max-content' }}>
-                        <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => { this.layout = 'blank'; this.createNewSlide(this.layout); })} />
-                        <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => { this.layout = 'title'; this.createNewSlide(this.layout); })}>
+                        <div
+                            className="layout"
+                            style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
+                            onClick={action(() => {
+                                this.layout = 'blank';
+                                this.createNewSlide(this.layout);
+                            })}
+                        />
+                        <div
+                            className="layout"
+                            style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
+                            onClick={action(() => {
+                                this.layout = 'title';
+                                this.createNewSlide(this.layout);
+                            })}>
                             <div className="title">Title</div>
                             <div className="subtitle">Subtitle</div>
                         </div>
-                        <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => { this.layout = 'header'; this.createNewSlide(this.layout); })}>
-                            <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>Section header</div>
+                        <div
+                            className="layout"
+                            style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
+                            onClick={action(() => {
+                                this.layout = 'header';
+                                this.createNewSlide(this.layout);
+                            })}>
+                            <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>
+                                Section header
+                            </div>
                         </div>
-                        <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => { this.layout = 'content'; this.createNewSlide(this.layout); })}>
-                            <div className="title" style={{ alignSelf: 'center' }}>Title</div>
+                        <div
+                            className="layout"
+                            style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }}
+                            onClick={action(() => {
+                                this.layout = 'content';
+                                this.createNewSlide(this.layout);
+                            })}>
+                            <div className="title" style={{ alignSelf: 'center' }}>
+                                Title
+                            </div>
                             <div className="content">Text goes here</div>
                         </div>
                     </div>
                 </div>
-            </div >
+            </div>
         );
     }
 
     @observable openLayouts: boolean = false;
     @observable addFreeform: boolean = true;
-    @observable layout: string = "";
-    @observable title: string = "";
+    @observable layout: string = '';
+    @observable title: string = '';
 
     @computed get newDocumentDropdown() {
         return (
             <div>
-                <div className={"presBox-ribbon"} onClick={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
+                <div className={'presBox-ribbon'} onClick={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
                     <div className="ribbon-box">
                         Slide Title: <br></br>
-                        <input className="ribbon-textInput" placeholder="..." type="text" name="fname"
-                            onChange={(e) => {
+                        <input
+                            className="ribbon-textInput"
+                            placeholder="..."
+                            type="text"
+                            name="fname"
+                            onChange={e => {
                                 e.stopPropagation();
                                 e.preventDefault();
-                                runInAction(() => this.title = e.target.value);
-                            }}>
-                        </input>
+                                runInAction(() => (this.title = e.target.value));
+                            }}></input>
                     </div>
                     <div className="ribbon-box">
                         Choose type:
                         <div className="ribbon-doubleButton">
-                            <div title="Text" className={'ribbon-toggle'} style={{ background: this.addFreeform ? "" : Colors.LIGHT_BLUE }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Text</div>
-                            <div title="Freeform" className={'ribbon-toggle'} style={{ background: this.addFreeform ? Colors.LIGHT_BLUE : "" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Freeform</div>
+                            <div title="Text" className={'ribbon-toggle'} style={{ background: this.addFreeform ? '' : Colors.LIGHT_BLUE }} onClick={action(() => (this.addFreeform = !this.addFreeform))}>
+                                Text
+                            </div>
+                            <div title="Freeform" className={'ribbon-toggle'} style={{ background: this.addFreeform ? Colors.LIGHT_BLUE : '' }} onClick={action(() => (this.addFreeform = !this.addFreeform))}>
+                                Freeform
+                            </div>
                         </div>
                     </div>
-                    <div className="ribbon-box" style={{ display: this.addFreeform ? "grid" : "none" }}>
+                    <div className="ribbon-box" style={{ display: this.addFreeform ? 'grid' : 'none' }}>
                         Preset layouts:
                         <div className="layout-container" style={{ height: this.openLayouts ? 'max-content' : '75px' }}>
-                            <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'blank')} />
-                            <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'title')}>
+                            <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'blank'))} />
+                            <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'title'))}>
                                 <div className="title">Title</div>
                                 <div className="subtitle">Subtitle</div>
                             </div>
-                            <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'header')}>
-                                <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>Section header</div>
+                            <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'header'))}>
+                                <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>
+                                    Section header
+                                </div>
                             </div>
-                            <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'content')}>
-                                <div className="title" style={{ alignSelf: 'center' }}>Title</div>
+                            <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'content'))}>
+                                <div className="title" style={{ alignSelf: 'center' }}>
+                                    Title
+                                </div>
                                 <div className="content">Text goes here</div>
                             </div>
-                            <div className="layout" style={{ border: this.layout === 'twoColumns' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'twoColumns')}>
-                                <div className="title" style={{ alignSelf: 'center', gridColumn: '1/3' }}>Title</div>
-                                <div className="content" style={{ gridColumn: 1, gridRow: 2 }}>Column one text</div>
-                                <div className="content" style={{ gridColumn: 2, gridRow: 2 }}>Column two text</div>
+                            <div className="layout" style={{ border: this.layout === 'twoColumns' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => (this.layout = 'twoColumns'))}>
+                                <div className="title" style={{ alignSelf: 'center', gridColumn: '1/3' }}>
+                                    Title
+                                </div>
+                                <div className="content" style={{ gridColumn: 1, gridRow: 2 }}>
+                                    Column one text
+                                </div>
+                                <div className="content" style={{ gridColumn: 2, gridRow: 2 }}>
+                                    Column two text
+                                </div>
                             </div>
                         </div>
-                        <div className="open-layout" onClick={action(() => this.openLayouts = !this.openLayouts)}>
-                            <FontAwesomeIcon style={{ transition: 'all 0.3s', transform: this.openLayouts ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={"caret-down"} size={"lg"} />
+                        <div className="open-layout" onClick={action(() => (this.openLayouts = !this.openLayouts))}>
+                            <FontAwesomeIcon style={{ transition: 'all 0.3s', transform: this.openLayouts ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={'caret-down'} size={'lg'} />
                         </div>
                     </div>
                     <div className="ribbon-final-box">
-                        <div className={this.title !== "" && (this.addFreeform && this.layout !== "" || !this.addFreeform) ? "ribbon-final-button-hidden" : "ribbon-final-button"} onClick={() => this.createNewSlide(this.layout, this.title, this.addFreeform)}>
+                        <div
+                            className={this.title !== '' && ((this.addFreeform && this.layout !== '') || !this.addFreeform) ? 'ribbon-final-button-hidden' : 'ribbon-final-button'}
+                            onClick={() => this.createNewSlide(this.layout, this.title, this.addFreeform)}>
                             Create New Slide
                         </div>
                     </div>
                 </div>
-            </div >
+            </div>
         );
     }
 
@@ -1763,7 +2106,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         let doc = undefined;
         if (layout) doc = this.createTemplate(layout);
         if (freeform && layout) doc = this.createTemplate(layout, title);
-        if (!freeform && !layout) doc = Docs.Create.TextDocument("", { _nativeWidth: 400, _width: 225, title: title });
+        if (!freeform && !layout) doc = Docs.Create.TextDocument('', { _nativeWidth: 400, _width: 225, title: title });
         if (doc) {
             const presCollection = Cast(this.layoutDoc.presCollection, Doc, null);
             const data = Cast(presCollection?.data, listSpec(Doc));
@@ -1773,10 +2116,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 TabDocView.PinDoc(doc);
                 this.gotoDocument(this.childDocs.length, this.activeItem);
             } else {
-                this.props.addDocTab(doc, "add:right");
+                this.props.addDocTab(doc, 'add:right');
             }
         }
-    }
+    };
 
     createTemplate = (layout: string, input?: string) => {
         const activeItem: Doc = this.activeItem;
@@ -1788,43 +2131,59 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             y = NumCast(targetDoc.y) + NumCast(targetDoc._height) + 20;
         }
         let doc = undefined;
-        const title = Docs.Create.TextDocument("Click to change title", { title: "Slide title", _width: 380, _height: 60, x: 10, y: 58, _fontSize: "24pt", });
-        const subtitle = Docs.Create.TextDocument("Click to change subtitle", { title: "Slide subtitle", _width: 380, _height: 50, x: 10, y: 118, _fontSize: "16pt" });
-        const header = Docs.Create.TextDocument("Click to change header", { title: "Slide header", _width: 380, _height: 65, x: 10, y: 80, _fontSize: "20pt" });
-        const contentTitle = Docs.Create.TextDocument("Click to change title", { title: "Slide title", _width: 380, _height: 60, x: 10, y: 10, _fontSize: "24pt" });
-        const content = Docs.Create.TextDocument("Click to change text", { title: "Slide text", _width: 380, _height: 145, x: 10, y: 70, _fontSize: "14pt" });
-        const content1 = Docs.Create.TextDocument("Click to change text", { title: "Column 1", _width: 185, _height: 140, x: 10, y: 80, _fontSize: "14pt" });
-        const content2 = Docs.Create.TextDocument("Click to change text", { title: "Column 2", _width: 185, _height: 140, x: 205, y: 80, _fontSize: "14pt" });
+        const title = Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 58, _fontSize: '24pt' });
+        const subtitle = Docs.Create.TextDocument('Click to change subtitle', { title: 'Slide subtitle', _width: 380, _height: 50, x: 10, y: 118, _fontSize: '16pt' });
+        const header = Docs.Create.TextDocument('Click to change header', { title: 'Slide header', _width: 380, _height: 65, x: 10, y: 80, _fontSize: '20pt' });
+        const contentTitle = Docs.Create.TextDocument('Click to change title', { title: 'Slide title', _width: 380, _height: 60, x: 10, y: 10, _fontSize: '24pt' });
+        const content = Docs.Create.TextDocument('Click to change text', { title: 'Slide text', _width: 380, _height: 145, x: 10, y: 70, _fontSize: '14pt' });
+        const content1 = Docs.Create.TextDocument('Click to change text', { title: 'Column 1', _width: 185, _height: 140, x: 10, y: 80, _fontSize: '14pt' });
+        const content2 = Docs.Create.TextDocument('Click to change text', { title: 'Column 2', _width: 185, _height: 140, x: 205, y: 80, _fontSize: '14pt' });
         switch (layout) {
             case 'blank':
-                doc = Docs.Create.FreeformDocument([], { title: input ? input : "Blank slide", _width: 400, _height: 225, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([], { title: input ? input : 'Blank slide', _width: 400, _height: 225, x: x, y: y });
                 break;
             case 'title':
-                doc = Docs.Create.FreeformDocument([title, subtitle], { title: input ? input : "Title slide", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([title, subtitle], { title: input ? input : 'Title slide', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             case 'header':
-                doc = Docs.Create.FreeformDocument([header], { title: input ? input : "Section header", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([header], { title: input ? input : 'Section header', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             case 'content':
-                doc = Docs.Create.FreeformDocument([contentTitle, content], { title: input ? input : "Title and content", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([contentTitle, content], { title: input ? input : 'Title and content', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             case 'twoColumns':
-                doc = Docs.Create.FreeformDocument([contentTitle, content1, content2], { title: input ? input : "Title and two columns", _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
+                doc = Docs.Create.FreeformDocument([contentTitle, content1, content2], { title: input ? input : 'Title and two columns', _width: 400, _height: 225, _fitContentsToBox: true, x: x, y: y });
                 break;
             default:
                 break;
         }
         return doc;
-    }
+    };
 
     // Dropdown that appears when the user wants to begin presenting (either minimize or sidebar view)
     @computed get presentDropdown() {
         return (
-            <div className={`dropdown-play ${this.presentTools ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
-                <div className="dropdown-play-button" onClick={undoBatch(action(() => { this.updateMinimize(); this.turnOffEdit(true); this.gotoDocument(this.itemIndex, this.activeItem); }))}>
+            <div className={`dropdown-play ${this.presentTools ? 'active' : ''}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
+                <div
+                    className="dropdown-play-button"
+                    onClick={undoBatch(
+                        action(() => {
+                            this.updateMinimize();
+                            this.turnOffEdit(true);
+                            this.gotoDocument(this.itemIndex, this.activeItem);
+                        })
+                    )}>
                     Mini-player
                 </div>
-                <div className="dropdown-play-button" onClick={undoBatch(action(() => { this.layoutDoc.presStatus = "manual"; this.turnOffEdit(true); this.gotoDocument(this.itemIndex, this.activeItem); }))}>
+                <div
+                    className="dropdown-play-button"
+                    onClick={undoBatch(
+                        action(() => {
+                            this.layoutDoc.presStatus = 'manual';
+                            this.turnOffEdit(true);
+                            this.gotoDocument(this.itemIndex, this.activeItem);
+                        })
+                    )}>
                     Sidebar player
                 </div>
             </div>
@@ -1835,7 +2194,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     nextKeyframe = (tagDoc: Doc, curDoc: Doc): void => {
         const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]);
-        const currentFrame = Cast(tagDoc._currentFrame, "number", null);
+        const currentFrame = Cast(tagDoc._currentFrame, 'number', null);
         if (currentFrame === undefined) {
             tagDoc._currentFrame = 0;
             // CollectionFreeFormDocumentView.setupScroll(tagDoc, 0);
@@ -1845,19 +2204,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0, tagDoc);
         tagDoc._currentFrame = Math.max(0, (currentFrame || 0) + 1);
         tagDoc.lastFrame = Math.max(NumCast(tagDoc._currentFrame), NumCast(tagDoc.lastFrame));
-    }
+    };
 
     @action
     prevKeyframe = (tagDoc: Doc, actItem: Doc): void => {
         const childDocs = DocListCast(tagDoc[Doc.LayoutFieldKey(tagDoc)]);
-        const currentFrame = Cast(tagDoc._currentFrame, "number", null);
+        const currentFrame = Cast(tagDoc._currentFrame, 'number', null);
         if (currentFrame === undefined) {
             tagDoc._currentFrame = 0;
             // CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
         }
         CollectionFreeFormDocumentView.gotoKeyframe(childDocs.slice());
         tagDoc._currentFrame = Math.max(0, (currentFrame || 0) - 1);
-    }
+    };
 
     /**
      * Returns the collection type as a string for headers
@@ -1868,15 +2227,33 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         let type: string = '';
         if (activeItem) {
             switch (targetDoc.type) {
-                case DocumentType.PDF: type = "PDF"; break;
-                case DocumentType.RTF: type = "Text node"; break;
-                case DocumentType.COL: type = "Collection"; break;
-                case DocumentType.AUDIO: type = "Audio"; break;
-                case DocumentType.VID: type = "Video"; break;
-                case DocumentType.IMG: type = "Image"; break;
-                case DocumentType.WEB: type = "Web page"; break;
-                case DocumentType.MAP: type = "Map"; break;
-                default: type = "Other node"; break;
+                case DocumentType.PDF:
+                    type = 'PDF';
+                    break;
+                case DocumentType.RTF:
+                    type = 'Text node';
+                    break;
+                case DocumentType.COL:
+                    type = 'Collection';
+                    break;
+                case DocumentType.AUDIO:
+                    type = 'Audio';
+                    break;
+                case DocumentType.VID:
+                    type = 'Video';
+                    break;
+                case DocumentType.IMG:
+                    type = 'Image';
+                    break;
+                case DocumentType.WEB:
+                    type = 'Web page';
+                    break;
+                case DocumentType.MAP:
+                    type = 'Map';
+                    break;
+                default:
+                    type = 'Other node';
+                    break;
             }
         }
         return type;
@@ -1885,66 +2262,122 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @observable private openActiveColorPicker: boolean = false;
     @observable private openViewedColorPicker: boolean = false;
 
-
-
     @computed get progressivizeDropdown() {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
         if (activeItem && targetDoc) {
-            const activeFontColor = targetDoc["pres-text-color"] ? StrCast(targetDoc["pres-text-color"]) : "Black";
-            const viewedFontColor = targetDoc["pres-text-viewed-color"] ? StrCast(targetDoc["pres-text-viewed-color"]) : "Black";
+            const activeFontColor = targetDoc['pres-text-color'] ? StrCast(targetDoc['pres-text-color']) : 'Black';
+            const viewedFontColor = targetDoc['pres-text-viewed-color'] ? StrCast(targetDoc['pres-text-viewed-color']) : 'Black';
             return (
                 <div>
-                    <div className={`presBox-ribbon ${this.progressivizeTools && this.layoutDoc.presStatus === "edit" ? "active" : ""}`} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
+                    <div
+                        className={`presBox-ribbon ${this.progressivizeTools && this.layoutDoc.presStatus === 'edit' ? 'active' : ''}`}
+                        onClick={e => e.stopPropagation()}
+                        onPointerUp={e => e.stopPropagation()}
+                        onPointerDown={e => e.stopPropagation()}>
                         <div className="ribbon-box">
                             {this.stringType} selected
-                            <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG || targetDoc.type === DocumentType.RTF ? "inline-flex" : "none" }}>
-                                <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.progressivizeChild}>Contents</div>
-                                <div className="ribbon-toggle" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.editProgressivize}>Edit</div>
+                            <div
+                                className="ribbon-doubleButton"
+                                style={{
+                                    borderTop: 'solid 1px darkgrey',
+                                    display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG || targetDoc.type === DocumentType.RTF ? 'inline-flex' : 'none',
+                                }}>
+                                <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.progressivizeChild}>
+                                    Contents
+                                </div>
+                                <div className="ribbon-toggle" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editProgressivize}>
+                                    Edit
+                                </div>
                             </div>
-                            <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? "inline-flex" : "none" }}>
+                            <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? 'inline-flex' : 'none' }}>
                                 <div className="presBox-subheading">Active text color</div>
-                                <div className="ribbon-colorBox" style={{ backgroundColor: activeFontColor, height: 15, width: 15 }} onClick={action(() => { this.openActiveColorPicker = !this.openActiveColorPicker; })}>
-                                </div>
+                                <div
+                                    className="ribbon-colorBox"
+                                    style={{ backgroundColor: activeFontColor, height: 15, width: 15 }}
+                                    onClick={action(() => {
+                                        this.openActiveColorPicker = !this.openActiveColorPicker;
+                                    })}></div>
                             </div>
                             {this.activeColorPicker}
-                            <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? "inline-flex" : "none" }}>
+                            <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? 'inline-flex' : 'none' }}>
                                 <div className="presBox-subheading">Viewed font color</div>
-                                <div className="ribbon-colorBox" style={{ backgroundColor: viewedFontColor, height: 15, width: 15 }} onClick={action(() => this.openViewedColorPicker = !this.openViewedColorPicker)}>
-                                </div>
+                                <div className="ribbon-colorBox" style={{ backgroundColor: viewedFontColor, height: 15, width: 15 }} onClick={action(() => (this.openViewedColorPicker = !this.openViewedColorPicker))}></div>
                             </div>
                             {this.viewedColorPicker}
-                            <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? "inline-flex" : "none" }}>
-                                <div className="ribbon-toggle" style={{ backgroundColor: activeItem.zoomProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.progressivizeZoom}>Zoom</div>
-                                <div className="ribbon-toggle" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.editZoomProgressivize}>Edit</div>
+                            <div
+                                className="ribbon-doubleButton"
+                                style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? 'inline-flex' : 'none' }}>
+                                <div className="ribbon-toggle" style={{ backgroundColor: activeItem.zoomProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.progressivizeZoom}>
+                                    Zoom
+                                </div>
+                                <div className="ribbon-toggle" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editZoomProgressivize}>
+                                    Edit
+                                </div>
                             </div>
-                            <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: targetDoc._viewType === "stacking" || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF ? "inline-flex" : "none" }}>
-                                <div className="ribbon-toggle" style={{ backgroundColor: activeItem.scrollProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.progressivizeScroll}>Scroll</div>
-                                <div className="ribbon-toggle" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.editScrollProgressivize}>Edit</div>
+                            <div
+                                className="ribbon-doubleButton"
+                                style={{
+                                    borderTop: 'solid 1px darkgrey',
+                                    display: targetDoc._viewType === 'stacking' || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF ? 'inline-flex' : 'none',
+                                }}>
+                                <div className="ribbon-toggle" style={{ backgroundColor: activeItem.scrollProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.progressivizeScroll}>
+                                    Scroll
+                                </div>
+                                <div className="ribbon-toggle" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? Colors.LIGHT_BLUE : '' }} onClick={this.editScrollProgressivize}>
+                                    Edit
+                                </div>
                             </div>
                         </div>
                         <div className="ribbon-final-box">
                             Frames
                             <div className="ribbon-doubleButton">
                                 <div className="ribbon-frameSelector">
-                                    <div key="back" title="back frame" className="backKeyframe" onClick={e => { e.stopPropagation(); this.prevKeyframe(targetDoc, activeItem); }}>
-                                        <FontAwesomeIcon icon={"caret-left"} size={"lg"} />
+                                    <div
+                                        key="back"
+                                        title="back frame"
+                                        className="backKeyframe"
+                                        onClick={e => {
+                                            e.stopPropagation();
+                                            this.prevKeyframe(targetDoc, activeItem);
+                                        }}>
+                                        <FontAwesomeIcon icon={'caret-left'} size={'lg'} />
                                     </div>
-                                    <div key="num" title="toggle view all" className="numKeyframe" style={{ color: targetDoc.keyFrameEditing ? "white" : "black", backgroundColor: targetDoc.keyFrameEditing ? Colors.MEDIUM_BLUE : Colors.LIGHT_BLUE }}
-                                        onClick={action(() => targetDoc.keyFrameEditing = !targetDoc.keyFrameEditing)} >
+                                    <div
+                                        key="num"
+                                        title="toggle view all"
+                                        className="numKeyframe"
+                                        style={{ color: targetDoc.keyFrameEditing ? 'white' : 'black', backgroundColor: targetDoc.keyFrameEditing ? Colors.MEDIUM_BLUE : Colors.LIGHT_BLUE }}
+                                        onClick={action(() => (targetDoc.keyFrameEditing = !targetDoc.keyFrameEditing))}>
                                         {NumCast(targetDoc._currentFrame)}
                                     </div>
-                                    <div key="fwd" title="forward frame" className="fwdKeyframe" onClick={e => { e.stopPropagation(); this.nextKeyframe(targetDoc, activeItem); }}>
-                                        <FontAwesomeIcon icon={"caret-right"} size={"lg"} />
+                                    <div
+                                        key="fwd"
+                                        title="forward frame"
+                                        className="fwdKeyframe"
+                                        onClick={e => {
+                                            e.stopPropagation();
+                                            this.nextKeyframe(targetDoc, activeItem);
+                                        }}>
+                                        <FontAwesomeIcon icon={'caret-right'} size={'lg'} />
                                     </div>
                                 </div>
-                                <Tooltip title={<><div className="dash-tooltip">{"Last frame"}</div></>}><div className="ribbon-property">{NumCast(targetDoc.lastFrame)}</div></Tooltip>
+                                <Tooltip
+                                    title={
+                                        <>
+                                            <div className="dash-tooltip">{'Last frame'}</div>
+                                        </>
+                                    }>
+                                    <div className="ribbon-property">{NumCast(targetDoc.lastFrame)}</div>
+                                </Tooltip>
                             </div>
                             <div className="ribbon-frameList">
                                 {this.frameListHeader}
                                 {this.frameList}
                             </div>
-                            <div className="ribbon-toggle" style={{ height: 20, backgroundColor: Colors.LIGHT_BLUE }} onClick={() => console.log(" TODO: play frames")}>Play</div>
+                            <div className="ribbon-toggle" style={{ height: 20, backgroundColor: Colors.LIGHT_BLUE }} onClick={() => console.log(' TODO: play frames')}>
+                                Play
+                            </div>
                         </div>
                     </div>
                 </div>
@@ -1958,37 +2391,41 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
         const val = String(color.hex);
-        targetDoc["pres-text-color"] = val;
+        targetDoc['pres-text-color'] = val;
         return true;
-    }
+    };
     @undoBatch
     @action
     switchPresented = (color: ColorState) => {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
         const val = String(color.hex);
-        targetDoc["pres-text-viewed-color"] = val;
+        targetDoc['pres-text-viewed-color'] = val;
         return true;
-    }
+    };
 
     @computed get activeColorPicker() {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
-        return !this.openActiveColorPicker ? (null) : <SketchPicker onChange={this.switchActive}
-            presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
-                '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
-                '#FFFFFF', '#f1efeb', 'transparent']}
-            color={StrCast(targetDoc["pres-text-color"])} />;
+        return !this.openActiveColorPicker ? null : (
+            <SketchPicker
+                onChange={this.switchActive}
+                presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
+                color={StrCast(targetDoc['pres-text-color'])}
+            />
+        );
     }
 
     @computed get viewedColorPicker() {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
-        return !this.openViewedColorPicker ? (null) : <SketchPicker onChange={this.switchPresented}
-            presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
-                '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
-                '#FFFFFF', '#f1efeb', 'transparent']}
-            color={StrCast(targetDoc["pres-text-viewed-color"])} />;
+        return !this.openViewedColorPicker ? null : (
+            <SketchPicker
+                onChange={this.switchPresented}
+                presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
+                color={StrCast(targetDoc['pres-text-viewed-color'])}
+            />
+        );
     }
 
     @action
@@ -1999,7 +2436,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             if (srcContext) this.togglePath(srcContext, true);
         }
         // Turn off the progressivize editors for each document
-        this.childDocs.forEach((doc) => {
+        this.childDocs.forEach(doc => {
             doc.editSnapZoomProgressivize = false;
             doc.editZoomProgressivize = false;
             const targetDoc = Cast(doc.presentationTargetDoc, Doc, null);
@@ -2008,7 +2445,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 // targetDoc.editScrollProgressivize = false;
             }
         });
-    }
+    };
 
     //Toggle whether the user edits or not
     @action
@@ -2016,14 +2453,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
         if (!targetDoc.editZoomProgressivize) {
-            if (!activeItem.zoomProgressivize) activeItem.zoomProgressivize = true; targetDoc.zoomProgressivize = true;
+            if (!activeItem.zoomProgressivize) activeItem.zoomProgressivize = true;
+            targetDoc.zoomProgressivize = true;
             targetDoc.editZoomProgressivize = true;
             activeItem.editZoomProgressivize = true;
         } else {
             targetDoc.editZoomProgressivize = false;
             activeItem.editZoomProgressivize = false;
         }
-    }
+    };
 
     //Toggle whether the user edits or not
     @action
@@ -2031,12 +2469,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const activeItem: Doc = this.activeItem;
         const targetDoc: Doc = this.targetDoc;
         if (!targetDoc.editScrollProgressivize) {
-            if (!targetDoc.scrollProgressivize) { targetDoc.scrollProgressivize = true; activeItem.scrollProgressivize = true; }
+            if (!targetDoc.scrollProgressivize) {
+                targetDoc.scrollProgressivize = true;
+                activeItem.scrollProgressivize = true;
+            }
             targetDoc.editScrollProgressivize = true;
         } else {
             targetDoc.editScrollProgressivize = false;
         }
-    }
+    };
 
     //Progressivize Zoom
     @action
@@ -2052,7 +2493,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             targetDoc._currentFrame = 0;
             targetDoc.lastFrame = 0;
         }
-    }
+    };
 
     //Progressivize Zoom
     @action
@@ -2068,7 +2509,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             targetDoc._currentFrame = 0;
             targetDoc.lastFrame = 0;
         }
-    }
+    };
 
     //Progressivize Child Docs
     @action
@@ -2077,12 +2518,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const targetDoc: Doc = this.targetDoc;
         targetDoc._currentFrame = targetDoc.lastFrame;
         if (!targetDoc.editProgressivize) {
-            if (!activeItem.presProgressivize) { activeItem.presProgressivize = true; targetDoc.presProgressivize = true; }
+            if (!activeItem.presProgressivize) {
+                activeItem.presProgressivize = true;
+                targetDoc.presProgressivize = true;
+            }
             targetDoc.editProgressivize = true;
         } else {
             targetDoc.editProgressivize = false;
         }
-    }
+    };
 
     @action
     progressivizeChild = (e: React.MouseEvent) => {
@@ -2104,40 +2548,48 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             targetDoc._currentFrame = 0;
             targetDoc.keyFrameEditing = true;
         }
-    }
+    };
 
     @action
     checkMovementLists = (doc: Doc, xlist: any, ylist: any) => {
         const x: List<number> = xlist;
         const y: List<number> = ylist;
         const tags: JSX.Element[] = [];
-        let pathPoints = ""; //List of all of the pathpoints that need to be added
+        let pathPoints = ''; //List of all of the pathpoints that need to be added
         for (let i = 0; i < x.length - 1; i++) {
             if (y[i] || x[i]) {
-                if (i === 0) pathPoints = (x[i] - 11) + "," + (y[i] + 33);
-                else pathPoints = pathPoints + " " + (x[i] - 11) + "," + (y[i] + 33);
-                tags.push(<div className="progressivizeMove-frame" style={{ position: 'absolute', top: y[i], left: x[i] }}>{i}</div>);
+                if (i === 0) pathPoints = x[i] - 11 + ',' + (y[i] + 33);
+                else pathPoints = pathPoints + ' ' + (x[i] - 11) + ',' + (y[i] + 33);
+                tags.push(
+                    <div className="progressivizeMove-frame" style={{ position: 'absolute', top: y[i], left: x[i] }}>
+                        {i}
+                    </div>
+                );
             }
         }
-        tags.push(<svg style={{ overflow: 'visible', position: 'absolute' }}><polyline
-            points={pathPoints}
-            style={{
-                position: 'absolute',
-                opacity: 1,
-                stroke: "#000000",
-                strokeWidth: 2,
-                strokeDasharray: '10 5',
-            }}
-            fill="none"
-        /></svg>);
+        tags.push(
+            <svg style={{ overflow: 'visible', position: 'absolute' }}>
+                <polyline
+                    points={pathPoints}
+                    style={{
+                        position: 'absolute',
+                        opacity: 1,
+                        stroke: '#000000',
+                        strokeWidth: 2,
+                        strokeDasharray: '10 5',
+                    }}
+                    fill="none"
+                />
+            </svg>
+        );
         return tags;
-    }
+    };
 
     @observable
     toggleDisplayMovement = (doc: Doc) => {
         if (doc.displayMovement) doc.displayMovement = false;
         else doc.displayMovement = true;
-    }
+    };
 
     @action
     checkList = (doc: Doc, list: any): number => {
@@ -2149,22 +2601,54 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             x[NumCast(doc._currentFrame)] = x[NumCast(doc._currentFrame) - 1];
             return x[NumCast(doc._currentFrame)];
         } else return 100;
-    }
+    };
 
     @computed get progressivizeChildDocs() {
         const targetDoc: Doc = this.targetDoc;
         const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
         const tags: JSX.Element[] = [];
         docs.forEach((doc, index) => {
-            if (doc["x-indexed"] && doc["y-indexed"]) {
-                tags.push(<div style={{ position: 'absolute', display: doc.displayMovement ? "block" : "none" }}>{this.checkMovementLists(doc, doc["x-indexed"], doc["y-indexed"])}</div>);
+            if (doc['x-indexed'] && doc['y-indexed']) {
+                tags.push(<div style={{ position: 'absolute', display: doc.displayMovement ? 'block' : 'none' }}>{this.checkMovementLists(doc, doc['x-indexed'], doc['y-indexed'])}</div>);
             }
             tags.push(
-                <div className="progressivizeButton" key={index} onPointerLeave={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0; }} onPointerOver={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5; }} onClick={e => { this.toggleDisplayMovement(doc); e.stopPropagation(); }} style={{ backgroundColor: doc.displayMovement ? Colors.LIGHT_BLUE : "#c8c8c8", top: NumCast(doc.y), left: NumCast(doc.x) }}>
-                    <div className="progressivizeButton-prev"><FontAwesomeIcon icon={"caret-left"} size={"lg"} onClick={e => { e.stopPropagation(); this.prevAppearFrame(doc, index); }} /></div>
-                    <div className="progressivizeButton-frame">{doc.appearFrame}</div>
-                    <div className="progressivizeButton-next"><FontAwesomeIcon icon={"caret-right"} size={"lg"} onClick={e => { e.stopPropagation(); this.nextAppearFrame(doc, index); }} /></div>
-                </div>);
+                <div
+                    className="progressivizeButton"
+                    key={index}
+                    onPointerLeave={() => {
+                        if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0;
+                    }}
+                    onPointerOver={() => {
+                        if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5;
+                    }}
+                    onClick={e => {
+                        this.toggleDisplayMovement(doc);
+                        e.stopPropagation();
+                    }}
+                    style={{ backgroundColor: doc.displayMovement ? Colors.LIGHT_BLUE : '#c8c8c8', top: NumCast(doc.y), left: NumCast(doc.x) }}>
+                    <div className="progressivizeButton-prev">
+                        <FontAwesomeIcon
+                            icon={'caret-left'}
+                            size={'lg'}
+                            onClick={e => {
+                                e.stopPropagation();
+                                this.prevAppearFrame(doc, index);
+                            }}
+                        />
+                    </div>
+                    <div className="progressivizeButton-frame">{NumCast(doc.appearFrame)}</div>
+                    <div className="progressivizeButton-next">
+                        <FontAwesomeIcon
+                            icon={'caret-right'}
+                            size={'lg'}
+                            onClick={e => {
+                                e.stopPropagation();
+                                this.nextAppearFrame(doc, index);
+                            }}
+                        />
+                    </div>
+                </div>
+            );
         });
         return tags;
     }
@@ -2173,25 +2657,25 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     nextAppearFrame = (doc: Doc, i: number): void => {
         // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
         // const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
-        const appearFrame = Cast(doc.appearFrame, "number", null);
+        const appearFrame = Cast(doc.appearFrame, 'number', null);
         if (appearFrame === undefined) {
             doc.appearFrame = 0;
         }
         doc.appearFrame = appearFrame + 1;
-        this.updateOpacityList(doc["opacity-indexed"], NumCast(doc.appearFrame));
-    }
+        this.updateOpacityList(doc['opacity-indexed'], NumCast(doc.appearFrame));
+    };
 
     @action
     prevAppearFrame = (doc: Doc, i: number): void => {
         // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null);
         // const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null);
-        const appearFrame = Cast(doc.appearFrame, "number", null);
+        const appearFrame = Cast(doc.appearFrame, 'number', null);
         if (appearFrame === undefined) {
             doc.appearFrame = 0;
         }
         doc.appearFrame = Math.max(0, appearFrame - 1);
-        this.updateOpacityList(doc["opacity-indexed"], NumCast(doc.appearFrame));
-    }
+        this.updateOpacityList(doc['opacity-indexed'], NumCast(doc.appearFrame));
+    };
 
     @action
     updateOpacityList = (list: any, frame: number) => {
@@ -2216,10 +2700,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             }
             list = x;
         }
-    }
+    };
 
     @computed get moreInfoDropdown() {
-        return (<div></div>);
+        return <div></div>;
     }
 
     @computed
@@ -2235,28 +2719,36 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         } else {
             CurrentUserUtils.propertiesWidth = 250;
         }
-    }
+    };
 
     @computed get toolbar() {
-        const propIcon = CurrentUserUtils.propertiesWidth > 0 ? "angle-double-right" : "angle-double-left";
-        const propTitle = CurrentUserUtils.propertiesWidth > 0 ? "Close Presentation Panel" : "Open Presentation Panel";
+        const propIcon = CurrentUserUtils.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left';
+        const propTitle = CurrentUserUtils.propertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel';
         const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
         const isMini: boolean = this.toolbarWidth <= 100;
-        const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === CurrentUserUtils.ActivePresentation);
+        const presKeyEvents: boolean = this.isPres && this._presKeyEventsActive && this.rootDoc === CurrentUserUtils.ActivePresentation;
         const activeColor = Colors.LIGHT_BLUE;
         const inactiveColor = Colors.WHITE;
-        return (mode === CollectionViewType.Carousel3D) ? (null) : (
+        return mode === CollectionViewType.Carousel3D ? null : (
             <div id="toolbarContainer" className={'presBox-toolbar'}>
                 {/* <Tooltip title={<><div className="dash-tooltip">{"Add new slide"}</div></>}><div className={`toolbar-button ${this.newDocumentTools ? "active" : ""}`} onClick={action(() => this.newDocumentTools = !this.newDocumentTools)}>
                     <FontAwesomeIcon icon={"plus"} />
                     <FontAwesomeIcon className={`dropdown ${this.newDocumentTools ? "active" : ""}`} icon={"angle-down"} />
                 </div></Tooltip> */}
-                <Tooltip title={<><div className="dash-tooltip">{"View paths"}</div></>}>
-                    <div style={{ opacity: this.childDocs.length > 1 && this.layoutDoc.presCollection ? 1 : 0.3, color: this._pathBoolean ? Colors.MEDIUM_BLUE : 'white', width: isMini ? "100%" : undefined }} className={"toolbar-button"} onClick={this.childDocs.length > 1 && this.layoutDoc.presCollection ? this.viewPaths : undefined}>
-                        <FontAwesomeIcon icon={"exchange-alt"} />
+                <Tooltip
+                    title={
+                        <>
+                            <div className="dash-tooltip">{'View paths'}</div>
+                        </>
+                    }>
+                    <div
+                        style={{ opacity: this.childDocs.length > 1 && this.layoutDoc.presCollection ? 1 : 0.3, color: this._pathBoolean ? Colors.MEDIUM_BLUE : 'white', width: isMini ? '100%' : undefined }}
+                        className={'toolbar-button'}
+                        onClick={this.childDocs.length > 1 && this.layoutDoc.presCollection ? this.viewPaths : undefined}>
+                        <FontAwesomeIcon icon={'exchange-alt'} />
                     </div>
                 </Tooltip>
-                {isMini ? (null) :
+                {isMini ? null : (
                     <>
                         <div className="toolbar-divider" />
                         {/* <Tooltip title={<><div className="dash-tooltip">{this._expandBoolean ? "Minimize all" : "Expand all"}</div></>}>
@@ -2267,18 +2759,28 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                             </div>
                         </Tooltip>
                         <div className="toolbar-divider" /> */}
-                        <Tooltip title={<><div className="dash-tooltip">{presKeyEvents ? "Keys are active" : "Keys are not active - click anywhere on the presentation trail to activate keys"}</div></>}>
+                        <Tooltip
+                            title={
+                                <>
+                                    <div className="dash-tooltip">{presKeyEvents ? 'Keys are active' : 'Keys are not active - click anywhere on the presentation trail to activate keys'}</div>
+                                </>
+                            }>
                             <div className="toolbar-button" style={{ cursor: presKeyEvents ? 'default' : 'pointer', position: 'absolute', right: 30, fontSize: 16 }}>
-                                <FontAwesomeIcon className={"toolbar-thumbtack"} icon={"keyboard"} style={{ color: presKeyEvents ? activeColor : inactiveColor }} />
+                                <FontAwesomeIcon className={'toolbar-thumbtack'} icon={'keyboard'} style={{ color: presKeyEvents ? activeColor : inactiveColor }} />
                             </div>
                         </Tooltip>
-                        <Tooltip title={<><div className="dash-tooltip">{propTitle}</div></>}>
+                        <Tooltip
+                            title={
+                                <>
+                                    <div className="dash-tooltip">{propTitle}</div>
+                                </>
+                            }>
                             <div className="toolbar-button" style={{ position: 'absolute', right: 4, fontSize: 16 }} onClick={this.toggleProperties}>
-                                <FontAwesomeIcon className={"toolbar-thumbtack"} icon={propIcon} style={{ color: CurrentUserUtils.propertiesWidth > 0 ? activeColor : inactiveColor }} />
+                                <FontAwesomeIcon className={'toolbar-thumbtack'} icon={propIcon} style={{ color: CurrentUserUtils.propertiesWidth > 0 ? activeColor : inactiveColor }} />
                             </div>
                         </Tooltip>
                     </>
-                }
+                )}
             </div>
         );
     }
@@ -2292,35 +2794,45 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
         const isMini: boolean = this.toolbarWidth <= 100;
         return (
-            <div className="presBox-buttons" style={{ display: !this.rootDoc._chromeHidden ? "none" : undefined }}>
-                {isMini ? (null) : <select className="presBox-viewPicker"
-                    style={{ display: this.layoutDoc.presStatus === "edit" ? "block" : "none" }}
-                    onPointerDown={e => e.stopPropagation()}
-                    onChange={this.viewChanged}
-                    value={mode}>
-                    <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Stacking}>List</option>
-                    <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Tree}>Tree</option>
-                    {Doc.noviceMode ? (null) : <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel3D}>3D Carousel</option>}
-                </select>}
+            <div className="presBox-buttons" style={{ display: !this.rootDoc._chromeHidden ? 'none' : undefined }}>
+                {isMini ? null : (
+                    <select className="presBox-viewPicker" style={{ display: this.layoutDoc.presStatus === 'edit' ? 'block' : 'none' }} onPointerDown={e => e.stopPropagation()} onChange={this.viewChanged} value={mode}>
+                        <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Stacking}>
+                            List
+                        </option>
+                        <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Tree}>
+                            Tree
+                        </option>
+                        {Doc.noviceMode ? null : (
+                            <option onPointerDown={e => e.stopPropagation()} value={CollectionViewType.Carousel3D}>
+                                3D Carousel
+                            </option>
+                        )}
+                    </select>
+                )}
                 <div className="presBox-presentPanel" style={{ opacity: this.childDocs.length ? 1 : 0.3 }}>
-                    <span className={`presBox-button ${this.layoutDoc.presStatus === "edit" ? "present" : ""}`}>
-                        <div className="presBox-button-left"
+                    <span className={`presBox-button ${this.layoutDoc.presStatus === 'edit' ? 'present' : ''}`}>
+                        <div
+                            className="presBox-button-left"
                             onClick={undoBatch(() => {
                                 if (this.childDocs.length) {
-                                    this.layoutDoc.presStatus = "manual";
+                                    this.layoutDoc.presStatus = 'manual';
                                     this.gotoDocument(this.itemIndex, this.activeItem);
                                 }
                             })}>
-                            <FontAwesomeIcon icon={"play-circle"} />
-                            <div style={{ display: this.props.PanelWidth() > 200 ? "inline-flex" : "none" }}>&nbsp; Present</div>
+                            <FontAwesomeIcon icon={'play-circle'} />
+                            <div style={{ display: this.props.PanelWidth() > 200 ? 'inline-flex' : 'none' }}>&nbsp; Present</div>
                         </div>
-                        {(mode === CollectionViewType.Carousel3D || isMini) ? (null) : <div className={`presBox-button-right ${this.presentTools ? "active" : ""}`}
-                            onClick={(action(() => {
-                                if (this.childDocs.length) this.presentTools = !this.presentTools;
-                            }))}>
-                            <FontAwesomeIcon className="dropdown" style={{ margin: 0, transform: this.presentTools ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={"angle-down"} />
-                            {this.presentDropdown}
-                        </div>}
+                        {mode === CollectionViewType.Carousel3D || isMini ? null : (
+                            <div
+                                className={`presBox-button-right ${this.presentTools ? 'active' : ''}`}
+                                onClick={action(() => {
+                                    if (this.childDocs.length) this.presentTools = !this.presentTools;
+                                })}>
+                                <FontAwesomeIcon className="dropdown" style={{ margin: 0, transform: this.presentTools ? 'rotate(180deg)' : 'rotate(0deg)' }} icon={'angle-down'} />
+                                {this.presentDropdown}
+                            </div>
+                        )}
                     </span>
                     {this.playButtons}
                 </div>
@@ -2332,7 +2844,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     getList = (list: any): List<number> => {
         const x: List<number> = list;
         return x;
-    }
+    };
 
     @action
     updateList = (list: any): List<number> => {
@@ -2341,7 +2853,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         x.length + 1;
         x[x.length - 1] = NumCast(targetDoc._scrollY);
         return x;
-    }
+    };
 
     @action
     newFrame = () => {
@@ -2350,7 +2862,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const type: string = StrCast(targetDoc.type);
         if (!activeItem.frameList) activeItem.frameList = new List<number>();
         switch (type) {
-            case (DocumentType.PDF || DocumentType.RTF || DocumentType.WEB):
+            case DocumentType.PDF || DocumentType.RTF || DocumentType.WEB:
                 this.updateList(activeItem.frameList);
                 break;
             case DocumentType.COL:
@@ -2358,20 +2870,46 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             default:
                 break;
         }
-    }
+    };
 
     @computed get frameListHeader() {
-        return (<div className="frameList-header">
-            &nbsp; Frames {this.panable ? <i>Panable</i> : this.scrollable ? <i>Scrollable</i> : (null)}
-            <div className={"frameList-headerButtons"}>
-                <Tooltip title={<><div className="dash-tooltip">{"Add frame by example"}</div></>}><div className={"headerButton"} onClick={e => { e.stopPropagation(); this.newFrame(); }}>
-                    <FontAwesomeIcon icon={"plus"} onPointerDown={e => e.stopPropagation()} />
-                </div></Tooltip>
-                <Tooltip title={<><div className="dash-tooltip">{"Edit in collection"}</div></>}><div className={"headerButton"} onClick={e => { e.stopPropagation(); console.log('New frame'); }}>
-                    <FontAwesomeIcon icon={"edit"} onPointerDown={e => e.stopPropagation()} />
-                </div></Tooltip>
+        return (
+            <div className="frameList-header">
+                &nbsp; Frames {this.panable ? <i>Panable</i> : this.scrollable ? <i>Scrollable</i> : null}
+                <div className={'frameList-headerButtons'}>
+                    <Tooltip
+                        title={
+                            <>
+                                <div className="dash-tooltip">{'Add frame by example'}</div>
+                            </>
+                        }>
+                        <div
+                            className={'headerButton'}
+                            onClick={e => {
+                                e.stopPropagation();
+                                this.newFrame();
+                            }}>
+                            <FontAwesomeIcon icon={'plus'} onPointerDown={e => e.stopPropagation()} />
+                        </div>
+                    </Tooltip>
+                    <Tooltip
+                        title={
+                            <>
+                                <div className="dash-tooltip">{'Edit in collection'}</div>
+                            </>
+                        }>
+                        <div
+                            className={'headerButton'}
+                            onClick={e => {
+                                e.stopPropagation();
+                                console.log('New frame');
+                            }}>
+                            <FontAwesomeIcon icon={'edit'} onPointerDown={e => e.stopPropagation()} />
+                        </div>
+                    </Tooltip>
+                </div>
             </div>
-        </div>);
+        );
     }
 
     @computed get frameList() {
@@ -2379,66 +2917,118 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const targetDoc: Doc = this.targetDoc;
         const frameList: List<number> = this.getList(activeItem.frameList);
         if (frameList) {
-            const frameItems = frameList.map((value) =>
-                <div className="framList-item">
-
-                </div>
-            );
-            return (
-
-                <div className="frameList-container">
-                    {frameItems}
-                </div>
-            );
-        } else return (null);
-
+            const frameItems = frameList.map(value => <div className="framList-item"></div>);
+            return <div className="frameList-container">{frameItems}</div>;
+        } else return null;
     }
 
     @computed get playButtonFrames() {
         const targetDoc: Doc = this.targetDoc;
         return (
             <>
-                {this.targetDoc ? <div className="presPanel-button-frame" style={{ display: targetDoc.lastFrame !== undefined && targetDoc.lastFrame >= 0 ? "inline-flex" : "none" }}>
-                    <div>{targetDoc._currentFrame}</div>
-                    <div className="presPanel-divider" style={{ border: 'solid 0.5px white', height: '60%' }}></div>
-                    <div>{targetDoc.lastFrame}</div>
-                </div> : null}
+                {this.targetDoc ? (
+                    <div className="presPanel-button-frame" style={{ display: targetDoc.lastFrame !== undefined && targetDoc.lastFrame >= 0 ? 'inline-flex' : 'none' }}>
+                        <div>{NumCast(targetDoc._currentFrame)}</div>
+                        <div className="presPanel-divider" style={{ border: 'solid 0.5px white', height: '60%' }}></div>
+                        <div>{NumCast(targetDoc.lastFrame)}</div>
+                    </div>
+                ) : null}
             </>
         );
     }
 
     @computed get playButtons() {
-        const presEnd: boolean = !this.layoutDoc.presLoop && (this.itemIndex === this.childDocs.length - 1);
-        const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0);
+        const presEnd: boolean = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1;
+        const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0;
         // Case 1: There are still other frames and should go through all frames before going to next slide
-        return (<div className="presPanelOverlay" style={{ display: this.layoutDoc.presStatus !== "edit" ? "inline-flex" : "none" }}>
-            <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : 'white' }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
-            <div className="presPanel-divider"></div>
-            <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} onClick={() => { this.back(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } }}><FontAwesomeIcon icon={"arrow-left"} /></div>
-            <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === PresStatus.Autoplay ? "pause" : "play"} /></div></Tooltip>
-            <div className="presPanel-button" style={{ opacity: presEnd ? 0.4 : 1 }} onClick={() => { this.next(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } }}><FontAwesomeIcon icon={"arrow-right"} /></div>
-            <div className="presPanel-divider"></div>
-            <Tooltip title={<><div className="dash-tooltip">{"Click to return to 1st slide"}</div></>}><div className="presPanel-button" style={{ border: 'solid 1px white' }} onClick={() => this.gotoDocument(0, this.activeItem)}><b>1</b></div></Tooltip>
-            <div
-                className="presPanel-button-text"
-                onClick={() => this.gotoDocument(0, this.activeItem)}
-                style={{ display: this.props.PanelWidth() > 250 ? "inline-flex" : "none" }}>
-                Slide {this.itemIndex + 1} / {this.childDocs.length}
-                {this.playButtonFrames}
+        return (
+            <div className="presPanelOverlay" style={{ display: this.layoutDoc.presStatus !== 'edit' ? 'inline-flex' : 'none' }}>
+                <Tooltip
+                    title={
+                        <>
+                            <div className="dash-tooltip">{'Loop'}</div>
+                        </>
+                    }>
+                    <div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : 'white' }} onClick={() => (this.layoutDoc.presLoop = !this.layoutDoc.presLoop)}>
+                        <FontAwesomeIcon icon={'redo-alt'} />
+                    </div>
+                </Tooltip>
+                <div className="presPanel-divider"></div>
+                <div
+                    className="presPanel-button"
+                    style={{ opacity: presStart ? 0.4 : 1 }}
+                    onClick={() => {
+                        this.back();
+                        if (this._presTimer) {
+                            clearTimeout(this._presTimer);
+                            this.layoutDoc.presStatus = PresStatus.Manual;
+                        }
+                    }}>
+                    <FontAwesomeIcon icon={'arrow-left'} />
+                </div>
+                <Tooltip
+                    title={
+                        <>
+                            <div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>
+                        </>
+                    }>
+                    <div className="presPanel-button" onClick={this.startOrPause}>
+                        <FontAwesomeIcon icon={this.layoutDoc.presStatus === PresStatus.Autoplay ? 'pause' : 'play'} />
+                    </div>
+                </Tooltip>
+                <div
+                    className="presPanel-button"
+                    style={{ opacity: presEnd ? 0.4 : 1 }}
+                    onClick={() => {
+                        this.next();
+                        if (this._presTimer) {
+                            clearTimeout(this._presTimer);
+                            this.layoutDoc.presStatus = PresStatus.Manual;
+                        }
+                    }}>
+                    <FontAwesomeIcon icon={'arrow-right'} />
+                </div>
+                <div className="presPanel-divider"></div>
+                <Tooltip
+                    title={
+                        <>
+                            <div className="dash-tooltip">{'Click to return to 1st slide'}</div>
+                        </>
+                    }>
+                    <div className="presPanel-button" style={{ border: 'solid 1px white' }} onClick={() => this.gotoDocument(0, this.activeItem)}>
+                        <b>1</b>
+                    </div>
+                </Tooltip>
+                <div className="presPanel-button-text" onClick={() => this.gotoDocument(0, this.activeItem)} style={{ display: this.props.PanelWidth() > 250 ? 'inline-flex' : 'none' }}>
+                    Slide {this.itemIndex + 1} / {this.childDocs.length}
+                    {this.playButtonFrames}
+                </div>
+                <div className="presPanel-divider"></div>
+                {this.props.PanelWidth() > 250 ? (
+                    <div
+                        className="presPanel-button-text"
+                        onClick={undoBatch(
+                            action(() => {
+                                this.layoutDoc.presStatus = 'edit';
+                                clearTimeout(this._presTimer);
+                            })
+                        )}>
+                        EXIT
+                    </div>
+                ) : (
+                    <div className="presPanel-button" onClick={undoBatch(action(() => (this.layoutDoc.presStatus = 'edit')))}>
+                        <FontAwesomeIcon icon={'times'} />
+                    </div>
+                )}
             </div>
-            <div className="presPanel-divider"></div>
-            {this.props.PanelWidth() > 250 ? <div className="presPanel-button-text" onClick={undoBatch(action(() => { this.layoutDoc.presStatus = "edit"; clearTimeout(this._presTimer); }))}>EXIT</div>
-                : <div className="presPanel-button" onClick={undoBatch(action(() => this.layoutDoc.presStatus = "edit"))}>
-                    <FontAwesomeIcon icon={"times"} />
-                </div>}
-        </div>);
+        );
     }
 
     @action
     startOrPause = () => {
         if (this.layoutDoc.presStatus === PresStatus.Manual || this.layoutDoc.presStatus === PresStatus.Edit) this.startAutoPres(this.itemIndex);
         else this.pauseAutoPres();
-    }
+    };
 
     @action
     prevClicked = (e: PointerEvent) => {
@@ -2447,7 +3037,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             clearTimeout(this._presTimer);
             this.layoutDoc.presStatus = PresStatus.Manual;
         }
-    }
+    };
 
     @action
     nextClicked = (e: PointerEvent) => {
@@ -2456,35 +3046,35 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             clearTimeout(this._presTimer);
             this.layoutDoc.presStatus = PresStatus.Manual;
         }
-    }
+    };
     @undoBatch
     @action
     exitClicked = (e: PointerEvent) => {
         this.updateMinimize();
         this.layoutDoc.presStatus = PresStatus.Edit;
         clearTimeout(this._presTimer);
-    }
+    };
 
     @action
     startMarqueeCreateSlide = () => {
         PresBox.startMarquee = true;
-    }
+    };
 
     AddToMap = (treeViewDoc: Doc, index: number[]): Doc[] => {
         var indexNum = 0;
         for (let i = 0; i < index.length; i++) {
-            indexNum += (index[i] * (10 ** (-i)));
+            indexNum += index[i] * 10 ** -i;
         }
         if (this._treeViewMap.get(treeViewDoc) !== indexNum) {
             this._treeViewMap.set(treeViewDoc, indexNum);
             const sorted = this.sort(this._treeViewMap);
             const curList = DocListCast(this.dataDoc[this.presFieldKey]);
-            if (sorted.length !== curList.length || sorted.some((doc,ind) => doc !== curList[ind])) {
+            if (sorted.length !== curList.length || sorted.some((doc, ind) => doc !== curList[ind])) {
                 this.dataDoc[this.presFieldKey] = new List<Doc>(sorted); // this is a flat array of Docs
             }
         }
         return this.childDocs;
-    }
+    };
 
     RemFromMap = (treeViewDoc: Doc, index: number[]): Doc[] => {
         if (!this._unmounting && this.isTree) {
@@ -2492,10 +3082,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.dataDoc[this.presFieldKey] = new List<Doc>(this.sort(this._treeViewMap));
         }
         return this.childDocs;
-    }
+    };
 
     // TODO: [AL] implement sort function for an array of numbers (e.g. arr[1,2,4] v arr[1,2,1])
-    sort = (treeViewMap: Map<Doc, number>) => [...treeViewMap.entries()].sort((a: [Doc, number], b: [Doc, number]) => a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0).map(kv => kv[0]);
+    sort = (treeViewMap: Map<Doc, number>) => [...treeViewMap.entries()].sort((a: [Doc, number], b: [Doc, number]) => (a[1] > b[1] ? 1 : a[1] < b[1] ? -1 : 0)).map(kv => kv[0]);
 
     render() {
         // calling this method for keyEvents
@@ -2503,42 +3093,73 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         // needed to ensure that the childDocs are loaded for looking up fields
         this.childDocs.slice();
         const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
-        const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === CurrentUserUtils.ActivePresentation);
-        const presEnd: boolean = !this.layoutDoc.presLoop && (this.itemIndex === this.childDocs.length - 1);
-        const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0);
-        return DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.rootDoc) ?
+        const presKeyEvents: boolean = this.isPres && this._presKeyEventsActive && this.rootDoc === CurrentUserUtils.ActivePresentation;
+        const presEnd: boolean = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1;
+        const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0;
+        return DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.rootDoc) ? (
             <div className="miniPres" onClick={e => e.stopPropagation()}>
-                <div className="presPanelOverlay" style={{ display: "inline-flex", height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}>
-                    <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : undefined }}
-                        onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, () => this.layoutDoc.presLoop = !this.layoutDoc.presLoop, false, false)}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip>
+                <div className="presPanelOverlay" style={{ display: 'inline-flex', height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}>
+                    <Tooltip
+                        title={
+                            <>
+                                <div className="dash-tooltip">{'Loop'}</div>
+                            </>
+                        }>
+                        <div
+                            className="presPanel-button"
+                            style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : undefined }}
+                            onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, () => (this.layoutDoc.presLoop = !this.layoutDoc.presLoop), false, false)}>
+                            <FontAwesomeIcon icon={'redo-alt'} />
+                        </div>
+                    </Tooltip>
                     <div className="presPanel-divider"></div>
-                    <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }}
-                        onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.prevClicked, false, false)}><FontAwesomeIcon icon={"arrow-left"} /></div>
-                    <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button"
-                        onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.startOrPause, false, false)}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div></Tooltip>
-                    <div className="presPanel-button" style={{ opacity: presEnd ? 0.4 : 1 }}
-                        onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.nextClicked, false, false)}><FontAwesomeIcon icon={"arrow-right"} /></div>
+                    <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.prevClicked, false, false)}>
+                        <FontAwesomeIcon icon={'arrow-left'} />
+                    </div>
+                    <Tooltip
+                        title={
+                            <>
+                                <div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}</div>
+                            </>
+                        }>
+                        <div className="presPanel-button" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.startOrPause, false, false)}>
+                            <FontAwesomeIcon icon={this.layoutDoc.presStatus === 'auto' ? 'pause' : 'play'} />
+                        </div>
+                    </Tooltip>
+                    <div className="presPanel-button" style={{ opacity: presEnd ? 0.4 : 1 }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.nextClicked, false, false)}>
+                        <FontAwesomeIcon icon={'arrow-right'} />
+                    </div>
                     <div className="presPanel-divider"></div>
-                    <Tooltip title={<><div className="dash-tooltip">{"Click to return to 1st slide"}</div></>}><div className="presPanel-button" style={{ border: 'solid 1px white' }}
-                        onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, () => this.gotoDocument(0, this.activeItem), false, false)}><b>1</b></div></Tooltip>
+                    <Tooltip
+                        title={
+                            <>
+                                <div className="dash-tooltip">{'Click to return to 1st slide'}</div>
+                            </>
+                        }>
+                        <div className="presPanel-button" style={{ border: 'solid 1px white' }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, () => this.gotoDocument(0, this.activeItem), false, false)}>
+                            <b>1</b>
+                        </div>
+                    </Tooltip>
                     <div className="presPanel-button-text">
                         Slide {this.itemIndex + 1} / {this.childDocs.length}
                         {this.playButtonFrames}
                     </div>
                     <div className="presPanel-divider" />
-                    <div className="presPanel-button-text" onPointerDown={e =>
-                        setupMoveUpEvents(this, e, returnFalse, returnFalse, this.exitClicked, false, false)}>EXIT</div>
+                    <div className="presPanel-button-text" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, returnFalse, this.exitClicked, false, false)}>
+                        EXIT
+                    </div>
                 </div>
-            </div >
-            :
-            <div className="presBox-cont" style={{ minWidth: DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc) ? 240 : undefined }} >
+            </div>
+        ) : (
+            <div className="presBox-cont" style={{ minWidth: DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc) ? 240 : undefined }}>
                 {this.topPanel}
                 {this.toolbar}
                 {this.newDocumentToolbarDropdown}
                 <div className="presBox-listCont">
                     <div className="Slide" style={{ height: `calc(100% - 30px)` }}>
-                        {mode !== CollectionViewType.Invalid ?
-                            <CollectionView {...this.props}
+                        {mode !== CollectionViewType.Invalid ? (
+                            <CollectionView
+                                {...this.props}
                                 ContainingCollectionDoc={this.props.Document}
                                 PanelWidth={this.props.PanelWidth}
                                 PanelHeight={this.panelHeight}
@@ -2556,25 +3177,29 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 AddToMap={this.AddToMap}
                                 RemFromMap={this.RemFromMap}
                                 hierarchyIndex={[]}
-                            /> : (null)
-                        }
+                            />
+                        ) : null}
                     </div>
 
-                    { // if the document type is a presentation, then the collection stacking view has a "+ new slide" button at the bottom of the stack
-                        <Tooltip title={<div className="dash-tooltip">{"Click on document to pin to presentaiton or make a marquee selection to pin your desired view"}</div>}>
+                    {
+                        // if the document type is a presentation, then the collection stacking view has a "+ new slide" button at the bottom of the stack
+                        <Tooltip title={<div className="dash-tooltip">{'Click on document to pin to presentaiton or make a marquee selection to pin your desired view'}</div>}>
                             <button className="add-slide-button" onPointerDown={this.startMarqueeCreateSlide}>
                                 + NEW SLIDE
                             </button>
                         </Tooltip>
                     }
                 </div>
-            </div>;
+            </div>
+        );
     }
 }
 
-ScriptingGlobals.add(function navigateToDoc(bestTarget:Doc, activeItem:Doc) {
+ScriptingGlobals.add(function navigateToDoc(bestTarget: Doc, activeItem: Doc) {
     const srcContext = Cast(bestTarget.context, Doc, null) ?? Cast(Cast(bestTarget.annotationOn, Doc, null)?.context, Doc, null);
-    const openInTab = (doc: Doc, finished?: () => void) => {CollectionDockingView.AddSplit(doc, "right"); finished?.(); };
-    DocumentManager.Instance.jumpToDocument(bestTarget, true, openInTab, srcContext ? [srcContext]:[], 
-        undefined, undefined, undefined, () => PresBox.navigateToDoc(bestTarget, activeItem, true), undefined, true, NumCast(activeItem.presZoom));
-});
\ No newline at end of file
+    const openInTab = (doc: Doc, finished?: () => void) => {
+        CollectionDockingView.AddSplit(doc, 'right');
+        finished?.();
+    };
+    DocumentManager.Instance.jumpToDocument(bestTarget, true, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, () => PresBox.navigateToDoc(bestTarget, activeItem, true), undefined, true, NumCast(activeItem.presZoom));
+});
diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx
index 574193614..c5177de90 100644
--- a/src/client/views/search/SearchBox.tsx
+++ b/src/client/views/search/SearchBox.tsx
@@ -1,4 +1,4 @@
-import { Tooltip } from "@material-ui/core";
+import { Tooltip } from '@material-ui/core';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -6,13 +6,12 @@ import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field } from '../..
 import { Id } from '../../../fields/FieldSymbols';
 import { StrCast } from '../../../fields/Types';
 import { DocUtils } from '../../documents/Documents';
-import { DocumentType } from "../../documents/DocumentTypes";
+import { DocumentType } from '../../documents/DocumentTypes';
 import { DocumentManager } from '../../util/DocumentManager';
-import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { ViewBoxBaseComponent } from "../DocComponent";
+import { CollectionDockingView } from '../collections/CollectionDockingView';
+import { ViewBoxBaseComponent } from '../DocComponent';
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
-import "./SearchBox.scss";
-
+import './SearchBox.scss';
 
 const DAMPENING_FACTOR = 0.9;
 const MAX_ITERATIONS = 25;
@@ -29,13 +28,15 @@ export interface SearchBoxProps extends FieldViewProps {
  */
 @observer
 export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(SearchBox, fieldKey);
+    }
     public static Instance: SearchBox;
 
     private _inputRef = React.createRef<HTMLInputElement>();
 
-    @observable _searchString = "";
-    @observable _docTypeString = "all";
+    @observable _searchString = '';
+    @observable _docTypeString = 'all';
     @observable _results: Map<Doc, string[]> = new Map<Doc, string[]>();
     @observable _pageRanks: Map<Doc, number> = new Map<Doc, number>();
     @observable _linkedDocsOut: Map<Doc, Set<Doc>> = new Map<Doc, Set<Doc>>();
@@ -52,7 +53,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
         SearchBox.Instance = this;
     }
 
-    /** 
+    /**
      * This method is called when the SearchBox component is first mounted. When the user opens
      * the search panel, the search input box is automatically selected. This allows the user to
      * type in the search input box immediately, without needing clicking on it first.
@@ -75,7 +76,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
      * This method is called when the text in the search input box is modified by the user. The
      * _searchString is updated to the new value of the text in the input box and submitSearch
      * is called to update the search results accordingly.
-     * 
+     *
      * (Note: There is no longer a need to press enter to submit a search. Any update to the input
      * causes a search to be submitted automatically.)
      */
@@ -88,7 +89,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
      * This method is called when the option in the select drop-down menu is changed. The
      * _docTypeString is updated to the new value of the option in the drop-down menu. This
      * is used to filter the results of the search to documents of a specific type.
-     * 
+     *
      * (Note: This doesn't affect the results array, so there is no need to submit a new
      * search here. The results of the search on the _searchString query are simply filtered
      * by type directly before rendering them.)
@@ -134,8 +135,8 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
             docs.filter(d => d && !visited.includes(d)).forEach(d => {
                 visited.push(d);
                 const fieldKey = Doc.LayoutFieldKey(d);
-                const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
-                const data = d[annos ? fieldKey + "-annotations" : fieldKey];
+                const annos = !Field.toString(Doc.LayoutField(d) as Field).includes('CollectionView');
+                const data = d[annos ? fieldKey + '-annotations' : fieldKey];
                 data && newarray.push(...DocListCast(data));
                 func(depth, d);
             });
@@ -156,14 +157,18 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
         var depth = 0;
         while (docs.length > 0) {
             newarray = [];
-            await Promise.all(docs.filter(d => d).map(async d => {
-                const fieldKey = Doc.LayoutFieldKey(d);
-                const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
-                const data = d[annos ? fieldKey + "-annotations" : fieldKey];
-                const docs = await DocListCastAsync(data);
-                docs && newarray.push(...docs);
-                func(depth, d);
-            }));
+            await Promise.all(
+                docs
+                    .filter(d => d)
+                    .map(async d => {
+                        const fieldKey = Doc.LayoutFieldKey(d);
+                        const annos = !Field.toString(Doc.LayoutField(d) as Field).includes('CollectionView');
+                        const data = d[annos ? fieldKey + '-annotations' : fieldKey];
+                        const docs = await DocListCastAsync(data);
+                        docs && newarray.push(...docs);
+                        func(depth, d);
+                    })
+            );
             docs = newarray;
             depth++;
         }
@@ -177,11 +182,10 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
      * right side of each search result.
      */
     static formatType(type: String): String {
-        if (type === "pdf") {
-            return "PDF";
-        }
-        else if (type === "image") {
-            return "Img";
+        if (type === 'pdf') {
+            return 'PDF';
+        } else if (type === 'image') {
+            return 'Img';
         }
 
         return type.charAt(0).toUpperCase() + type.substring(1, 3);
@@ -198,9 +202,40 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
     @action
     searchCollection(query: string) {
         const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING];
-        const blockedKeys = ["x", "y", "proto", "width", "autoHeight", "acl-Override", "acl-Public", "context", "zIndex", "height", "text-scrollHeight", "text-height", "cloneFieldFilter", "isPrototype", "text-annotations",
-            "dragFactory-count", "text-noTemplate", "aliases", "system", "layoutKey", "baseProto", "xMargin", "yMargin", "links", "layout", "layout_keyValue", "fitWidth", "viewType", "title-custom",
-            "panX", "panY", "viewScale"];
+        const blockedKeys = [
+            'x',
+            'y',
+            'proto',
+            'width',
+            'autoHeight',
+            'acl-Override',
+            'acl-Public',
+            'context',
+            'zIndex',
+            'height',
+            'text-scrollHeight',
+            'text-height',
+            'cloneFieldFilter',
+            'isPrototype',
+            'text-annotations',
+            'dragFactory-count',
+            'text-noTemplate',
+            'aliases',
+            'system',
+            'layoutKey',
+            'baseProto',
+            'xMargin',
+            'yMargin',
+            'links',
+            'layout',
+            'layout_keyValue',
+            'fitWidth',
+            'viewType',
+            'title-custom',
+            'panX',
+            'panY',
+            'viewScale',
+        ];
         const collection = CollectionDockingView.Instance;
         query = query.toLowerCase();
 
@@ -211,10 +246,15 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
             const docs = DocListCast(collection.rootDoc[Doc.LayoutFieldKey(collection.rootDoc)]);
             const docIDs: String[] = [];
             SearchBox.foreachRecursiveDoc(docs, (depth: number, doc: Doc) => {
-                const dtype = StrCast(doc.type, "string") as DocumentType;
+                const dtype = StrCast(doc.type, 'string') as DocumentType;
                 if (dtype && !blockedTypes.includes(dtype) && !docIDs.includes(doc[Id]) && depth > 0) {
                     const hlights = new Set<string>();
-                    SearchBox.documentKeys(doc).forEach(key => Field.toString(doc[key] as Field).toLowerCase().includes(query) && hlights.add(key));
+                    SearchBox.documentKeys(doc).forEach(
+                        key =>
+                            Field.toString(doc[key] as Field)
+                                .toLowerCase()
+                                .includes(query) && hlights.add(key)
+                    );
                     blockedKeys.forEach(key => hlights.delete(key));
 
                     if (Array.from(hlights.keys()).length > 0) {
@@ -250,18 +290,16 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
                 this._results.forEach((_, linkedDoc) => {
                     this._linkedDocsIn.get(linkedDoc)?.add(doc);
                 });
-            }
-            else {
+            } else {
                 const linkedDocSet = new Set<Doc>();
 
-                Doc.GetProto(doc)[DirectLinksSym].forEach((link) => {
+                Doc.GetProto(doc)[DirectLinksSym].forEach(link => {
                     const d1 = link?.anchor1 as Doc;
                     const d2 = link?.anchor2 as Doc;
                     if (doc === d1 && this._results.has(d2)) {
                         linkedDocSet.add(d2);
                         this._linkedDocsIn.get(d2)?.add(doc);
-                    }
-                    else if (doc === d2 && this._results.has(d1)) {
+                    } else if (doc === d2 && this._results.has(d1)) {
                         linkedDocSet.add(d1);
                         this._linkedDocsIn.get(d1)?.add(doc);
                     }
@@ -289,8 +327,8 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
         this._results.forEach((_, doc) => {
             let nextPageRank = pageRankFromAll;
 
-            this._linkedDocsIn.get(doc)?.forEach((linkedDoc) => {
-                nextPageRank += DAMPENING_FACTOR * (this._pageRanks.get(linkedDoc) ?? 0) / (this._linkedDocsOut.get(linkedDoc)?.size ?? 1);
+            this._linkedDocsIn.get(doc)?.forEach(linkedDoc => {
+                nextPageRank += (DAMPENING_FACTOR * (this._pageRanks.get(linkedDoc) ?? 0)) / (this._linkedDocsOut.get(linkedDoc)?.size ?? 1);
             });
 
             nextPageRanks.set(doc, nextPageRank);
@@ -328,7 +366,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
      */
     static documentKeys(doc: Doc) {
         const keys: { [key: string]: boolean } = {};
-        Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => keys[key] = false));
+        Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => (keys[key] = false)));
         return Array.from(Object.keys(keys));
     }
 
@@ -342,15 +380,13 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
 
         const query = StrCast(this._searchString);
         Doc.SetSearchQuery(query);
-        Array.from(this._results.keys()).forEach(doc =>
-            DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.(this._searchString, undefined, true)
-        );
+        Array.from(this._results.keys()).forEach(doc => DocumentManager.Instance.getFirstDocumentView(doc)?.ComponentView?.search?.(this._searchString, undefined, true));
         this._results.clear();
 
         if (query) {
             this.searchCollection(query);
         }
-    }
+    };
 
     /**
      * This method resets the search by iterating through each result and removing all
@@ -372,7 +408,7 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
      */
     selectElement = async (doc: Doc, finishFunc: () => void) => {
         await DocumentManager.Instance.jumpToDocument(doc, true, undefined, [], undefined, undefined, undefined, finishFunc);
-    }
+    };
 
     /**
      * This method returns a JSX list of the options in the select drop-down menu, which
@@ -380,9 +416,13 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
      */
     @computed
     public get selectOptions() {
-        const selectValues = ["all", "rtf", "image", "pdf", "web", "video", "audio", "collection"];
+        const selectValues = ['all', 'rtf', 'image', 'pdf', 'web', 'video', 'audio', 'collection'];
 
-        return selectValues.map(value => <option key={value} value={value}>{SearchBox.formatType(value)}</option>);
+        return selectValues.map(value => (
+            <option key={value} value={value}>
+                {SearchBox.formatType(value)}
+            </option>
+        ));
     }
 
     /**
@@ -393,39 +433,37 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
 
         const isLinkSearch: boolean = this.props.linkSearch;
 
-        const sortedResults = Array.from(this._results.entries()).sort((a, b) => (this._pageRanks.get(b[0]) ?? 0) - (this._pageRanks.get(a[0]) ?? 0));  // sorted by page rank
+        const sortedResults = Array.from(this._results.entries()).sort((a, b) => (this._pageRanks.get(b[0]) ?? 0) - (this._pageRanks.get(a[0]) ?? 0)); // sorted by page rank
 
         const resultsJSX = Array();
 
-        sortedResults.forEach((result) => {
-            var className = "searchBox-results-scroll-view-result";
+        sortedResults.forEach(result => {
+            var className = 'searchBox-results-scroll-view-result';
 
             if (this._selectedResult === result[0]) {
-                className += " searchBox-results-scroll-view-result-selected";
+                className += ' searchBox-results-scroll-view-result-selected';
             }
 
             const formattedType = SearchBox.formatType(StrCast(result[0].type));
             const title = result[0].title;
 
-            if (this._docTypeString === "all" || this._docTypeString === result[0].type) {
+            if (this._docTypeString === 'all' || this._docTypeString === result[0].type) {
                 validResults++;
                 resultsJSX.push(
-                    <Tooltip key={result[0][Id]} placement={"right"} title={<><div className="dash-tooltip">{title}</div></>}>
-                        <div onClick={isLinkSearch ?
-                            () => this.makeLink(result[0]) :
-                            e => {
-                                this.onResultClick(result[0]);
-                                e.stopPropagation();
-                            }} className={className}>
-                            <div className="searchBox-result-title">
-                                {title}
-                            </div>
-                            <div className="searchBox-result-type">
-                                {formattedType}
-                            </div>
-                            <div className="searchBox-result-keys">
-                                {result[1].join(", ")}
-                            </div>
+                    <Tooltip key={result[0][Id]} placement={'right'} title={<div className="dash-tooltip">{title as string}</div>}>
+                        <div
+                            onClick={
+                                isLinkSearch
+                                    ? () => this.makeLink(result[0])
+                                    : e => {
+                                          this.onResultClick(result[0]);
+                                          e.stopPropagation();
+                                      }
+                            }
+                            className={className}>
+                            <div className="searchBox-result-title">{title as string}</div>
+                            <div className="searchBox-result-type">{formattedType}</div>
+                            <div className="searchBox-result-keys">{result[1].join(', ')}</div>
                         </div>
                     </Tooltip>
                 );
@@ -433,22 +471,31 @@ export class SearchBox extends ViewBoxBaseComponent<SearchBoxProps>() {
         });
 
         return (
-            <div style={{ pointerEvents: "all" }} className="searchBox-container">
-                <div className="searchBox-bar" >
-                    {isLinkSearch ? (null) : <select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}>
-                        {this.selectOptions}
-                    </select>}
-                    <input defaultValue={""} autoComplete="off" onChange={this.onInputChange} onKeyPress={e => e.key === "Enter" ? this.submitSearch() : null} type="text" placeholder="Search..." id="search-input" className="searchBox-input" style={{ width: isLinkSearch ? "100%" : undefined, borderRadius: isLinkSearch ? "5px" : undefined }} ref={this._inputRef} />
-                </div >
+            <div style={{ pointerEvents: 'all' }} className="searchBox-container">
+                <div className="searchBox-bar">
+                    {isLinkSearch ? null : (
+                        <select name="type" id="searchBox-type" className="searchBox-type" onChange={this.onSelectChange}>
+                            {this.selectOptions}
+                        </select>
+                    )}
+                    <input
+                        defaultValue={''}
+                        autoComplete="off"
+                        onChange={this.onInputChange}
+                        onKeyPress={e => (e.key === 'Enter' ? this.submitSearch() : null)}
+                        type="text"
+                        placeholder="Search..."
+                        id="search-input"
+                        className="searchBox-input"
+                        style={{ width: isLinkSearch ? '100%' : undefined, borderRadius: isLinkSearch ? '5px' : undefined }}
+                        ref={this._inputRef}
+                    />
+                </div>
                 <div className="searchBox-results-container">
-                    <div className="searchBox-results-count">
-                        {`${validResults}` + " result" + (validResults === 1 ? "" : "s")}
-                    </div>
-                    <div className="searchBox-results-scroll-view">
-                        {resultsJSX}
-                    </div>
+                    <div className="searchBox-results-count">{`${validResults}` + ' result' + (validResults === 1 ? '' : 's')}</div>
+                    <div className="searchBox-results-scroll-view">{resultsJSX}</div>
                 </div>
-            </div >
+            </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index bf06faeb9..e1360553a 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -1,52 +1,236 @@
-
 import { library } from '@fortawesome/fontawesome-svg-core';
 import {
-    faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
-    faTerminal, faToggleOn, faFile as fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard,
-    faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt,
-    faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter,
-    faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote,
-    faThumbtack, faTree, faTv, faBook, faUndoAlt, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faHome, faLongArrowAltLeft, faBars, faTh, faChevronLeft,
-    faAlignRight, faAlignLeft
+    faTasks,
+    faReply,
+    faQuoteLeft,
+    faHandPointLeft,
+    faFolderOpen,
+    faAngleDoubleLeft,
+    faExternalLinkSquareAlt,
+    faMobile,
+    faThLarge,
+    faWindowClose,
+    faEdit,
+    faTrashAlt,
+    faPalette,
+    faAngleRight,
+    faBell,
+    faTrash,
+    faCamera,
+    faExpand,
+    faCaretDown,
+    faCaretLeft,
+    faCaretRight,
+    faCaretSquareDown,
+    faCaretSquareRight,
+    faArrowsAltH,
+    faPlus,
+    faMinus,
+    faTerminal,
+    faToggleOn,
+    faFile as fileSolid,
+    faExternalLinkAlt,
+    faLocationArrow,
+    faSearch,
+    faFileDownload,
+    faStop,
+    faCalculator,
+    faWindowMaximize,
+    faAddressCard,
+    faQuestionCircle,
+    faArrowLeft,
+    faArrowRight,
+    faArrowDown,
+    faArrowUp,
+    faBolt,
+    faBullseye,
+    faCaretUp,
+    faCat,
+    faCheck,
+    faChevronRight,
+    faClipboard,
+    faClone,
+    faCloudUploadAlt,
+    faCommentAlt,
+    faCompressArrowsAlt,
+    faCut,
+    faEllipsisV,
+    faEraser,
+    faExclamation,
+    faFileAlt,
+    faFileAudio,
+    faFilePdf,
+    faFilm,
+    faFilter,
+    faFont,
+    faGlobeAsia,
+    faHighlighter,
+    faLongArrowAltRight,
+    faMicrophone,
+    faMousePointer,
+    faMusic,
+    faObjectGroup,
+    faPause,
+    faPen,
+    faPenNib,
+    faPhone,
+    faPlay,
+    faPortrait,
+    faRedoAlt,
+    faStamp,
+    faStickyNote,
+    faThumbtack,
+    faTree,
+    faTv,
+    faBook,
+    faUndoAlt,
+    faVideo,
+    faAsterisk,
+    faBrain,
+    faImage,
+    faPaintBrush,
+    faTimes,
+    faEye,
+    faHome,
+    faLongArrowAltLeft,
+    faBars,
+    faTh,
+    faChevronLeft,
+    faAlignRight,
+    faAlignLeft,
 } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import * as React from "react";
+import * as React from 'react';
 import { Docs, DocumentOptions, DocUtils } from '../client/documents/Documents';
-import { DocumentType } from "../client/documents/DocumentTypes";
+import { DocumentType } from '../client/documents/DocumentTypes';
 import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
 import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
 import { SettingsManager, ColorScheme } from '../client/util/SettingsManager';
 import { Transform } from '../client/util/Transform';
-import { UndoManager } from "../client/util/UndoManager";
+import { UndoManager } from '../client/util/UndoManager';
 import { TabDocView } from '../client/views/collections/TabDocView';
-import { CollectionViewType } from "../client/views/collections/CollectionView";
-import { GestureOverlay } from "../client/views/GestureOverlay";
-import { AudioBox } from "../client/views/nodes/AudioBox";
+import { CollectionViewType } from '../client/views/collections/CollectionView';
+import { GestureOverlay } from '../client/views/GestureOverlay';
+import { AudioBox } from '../client/views/nodes/AudioBox';
 import { DocumentView } from '../client/views/nodes/DocumentView';
-import { RichTextMenu } from "../client/views/nodes/formattedText/RichTextMenu";
-import { RadialMenu } from "../client/views/nodes/RadialMenu";
+import { RichTextMenu } from '../client/views/nodes/formattedText/RichTextMenu';
+import { RadialMenu } from '../client/views/nodes/RadialMenu';
 import { Doc, DocListCast } from '../fields/Doc';
 import { InkTool } from '../fields/InkField';
-import { List } from "../fields/List";
-import { ScriptField } from "../fields/ScriptField";
-import { Cast, FieldValue } from '../fields/Types';
+import { List } from '../fields/List';
+import { ScriptField } from '../fields/ScriptField';
+import { Cast, FieldValue, StrCast } from '../fields/Types';
 import { emptyFunction, emptyPath, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero } from '../Utils';
-import { AudioUpload } from "./AudioUpload";
-import { Uploader } from "./ImageUpload";
-import "./AudioUpload.scss";
-import "./ImageUpload.scss";
-import "./MobileInterface.scss";
-
-library.add(...[faTasks, faReply, faQuoteLeft, faHandPointLeft, faFolderOpen, faAngleDoubleLeft, faExternalLinkSquareAlt, faMobile, faThLarge, faWindowClose, faEdit, faTrashAlt, faPalette, faAngleRight, faBell, faTrash, faCamera, faExpand, faCaretDown, faCaretLeft, faCaretRight, faCaretSquareDown, faCaretSquareRight, faArrowsAltH, faPlus, faMinus,
-    faTerminal, faToggleOn, fileSolid, faExternalLinkAlt, faLocationArrow, faSearch, faFileDownload, faStop, faCalculator, faWindowMaximize, faAddressCard,
-    faQuestionCircle, faArrowLeft, faArrowRight, faArrowDown, faArrowUp, faBolt, faBullseye, faCaretUp, faCat, faCheck, faChevronRight, faClipboard, faClone, faCloudUploadAlt,
-    faCommentAlt, faCompressArrowsAlt, faCut, faEllipsisV, faEraser, faExclamation, faFileAlt, faFileAudio, faFilePdf, faFilm, faFilter, faFont, faGlobeAsia, faHighlighter,
-    faLongArrowAltRight, faMicrophone, faMousePointer, faMusic, faObjectGroup, faPause, faPen, faPenNib, faPhone, faPlay, faPortrait, faRedoAlt, faStamp, faStickyNote,
-    faThumbtack, faTree, faTv, faUndoAlt, faBook, faVideo, faAsterisk, faBrain, faImage, faPaintBrush, faTimes, faEye, faHome, faLongArrowAltLeft, faBars, faTh, faChevronLeft,
-    faAlignLeft, faAlignRight].map(m => m as any));
-
+import { AudioUpload } from './AudioUpload';
+import { Uploader } from './ImageUpload';
+import './AudioUpload.scss';
+import './ImageUpload.scss';
+import './MobileInterface.scss';
+
+library.add(
+    ...[
+        faTasks,
+        faReply,
+        faQuoteLeft,
+        faHandPointLeft,
+        faFolderOpen,
+        faAngleDoubleLeft,
+        faExternalLinkSquareAlt,
+        faMobile,
+        faThLarge,
+        faWindowClose,
+        faEdit,
+        faTrashAlt,
+        faPalette,
+        faAngleRight,
+        faBell,
+        faTrash,
+        faCamera,
+        faExpand,
+        faCaretDown,
+        faCaretLeft,
+        faCaretRight,
+        faCaretSquareDown,
+        faCaretSquareRight,
+        faArrowsAltH,
+        faPlus,
+        faMinus,
+        faTerminal,
+        faToggleOn,
+        fileSolid,
+        faExternalLinkAlt,
+        faLocationArrow,
+        faSearch,
+        faFileDownload,
+        faStop,
+        faCalculator,
+        faWindowMaximize,
+        faAddressCard,
+        faQuestionCircle,
+        faArrowLeft,
+        faArrowRight,
+        faArrowDown,
+        faArrowUp,
+        faBolt,
+        faBullseye,
+        faCaretUp,
+        faCat,
+        faCheck,
+        faChevronRight,
+        faClipboard,
+        faClone,
+        faCloudUploadAlt,
+        faCommentAlt,
+        faCompressArrowsAlt,
+        faCut,
+        faEllipsisV,
+        faEraser,
+        faExclamation,
+        faFileAlt,
+        faFileAudio,
+        faFilePdf,
+        faFilm,
+        faFilter,
+        faFont,
+        faGlobeAsia,
+        faHighlighter,
+        faLongArrowAltRight,
+        faMicrophone,
+        faMousePointer,
+        faMusic,
+        faObjectGroup,
+        faPause,
+        faPen,
+        faPenNib,
+        faPhone,
+        faPlay,
+        faPortrait,
+        faRedoAlt,
+        faStamp,
+        faStickyNote,
+        faThumbtack,
+        faTree,
+        faTv,
+        faUndoAlt,
+        faBook,
+        faVideo,
+        faAsterisk,
+        faBrain,
+        faImage,
+        faPaintBrush,
+        faTimes,
+        faEye,
+        faHome,
+        faLongArrowAltLeft,
+        faBars,
+        faTh,
+        faChevronLeft,
+        faAlignLeft,
+        faAlignRight,
+    ].map(m => m as any)
+);
 
 @observer
 export class MobileInterface extends React.Component {
@@ -64,36 +248,38 @@ export class MobileInterface extends React.Component {
     @observable private _homeDoc: Doc = this._mainDoc; // home menu as a document
     @observable private _parents: Array<Doc> = []; // array of parent docs (for pathbar)
 
-    @computed private get mainContainer() { return Doc.UserDoc() ? FieldValue(Cast(Doc.UserDoc().activeMobile, Doc)) : CurrentUserUtils.GuestMobile; }
+    @computed private get mainContainer() {
+        return Doc.UserDoc() ? FieldValue(Cast(Doc.UserDoc().activeMobile, Doc)) : CurrentUserUtils.GuestMobile;
+    }
 
     constructor(props: Readonly<{}>) {
         super(props);
-        this._library = CurrentUserUtils.setupDashboards(Doc.UserDoc(), "myDashboards"); // to access documents in Dash Web
+        this._library = CurrentUserUtils.setupDashboards(Doc.UserDoc(), 'myDashboards'); // to access documents in Dash Web
         MobileInterface.Instance = this;
     }
 
     @action
     componentDidMount = () => {
         // if the home menu is in list view -> adjust the menu toggle appropriately
-        this._menuListView = this._homeDoc._viewType === "stacking" ? true : false;
+        this._menuListView = this._homeDoc._viewType === 'stacking' ? true : false;
         CurrentUserUtils.ActiveTool = InkTool.None; // ink should intially be set to none
         Doc.UserDoc().activeMobile = this._homeDoc; // active mobile set to home
         AudioBox.Enabled = true;
 
         // remove double click to avoid mobile zoom in
-        document.removeEventListener("dblclick", this.onReactDoubleClick);
-        document.addEventListener("dblclick", this.onReactDoubleClick);
-    }
+        document.removeEventListener('dblclick', this.onReactDoubleClick);
+        document.addEventListener('dblclick', this.onReactDoubleClick);
+    };
 
     @action
     componentWillUnmount = () => {
         document.removeEventListener('dblclick', this.onReactDoubleClick);
-    }
+    };
 
     // Prevent zooming in when double tapping the screen
     onReactDoubleClick = (e: MouseEvent) => {
         e.stopPropagation();
-    }
+    };
 
     // Switch the mobile view to the given doc
     @action
@@ -110,7 +296,7 @@ export class MobileInterface extends React.Component {
         // Ensures that switching to home is not registed
         UndoManager.undoStack.length = 0;
         UndoManager.redoStack.length = 0;
-    }
+    };
 
     // For toggling the hamburger menu
     @action
@@ -120,29 +306,29 @@ export class MobileInterface extends React.Component {
         if (this._ink) {
             this.onSwitchInking();
         }
-    }
+    };
     /**
      * Method called when 'Library' button is pressed on the home screen
      */
     switchToLibrary = async () => {
         this.switchCurrentView(this._library);
-        runInAction(() => this._homeMenu = false);
+        runInAction(() => (this._homeMenu = false));
         this.toggleSidebar();
-    }
+    };
 
     /**
      * Back method for navigating through items
      */
     @action
     back = () => {
-        const header = document.getElementById("header") as HTMLElement;
+        const header = document.getElementById('header') as HTMLElement;
         const doc = Cast(this._parents.pop(), Doc) as Doc; // Parent document
         // Case 1: Parent document is 'dashboards'
-        if (doc === Cast(this._library, Doc) as Doc) {
+        if (doc === (Cast(this._library, Doc) as Doc)) {
             this.dashboards = null;
             this.switchCurrentView(this._library);
             // Case 2: Parent document is the 'home' menu (root node)
-        } else if (doc === Cast(this._homeDoc, Doc) as Doc) {
+        } else if (doc === (Cast(this._homeDoc, Doc) as Doc)) {
             this._homeMenu = true;
             this._parents = [];
             this.dashboards = null;
@@ -155,7 +341,7 @@ export class MobileInterface extends React.Component {
             header.textContent = String(doc.title);
         }
         this._ink = false; // turns ink off
-    }
+    };
 
     /**
      * Return 'Home", which implies returning to 'Home' menu buttons
@@ -171,7 +357,7 @@ export class MobileInterface extends React.Component {
         if (this._sidebarActive) {
             this.toggleSidebar();
         }
-    }
+    };
 
     /**
      * Return to primary Dashboard in library (Dashboards Doc)
@@ -182,7 +368,7 @@ export class MobileInterface extends React.Component {
         this.switchCurrentView(this._library);
         this._homeMenu = false;
         this.dashboards = null;
-    }
+    };
 
     /**
      * Note: window.innerWidth and window.screen.width compute different values.
@@ -190,14 +376,14 @@ export class MobileInterface extends React.Component {
      * display resolution which computes differently.
      */
     returnWidth = () => window.innerWidth; //The windows width
-    returnHeight = () => (window.innerHeight - 300); //Calculating the windows height (-300 to account for topbar)
-    whitebackground = () => "white";
+    returnHeight = () => window.innerHeight - 300; //Calculating the windows height (-300 to account for topbar)
+    whitebackground = () => 'white';
     /**
      * DocumentView for graphic display of all documents
      */
     @computed get displayDashboards() {
-        return !this.mainContainer ? (null) :
-            <div style={{ position: "relative", top: '198px', height: `calc(100% - 350px)`, width: "100%", left: "0%" }}>
+        return !this.mainContainer ? null : (
+            <div style={{ position: 'relative', top: '198px', height: `calc(100% - 350px)`, width: '100%', left: '0%' }}>
                 <DocumentView
                     Document={this.mainContainer}
                     DataDoc={undefined}
@@ -223,7 +409,8 @@ export class MobileInterface extends React.Component {
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
                 />
-            </div>;
+            </div>
+        );
     }
 
     /**
@@ -233,20 +420,19 @@ export class MobileInterface extends React.Component {
      */
     handleClick = async (doc: Doc) => {
         runInAction(() => {
-            if (doc.type !== "collection" && this._sidebarActive) {
+            if (doc.type !== 'collection' && this._sidebarActive) {
                 this._parents.push(this._activeDoc);
                 this.switchCurrentView(doc);
                 this._homeMenu = false;
                 this.toggleSidebar();
-            }
-            else {
+            } else {
                 this._parents.push(this._activeDoc);
                 this.switchCurrentView(doc);
                 this._homeMenu = false;
                 this.dashboards = doc;
             }
         });
-    }
+    };
 
     /**
      * Called when an item in the library is clicked and should
@@ -260,30 +446,31 @@ export class MobileInterface extends React.Component {
         this._homeMenu = false;
         this.dashboards = doc;
         this.toggleSidebar();
-    }
+    };
 
     // Renders the graphical pathbar
     renderPathbar = () => {
         const docPath = [...this._parents, this._activeDoc];
-        const items = docPath.map((doc: Doc, index: any) =>
+        const items = docPath.map((doc: Doc, index: any) => (
             <div className="pathbarItem" key={index}>
-                {index === 0 ? (null) : <FontAwesomeIcon key="icon" className="pathIcon" icon="angle-right" size="lg" />}
-                <div className="pathbarText"
-                    style={{ backgroundColor: this._homeMenu || doc === this._activeDoc ? "rgb(119,17,37)" : undefined }}
-                    onClick={() => this.handlePathClick(doc, index)}>{doc.title}
+                {index === 0 ? null : <FontAwesomeIcon key="icon" className="pathIcon" icon="angle-right" size="lg" />}
+                <div className="pathbarText" style={{ backgroundColor: this._homeMenu || doc === this._activeDoc ? 'rgb(119,17,37)' : undefined }} onClick={() => this.handlePathClick(doc, index)}>
+                    {StrCast(doc.title)}
                 </div>
-            </div>);
-        return (<div className="pathbar">
-            <div className="scrollmenu">
-                {items}
             </div>
-            {!this._parents.length ? (null) :
-                <div className="back" >
-                    <FontAwesomeIcon onClick={this.back} icon={"chevron-left"} color="white" size={"2x"} />
-                </div>}
-            <div className="hidePath" />
-        </div>);
-    }
+        ));
+        return (
+            <div className="pathbar">
+                <div className="scrollmenu">{items}</div>
+                {!this._parents.length ? null : (
+                    <div className="back">
+                        <FontAwesomeIcon onClick={this.back} icon={'chevron-left'} color="white" size={'2x'} />
+                    </div>
+                )}
+                <div className="hidePath" />
+            </div>
+        );
+    };
 
     // Handles when user clicks on a document in the pathbar
     @action
@@ -300,7 +487,7 @@ export class MobileInterface extends React.Component {
             this.switchCurrentView(doc);
             this._parents.length = index;
         }
-    }
+    };
 
     // Renders the contents of the menu and sidebar
     @computed get renderDefaultContent() {
@@ -309,7 +496,9 @@ export class MobileInterface extends React.Component {
                 <div>
                     <div className="navbar">
                         <FontAwesomeIcon className="home" icon="home" onClick={this.returnHome} />
-                        <div className="header" id="header">{this._homeDoc.title}</div>
+                        <div className="header" id="header">
+                            {StrCast(this._homeDoc.title)}
+                        </div>
                         <div className="cover" id="cover" onClick={e => e.stopPropagation()}></div>
                         <div className="toggle-btn" id="menuButton" onClick={this.toggleSidebar}>
                             <span></span>
@@ -328,14 +517,17 @@ export class MobileInterface extends React.Component {
         }
         // returns a list of navbar buttons as 'buttons'
         const buttons = DocListCast(dashboards.data).map((doc: Doc, index: any) => {
-            if (doc.type !== "ink") {
+            if (doc.type !== 'ink') {
                 return (
-                    <div
-                        className="item"
-                        key={index}>
-                        <div className="item-title" onClick={() => this.handleClick(doc)}> {doc.title} </div>
-                        <div className="item-type" onClick={() => this.handleClick(doc)}>{doc.type}</div>
-                        <FontAwesomeIcon onClick={() => this.handleClick(doc)} className="right" icon="angle-right" size="lg" style={{ display: `${doc.type === "collection" ? "block" : "none"}` }} />
+                    <div className="item" key={index}>
+                        <div className="item-title" onClick={() => this.handleClick(doc)}>
+                            {' '}
+                            {doc.title as string}{' '}
+                        </div>
+                        <div className="item-type" onClick={() => this.handleClick(doc)}>
+                            {doc.type as string}
+                        </div>
+                        <FontAwesomeIcon onClick={() => this.handleClick(doc)} className="right" icon="angle-right" size="lg" style={{ display: `${doc.type === 'collection' ? 'block' : 'none'}` }} />
                         <FontAwesomeIcon className="open" onClick={() => this.openFromSidebar(doc)} icon="external-link-alt" size="lg" />
                     </div>
                 );
@@ -346,43 +538,39 @@ export class MobileInterface extends React.Component {
             <div>
                 <div className="navbar">
                     <FontAwesomeIcon className="home" icon="home" onClick={this.returnHome} />
-                    <div className="header" id="header">{this._sidebarActive ? "library" : this._activeDoc.title}</div>
-                    <div className={`toggle-btn ${this._sidebarActive ? "active" : ""}`} onClick={this.toggleSidebar}>
+                    <div className="header" id="header">
+                        {this._sidebarActive ? 'library' : (this._activeDoc.title as string)}
+                    </div>
+                    <div className={`toggle-btn ${this._sidebarActive ? 'active' : ''}`} onClick={this.toggleSidebar}>
                         <span></span>
                         <span></span>
                         <span></span>
                     </div>
-                    <div className={`background ${this._sidebarActive ? "active" : ""}`} onClick={this.toggleSidebar}></div>
+                    <div className={`background ${this._sidebarActive ? 'active' : ''}`} onClick={this.toggleSidebar}></div>
                 </div>
                 {this.renderPathbar()}
-                <div className={`sidebar ${this._sidebarActive ? "active" : ""}`}>
+                <div className={`sidebar ${this._sidebarActive ? 'active' : ''}`}>
                     <div className="sidebarButtons">
-                        {this.dashboards ?
+                        {this.dashboards ? (
                             <>
                                 {buttons}
-                                <div
-                                    className="item" key="home"
-                                    onClick={this.returnMain}
-                                    style={{ opacity: 0.7 }}>
+                                <div className="item" key="home" onClick={this.returnMain} style={{ opacity: 0.7 }}>
                                     <FontAwesomeIcon className="right" icon="angle-double-left" size="lg" />
                                     <div className="item-type">Return to dashboards</div>
                                 </div>
-                            </> :
+                            </>
+                        ) : (
                             <>
                                 {buttons}
-                                <div
-                                    className="item"
-                                    style={{ opacity: 0.7 }}
-                                    onClick={() => this.createNewDashboard()}>
+                                <div className="item" style={{ opacity: 0.7 }} onClick={() => this.createNewDashboard()}>
                                     <FontAwesomeIcon className="right" icon="plus" size="lg" />
                                     <div className="item-type">Create New Dashboard</div>
                                 </div>
                             </>
-                        }
+                        )}
                     </div>
                 </div>
-                <div className={`blanket ${this._sidebarActive ? "active" : ""}`} onClick={this.toggleSidebar}>
-                </div>
+                <div className={`blanket ${this._sidebarActive ? 'active' : ''}`} onClick={this.toggleSidebar}></div>
             </div>
         );
     }
@@ -397,26 +585,26 @@ export class MobileInterface extends React.Component {
         const freeformOptions: DocumentOptions = {
             x: 0,
             y: 400,
-            title: "Collection " + dashboardCount,
+            title: 'Collection ' + dashboardCount,
         };
         const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
-        const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, "row");
+        const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, 'row');
 
         const toggleTheme = ScriptField.MakeScript(`self.colorScheme = self.colorScheme ? undefined: ${ColorScheme.Dark}}`);
         const toggleComic = ScriptField.MakeScript(`toggleComicMode()`);
         const cloneDashboard = ScriptField.MakeScript(`cloneDashboard()`);
         dashboardDoc.contextMenuScripts = new List<ScriptField>([toggleTheme!, toggleComic!, cloneDashboard!]);
-        dashboardDoc.contextMenuLabels = new List<string>(["Toggle Theme Colors", "Toggle Comic Mode", "New Dashboard Layout"]);
+        dashboardDoc.contextMenuLabels = new List<string>(['Toggle Theme Colors', 'Toggle Comic Mode', 'New Dashboard Layout']);
 
-        Doc.AddDocToList(scens, "data", dashboardDoc);
-    }
+        Doc.AddDocToList(scens, 'data', dashboardDoc);
+    };
 
     // Button for switching between pen and ink mode
     @action
     onSwitchInking = () => {
-        const button = document.getElementById("inkButton") as HTMLElement;
-        button.style.backgroundColor = this._ink ? "white" : "black";
-        button.style.color = this._ink ? "black" : "white";
+        const button = document.getElementById('inkButton') as HTMLElement;
+        button.style.backgroundColor = this._ink ? 'white' : 'black';
+        button.style.color = this._ink ? 'black' : 'white';
 
         if (!this._ink) {
             CurrentUserUtils.ActiveTool = InkTool.Pen;
@@ -425,23 +613,20 @@ export class MobileInterface extends React.Component {
             CurrentUserUtils.ActiveTool = InkTool.None;
             this._ink = false;
         }
-    }
+    };
 
     // The static ink menu that appears at the top
     @computed get inkMenu() {
-        return this._activeDoc._viewType !== CollectionViewType.Docking || !this._ink ? (null) :
-            <div className="colorSelector">
-                {/* <CollectionFreeFormViewChrome /> */}
-            </div>;
+        return this._activeDoc._viewType !== CollectionViewType.Docking || !this._ink ? null : <div className="colorSelector">{/* <CollectionFreeFormViewChrome /> */}</div>;
     }
 
     // DocButton that uses UndoManager and handles the opacity change if CanUndo is true
     @computed get undo() {
-        if (this.mainContainer && this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc &&
-            this._activeDoc !== Doc.SharingDoc() && this._activeDoc.title !== "WORKSPACES") {
+        if (this.mainContainer && this._activeDoc.type === 'collection' && this._activeDoc !== this._homeDoc && this._activeDoc !== Doc.SharingDoc() && this._activeDoc.title !== 'WORKSPACES') {
             return (
-                <div className="docButton"
-                    style={{ backgroundColor: "black", color: "white", fontSize: "60", opacity: UndoManager.CanUndo() ? "1" : "0.4", }}
+                <div
+                    className="docButton"
+                    style={{ backgroundColor: 'black', color: 'white', fontSize: '60', opacity: UndoManager.CanUndo() ? '1' : '0.4' }}
                     id="undoButton"
                     title="undo"
                     onClick={(e: React.MouseEvent) => {
@@ -449,17 +634,18 @@ export class MobileInterface extends React.Component {
                         e.stopPropagation();
                     }}>
                     <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="undo-alt" />
-                </div>);
-        } else return (null);
+                </div>
+            );
+        } else return null;
     }
 
     // DocButton that uses UndoManager and handles the opacity change if CanRedo is true
     @computed get redo() {
-        if (this.mainContainer && this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc &&
-            this._activeDoc !== Doc.SharingDoc() && this._activeDoc.title !== "WORKSPACES") {
+        if (this.mainContainer && this._activeDoc.type === 'collection' && this._activeDoc !== this._homeDoc && this._activeDoc !== Doc.SharingDoc() && this._activeDoc.title !== 'WORKSPACES') {
             return (
-                <div className="docButton"
-                    style={{ backgroundColor: "black", color: "white", fontSize: "60", opacity: UndoManager.CanRedo() ? "1" : "0.4", }}
+                <div
+                    className="docButton"
+                    style={{ backgroundColor: 'black', color: 'white', fontSize: '60', opacity: UndoManager.CanRedo() ? '1' : '0.4' }}
                     id="undoButton"
                     title="redo"
                     onClick={(e: React.MouseEvent) => {
@@ -467,78 +653,81 @@ export class MobileInterface extends React.Component {
                         e.stopPropagation();
                     }}>
                     <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="redo-alt" />
-                </div>);
-        } else return (null);
+                </div>
+            );
+        } else return null;
     }
 
     // DocButton for switching into ink mode
     @computed get drawInk() {
-        return !this.mainContainer || this._activeDoc._viewType !== CollectionViewType.Docking ? (null) :
-            <div className="docButton"
-                id="inkButton"
-                title={Doc.isDocPinned(this._activeDoc) ? "Pen on" : "Pen off"}
-                onClick={this.onSwitchInking}>
+        return !this.mainContainer || this._activeDoc._viewType !== CollectionViewType.Docking ? null : (
+            <div className="docButton" id="inkButton" title={Doc.isDocPinned(this._activeDoc) ? 'Pen on' : 'Pen off'} onClick={this.onSwitchInking}>
                 <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="pen-nib" />
-            </div>;
+            </div>
+        );
     }
 
     // DocButton: Button that appears on the bottom of the screen to initiate image upload
     @computed get uploadImageButton() {
-        if (this._activeDoc.type === DocumentType.COL && this._activeDoc !== this._homeDoc && this._activeDoc._viewType !== CollectionViewType.Docking && this._activeDoc.title !== "WORKSPACES") {
-            return <div className="docButton"
-                id="imageButton"
-                title={Doc.isDocPinned(this._activeDoc) ? "Pen on" : "Pen off"}
-                onClick={this.toggleUpload}>
-                <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="upload" />
-            </div>;
-        } else return (null);
+        if (this._activeDoc.type === DocumentType.COL && this._activeDoc !== this._homeDoc && this._activeDoc._viewType !== CollectionViewType.Docking && this._activeDoc.title !== 'WORKSPACES') {
+            return (
+                <div className="docButton" id="imageButton" title={Doc.isDocPinned(this._activeDoc) ? 'Pen on' : 'Pen off'} onClick={this.toggleUpload}>
+                    <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="upload" />
+                </div>
+            );
+        } else return null;
     }
 
     // DocButton to download images on the mobile
     @computed get downloadDocument() {
-        if (this._activeDoc.type === "image" || this._activeDoc.type === "pdf" || this._activeDoc.type === "video") {
-            return <div className="docButton"
-                title={"Download Image"}
-                style={{ backgroundColor: "white", color: "black" }}
-                onClick={e => window.open(this._activeDoc["data-path"]?.toString())}> {/*  daa-path holds the url */}
-                <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="download" />
-            </div>;
-        } else return (null);
+        if (this._activeDoc.type === 'image' || this._activeDoc.type === 'pdf' || this._activeDoc.type === 'video') {
+            return (
+                <div className="docButton" title={'Download Image'} style={{ backgroundColor: 'white', color: 'black' }} onClick={e => window.open(this._activeDoc['data-path']?.toString())}>
+                    {' '}
+                    {/*  daa-path holds the url */}
+                    <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="download" />
+                </div>
+            );
+        } else return null;
     }
 
     // DocButton for pinning images to presentation
     @computed get pinToPresentation() {
         // Only making button available if it is an image
-        if (!(this._activeDoc.type === "collection" || this._activeDoc.type === "presentation")) {
+        if (!(this._activeDoc.type === 'collection' || this._activeDoc.type === 'presentation')) {
             const isPinned = this._activeDoc && Doc.isDocPinned(this._activeDoc);
-            return <div className="docButton"
-                title={Doc.isDocPinned(this._activeDoc) ? "Unpin from presentation" : "Pin to presentation"}
-                style={{ backgroundColor: isPinned ? "black" : "white", color: isPinned ? "white" : "black" }}
-                onClick={e => TabDocView.PinDoc(this._activeDoc)}>
-                <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
-            </div>;
-        } else return (null);
+            return (
+                <div
+                    className="docButton"
+                    title={Doc.isDocPinned(this._activeDoc) ? 'Unpin from presentation' : 'Pin to presentation'}
+                    style={{ backgroundColor: isPinned ? 'black' : 'white', color: isPinned ? 'white' : 'black' }}
+                    onClick={e => TabDocView.PinDoc(this._activeDoc)}>
+                    <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
+                </div>
+            );
+        } else return null;
     }
 
     // Buttons for switching the menu between large and small icons
     @computed get switchMenuView() {
-        return this._activeDoc.title !== this._homeDoc.title ? (null) :
+        return this._activeDoc.title !== this._homeDoc.title ? null : (
             <div className="homeSwitch">
-                <div className={`list ${!this._menuListView ? "active" : ""}`} onClick={this.changeToIconView}>
+                <div className={`list ${!this._menuListView ? 'active' : ''}`} onClick={this.changeToIconView}>
                     <FontAwesomeIcon size="sm" icon="th-large" />
                 </div>
-                <div className={`list ${this._menuListView ? "active" : ""}`} onClick={this.changeToListView}>
+                <div className={`list ${this._menuListView ? 'active' : ''}`} onClick={this.changeToListView}>
                     <FontAwesomeIcon size="sm" icon="bars" />
                 </div>
-            </div>;
+            </div>
+        );
     }
 
     // Logic for switching the menu into the icons
     @action
     changeToIconView = () => {
-        if (this._homeDoc._viewType = "stacking") {
+        if ((this._homeDoc._viewType = 'stacking')) {
             this._menuListView = false;
-            this._homeDoc._viewType = "masonry";
+            this._homeDoc._viewType = 'masonry';
             this._homeDoc.columnWidth = 300;
             this._homeDoc._columnWidth = 300;
             const menuButtons = DocListCast(this._homeDoc.data);
@@ -551,13 +740,13 @@ export class MobileInterface extends React.Component {
                 doc._nativeWidth = 400;
             });
         }
-    }
+    };
 
     // Logic for switching the menu into the stacking view
     @action
     changeToListView = () => {
-        if (this._homeDoc._viewType = "masonry") {
-            this._homeDoc._viewType = "stacking";
+        if ((this._homeDoc._viewType = 'masonry')) {
+            this._homeDoc._viewType = 'stacking';
             this._menuListView = true;
             const menuButtons = DocListCast(this._homeDoc.data);
             menuButtons.map(doc => {
@@ -568,7 +757,7 @@ export class MobileInterface extends React.Component {
                 doc._nativeWidth = 900;
             });
         }
-    }
+    };
 
     // For setting up the presentation document for the home menu
     @action
@@ -579,49 +768,48 @@ export class MobileInterface extends React.Component {
             this.switchCurrentView(presentation);
             this._homeMenu = false;
         }
-    }
+    };
 
     // For toggling image upload pop up
     @action
-    toggleUpload = () => this._imageUploadActive = !this._imageUploadActive
+    toggleUpload = () => (this._imageUploadActive = !this._imageUploadActive);
 
     // For toggling audio record and dictate pop up
     @action
-    toggleAudio = () => this._audioUploadActive = !this._audioUploadActive
+    toggleAudio = () => (this._audioUploadActive = !this._audioUploadActive);
 
     // Button for toggling the upload pop up in a collection
     @action
     toggleUploadInCollection = () => {
-        const button = document.getElementById("imageButton") as HTMLElement;
-        button.style.backgroundColor = this._imageUploadActive ? "white" : "black";
-        button.style.color = this._imageUploadActive ? "black" : "white";
+        const button = document.getElementById('imageButton') as HTMLElement;
+        button.style.backgroundColor = this._imageUploadActive ? 'white' : 'black';
+        button.style.color = this._imageUploadActive ? 'black' : 'white';
 
         this._imageUploadActive = !this._imageUploadActive;
-    }
+    };
 
     // For closing the image upload pop up
     @action
     closeUpload = () => {
         this._imageUploadActive = false;
-    }
+    };
 
     // Returns the image upload pop up
     @computed get uploadImage() {
-        const doc = !this._homeMenu ? this._activeDoc : Cast(Doc.SharingDoc(), Doc) as Doc;
+        const doc = !this._homeMenu ? this._activeDoc : (Cast(Doc.SharingDoc(), Doc) as Doc);
         return <Uploader Document={doc} />;
     }
 
-    // Radial menu can only be used if it is a colleciton and it is not a homeDoc 
+    // Radial menu can only be used if it is a colleciton and it is not a homeDoc
     // (and cannot be used on Dashboard to avoid pin to presentation opening on right)
     @computed get displayRadialMenu() {
-        return this._activeDoc.type === "collection" && this._activeDoc !== this._homeDoc &&
-            this._activeDoc._viewType !== CollectionViewType.Docking ? <RadialMenu /> : (null);
+        return this._activeDoc.type === 'collection' && this._activeDoc !== this._homeDoc && this._activeDoc._viewType !== CollectionViewType.Docking ? <RadialMenu /> : null;
     }
 
     onDragOver = (e: React.DragEvent) => {
         e.preventDefault();
         e.stopPropagation();
-    }
+    };
 
     /**
      * MENU BUTTON
@@ -633,22 +821,22 @@ export class MobileInterface extends React.Component {
         const mobileUpload = Cast(Doc.SharingDoc(), Doc) as Doc;
         this.switchCurrentView(mobileUpload);
         this._homeMenu = false;
-    }
+    };
 
     render() {
         return (
             <div className="mobileInterface-container" onDragOver={this.onDragOver}>
                 <SettingsManager />
-                <div className={`image-upload ${this._imageUploadActive ? "active" : ""}`}>
-                    {this.uploadImage}
-                </div>
-                <div className={`audio-upload ${this._audioUploadActive ? "active" : ""}`}>
+                <div className={`image-upload ${this._imageUploadActive ? 'active' : ''}`}>{this.uploadImage}</div>
+                <div className={`audio-upload ${this._audioUploadActive ? 'active' : ''}`}>
                     <AudioUpload />
                 </div>
                 {this.switchMenuView}
                 {this.inkMenu}
                 <GestureOverlay>
-                    <div style={{ display: "none" }}><RichTextMenu key="rich" /></div>
+                    <div style={{ display: 'none' }}>
+                        <RichTextMenu key="rich" />
+                    </div>
                     <div className="docButtonContainer">
                         {this.pinToPresentation}
                         {this.downloadDocument}
@@ -666,21 +854,31 @@ export class MobileInterface extends React.Component {
     }
 }
 
-
 //Global functions for mobile menu
-ScriptingGlobals.add(function switchToMobileLibrary() { return MobileInterface.Instance.switchToLibrary(); },
-    "opens the library to navigate through dashboards on Dash Mobile");
-ScriptingGlobals.add(function openMobileUploads() { return MobileInterface.Instance.toggleUpload(); },
-    "opens the upload files menu for Dash Mobile");
-ScriptingGlobals.add(function switchToMobileUploadCollection() { return MobileInterface.Instance.switchToMobileUploads(); },
-    "opens the mobile uploads collection on Dash Mobile");
-ScriptingGlobals.add(function openMobileAudio() { return MobileInterface.Instance.toggleAudio(); },
-    "opens the record and dictate menu on Dash Mobile");
-ScriptingGlobals.add(function switchToMobilePresentation() { return MobileInterface.Instance.setupDefaultPresentation(); },
-    "opens the presentation on Dash Mobile");
-ScriptingGlobals.add(function openMobileSettings() { return SettingsManager.Instance.open(); },
-    "opens settings on Dash Mobile");
+ScriptingGlobals.add(function switchToMobileLibrary() {
+    return MobileInterface.Instance.switchToLibrary();
+}, 'opens the library to navigate through dashboards on Dash Mobile');
+ScriptingGlobals.add(function openMobileUploads() {
+    return MobileInterface.Instance.toggleUpload();
+}, 'opens the upload files menu for Dash Mobile');
+ScriptingGlobals.add(function switchToMobileUploadCollection() {
+    return MobileInterface.Instance.switchToMobileUploads();
+}, 'opens the mobile uploads collection on Dash Mobile');
+ScriptingGlobals.add(function openMobileAudio() {
+    return MobileInterface.Instance.toggleAudio();
+}, 'opens the record and dictate menu on Dash Mobile');
+ScriptingGlobals.add(function switchToMobilePresentation() {
+    return MobileInterface.Instance.setupDefaultPresentation();
+}, 'opens the presentation on Dash Mobile');
+ScriptingGlobals.add(function openMobileSettings() {
+    return SettingsManager.Instance.open();
+}, 'opens settings on Dash Mobile');
 
 // Other global functions for mobile
-ScriptingGlobals.add(function switchMobileView(doc: Doc, renderView?: () => JSX.Element, onSwitch?: () => void) { return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch); },
-    "changes the active document displayed on the Dash Mobile", "(doc: any)");
\ No newline at end of file
+ScriptingGlobals.add(
+    function switchMobileView(doc: Doc, renderView?: () => JSX.Element, onSwitch?: () => void) {
+        return MobileInterface.Instance.switchCurrentView(doc, renderView, onSwitch);
+    },
+    'changes the active document displayed on the Dash Mobile',
+    '(doc: any)'
+);
diff --git a/src/server/authentication/AuthenticationManager.ts b/src/server/authentication/AuthenticationManager.ts
index b736f0d35..3622be4c5 100644
--- a/src/server/authentication/AuthenticationManager.ts
+++ b/src/server/authentication/AuthenticationManager.ts
@@ -1,14 +1,14 @@
-import { default as User, DashUserModel } from "./DashUserModel";
-import { Request, Response, NextFunction } from "express";
-import * as passport from "passport";
-import { IVerifyOptions } from "passport-local";
-import "./Passport";
-import flash = require("express-flash");
+import { default as User, DashUserModel } from './DashUserModel';
+import { Request, Response, NextFunction } from 'express';
+import * as passport from 'passport';
+import { IVerifyOptions } from 'passport-local';
+import './Passport';
+import flash = require('express-flash');
 import * as async from 'async';
 import * as nodemailer from 'nodemailer';
-import c = require("crypto");
-import { Utils } from "../../Utils";
-import { MailOptions } from "nodemailer/lib/stream-transport";
+import c = require('crypto');
+import { emptyFunction, Utils } from '../../Utils';
+import { MailOptions } from 'nodemailer/lib/stream-transport';
 
 /**
  * GET /signup
@@ -17,10 +17,10 @@ import { MailOptions } from "nodemailer/lib/stream-transport";
  */
 export let getSignup = (req: Request, res: Response) => {
     if (req.user) {
-        return res.redirect("/home");
+        return res.redirect('/home');
     }
-    res.render("signup.pug", {
-        title: "Sign Up",
+    res.render('signup.pug', {
+        title: 'Sign Up',
         user: req.user,
     });
 };
@@ -30,15 +30,15 @@ export let getSignup = (req: Request, res: Response) => {
  * Create a new local account.
  */
 export let postSignup = (req: Request, res: Response, next: NextFunction) => {
-    req.assert("email", "Email is not valid").isEmail();
-    req.assert("password", "Password must be at least 4 characters long").len({ min: 4 });
-    req.assert("confirmPassword", "Passwords do not match").equals(req.body.password);
-    req.sanitize("email").normalizeEmail({ gmail_remove_dots: false });
+    req.assert('email', 'Email is not valid').isEmail();
+    req.assert('password', 'Password must be at least 4 characters long').len({ min: 4 });
+    req.assert('confirmPassword', 'Passwords do not match').equals(req.body.password);
+    req.sanitize('email').normalizeEmail({ gmail_remove_dots: false });
 
     const errors = req.validationErrors();
 
     if (errors) {
-        return res.redirect("/signup");
+        return res.redirect('/signup');
     }
 
     const email = req.body.email as String;
@@ -50,25 +50,30 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
         userDocumentId: Utils.GenerateGuid(),
         sharingDocumentId: Utils.GenerateGuid(),
         linkDatabaseId: Utils.GenerateGuid(),
-        cacheDocumentIds: ""
+        cacheDocumentIds: '',
     } as Partial<DashUserModel>;
 
     const user = new User(model);
 
     User.findOne({ email }, (err: any, existingUser: any) => {
-        if (err) { return next(err); }
+        if (err) {
+            return next(err);
+        }
         if (existingUser) {
-            return res.redirect("/login");
+            return res.redirect('/login');
         }
         user.save((err: any) => {
-            if (err) { return next(err); }
-            req.logIn(user, (err) => {
-                if (err) { return next(err); }
+            if (err) {
+                return next(err);
+            }
+            req.logIn(user, err => {
+                if (err) {
+                    return next(err);
+                }
                 tryRedirectToTarget(req, res);
             });
         });
     });
-
 };
 
 const tryRedirectToTarget = (req: Request, res: Response) => {
@@ -76,11 +81,10 @@ const tryRedirectToTarget = (req: Request, res: Response) => {
     if (req.session && target) {
         res.redirect(target);
     } else {
-        res.redirect("/home");
+        res.redirect('/home');
     }
 };
 
-
 /**
  * GET /login
  * Login page.
@@ -88,11 +92,11 @@ const tryRedirectToTarget = (req: Request, res: Response) => {
 export let getLogin = (req: Request, res: Response) => {
     if (req.user) {
         //req.session.target = undefined;
-        return res.redirect("/home");
+        return res.redirect('/home');
     }
-    res.render("login.pug", {
-        title: "Log In",
-        user: req.user
+    res.render('login.pug', {
+        title: 'Log In',
+        user: req.user,
     });
 };
 
@@ -102,24 +106,30 @@ export let getLogin = (req: Request, res: Response) => {
  * On failure, redirect to signup page
  */
 export let postLogin = (req: Request, res: Response, next: NextFunction) => {
-    req.assert("email", "Email is not valid").isEmail();
-    req.assert("password", "Password cannot be blank").notEmpty();
-    req.sanitize("email").normalizeEmail({ gmail_remove_dots: false });
+    req.assert('email', 'Email is not valid').isEmail();
+    req.assert('password', 'Password cannot be blank').notEmpty();
+    req.sanitize('email').normalizeEmail({ gmail_remove_dots: false });
 
     const errors = req.validationErrors();
 
     if (errors) {
-        req.flash("errors", "Unable to login at this time. Please try again.");
-        return res.redirect("/signup");
+        req.flash('errors', 'Unable to login at this time. Please try again.');
+        return res.redirect('/signup');
     }
 
-    passport.authenticate("local", (err: Error, user: DashUserModel, _info: IVerifyOptions) => {
-        if (err) { next(err); return; }
+    passport.authenticate('local', (err: Error, user: DashUserModel, _info: IVerifyOptions) => {
+        if (err) {
+            next(err);
+            return;
+        }
         if (!user) {
-            return res.redirect("/signup");
+            return res.redirect('/signup');
         }
-        req.logIn(user, (err) => {
-            if (err) { next(err); return; }
+        req.logIn(user, err => {
+            if (err) {
+                next(err);
+                return;
+            }
             tryRedirectToTarget(req, res);
         });
     })(req, res, next);
@@ -131,140 +141,154 @@ export let postLogin = (req: Request, res: Response, next: NextFunction) => {
  * and destroys the user's current session.
  */
 export let getLogout = (req: Request, res: Response) => {
-    req.logout();
+    req.logout(emptyFunction);
     const sess = req.session;
     if (sess) {
-        sess.destroy((err) => { if (err) { console.log(err); } });
+        sess.destroy(err => {
+            if (err) {
+                console.log(err);
+            }
+        });
     }
-    res.redirect("/login");
+    res.redirect('/login');
 };
 
 export let getForgot = function (req: Request, res: Response) {
-    res.render("forgot.pug", {
-        title: "Recover Password",
+    res.render('forgot.pug', {
+        title: 'Recover Password',
         user: req.user,
     });
 };
 
 export let postForgot = function (req: Request, res: Response, next: NextFunction) {
     const email = req.body.email;
-    async.waterfall([
-        function (done: any) {
-            c.randomBytes(20, function (err: any, buffer: Buffer) {
-                if (err) {
-                    done(null);
-                    return;
-                }
-                done(null, buffer.toString('hex'));
-            });
-        },
-        function (token: string, done: any) {
-            User.findOne({ email }, function (err: any, user: DashUserModel) {
-                if (!user) {
-                    // NO ACCOUNT WITH SUBMITTED EMAIL
-                    res.redirect("/forgotPassword");
-                    return;
-                }
-                user.passwordResetToken = token;
-                user.passwordResetExpires = new Date(Date.now() + 3600000); // 1 HOUR
-                user.save(function (err: any) {
-                    done(null, token, user);
+    async.waterfall(
+        [
+            function (done: any) {
+                c.randomBytes(20, function (err: any, buffer: Buffer) {
+                    if (err) {
+                        done(null);
+                        return;
+                    }
+                    done(null, buffer.toString('hex'));
                 });
-            });
-        },
-        function (token: Uint16Array, user: DashUserModel, done: any) {
-            const smtpTransport = nodemailer.createTransport({
-                service: 'Gmail',
-                auth: {
-                    user: 'browndashptc@gmail.com',
-                    pass: 'TsarNicholas#2'
-                }
-            });
-            const mailOptions = {
-                to: user.email,
-                from: 'browndashptc@gmail.com',
-                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 + '/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) {
-                // req.flash('info', 'An e-mail has been sent to ' + user.email + ' with further instructions.');
-                done(null, err, 'done');
-            });
+            },
+            function (token: string, done: any) {
+                User.findOne({ email }, function (err: any, user: DashUserModel) {
+                    if (!user) {
+                        // NO ACCOUNT WITH SUBMITTED EMAIL
+                        res.redirect('/forgotPassword');
+                        return;
+                    }
+                    user.passwordResetToken = token;
+                    user.passwordResetExpires = new Date(Date.now() + 3600000); // 1 HOUR
+                    user.save(function (err: any) {
+                        done(null, token, user);
+                    });
+                });
+            },
+            function (token: Uint16Array, user: DashUserModel, done: any) {
+                const smtpTransport = nodemailer.createTransport({
+                    service: 'Gmail',
+                    auth: {
+                        user: 'browndashptc@gmail.com',
+                        pass: 'TsarNicholas#2',
+                    },
+                });
+                const mailOptions = {
+                    to: user.email,
+                    from: 'browndashptc@gmail.com',
+                    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 +
+                        '/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) {
+                    // req.flash('info', 'An e-mail has been sent to ' + user.email + ' with further instructions.');
+                    done(null, err, 'done');
+                });
+            },
+        ],
+        function (err) {
+            if (err) return next(err);
+            res.redirect('/forgotPassword');
         }
-    ], function (err) {
-        if (err) return next(err);
-        res.redirect("/forgotPassword");
-    });
+    );
 };
 
 export let getReset = function (req: Request, res: Response) {
     User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err: any, user: DashUserModel) {
         if (!user || err) {
-            return res.redirect("/forgotPassword");
+            return res.redirect('/forgotPassword');
         }
-        res.render("reset.pug", {
-            title: "Reset Password",
+        res.render('reset.pug', {
+            title: 'Reset Password',
             user: req.user,
         });
     });
 };
 
 export let postReset = function (req: Request, res: Response) {
-    async.waterfall([
-        function (done: any) {
-            User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err: any, user: DashUserModel) {
-                if (!user || err) {
-                    return res.redirect('back');
-                }
+    async.waterfall(
+        [
+            function (done: any) {
+                User.findOne({ passwordResetToken: req.params.token, passwordResetExpires: { $gt: Date.now() } }, function (err: any, user: DashUserModel) {
+                    if (!user || err) {
+                        return res.redirect('back');
+                    }
 
-                req.assert("password", "Password must be at least 4 characters long").len({ min: 4 });
-                req.assert("confirmPassword", "Passwords do not match").equals(req.body.password);
+                    req.assert('password', 'Password must be at least 4 characters long').len({ min: 4 });
+                    req.assert('confirmPassword', 'Passwords do not match').equals(req.body.password);
 
-                if (req.validationErrors()) {
-                    return res.redirect('back');
-                }
+                    if (req.validationErrors()) {
+                        return res.redirect('back');
+                    }
 
-                user.password = req.body.password;
-                user.passwordResetToken = undefined;
-                user.passwordResetExpires = undefined;
+                    user.password = req.body.password;
+                    user.passwordResetToken = undefined;
+                    user.passwordResetExpires = undefined;
 
-                user.save(function (err) {
-                    if (err) {
-                        res.redirect("/login");
-                        return;
-                    }
-                    req.logIn(user, function (err) {
+                    user.save(function (err) {
                         if (err) {
+                            res.redirect('/login');
                             return;
                         }
+                        req.logIn(user, function (err) {
+                            if (err) {
+                                return;
+                            }
+                        });
+                        done(null, user);
                     });
-                    done(null, user);
                 });
-            });
-        },
-        function (user: DashUserModel, done: any) {
-            const smtpTransport = nodemailer.createTransport({
-                service: 'Gmail',
-                auth: {
-                    user: 'browndashptc@gmail.com',
-                    pass: 'TsarNicholas#2'
-                }
-            });
-            const mailOptions = {
-                to: user.email,
-                from: 'browndashptc@gmail.com',
-                subject: 'Your password has been changed',
-                text: 'Hello,\n\n' +
-                    'This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n'
-            } as MailOptions;
-            smtpTransport.sendMail(mailOptions, function (err) {
-                done(null, err);
-            });
+            },
+            function (user: DashUserModel, done: any) {
+                const smtpTransport = nodemailer.createTransport({
+                    service: 'Gmail',
+                    auth: {
+                        user: 'browndashptc@gmail.com',
+                        pass: 'TsarNicholas#2',
+                    },
+                });
+                const mailOptions = {
+                    to: user.email,
+                    from: 'browndashptc@gmail.com',
+                    subject: 'Your password has been changed',
+                    text: 'Hello,\n\n' + 'This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n',
+                } as MailOptions;
+                smtpTransport.sendMail(mailOptions, function (err) {
+                    done(null, err);
+                });
+            },
+        ],
+        function (err) {
+            res.redirect('/login');
         }
-    ], function (err) {
-        res.redirect("/login");
-    });
-};
\ No newline at end of file
+    );
+};
-- 
cgit v1.2.3-70-g09d2


From 146f8622d5bac2edc6b09f57c173bd057dfbcfad Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 8 Jul 2022 00:17:26 -0400
Subject: restructured currentUserUtils to avoid having import cycles.

---
 package-lock.json                                  |   39 -
 src/client/DocServer.ts                            |   91 +-
 src/client/apis/youtube/YoutubeBox.tsx             |  281 ++--
 src/client/documents/DocumentTypes.ts              |  100 +-
 src/client/documents/Documents.ts                  | 1494 ++++++++++-------
 src/client/util/CurrentUserUtils.ts                |  349 +---
 src/client/util/DocumentManager.ts                 |  184 ++-
 src/client/util/History.ts                         |   45 +-
 src/client/util/LinkFollower.ts                    |  112 ++
 src/client/util/LinkManager.ts                     |  259 ++-
 src/client/util/SelectionManager.ts                |   29 +-
 src/client/util/SettingsManager.tsx                |  471 +++---
 src/client/util/SharingManager.tsx                 |   11 +-
 src/client/views/DashboardView.tsx                 |  400 +++--
 src/client/views/DocComponent.tsx                  |  143 +-
 src/client/views/DocumentButtonBar.tsx             |  449 +++--
 src/client/views/DocumentDecorations.tsx           |   68 +-
 src/client/views/GestureOverlay.tsx                |  740 +++++----
 src/client/views/GlobalKeyHandler.ts               |  279 ++--
 src/client/views/InkStrokeProperties.ts            |  278 ++--
 src/client/views/InkTranscription.tsx              |    3 +-
 src/client/views/LightboxView.tsx                  |  258 +--
 src/client/views/Main.tsx                          |    2 +-
 src/client/views/MainView.tsx                      |   65 +-
 src/client/views/MarqueeAnnotator.tsx              |  226 +--
 src/client/views/OverlayView.tsx                   |  197 ++-
 src/client/views/PreviewCursor.tsx                 |  200 ++-
 src/client/views/PropertiesButtons.tsx             |  386 +++--
 src/client/views/PropertiesDocContextSelector.tsx  |   67 +-
 src/client/views/PropertiesView.tsx                | 1729 +++++++++++---------
 src/client/views/SidebarAnnos.tsx                  |  128 +-
 src/client/views/StyleProvider.tsx                 |   11 +-
 src/client/views/TemplateMenu.tsx                  |  189 +--
 .../views/collections/CollectionDockingView.tsx    |   25 +-
 src/client/views/collections/CollectionMenu.tsx    | 1246 +++++++-------
 .../collections/CollectionStackedTimeline.tsx      |  716 ++++----
 .../views/collections/CollectionStackingView.tsx   |    6 +-
 src/client/views/collections/CollectionSubView.tsx |  286 ++--
 .../views/collections/CollectionTreeView.tsx       |    9 +-
 src/client/views/collections/CollectionView.tsx    |   75 +-
 src/client/views/collections/TabDocView.tsx        |   16 +-
 src/client/views/collections/TreeView.tsx          | 1168 +++++++------
 .../CollectionFreeFormLayoutEngines.tsx            |  248 ++-
 .../CollectionFreeFormLinkView.tsx                 |  201 ++-
 .../CollectionFreeFormRemoteCursors.tsx            |  105 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  |   28 +-
 .../collections/collectionFreeForm/MarqueeView.tsx |  517 +++---
 .../collectionLinear/CollectionLinearView.tsx      |    9 +-
 src/client/views/linking/LinkMenuItem.tsx          |    3 +-
 src/client/views/linking/LinkPopup.tsx             |   31 +-
 src/client/views/nodes/AudioBox.tsx                |  613 +++----
 src/client/views/nodes/ColorBox.tsx                |   96 +-
 src/client/views/nodes/DocumentView.tsx            | 1460 ++++++++++-------
 src/client/views/nodes/FilterBox.tsx               |  486 +++---
 src/client/views/nodes/ImageBox.tsx                |  362 ++--
 src/client/views/nodes/LinkAnchorBox.tsx           |  167 +-
 src/client/views/nodes/LinkDocPreview.tsx          |    3 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |    5 +-
 src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx |  111 +-
 src/client/views/nodes/ScreenshotBox.tsx           |  489 +++---
 src/client/views/nodes/VideoBox.tsx                |  958 ++++++-----
 src/client/views/nodes/WebBox.tsx                  |  818 +++++----
 src/client/views/nodes/button/FontIconBox.tsx      |   25 +-
 .../views/nodes/formattedText/DashDocView.tsx      |   15 +-
 .../views/nodes/formattedText/DashFieldView.tsx    |    2 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   11 +-
 src/client/views/nodes/trails/PresBox.tsx          |   48 +-
 src/client/views/nodes/trails/PresElementBox.tsx   |  440 ++---
 src/client/views/pdf/Annotation.tsx                |  108 +-
 src/client/views/pdf/PDFViewer.tsx                 |  455 +++---
 src/client/views/topbar/TopBar.tsx                 |  180 +-
 src/client/views/webcam/DashWebRTCVideo.tsx        |   88 +-
 src/fields/Doc.ts                                  |  151 +-
 src/mobile/MobileInterface.tsx                     |   19 +-
 src/mobile/MobileMain.tsx                          |   32 +-
 75 files changed, 11685 insertions(+), 9429 deletions(-)
 create mode 100644 src/client/util/LinkFollower.ts

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

diff --git a/package-lock.json b/package-lock.json
index ed3160696..3b9cda8bb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5059,16 +5059,6 @@
             "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
             "dev": true
         },
-        "d": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
-            "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
-            "dev": true,
-            "requires": {
-                "es5-ext": "^0.10.50",
-                "type": "^1.0.1"
-            }
-        },
         "d3-array": {
             "version": "2.12.1",
             "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
@@ -6218,28 +6208,6 @@
                 "is-symbol": "^1.0.2"
             }
         },
-        "es5-ext": {
-            "version": "0.10.61",
-            "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.61.tgz",
-            "integrity": "sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==",
-            "dev": true,
-            "requires": {
-                "es6-iterator": "^2.0.3",
-                "es6-symbol": "^3.1.3",
-                "next-tick": "^1.1.0"
-            }
-        },
-        "es6-iterator": {
-            "version": "2.0.3",
-            "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
-            "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
-            "dev": true,
-            "requires": {
-                "d": "1",
-                "es5-ext": "^0.10.35",
-                "es6-symbol": "^3.1.1"
-            }
-        },
         "es6-promise": {
             "version": "3.2.1",
             "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
@@ -6251,7 +6219,6 @@
             "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
             "dev": true,
             "requires": {
-                "d": "^1.0.1",
                 "ext": "^1.1.2"
             }
         },
@@ -20748,12 +20715,6 @@
             "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
             "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
         },
-        "type": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
-            "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
-            "dev": true
-        },
         "type-check": {
             "version": "0.3.2",
             "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index c9a30b8e3..1b0ba6bc3 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -1,27 +1,27 @@
-import * as io from 'socket.io-client';
-import { MessageStore, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent } from "./../server/Message";
-import { Opt, Doc, UpdatingFromServer, updateCachedAcls } from '../fields/Doc';
-import { Utils, emptyFunction } from '../Utils';
-import { SerializationHelper } from './util/SerializationHelper';
-import { RefField } from '../fields/RefField';
-import { Id, HandleUpdate, Parent } from '../fields/FieldSymbols';
-import { GestureOverlay } from './views/GestureOverlay';
-import MobileInkOverlay from '../mobile/MobileInkOverlay';
 import { runInAction } from 'mobx';
+import * as rp from 'request-promise';
+import * as io from 'socket.io-client';
+import { Doc, Opt, UpdatingFromServer } from '../fields/Doc';
+import { HandleUpdate, Id, Parent } from '../fields/FieldSymbols';
 import { ObjectField } from '../fields/ObjectField';
+import { RefField } from '../fields/RefField';
 import { StrCast } from '../fields/Types';
-import * as rp from 'request-promise';
+import MobileInkOverlay from '../mobile/MobileInkOverlay';
+import { emptyFunction, Utils } from '../Utils';
+import { GestureContent, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, YoutubeQueryTypes } from './../server/Message';
+import { SerializationHelper } from './util/SerializationHelper';
+import { GestureOverlay } from './views/GestureOverlay';
 
 /**
  * This class encapsulates the transfer and cross-client synchronization of
  * data stored only in documents (RefFields). In the process, it also
  * creates and maintains a cache of documents so that they can be accessed
  * more efficiently. Currently, there is no cache eviction scheme in place.
- * 
+ *
  * NOTE: while this class is technically abstracted to work with any [RefField], because
  * [Doc] instances are the only [RefField] we need / have implemented at the moment, the documentation
  * will treat all data used here as [Doc]s
- * 
+ *
  * Any time we want to write a new field to the database (via the server)
  * or update ourselves based on the server's update message, that occurs here
  */
@@ -32,12 +32,12 @@ export namespace DocServer {
         const strings: string[] = [];
         Array.from(Object.keys(_cache)).forEach(key => {
             const doc = _cache[key];
-            if (doc instanceof Doc) strings.push(StrCast(doc.author) + " " + StrCast(doc.title) + " " + StrCast(Doc.GetT(doc, "title", "string", true)));
+            if (doc instanceof Doc) strings.push(StrCast(doc.author) + ' ' + StrCast(doc.title) + ' ' + StrCast(Doc.GetT(doc, 'title', 'string', true)));
         });
-        print && strings.sort().forEach((str, i) => console.log(i.toString() + " " + str));
-        rp.post(Utils.prepend("/setCacheDocumentIds"), {
+        print && strings.sort().forEach((str, i) => console.log(i.toString() + ' ' + str));
+        rp.post(Utils.prepend('/setCacheDocumentIds'), {
             body: {
-                cacheDocumentIds: Array.from(Object.keys(_cache)).join(";"),
+                cacheDocumentIds: Array.from(Object.keys(_cache)).join(';'),
             },
             json: true,
         });
@@ -50,8 +50,8 @@ export namespace DocServer {
     export enum WriteMode {
         Default = 0, //Anything goes
         Playground = 1, //Playground (write own/no read other updates)
-        LiveReadonly = 2,//Live Readonly (no write/read others)
-        LivePlayground = 3,//Live Playground (write own/read others)
+        LiveReadonly = 2, //Live Readonly (no write/read others)
+        LivePlayground = 3, //Live Playground (write own/read others)
     }
     const fieldWriteModes: { [field: string]: WriteMode } = {};
     const docsWithUpdates: { [field: string]: Set<Doc> } = {};
@@ -62,7 +62,7 @@ export namespace DocServer {
         livePlaygroundFields.forEach(f => DocServer.setFieldWriteMode(f, DocServer.WriteMode.Playground));
     }
     export function IsPlaygroundField(field: string) {
-        return DocServer.PlaygroundFields?.includes(field.replace(/^_/, ""));
+        return DocServer.PlaygroundFields?.includes(field.replace(/^_/, ''));
     }
 
     export function setFieldWriteMode(field: string, writeMode: WriteMode) {
@@ -83,7 +83,7 @@ export namespace DocServer {
     export function registerDocWithCachedUpdate(doc: Doc, field: string, oldValue: any) {
         let list = docsWithUpdates[field];
         if (!list) {
-            list = docsWithUpdates[field] = new Set;
+            list = docsWithUpdates[field] = new Set();
         }
         if (!list.has(doc)) {
             Doc.AddCachedUpdate(doc, field, oldValue);
@@ -92,7 +92,6 @@ export namespace DocServer {
     }
 
     export namespace Mobile {
-
         export function dispatchGesturePoints(content: GestureContent) {
             Utils.Emit(_socket, MessageStore.GesturePoints, content);
         }
@@ -111,17 +110,17 @@ export namespace DocServer {
         }
     }
 
-    const instructions = "This page will automatically refresh after this alert is closed. Expect to reconnect after about 30 seconds.";
+    const instructions = 'This page will automatically refresh after this alert is closed. Expect to reconnect after about 30 seconds.';
     function alertUser(connectionTerminationReason: string) {
         switch (connectionTerminationReason) {
-            case "crash":
+            case 'crash':
                 alert(`Dash has temporarily crashed. Administrators have been notified and the server is restarting itself. ${instructions}`);
                 break;
-            case "temporary":
+            case 'temporary':
                 alert(`An administrator has chosen to restart the server. ${instructions}`);
                 break;
-            case "exit":
-                alert("An administrator has chosen to kill the server. Do not expect to reconnect until administrators start the server.");
+            case 'exit':
+                alert('An administrator has chosen to kill the server. Do not expect to reconnect until administrators start the server.');
                 break;
             default:
                 console.log(`Received an unknown ConnectionTerminated message: ${connectionTerminationReason}`);
@@ -132,7 +131,7 @@ export namespace DocServer {
     export function init(protocol: string, hostname: string, port: number, identifier: string) {
         _cache = {};
         GUID = identifier;
-        protocol = protocol.startsWith("https") ? "wss" : "ws";
+        protocol = protocol.startsWith('https') ? 'wss' : 'ws';
         _socket = io.connect(`${protocol}://${hostname}:${port}`);
         // io.connect(`https://7f079dda.ngrok.io`);// if using ngrok, create a special address for the websocket
 
@@ -153,17 +152,17 @@ export namespace DocServer {
         Utils.AddServerHandler(_socket, MessageStore.ConnectionTerminated, alertUser);
 
         // mobile ink overlay socket events to communicate between mobile view and desktop view
-        _socket.addEventListener("receiveGesturePoints", (content: GestureContent) => {
+        _socket.addEventListener('receiveGesturePoints', (content: GestureContent) => {
             MobileInkOverlay.Instance.drawStroke(content);
         });
-        _socket.addEventListener("receiveOverlayTrigger", (content: MobileInkOverlayContent) => {
+        _socket.addEventListener('receiveOverlayTrigger', (content: MobileInkOverlayContent) => {
             GestureOverlay.Instance.enableMobileInkOverlay(content);
             MobileInkOverlay.Instance.initMobileInkOverlay(content);
         });
-        _socket.addEventListener("receiveUpdateOverlayPosition", (content: UpdateMobileInkOverlayPositionContent) => {
+        _socket.addEventListener('receiveUpdateOverlayPosition', (content: UpdateMobileInkOverlayPositionContent) => {
             MobileInkOverlay.Instance.updatePosition(content);
         });
-        _socket.addEventListener("receiveMobileDocumentUpload", (content: MobileDocumentUploadContent) => {
+        _socket.addEventListener('receiveMobileDocumentUpload', (content: MobileDocumentUploadContent) => {
             MobileInkOverlay.Instance.uploadDocument(content);
         });
     }
@@ -173,12 +172,11 @@ export namespace DocServer {
     }
 
     export namespace Control {
-
         let _isReadOnly = false;
         export function makeReadOnly() {
             if (!_isReadOnly) {
                 _isReadOnly = true;
-                _CreateField = field => _cache[field[Id]] = field;
+                _CreateField = field => (_cache[field[Id]] = field);
                 _UpdateField = emptyFunction;
                 _RespondToUpdate = emptyFunction; // bcz: option: don't clear RespondToUpdate to continue to receive updates as others change the DB
             }
@@ -195,8 +193,9 @@ export namespace DocServer {
             }
         }
 
-        export function isReadOnly() { return _isReadOnly; }
-
+        export function isReadOnly() {
+            return _isReadOnly;
+        }
     }
 
     /**
@@ -209,7 +208,6 @@ export namespace DocServer {
     }
 
     export namespace Util {
-
         /**
          * Emits a message to the server that wipes
          * all documents in the database.
@@ -217,7 +215,6 @@ export namespace DocServer {
         export function deleteDatabase() {
             Utils.Emit(_socket, MessageStore.DeleteAll, {});
         }
-
     }
 
     // RETRIEVE DOCS FROM SERVER
@@ -258,8 +255,7 @@ export namespace DocServer {
                     });
                     cached[UpdatingFromServer] = false;
                     return cached;
-                }
-                else if (field !== undefined) {
+                } else if (field !== undefined) {
                     _cache[id] = field;
                 } else {
                     delete _cache[id];
@@ -272,7 +268,7 @@ export namespace DocServer {
             // here, indicate that the document associated with this id is currently
             // being retrieved and cached
             !force && (_cache[id] = deserializeField);
-            return force ? cached as any : deserializeField;
+            return force ? (cached as any) : deserializeField;
         } else if (cached instanceof Promise) {
             // BEING RETRIEVED AND CACHED => some other caller previously (likely recently) called GetRefField(s),
             // and requested the document I'm looking for. Shouldn't fetch again, just
@@ -316,7 +312,6 @@ export namespace DocServer {
         Utils.EmitCallback(_socket, MessageStore.YoutubeApiQuery, { type: YoutubeQueryTypes.VideoDetails, videoIds: videoIds }, callBack);
     }
 
-
     /**
      * Given a list of Doc GUIDs, this utility function will asynchronously attempt to each id's associated
      * field, first looking in the RefField cache and then communicating with
@@ -381,7 +376,7 @@ export namespace DocServer {
                                 return deserialized;
                             });
                             // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache)
-                            // we set the value at the field's id to a promise that will resolve to the field. 
+                            // we set the value at the field's id to a promise that will resolve to the field.
                             // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method).
                             // The mapping in the .then call ensures that when other callers await these promises, they'll
                             // get the resolved field
@@ -391,7 +386,7 @@ export namespace DocServer {
                             proms.push(prom);
                         } else if (cached instanceof Promise) {
                             proms.push(cached as any);
-                            cached.then((f: any) => fieldMap[field.id] = f);
+                            cached.then((f: any) => (fieldMap[field.id] = f));
                         } else if (field) {
                             proms.push(cached as any);
                             fieldMap[field.id] = DocServer.GetCachedRefField(field.id) || field;
@@ -413,20 +408,19 @@ export namespace DocServer {
                 const field = fields[id];
                 map[id] = field;
             });
-
         }
 
         // 7) those promises we encountered in the else if of 1), which represent
         // other callers having already submitted a request to the server for (a) document(s)
         // in which we're interested, must still be awaited so that we can return the proper
-        // values for those as well. 
+        // values for those as well.
         //
         // fortunately, those other callers will also hit their own version of 6) and clean up
         // the shared cache when these promises resolve, so all we have to do is...
         const otherCallersFetching = await Promise.all(promises);
         // ...extract the RefFields returned from the resolution of those promises and add them to our
         // own map.
-        waitingIds.forEach((id, index) => map[id] = otherCallersFetching[index]);
+        waitingIds.forEach((id, index) => (map[id] = otherCallersFetching[index]));
 
         // now, we return our completed mapping from all of the ids that were passed into the method
         // to their actual RefField | undefined values. This return value either becomes the input
@@ -476,7 +470,7 @@ export namespace DocServer {
     }
 
     function _UpdateFieldImpl(id: string, diff: any) {
-        (!DocServer.Control.isReadOnly()) && Utils.Emit(_socket, MessageStore.UpdateField, { id, diff });
+        !DocServer.Control.isReadOnly() && Utils.Emit(_socket, MessageStore.UpdateField, { id, diff });
     }
 
     let _UpdateField: (id: string, diff: any) => void = errorFunc;
@@ -520,12 +514,11 @@ export namespace DocServer {
         Utils.Emit(_socket, MessageStore.DeleteFields, ids);
     }
 
-
     function _respondToDeleteImpl(ids: string | string[]) {
         function deleteId(id: string) {
             delete _cache[id];
         }
-        if (typeof ids === "string") {
+        if (typeof ids === 'string') {
             deleteId(ids);
         } else if (Array.isArray(ids)) {
             ids.map(deleteId);
diff --git a/src/client/apis/youtube/YoutubeBox.tsx b/src/client/apis/youtube/YoutubeBox.tsx
index e14dc60b4..05879a247 100644
--- a/src/client/apis/youtube/YoutubeBox.tsx
+++ b/src/client/apis/youtube/YoutubeBox.tsx
@@ -1,17 +1,16 @@
 import { action, observable, runInAction } from 'mobx';
-import { observer } from "mobx-react";
-import { Doc, DocListCastAsync } from "../../../fields/Doc";
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { Utils } from "../../../Utils";
-import { DocServer } from "../../DocServer";
-import { Docs } from "../../documents/Documents";
-import { DocumentDecorations } from "../../views/DocumentDecorations";
-import { FieldView, FieldViewProps } from "../../views/nodes/FieldView";
-import "../../views/nodes/WebBox.scss";
-import "./YoutubeBox.scss";
-import React = require("react");
+import { observer } from 'mobx-react';
+import { Doc, DocListCastAsync } from '../../../fields/Doc';
 import { InkTool } from '../../../fields/InkField';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { Utils } from '../../../Utils';
+import { DocServer } from '../../DocServer';
+import { Docs } from '../../documents/Documents';
+import { DocumentDecorations } from '../../views/DocumentDecorations';
+import { FieldView, FieldViewProps } from '../../views/nodes/FieldView';
+import '../../views/nodes/WebBox.scss';
+import './YoutubeBox.scss';
+import React = require('react');
 
 interface VideoTemplate {
     thumbnailUrl: string;
@@ -29,19 +28,19 @@ interface VideoTemplate {
  */
 @observer
 export class YoutubeBox extends React.Component<FieldViewProps> {
-
     @observable YoutubeSearchElement: HTMLInputElement | undefined;
     @observable searchResultsFound: boolean = false;
     @observable searchResults: any[] = [];
     @observable videoClicked: boolean = false;
-    @observable selectedVideoUrl: string = "";
+    @observable selectedVideoUrl: string = '';
     @observable lisOfBackUp: JSX.Element[] = [];
     @observable videoIds: string | undefined;
     @observable videoDetails: any[] = [];
     @observable curVideoTemplates: VideoTemplate[] = [];
 
-
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(YoutubeBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(YoutubeBox, fieldKey);
+    }
 
     /**
      * When component mounts, last search's results are laoded in based on the back up stored
@@ -54,19 +53,15 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
         const castedDetailBackUp = Cast(this.props.Document.cachedDetails, Doc);
         const awaitedDetails = await castedDetailBackUp;
 
-
         if (awaitedBackUp) {
-
-
             const jsonList = await DocListCastAsync(awaitedBackUp.json);
             const jsonDetailList = await DocListCastAsync(awaitedDetails!.json);
 
             if (jsonList!.length !== 0) {
-                runInAction(() => this.searchResultsFound = true);
+                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 (const video of jsonList!) {
-
                     const videoId = await Cast(video.id, Doc);
                     const id = StrCast(videoId!.videoId);
                     const snippet = await Cast(video.snippet, Doc);
@@ -75,10 +70,10 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
                     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 pusblishDate = this.roundPublishTime(StrCast(snippet!.publishedAt))!;
                     const channelTitle = StrCast(snippet!.channelTitle);
-                    let duration: string = "";
-                    let viewCount: string = "";
+                    let duration: string = '';
+                    let viewCount: string = '';
                     if (jsonDetailList!.length !== 0) {
                         const contentDetails = await Cast(jsonDetailList![index].contentDetails, Doc);
                         const statistics = await Cast(jsonDetailList![index].statistics, Doc);
@@ -86,7 +81,16 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
                         viewCount = this.abbreviateViewCount(parseInt(StrCast(statistics!.viewCount)))!;
                     }
                     index = index + 1;
-                    const 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));
                 }
             }
@@ -96,20 +100,20 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
     _ignore = 0;
     onPreWheel = (e: React.WheelEvent) => {
         this._ignore = e.timeStamp;
-    }
+    };
     onPrePointer = (e: React.PointerEvent) => {
         this._ignore = e.timeStamp;
-    }
+    };
     onPostPointer = (e: React.PointerEvent) => {
         if (this._ignore !== e.timeStamp) {
             e.stopPropagation();
         }
-    }
+    };
     onPostWheel = (e: React.WheelEvent) => {
         if (this._ignore !== e.timeStamp) {
             e.stopPropagation();
         }
-    }
+    };
 
     /**
      * Function that submits the title entered by user on enter press.
@@ -117,12 +121,11 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
     onEnterKeyDown = (e: React.KeyboardEvent) => {
         if (e.keyCode === 13) {
             const submittedTitle = this.YoutubeSearchElement!.value;
-            this.YoutubeSearchElement!.value = "";
+            this.YoutubeSearchElement!.value = '';
             this.YoutubeSearchElement!.blur();
             DocServer.getYoutubeVideos(submittedTitle, this.processesVideoResults);
-
         }
-    }
+    };
 
     /**
      * The callback that is passed in to server, which functions as a way to
@@ -134,12 +137,12 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
         this.searchResults = videos;
         if (this.searchResults.length > 0) {
             this.searchResultsFound = true;
-            this.videoIds = "";
-            videos.forEach((video) => {
-                if (this.videoIds === "") {
+            this.videoIds = '';
+            videos.forEach(video => {
+                if (this.videoIds === '') {
                     this.videoIds = video.id.videoId;
                 } else {
-                    this.videoIds = this.videoIds! + ", " + video.id.videoId;
+                    this.videoIds = this.videoIds! + ', ' + video.id.videoId;
                 }
             });
             //Asking for details that include duration and viewCount from server for videoIds
@@ -149,7 +152,7 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
                 this.videoClicked = false;
             }
         }
-    }
+    };
 
     /**
      * The callback that is given to server to process and receive returned details about the videos.
@@ -157,28 +160,26 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
     @action
     processVideoDetails = (videoDetails: any[]) => {
         this.videoDetails = videoDetails;
-        this.props.Document.cachedDetails = Doc.Get.FromJson({ data: videoDetails, title: "detailBackUp" });
-    }
+        this.props.Document.cachedDetails = Doc.Get.FromJson({ data: videoDetails, title: 'detailBackUp' });
+    };
 
     /**
      * The function that stores the search results in the props document.
      */
     backUpSearchResults = (videos: any[]) => {
-        this.props.Document.cachedSearchResults = Doc.Get.FromJson({ data: videos, title: "videosBackUp" });
-    }
+        this.props.Document.cachedSearchResults = Doc.Get.FromJson({ data: videos, title: 'videosBackUp' });
+    };
 
     /**
      * The function that filters out escaped characters returned by the api
      * in the title of the videos.
      */
     filterYoutubeTitleResult = (resultTitle: string) => {
-        let processedTitle: string = resultTitle.replace(/&amp;/g, "&");//.ReplaceAll("&amp;", "&");
+        let processedTitle: string = resultTitle.replace(/&amp;/g, '&'); //.ReplaceAll("&amp;", "&");
         processedTitle = processedTitle.replace(/"&#39;/g, "'");
-        processedTitle = processedTitle.replace(/&quot;/g, "\"");
+        processedTitle = processedTitle.replace(/&quot;/g, '"');
         return processedTitle;
-    }
-
-
+    };
 
     /**
      * The function that converts ISO date, which is passed in, to normal date and finds the
@@ -195,7 +196,6 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
         const totalMonths = totalDays / 30.417;
         const totalYears = totalMonths / 12;
 
-
         const truncYears = Math.trunc(totalYears);
         const truncMonths = Math.trunc(totalMonths);
         const truncDays = Math.trunc(totalDays);
@@ -203,62 +203,61 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
         const truncMin = Math.trunc(totalMin);
         const truncSec = Math.trunc(totalSeconds);
 
-        let pluralCase = "";
+        let pluralCase = '';
 
         if (truncYears !== 0) {
-            truncYears > 1 ? pluralCase = "s" : pluralCase = "";
-            return truncYears + " year" + pluralCase + " ago";
+            truncYears > 1 ? (pluralCase = 's') : (pluralCase = '');
+            return truncYears + ' year' + pluralCase + ' ago';
         } else if (truncMonths !== 0) {
-            truncMonths > 1 ? pluralCase = "s" : pluralCase = "";
-            return truncMonths + " month" + pluralCase + " ago";
+            truncMonths > 1 ? (pluralCase = 's') : (pluralCase = '');
+            return truncMonths + ' month' + pluralCase + ' ago';
         } else if (truncDays !== 0) {
-            truncDays > 1 ? pluralCase = "s" : pluralCase = "";
-            return truncDays + " day" + pluralCase + " ago";
+            truncDays > 1 ? (pluralCase = 's') : (pluralCase = '');
+            return truncDays + ' day' + pluralCase + ' ago';
         } else if (truncHours !== 0) {
-            truncHours > 1 ? pluralCase = "s" : pluralCase = "";
-            return truncHours + " hour" + pluralCase + " ago";
+            truncHours > 1 ? (pluralCase = 's') : (pluralCase = '');
+            return truncHours + ' hour' + pluralCase + ' ago';
         } else if (truncMin !== 0) {
-            truncMin > 1 ? pluralCase = "s" : pluralCase = "";
-            return truncMin + " minute" + pluralCase + " ago";
+            truncMin > 1 ? (pluralCase = 's') : (pluralCase = '');
+            return truncMin + ' minute' + pluralCase + ' ago';
         } else if (truncSec !== 0) {
-            truncSec > 1 ? pluralCase = "s" : pluralCase = "";
-            return truncSec + " second" + pluralCase + " ago";
+            truncSec > 1 ? (pluralCase = 's') : (pluralCase = '');
+            return truncSec + ' second' + pluralCase + ' ago';
         }
-    }
+    };
 
     /**
      * The function that converts the passed in ISO time to normal duration time.
      */
     convertIsoTimeToDuration = (isoDur: string) => {
-
-        const 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];
+            2 !== convertedTime[0].length && (convertedTime[0] = '0' + convertedTime[0]), (convertedTime[0] = '0:' + convertedTime[0]);
         } else {
             for (var r = 1, l = convertedTime.length - 1; l >= r; r++) {
-                2 !== convertedTime[r].length && (convertedTime[r] = "0" + convertedTime[r]);
+                2 !== convertedTime[r].length && (convertedTime[r] = '0' + convertedTime[r]);
             }
         }
 
-        return convertedTime.join(":");
-    }
+        return convertedTime.join(':');
+    };
 
     /**
-     * The function that rounds the viewCount to the nearest 
+     * The function that rounds the viewCount to the nearest
      * thousand, million or billion, given a viewCount number.
      */
     abbreviateViewCount = (viewCount: number) => {
         if (viewCount < 1000) {
             return viewCount.toString();
         } else if (viewCount >= 1000 && viewCount < 1000000) {
-            return (Math.trunc(viewCount / 1000)) + "K";
+            return Math.trunc(viewCount / 1000) + 'K';
         } else if (viewCount >= 1000000 && viewCount < 1000000000) {
-            return (Math.trunc(viewCount / 1000000)) + "M";
+            return Math.trunc(viewCount / 1000000) + 'M';
         } else if (viewCount >= 1000000000) {
-            return (Math.trunc(viewCount / 1000000000)) + "B";
+            return Math.trunc(viewCount / 1000000000) + 'B';
         }
-    }
+    };
 
     /**
      * The function that is called to decide on what'll be rendered by the component.
@@ -268,63 +267,69 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
     renderSearchResultsOrVideo = () => {
         if (this.searchResultsFound) {
             if (this.searchResults.length !== 0) {
-                return <ul>
-                    {this.searchResults.map((video, index) => {
-                        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) {
-                            duration = this.convertIsoTimeToDuration(this.videoDetails[index].contentDetails.duration);
-                            viewCount = this.abbreviateViewCount(this.videoDetails[index].statistics.viewCount);
-                        }
-
-
-                        return <li onClick={() => this.embedVideoOnClick(video.id.videoId, filteredTitle)} key={Utils.GenerateGuid()}>
-                            <div className="search_wrapper">
-                                <div style={{ backgroundColor: "yellow" }}>
-                                    <img src={video.snippet.thumbnails.medium.url} />
-                                    <span className="video_duration">{duration}</span>
-                                </div>
-                                <div className="textual_info">
-                                    <span className="videoTitle">{filteredTitle}</span>
-                                    <span className="channelName">{channelTitle}</span>
-                                    <span className="viewCount">{viewCount}</span>
-                                    <span className="publish_time">{pusblishDate}</span>
-                                    <p className="video_description">{videoDescription}</p>
-
-                                </div>
-                            </div>
-                        </li>;
-                    })}
-                </ul>;
+                return (
+                    <ul>
+                        {this.searchResults.map((video, index) => {
+                            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) {
+                                duration = this.convertIsoTimeToDuration(this.videoDetails[index].contentDetails.duration);
+                                viewCount = this.abbreviateViewCount(this.videoDetails[index].statistics.viewCount);
+                            }
+
+                            return (
+                                <li onClick={() => this.embedVideoOnClick(video.id.videoId, filteredTitle)} key={Utils.GenerateGuid()}>
+                                    <div className="search_wrapper">
+                                        <div style={{ backgroundColor: 'yellow' }}>
+                                            <img src={video.snippet.thumbnails.medium.url} />
+                                            <span className="video_duration">{duration}</span>
+                                        </div>
+                                        <div className="textual_info">
+                                            <span className="videoTitle">{filteredTitle}</span>
+                                            <span className="channelName">{channelTitle}</span>
+                                            <span className="viewCount">{viewCount}</span>
+                                            <span className="publish_time">{pusblishDate}</span>
+                                            <p className="video_description">{videoDescription}</p>
+                                        </div>
+                                    </div>
+                                </li>
+                            );
+                        })}
+                    </ul>
+                );
             } else if (this.curVideoTemplates.length !== 0) {
-                return <ul>
-                    {this.curVideoTemplates.map((video: VideoTemplate) => {
-                        return <li onClick={() => this.embedVideoOnClick(video.videoId, video.videoTitle)} key={Utils.GenerateGuid()}>
-                            <div className="search_wrapper">
-                                <div style={{ backgroundColor: "yellow" }}>
-                                    <img src={video.thumbnailUrl} />
-                                    <span className="video_duration">{video.duration}</span>
-                                </div>
-                                <div className="textual_info">
-                                    <span className="videoTitle">{video.videoTitle}</span>
-                                    <span className="channelName">{video.channelTitle}</span>
-                                    <span className="viewCount">{video.viewCount}</span>
-                                    <span className="publish_time">{video.publishDate}</span>
-                                    <p className="video_description">{video.videoDescription}</p>
-                                </div>
-                            </div>
-                        </li>;
-                    })}
-                </ul>;
+                return (
+                    <ul>
+                        {this.curVideoTemplates.map((video: VideoTemplate) => {
+                            return (
+                                <li onClick={() => this.embedVideoOnClick(video.videoId, video.videoTitle)} key={Utils.GenerateGuid()}>
+                                    <div className="search_wrapper">
+                                        <div style={{ backgroundColor: 'yellow' }}>
+                                            <img src={video.thumbnailUrl} />
+                                            <span className="video_duration">{video.duration}</span>
+                                        </div>
+                                        <div className="textual_info">
+                                            <span className="videoTitle">{video.videoTitle}</span>
+                                            <span className="channelName">{video.channelTitle}</span>
+                                            <span className="viewCount">{video.viewCount}</span>
+                                            <span className="publish_time">{video.publishDate}</span>
+                                            <p className="video_description">{video.videoDescription}</p>
+                                        </div>
+                                    </div>
+                                </li>
+                            );
+                        })}
+                    </ul>
+                );
             }
         } else {
-            return (null);
+            return null;
         }
-    }
+    };
 
     /**
      * Given a videoId and title, creates a new youtube embedded url, and uses that
@@ -332,7 +337,7 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
      */
     @action
     embedVideoOnClick = (videoId: string, filteredTitle: string) => {
-        const embeddedUrl = "https://www.youtube.com/embed/" + videoId;
+        const embeddedUrl = 'https://www.youtube.com/embed/' + videoId;
         this.selectedVideoUrl = embeddedUrl;
         const addFunction = this.props.addDocument!;
         const newVideoX = NumCast(this.props.Document.x);
@@ -340,24 +345,24 @@ export class YoutubeBox extends React.Component<FieldViewProps> {
 
         addFunction(Docs.Create.VideoDocument(embeddedUrl, { title: filteredTitle, _width: 400, _height: 315, x: newVideoX, y: newVideoY }));
         this.videoClicked = true;
-    }
+    };
 
     render() {
-        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!} />
+        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>;
+            </div>
+        );
 
         const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
 
-        const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.ActiveTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
+        const classname = 'webBox-cont' + (this.props.isSelected() && Doc.ActiveTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? '-interactive' : '');
         return (
             <>
-                <div className={classname}  >
-                    {content}
-                </div>
-                {!frozen ? (null) : <div className="webBox-overlay" onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer} />}
-            </>);
+                <div className={classname}>{content}</div>
+                {!frozen ? null : <div className="webBox-overlay" onWheel={this.onPreWheel} onPointerDown={this.onPrePointer} onPointerMove={this.onPrePointer} onPointerUp={this.onPrePointer} />}
+            </>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts
index 2343c2f34..4e98a90a3 100644
--- a/src/client/documents/DocumentTypes.ts
+++ b/src/client/documents/DocumentTypes.ts
@@ -1,46 +1,66 @@
 export enum DocumentType {
-    NONE = "none",
+    NONE = 'none',
 
     // core data types
-    RTF = "rtf",
-    IMG = "image",
-    WEB = "web",
-    COL = "collection",
-    KVP = "kvp",
-    VID = "video",
-    AUDIO = "audio",
-    REC = "recording",
-    PDF = "pdf",
-    INK = "inks",
-    SCREENSHOT = "screenshot",
-    FONTICON = "fonticonbox",
-    FILTER = "filter",
-    SEARCH = "search",          // search query
-    LABEL = "label",            // simple text label
-    BUTTON = "button",          // onClick button
-    WEBCAM = "webcam",          // webcam
-    MARKER = "marker",          // generic marker document not intended to be viewed independently of its context (e.g., for text selections in PDF/Web/RTF)
-    DATE = "date",              // calendar view of a date
-    SCRIPTING = "script",       // script editor
-    EQUATION = "equation",      // equation editor
-    FUNCPLOT = "funcplot",      // function plotter
-    MAP = "map",
-    DATAVIZ = "dataviz",
+    RTF = 'rtf',
+    IMG = 'image',
+    WEB = 'web',
+    COL = 'collection',
+    KVP = 'kvp',
+    VID = 'video',
+    AUDIO = 'audio',
+    REC = 'recording',
+    PDF = 'pdf',
+    INK = 'inks',
+    SCREENSHOT = 'screenshot',
+    FONTICON = 'fonticonbox',
+    FILTER = 'filter',
+    SEARCH = 'search', // search query
+    LABEL = 'label', // simple text label
+    BUTTON = 'button', // onClick button
+    WEBCAM = 'webcam', // webcam
+    MARKER = 'marker', // generic marker document not intended to be viewed independently of its context (e.g., for text selections in PDF/Web/RTF)
+    DATE = 'date', // calendar view of a date
+    SCRIPTING = 'script', // script editor
+    EQUATION = 'equation', // equation editor
+    FUNCPLOT = 'funcplot', // function plotter
+    MAP = 'map',
+    DATAVIZ = 'dataviz',
 
     // special purpose wrappers that either take no data or are compositions of lower level types
-    LINK = "link",
-    LINKANCHOR = "linkanchor",
-    IMPORT = "import",
-    SLIDER = "slider",
-    PRES = "presentation",
-    PRESELEMENT = "preselement",
-    COLOR = "color",
-    YOUTUBE = "youtube",
-    SEARCHITEM = "searchitem",
-    COMPARISON = "comparison",
-    GROUP = "group",
+    LINK = 'link',
+    LINKANCHOR = 'linkanchor',
+    IMPORT = 'import',
+    SLIDER = 'slider',
+    PRES = 'presentation',
+    PRESELEMENT = 'preselement',
+    COLOR = 'color',
+    YOUTUBE = 'youtube',
+    SEARCHITEM = 'searchitem',
+    COMPARISON = 'comparison',
+    GROUP = 'group',
 
-    LINKDB = "linkdb",          // database of links  ??? why do we have this
-    SCRIPTDB = "scriptdb",      // database of scripts
-    GROUPDB = "groupdb",        // database of groups
-}
\ No newline at end of file
+    LINKDB = 'linkdb', // database of links  ??? why do we have this
+    SCRIPTDB = 'scriptdb', // database of scripts
+    GROUPDB = 'groupdb', // database of groups
+}
+export enum CollectionViewType {
+    Invalid = 'invalid',
+    Freeform = 'freeform',
+    Schema = 'schema',
+    Docking = 'docking',
+    Tree = 'tree',
+    Stacking = 'stacking',
+    Masonry = 'masonry',
+    Multicolumn = 'multicolumn',
+    Multirow = 'multirow',
+    Time = 'time',
+    Carousel = 'carousel',
+    Carousel3D = '3D Carousel',
+    Linear = 'linear',
+    //Staff = "staff",
+    Map = 'map',
+    Grid = 'grid',
+    Pile = 'pileup',
+    StackedTimeline = 'stacked timeline',
+}
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 65b3db0e2..ed4c99d70 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1,124 +1,158 @@
-import { IconProp } from "@fortawesome/fontawesome-svg-core";
-import { action, runInAction } from "mobx";
-import { basename } from "path";
-import { DateField } from "../../fields/DateField";
-import { Doc, DocListCast, DocListCastAsync, Field, Initializing, Opt, updateCachedAcls } from "../../fields/Doc";
-import { Id } from "../../fields/FieldSymbols";
-import { HtmlField } from "../../fields/HtmlField";
-import { InkField, PointData } from "../../fields/InkField";
-import { List } from "../../fields/List";
-import { ProxyField } from "../../fields/Proxy";
-import { RichTextField } from "../../fields/RichTextField";
-import { SchemaHeaderField } from "../../fields/SchemaHeaderField";
-import { ComputedField, ScriptField } from "../../fields/ScriptField";
-import { Cast, NumCast, StrCast } from "../../fields/Types";
-import { AudioField, CsvField, ImageField, MapField, PdfField, RecordingField, VideoField, WebField, YoutubeField } from "../../fields/URLField";
-import { inheritParentAcls, SharingPermissions } from "../../fields/util";
-import { Upload } from "../../server/SharedMediaTypes";
-import { aggregateBounds, OmitKeys, Utils } from "../../Utils";
-import { YoutubeBox } from "../apis/youtube/YoutubeBox";
-import { DocServer } from "../DocServer";
-import { Networking } from "../Network";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { DocumentManager } from "../util/DocumentManager";
-import { dropActionType } from "../util/DragManager";
-import { DirectoryImportBox } from "../util/Import & Export/DirectoryImportBox";
-import { LinkManager } from "../util/LinkManager";
-import { ScriptingGlobals } from "../util/ScriptingGlobals";
-import { undoBatch, UndoManager } from "../util/UndoManager";
-import { CollectionDockingView } from "../views/collections/CollectionDockingView";
-import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView";
-import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
-import { ContextMenu } from "../views/ContextMenu";
-import { ContextMenuProps } from "../views/ContextMenuItem";
-import { DFLT_IMAGE_NATIVE_DIM } from "../views/global/globalCssVariables.scss";
-import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke } from "../views/InkingStroke";
-import { AudioBox } from "../views/nodes/AudioBox";
-import { FontIconBox } from "../views/nodes/button/FontIconBox";
-import { ColorBox } from "../views/nodes/ColorBox";
-import { ComparisonBox } from "../views/nodes/ComparisonBox";
-import { DataVizBox } from "../views/nodes/DataVizBox/DataVizBox";
-import { DocFocusOptions } from "../views/nodes/DocumentView";
-import { EquationBox } from "../views/nodes/EquationBox";
-import { FieldViewProps } from "../views/nodes/FieldView";
-import { FilterBox } from "../views/nodes/FilterBox";
-import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox";
-import { FunctionPlotBox } from "../views/nodes/FunctionPlotBox";
-import { ImageBox } from "../views/nodes/ImageBox";
-import { KeyValueBox } from "../views/nodes/KeyValueBox";
-import { LabelBox } from "../views/nodes/LabelBox";
-import { LinkBox } from "../views/nodes/LinkBox";
-import { LinkDescriptionPopup } from "../views/nodes/LinkDescriptionPopup";
-import { MapBox } from "../views/nodes/MapBox/MapBox";
-import { PDFBox } from "../views/nodes/PDFBox";
-import { RecordingBox } from "../views/nodes/RecordingBox/RecordingBox";
-import { ScreenshotBox } from "../views/nodes/ScreenshotBox";
-import { ScriptingBox } from "../views/nodes/ScriptingBox";
-import { SliderBox } from "../views/nodes/SliderBox";
-import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox";
-import { PresBox } from "../views/nodes/trails/PresBox";
-import { PresElementBox } from "../views/nodes/trails/PresElementBox";
-import { VideoBox } from "../views/nodes/VideoBox";
-import { WebBox } from "../views/nodes/WebBox";
-import { SearchBox } from "../views/search/SearchBox";
-import { DocumentType } from "./DocumentTypes";
-
-const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", ""));
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { action, runInAction } from 'mobx';
+import { basename } from 'path';
+import { DateField } from '../../fields/DateField';
+import { Doc, DocListCast, DocListCastAsync, Field, Initializing, Opt, updateCachedAcls } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { HtmlField } from '../../fields/HtmlField';
+import { InkField, PointData } from '../../fields/InkField';
+import { List } from '../../fields/List';
+import { ProxyField } from '../../fields/Proxy';
+import { RichTextField } from '../../fields/RichTextField';
+import { SchemaHeaderField } from '../../fields/SchemaHeaderField';
+import { ComputedField, ScriptField } from '../../fields/ScriptField';
+import { Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../fields/Types';
+import { AudioField, CsvField, ImageField, MapField, PdfField, RecordingField, VideoField, WebField, YoutubeField } from '../../fields/URLField';
+import { inheritParentAcls, SharingPermissions } from '../../fields/util';
+import { Upload } from '../../server/SharedMediaTypes';
+import { aggregateBounds, OmitKeys, Utils } from '../../Utils';
+import { YoutubeBox } from '../apis/youtube/YoutubeBox';
+import { DocServer } from '../DocServer';
+import { Networking } from '../Network';
+import { DocumentManager } from '../util/DocumentManager';
+import { DragManager, dropActionType } from '../util/DragManager';
+import { DirectoryImportBox } from '../util/Import & Export/DirectoryImportBox';
+import { LinkManager } from '../util/LinkManager';
+import { ScriptingGlobals } from '../util/ScriptingGlobals';
+import { undoBatch, UndoManager } from '../util/UndoManager';
+import { CollectionDockingView } from '../views/collections/CollectionDockingView';
+import { DimUnit } from '../views/collections/collectionMulticolumn/CollectionMulticolumnView';
+import { CollectionView } from '../views/collections/CollectionView';
+import { ContextMenu } from '../views/ContextMenu';
+import { ContextMenuProps } from '../views/ContextMenuItem';
+import { DFLT_IMAGE_NATIVE_DIM } from '../views/global/globalCssVariables.scss';
+import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke } from '../views/InkingStroke';
+import { AudioBox } from '../views/nodes/AudioBox';
+import { FontIconBox } from '../views/nodes/button/FontIconBox';
+import { ColorBox } from '../views/nodes/ColorBox';
+import { ComparisonBox } from '../views/nodes/ComparisonBox';
+import { DataVizBox } from '../views/nodes/DataVizBox/DataVizBox';
+import { DocFocusOptions } from '../views/nodes/DocumentView';
+import { EquationBox } from '../views/nodes/EquationBox';
+import { FieldViewProps } from '../views/nodes/FieldView';
+import { FilterBox } from '../views/nodes/FilterBox';
+import { FormattedTextBox } from '../views/nodes/formattedText/FormattedTextBox';
+import { FunctionPlotBox } from '../views/nodes/FunctionPlotBox';
+import { ImageBox } from '../views/nodes/ImageBox';
+import { KeyValueBox } from '../views/nodes/KeyValueBox';
+import { LabelBox } from '../views/nodes/LabelBox';
+import { LinkBox } from '../views/nodes/LinkBox';
+import { LinkDescriptionPopup } from '../views/nodes/LinkDescriptionPopup';
+import { MapBox } from '../views/nodes/MapBox/MapBox';
+import { PDFBox } from '../views/nodes/PDFBox';
+import { RecordingBox } from '../views/nodes/RecordingBox/RecordingBox';
+import { ScreenshotBox } from '../views/nodes/ScreenshotBox';
+import { ScriptingBox } from '../views/nodes/ScriptingBox';
+import { SliderBox } from '../views/nodes/SliderBox';
+import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
+import { PresBox } from '../views/nodes/trails/PresBox';
+import { PresElementBox } from '../views/nodes/trails/PresElementBox';
+import { VideoBox } from '../views/nodes/VideoBox';
+import { WebBox } from '../views/nodes/WebBox';
+import { SearchBox } from '../views/search/SearchBox';
+import { CollectionViewType, DocumentType } from './DocumentTypes';
+
+const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace('px', ''));
 class EmptyBox {
     public static LayoutString() {
-        return "";
+        return '';
     }
 }
 export abstract class FInfo {
-    description: string = "";
+    description: string = '';
     fieldType?: string;
     values?: Field[];
     // format?: string; // format to display values (e.g, decimal places, $, etc)
     // parse?: ScriptField; // parse a value from a string
-    constructor(d: string) { this.description = d; }
+    constructor(d: string) {
+        this.description = d;
+    }
+}
+class BoolInfo extends FInfo {
+    fieldType? = 'boolean';
+    values?: boolean[] = [true, false];
+}
+class NumInfo extends FInfo {
+    fieldType? = 'number';
+    values?: number[] = [];
+    constructor(d: string, values?: number[]) {
+        super(d);
+        this.values = values;
+    }
+}
+class StrInfo extends FInfo {
+    fieldType? = 'string';
+    values?: string[] = [];
+    constructor(d: string, values?: string[]) {
+        super(d);
+        this.values = values;
+    }
+}
+class DocInfo extends FInfo {
+    fieldType? = 'Doc';
+    values?: Doc[] = [];
+    constructor(d: string, values?: Doc[]) {
+        super(d);
+        this.values = values;
+    }
+}
+class DimInfo extends FInfo {
+    fieldType? = 'DimUnit';
+    values? = [DimUnit.Pixel, DimUnit.Ratio];
+}
+class PEInfo extends FInfo {
+    fieldType? = 'pointerEvents';
+    values? = ['all', 'none'];
+}
+class DAInfo extends FInfo {
+    fieldType? = 'dropActionType';
+    values? = ['alias', 'copy', 'move', 'same', 'proto', 'none'];
 }
-class BoolInfo extends FInfo { fieldType?= "boolean";        values?: boolean[] = [true, false]; }
-class NumInfo  extends FInfo { fieldType?= "number";         values?: number[] = []; constructor(d:string, values?:number[]) { super(d); this.values = values; }}
-class StrInfo  extends FInfo { fieldType?= "string";         values?: string[] = []; constructor(d:string, values?:string[]) { super(d); this.values = values; }}
-class DocInfo  extends FInfo { fieldType?= "Doc";            values?: Doc[] = []; constructor(d:string, values?:Doc[]) { super(d); this.values = values; }}
-class DimInfo  extends FInfo { fieldType?= "DimUnit";        values?= [DimUnit.Pixel, DimUnit.Ratio]; }
-class PEInfo   extends FInfo { fieldType?= "pointerEvents";  values?= ["all", "none"]; }
-class DAInfo   extends FInfo { fieldType?= "dropActionType"; values?= ["alias", "copy", "move", "same", "proto", "none"]; }
 type BOOLt = BoolInfo | boolean;
-type NUMt  = NumInfo | number;
-type STRt  = StrInfo | string;
-type DOCt  = DocInfo | Doc;
-type DIMt  = DimInfo | typeof DimUnit.Pixel | typeof DimUnit.Ratio;
-type PEVt  = PEInfo | "none" | "all";
+type NUMt = NumInfo | number;
+type STRt = StrInfo | string;
+type DOCt = DocInfo | Doc;
+type DIMt = DimInfo | typeof DimUnit.Pixel | typeof DimUnit.Ratio;
+type PEVt = PEInfo | 'none' | 'all';
 type DROPt = DAInfo | dropActionType;
 export class DocumentOptions {
-    x?: NUMt = new NumInfo("x coordinate of document in a freeform view");
-    y?: NUMt = new NumInfo("y coordinage of document in a freeform view");
-    z?: NUMt = new NumInfo("whether document is in overlay (1) or not (0)", [1,0]);
-    system?: BOOLt = new BoolInfo("is this a system created/owned doc");
-    type?: STRt = new StrInfo("type of document", Array.from(Object.keys(DocumentType)));
+    x?: NUMt = new NumInfo('x coordinate of document in a freeform view');
+    y?: NUMt = new NumInfo('y coordinage of document in a freeform view');
+    z?: NUMt = new NumInfo('whether document is in overlay (1) or not (0)', [1, 0]);
+    system?: BOOLt = new BoolInfo('is this a system created/owned doc');
+    type?: STRt = new StrInfo('type of document', Array.from(Object.keys(DocumentType)));
     title?: string;
     _dropAction?: DROPt = new DAInfo("what should happen to this document when it's dropped somewhere else");
-    allowOverlayDrop?: BOOLt = new BoolInfo("can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?");
+    allowOverlayDrop?: BOOLt = new BoolInfo('can documents be dropped onto this document without using dragging title bar or holding down embed key (ctrl)?');
     childDropAction?: DROPt = new DAInfo("what should happen to the source document when it's dropped onto a child of a collection ");
-    targetDropAction?: DROPt = new DAInfo("what should happen to the source document when ??? ");
-    userColor?: STRt = new StrInfo("color associated with a Dash user (seen in header fields of shared documents)");
-    color?: STRt = new StrInfo("foreground color data doc");
-    backgroundColor?: STRt = new StrInfo("background color for data doc");
-    _autoHeight?: BOOLt = new BoolInfo("whether document automatically resizes vertically to display contents");
-    _headerHeight?: NUMt = new NumInfo("height of document header used for displaying title");
-    _headerFontSize?: NUMt = new NumInfo("font size of header of custom notes");
-    _headerPointerEvents?: PEVt = new PEInfo("types of events the header of a custom text document can consume");
-    _panX?: NUMt = new NumInfo("horizontal pan location of a freeform view");
-    _panY?: NUMt = new NumInfo("vertical pan location of a freeform view");
-    _width?: NUMt = new NumInfo("displayed width of a document");
-    _height?: NUMt = new NumInfo("displayed height of document");
-    _nativeWidth?: NUMt = new NumInfo("native width of document contents (e.g., the pixel width of an image)");
-    _nativeHeight?: NUMt = new NumInfo("native height of document contents (e.g., the pixel height of an image)");
+    targetDropAction?: DROPt = new DAInfo('what should happen to the source document when ??? ');
+    userColor?: STRt = new StrInfo('color associated with a Dash user (seen in header fields of shared documents)');
+    color?: STRt = new StrInfo('foreground color data doc');
+    backgroundColor?: STRt = new StrInfo('background color for data doc');
+    _autoHeight?: BOOLt = new BoolInfo('whether document automatically resizes vertically to display contents');
+    _headerHeight?: NUMt = new NumInfo('height of document header used for displaying title');
+    _headerFontSize?: NUMt = new NumInfo('font size of header of custom notes');
+    _headerPointerEvents?: PEVt = new PEInfo('types of events the header of a custom text document can consume');
+    _panX?: NUMt = new NumInfo('horizontal pan location of a freeform view');
+    _panY?: NUMt = new NumInfo('vertical pan location of a freeform view');
+    _width?: NUMt = new NumInfo('displayed width of a document');
+    _height?: NUMt = new NumInfo('displayed height of document');
+    _nativeWidth?: NUMt = new NumInfo('native width of document contents (e.g., the pixel width of an image)');
+    _nativeHeight?: NUMt = new NumInfo('native height of document contents (e.g., the pixel height of an image)');
     _dimMagnitude?: NUMt = new NumInfo("magnitude of collectionMulti{row,col} element's width or height");
     _dimUnit?: DIMt = new DimInfo("units of collectionMulti{row,col} element's width or height - 'px' or '*' for pixels or relative units");
-    _fitWidth?: BOOLt = new BoolInfo("whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)");
-    _fitContentsToBox?: BOOLt = new BoolInfo("whether a freeformview should zoom/scale to create a shrinkwrapped view of its content");
+    _fitWidth?: BOOLt = new BoolInfo('whether document should scale its contents to fit its rendered width or not (e.g., for PDFviews)');
+    _fitContentsToBox?: BOOLt = new BoolInfo('whether a freeformview should zoom/scale to create a shrinkwrapped view of its content');
     _contentBounds?: List<number>; // the (forced) bounds of the document to display. format is: [left, top, right, bottom]
     _lockedPosition?: boolean; // lock the x,y coordinates of the document so that it can't be dragged
     _lockedTransform?: boolean; // lock the panx,pany and scale parameters of the document so that it be panned/zoomed
@@ -126,11 +160,11 @@ export class DocumentOptions {
     _showTitle?: string; // field name to display in header (:hover is an optional suffix)
     _showCaption?: string; // which field to display in the caption area.  leave empty to have no caption
     _scrollTop?: number; // scroll location for pdfs
-    _noAutoscroll?: boolean;// whether collections autoscroll when this item is dragged
+    _noAutoscroll?: boolean; // whether collections autoscroll when this item is dragged
     _chromeHidden?: boolean; // whether the editing chrome for a document is hidden
     _searchDoc?: boolean; // is this a search document (used to change UI for search results in schema view)
     _forceActive?: boolean; // flag to handle pointer events when not selected (or otherwise active)
-    _stayInCollection?: boolean;// whether the document should remain in its collection when someone tries to drag and drop it elsewhere
+    _stayInCollection?: boolean; // whether the document should remain in its collection when someone tries to drag and drop it elsewhere
     _raiseWhenDragged?: boolean; // whether a document is brought to front when dragged.
     _hideContextMenu?: boolean; // whether the context menu can be shown
     _viewType?: string; // sub type of a collection
@@ -142,7 +176,7 @@ export class DocumentOptions {
     _xPadding?: number;
     _yPadding?: number;
     _itemIndex?: number; // which item index the carousel viewer is showing
-    _showSidebar?: boolean;  //whether an annotationsidebar should be displayed for text docuemnts
+    _showSidebar?: boolean; //whether an annotationsidebar should be displayed for text docuemnts
     _singleLine?: boolean; // whether text document is restricted to a single line (carriage returns make new document)
     _minFontSize?: number; // minimum font size for labelBoxes
     _maxFontSize?: number; // maximum font size for labelBoxes
@@ -158,11 +192,11 @@ export class DocumentOptions {
     _timecodeToShow?: number; // the time that a document should be displayed (e.g., when an annotation shows up as a video plays)
     _timecodeToHide?: number; // the time that a document should be hidden
     _timelineLabel?: boolean; // whether the document exists on a timeline
-    "_carousel-caption-xMargin"?: NUMt = new NumInfo("x margin of caption inside of a carouself collection");
-    "_carousel-caption-yMargin"?: NUMt = new NumInfo("y margin of caption inside of a carouself collection");
-    "icon-nativeWidth"?: NUMt = new NumInfo("native width of icon view");
-    "icon-nativeHeight"?: NUMt = new NumInfo("native height of icon view");
-    "dragFactory-count"?: NUMt = new NumInfo("number of items created from a drag button (used for setting title with incrementing index)");
+    '_carousel-caption-xMargin'?: NUMt = new NumInfo('x margin of caption inside of a carouself collection');
+    '_carousel-caption-yMargin'?: NUMt = new NumInfo('y margin of caption inside of a carouself collection');
+    'icon-nativeWidth'?: NUMt = new NumInfo('native width of icon view');
+    'icon-nativeHeight'?: NUMt = new NumInfo('native height of icon view');
+    'dragFactory-count'?: NUMt = new NumInfo('number of items created from a drag button (used for setting title with incrementing index)');
     lat?: number;
     lng?: number;
     infoWindowOpen?: boolean;
@@ -171,8 +205,8 @@ export class DocumentOptions {
     fieldValues?: List<any>; // possible field values used by fieldInfos
     fieldType?: string; // type of afield used by fieldInfos
     unrendered?: boolean; // denotes an annotation that is not rendered with a DocumentView (e.g, rtf/pdf text selections and links to scroll locations in web/pdf)
-     "acl-Public"?: string; // public permissions
-    "_acl-Public"?: string; // public permissions
+    'acl-Public'?: string; // public permissions
+    '_acl-Public'?: string; // public permissions
     version?: string; // version identifier for a document
     label?: string;
     hidden?: boolean;
@@ -181,7 +215,7 @@ export class DocumentOptions {
     mediaState?: string; // status of audio/video media document: "pendingRecording", "recording", "paused", "playing"
     recording?: boolean; // whether WebCam is recording or not
     autoPlayAnchors?: boolean; // whether to play audio/video when an anchor is clicked in a stackedTimeline.
-    dontPlayLinkOnSelect?: boolean;  // whether an audio/video should start playing when a link is followed to it.
+    dontPlayLinkOnSelect?: boolean; // whether an audio/video should start playing when a link is followed to it.
     toolTip?: string; // tooltip to display on hover
     contextMenuFilters?: List<ScriptField>;
     contextMenuScripts?: List<ScriptField>;
@@ -190,7 +224,7 @@ export class DocumentOptions {
     dontUndo?: boolean; // whether button clicks should be undoable (this is set to true for Undo/Redo/and sidebar buttons that open the siebar panel)
     description?: string; // added for links
     layout?: string | Doc; // default layout string for a document
-    contentPointerEvents?: string;  // pointer events allowed for content of a document view.  eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
+    contentPointerEvents?: string; // pointer events allowed for content of a document view.  eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
     childLimitHeight?: number; // whether to limit the height of collection children.  0 - means  height can be no bigger than width
     childLayoutTemplate?: Doc; // template for collection to use to render its children (see PresBox layout in tree view)
     childLayoutString?: string; // template string for collection to use to render its children
@@ -230,12 +264,12 @@ export class DocumentOptions {
     baseProto?: boolean; // is this a base prototoype
     dontRegisterView?: boolean;
     lookupField?: ScriptField; // script that returns the value of a field. This script is passed the rootDoc, layoutDoc, field, and container of the document.  see PresBox.
-    "onDoubleClick-rawScript"?: string; // onDoubleClick script in raw text form
-    "onChildDoubleClick-rawScript"?: string; // onChildDoubleClick script in raw text form
-    "onChildClick-rawScript"?: string; // on ChildClick script in raw text form
-    "onClick-rawScript"?: string; // onClick script in raw text form
-    "onCheckedClick-rawScript"?: string; // onChecked script in raw text form
-    "onCheckedClick-params"?: List<string>; // parameter list for onChecked treeview functions
+    'onDoubleClick-rawScript'?: string; // onDoubleClick script in raw text form
+    'onChildDoubleClick-rawScript'?: string; // onChildDoubleClick script in raw text form
+    'onChildClick-rawScript'?: string; // on ChildClick script in raw text form
+    'onClick-rawScript'?: string; // onClick script in raw text form
+    'onCheckedClick-rawScript'?: string; // onChecked script in raw text form
+    'onCheckedClick-params'?: List<string>; // parameter list for onChecked treeview functions
     columnHeaders?: List<SchemaHeaderField>; // headers for stacking views
     schemaHeaders?: List<SchemaHeaderField>; // headers for schema view
     clipWidth?: number; // percent transition from before to after in comparisonBox
@@ -270,7 +304,7 @@ export class DocumentOptions {
     linearViewSubMenu?: boolean;
     linearViewFloating?: boolean;
     flexGap?: number; // Linear view flex gap
-    flexDirection?: "unset" | "row" | "column" | "row-reverse" | "column-reverse";
+    flexDirection?: 'unset' | 'row' | 'column' | 'row-reverse' | 'column-reverse';
 
     layout_linkView?: Doc; // view template for a link document
     layout_keyValue?: string; // view tempalte for key value docs
@@ -278,8 +312,8 @@ export class DocumentOptions {
     linkDisplay?: boolean; // whether a link line should be dipslayed between the two link anchors
     anchor1?: Doc;
     anchor2?: Doc;
-    "anchor1-useLinkSmallAnchor"?: boolean; // whether anchor1 of a link should use a miniature anchor dot (as when the anchor is a text selection)
-    "anchor2-useLinkSmallAnchor"?: boolean; // whether anchor1 of a link should use a miniature anchor dot (as when the anchor is a text selection)
+    'anchor1-useLinkSmallAnchor'?: boolean; // whether anchor1 of a link should use a miniature anchor dot (as when the anchor is a text selection)
+    'anchor2-useLinkSmallAnchor'?: boolean; // whether anchor1 of a link should use a miniature anchor dot (as when the anchor is a text selection)
     ignoreClick?: boolean;
     onClick?: ScriptField;
     onDoubleClick?: ScriptField;
@@ -292,7 +326,7 @@ export class DocumentOptions {
     clickFactory?: Doc; // document to create when clicking on a button with a suitable onClick script
     onDragStart?: ScriptField; //script to execute at start of drag operation --  e.g., when a "creator" button is dragged this script generates a different document to drop
     cloneFieldFilter?: List<string>; // fields not to copy when the document is clonedclipboard?: Doc;
-    filterBoolean ?: string;
+    filterBoolean?: string;
     useCors?: boolean;
     icon?: string;
     target?: Doc; // available for use in scripts as the primary target document
@@ -306,9 +340,9 @@ export class DocumentOptions {
     treeViewHideHeader?: boolean; // whether to hide the header for a document in a tree view
     treeViewHideHeaderFields?: boolean; // whether to hide the drop down options for tree view items.
     treeViewGrowsHorizontally?: boolean; // whether an embedded tree view of the document can grow horizontally without growing vertically
-    treeViewChildDoubleClick?: ScriptField; // 
+    treeViewChildDoubleClick?: ScriptField; //
     // Action Button
-    buttonMenu?: boolean; // whether a action button should be displayed 
+    buttonMenu?: boolean; // whether a action button should be displayed
     buttonMenuDoc?: Doc;
     explainer?: string;
 
@@ -321,7 +355,7 @@ export class DocumentOptions {
     treeViewTruncateTitleWidth?: number;
     treeViewHasOverlay?: boolean; // whether the treeview has an overlay for freeform annotations
     treeViewType?: string; // whether treeview is a Slide, file system, or (default) collection hierarchy
-    sidebarColor?: string;  // background color of text sidebar
+    sidebarColor?: string; // background color of text sidebar
     sidebarViewType?: string; // collection type of text sidebar
     docMaxAutoHeight?: number; // maximum height for newly created (eg, from pasting) text documents
     text?: string;
@@ -331,192 +365,305 @@ export class DocumentOptions {
     selectedIndex?: number; // which item in a linear view has been selected using the "thumb doc" ui
     clipboard?: Doc;
     searchQuery?: string; // for quersyBox
-    useLinkSmallAnchor?: boolean;  // whether links to this document should use a miniature linkAnchorBox
+    useLinkSmallAnchor?: boolean; // whether links to this document should use a miniature linkAnchorBox
     border?: string; //for searchbox
     hoverBackgroundColor?: string; // background color of a label when hovered
     linkRelationshipList?: List<string>; // for storing different link relationships (when set by user in the link editor)
-    linkRelationshipSizes?: List<number>; //stores number of links contained in each relationship 
+    linkRelationshipSizes?: List<number>; //stores number of links contained in each relationship
     linkColorList?: List<string>; // colors of links corresponding to specific link relationships
 }
 export namespace Docs {
-
     export let newAccount: boolean = false;
 
     export namespace Prototypes {
-
         type LayoutSource = { LayoutString: (key: string) => string };
         type PrototypeTemplate = {
             layout: {
-                view: LayoutSource,
-                dataField: string
-            },
-            data?: any,
-            options?: Partial<DocumentOptions>
+                view: LayoutSource;
+                dataField: string;
+            };
+            data?: any;
+            options?: Partial<DocumentOptions>;
         };
         type TemplateMap = Map<DocumentType, PrototypeTemplate>;
         type PrototypeMap = Map<DocumentType, Doc>;
-        const defaultDataKey = "data";
+        const defaultDataKey = 'data';
 
         const TemplateMap: TemplateMap = new Map([
-            [DocumentType.RTF, {
-                layout: { view: FormattedTextBox, dataField: "text" },
-                options: {
-                    _height: 35, _xMargin: 10, _yMargin: 10, nativeDimModifiable: true, treeViewGrowsHorizontally: true,
-                    forceReflow: true, links: "@links(self)"
-                }
-            }],
-            [DocumentType.SEARCH, {
-                layout: { view: SearchBox, dataField: defaultDataKey },
-                options: { _width: 400, links: "@links(self)" }
-            }],
-            [DocumentType.FILTER, {
-                layout: { view: FilterBox, dataField: defaultDataKey },
-                options: { _width: 400, links: "@links(self)" }
-            }],
-            [DocumentType.COLOR, {
-                layout: { view: ColorBox, dataField: defaultDataKey },
-                options: { _nativeWidth: 220, _nativeHeight: 300, links: "@links(self)" }
-            }],
-            [DocumentType.IMG, {
-                layout: { view: ImageBox, dataField: defaultDataKey },
-                options: { links: "@links(self)" }
-            }],
-            [DocumentType.WEB, {
-                layout: { view: WebBox, dataField: defaultDataKey },
-                options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: "@links(self)" }
-            }],
-            [DocumentType.COL, {
-                layout: { view: CollectionView, dataField: defaultDataKey },
-                options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1, links: "@links(self)" }
-            }],
-            [DocumentType.KVP, {
-                layout: { view: KeyValueBox, dataField: defaultDataKey },
-                options: { _fitWidth: true, _height: 150 }
-            }],
-            [DocumentType.VID, {
-                layout: { view: VideoBox, dataField: defaultDataKey },
-                options: { _currentTimecode: 0, links: "@links(self)" },
-            }],
-            [DocumentType.AUDIO, {
-                layout: { view: AudioBox, dataField: defaultDataKey },
-                options: { _height: 100, backgroundColor: "lightGray", forceReflow: true, nativeDimModifiable: true, links: "@links(self)" }
-            }],
-            [DocumentType.REC, {
-                layout: { view: VideoBox, dataField: defaultDataKey },
-                options: { _height: 100, backgroundColor: "pink", links: "@links(self)" }
-            }],
-            [DocumentType.PDF, {
-                layout: { view: PDFBox, dataField: defaultDataKey },
-                options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: "@links(self)" }
-            }],
-            [DocumentType.MAP, {
-                layout: { view: MapBox, dataField: defaultDataKey },
-                options: { _height: 600, _width: 800, nativeDimModifiable: true, links: "@links(self)" }
-            }],
-            [DocumentType.IMPORT, {
-                layout: { view: DirectoryImportBox, dataField: defaultDataKey },
-                options: { _height: 150 }
-            }],
-            [DocumentType.LINK, {
-                layout: { view: LinkBox, dataField: defaultDataKey },
-                options: {
-                    childDontRegisterViews: true, _isLinkButton: true, _height: 150, description: "", showCaption: "description",
-                    backgroundColor: "lightblue", // lightblue is default color for linking dot and link documents text comment area
-                    links: "@links(self)",
-                    _removeDropProperties: new List(["isLinkButton"]),
-                }
-            }],
-            [DocumentType.LINKDB, {
-                data: new List<Doc>(),
-                layout: { view: EmptyBox, dataField: defaultDataKey },
-                options: { childDropAction: "alias", title: "Global Link Database" }
-            }],
-            [DocumentType.SCRIPTDB, {
-                data: new List<Doc>(),
-                layout: { view: EmptyBox, dataField: defaultDataKey },
-                options: { childDropAction: "alias", title: "Global Script Database" }
-            }],
-            [DocumentType.SCRIPTING, {
-                layout: { view: ScriptingBox, dataField: defaultDataKey },
-                options: { links: "@links(self)" }
-            }],
-            [DocumentType.YOUTUBE, {
-                layout: { view: YoutubeBox, dataField: defaultDataKey }
-            }],
-            [DocumentType.LABEL, {
-                layout: { view: LabelBox, dataField: defaultDataKey },
-                options: { links: "@links(self)", _singleLine: true }
-            }],
-            [DocumentType.EQUATION, {
-                layout: { view: EquationBox, dataField: defaultDataKey },
-                options: { links: "@links(self)", nativeDimModifiable: true, hideResizeHandles: true, hideDecorationTitle: true }
-            }],
-            [DocumentType.FUNCPLOT, {
-                layout: { view: FunctionPlotBox, dataField: defaultDataKey },
-                options: { nativeDimModifiable: true, links: "@links(self)" }
-            }],
-            [DocumentType.BUTTON, {
-                layout: { view: LabelBox, dataField: "onClick" },
-                options: { links: "@links(self)" }
-            }],
-            [DocumentType.SLIDER, {
-                layout: { view: SliderBox, dataField: defaultDataKey, },
-                options: { links: "@links(self)", treeViewGrowsHorizontally: true }
-            }],
-            [DocumentType.PRES, {
-                layout: { view: PresBox, dataField: defaultDataKey },
-                options: { links: "@links(self)" }
-            }],
-            [DocumentType.FONTICON, {
-                layout: { view: FontIconBox, dataField: defaultDataKey },
-                options: { hideLinkButton: true, _width: 40, _height: 40, borderRounding: "100%", links: "@links(self)" },
-            }],
-            [DocumentType.WEBCAM, {
-                layout: { view: RecordingBox, dataField: defaultDataKey },
-                options: { links: "@links(self)" }
-            }],
-            [DocumentType.PRESELEMENT, {
-                layout: { view: PresElementBox, dataField: defaultDataKey }
-            }],
-            [DocumentType.MARKER, {
-                layout: { view: CollectionView, dataField: defaultDataKey },
-                options: { links: "@links(self)", hideLinkButton: true, pointerEvents: "none" }
-            }],
-            [DocumentType.INK, { // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method
-                layout: { view: InkingStroke, dataField: defaultDataKey },
-                options: { links: "@links(self)" }
-            }],
-            [DocumentType.SCREENSHOT, {
-                layout: { view: ScreenshotBox, dataField: defaultDataKey },
-                options: { links: "@links(self)" }
-            }],
-            [DocumentType.COMPARISON, {
-                layout: { view: ComparisonBox, dataField: defaultDataKey },
-                options: { clipWidth: 50, nativeDimModifiable: true, backgroundColor: "gray", targetDropAction: "alias", links: "@links(self)" }
-            }],
-            [DocumentType.GROUPDB, {
-                data: new List<Doc>(),
-                layout: { view: EmptyBox, dataField: defaultDataKey },
-                options: { childDropAction: "alias", title: "Global Group Database" }
-            }],
-            [DocumentType.GROUP, {
-                layout: { view: EmptyBox, dataField: defaultDataKey },
-                options: { links: "@links(self)" }
-            }],
-            [DocumentType.DATAVIZ, {
-                layout: { view: DataVizBox, dataField: defaultDataKey },
-                options: { _fitWidth: true, nativeDimModifiable: true, links: "@links(self)" }
-            }]
+            [
+                DocumentType.RTF,
+                {
+                    layout: { view: FormattedTextBox, dataField: 'text' },
+                    options: {
+                        _height: 35,
+                        _xMargin: 10,
+                        _yMargin: 10,
+                        nativeDimModifiable: true,
+                        treeViewGrowsHorizontally: true,
+                        forceReflow: true,
+                        links: '@links(self)',
+                    },
+                },
+            ],
+            [
+                DocumentType.SEARCH,
+                {
+                    layout: { view: SearchBox, dataField: defaultDataKey },
+                    options: { _width: 400, links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.FILTER,
+                {
+                    layout: { view: FilterBox, dataField: defaultDataKey },
+                    options: { _width: 400, links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.COLOR,
+                {
+                    layout: { view: ColorBox, dataField: defaultDataKey },
+                    options: { _nativeWidth: 220, _nativeHeight: 300, links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.IMG,
+                {
+                    layout: { view: ImageBox, dataField: defaultDataKey },
+                    options: { links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.WEB,
+                {
+                    layout: { view: WebBox, dataField: defaultDataKey },
+                    options: { _height: 300, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.COL,
+                {
+                    layout: { view: CollectionView, dataField: defaultDataKey },
+                    options: { _fitWidth: true, _panX: 0, _panY: 0, _viewScale: 1, links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.KVP,
+                {
+                    layout: { view: KeyValueBox, dataField: defaultDataKey },
+                    options: { _fitWidth: true, _height: 150 },
+                },
+            ],
+            [
+                DocumentType.VID,
+                {
+                    layout: { view: VideoBox, dataField: defaultDataKey },
+                    options: { _currentTimecode: 0, links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.AUDIO,
+                {
+                    layout: { view: AudioBox, dataField: defaultDataKey },
+                    options: { _height: 100, backgroundColor: 'lightGray', forceReflow: true, nativeDimModifiable: true, links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.REC,
+                {
+                    layout: { view: VideoBox, dataField: defaultDataKey },
+                    options: { _height: 100, backgroundColor: 'pink', links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.PDF,
+                {
+                    layout: { view: PDFBox, dataField: defaultDataKey },
+                    options: { _curPage: 1, _fitWidth: true, nativeDimModifiable: true, nativeHeightUnfrozen: true, links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.MAP,
+                {
+                    layout: { view: MapBox, dataField: defaultDataKey },
+                    options: { _height: 600, _width: 800, nativeDimModifiable: true, links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.IMPORT,
+                {
+                    layout: { view: DirectoryImportBox, dataField: defaultDataKey },
+                    options: { _height: 150 },
+                },
+            ],
+            [
+                DocumentType.LINK,
+                {
+                    layout: { view: LinkBox, dataField: defaultDataKey },
+                    options: {
+                        childDontRegisterViews: true,
+                        _isLinkButton: true,
+                        _height: 150,
+                        description: '',
+                        showCaption: 'description',
+                        backgroundColor: 'lightblue', // lightblue is default color for linking dot and link documents text comment area
+                        links: '@links(self)',
+                        _removeDropProperties: new List(['isLinkButton']),
+                    },
+                },
+            ],
+            [
+                DocumentType.LINKDB,
+                {
+                    data: new List<Doc>(),
+                    layout: { view: EmptyBox, dataField: defaultDataKey },
+                    options: { childDropAction: 'alias', title: 'Global Link Database' },
+                },
+            ],
+            [
+                DocumentType.SCRIPTDB,
+                {
+                    data: new List<Doc>(),
+                    layout: { view: EmptyBox, dataField: defaultDataKey },
+                    options: { childDropAction: 'alias', title: 'Global Script Database' },
+                },
+            ],
+            [
+                DocumentType.SCRIPTING,
+                {
+                    layout: { view: ScriptingBox, dataField: defaultDataKey },
+                    options: { links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.YOUTUBE,
+                {
+                    layout: { view: YoutubeBox, dataField: defaultDataKey },
+                },
+            ],
+            [
+                DocumentType.LABEL,
+                {
+                    layout: { view: LabelBox, dataField: defaultDataKey },
+                    options: { links: '@links(self)', _singleLine: true },
+                },
+            ],
+            [
+                DocumentType.EQUATION,
+                {
+                    layout: { view: EquationBox, dataField: defaultDataKey },
+                    options: { links: '@links(self)', nativeDimModifiable: true, hideResizeHandles: true, hideDecorationTitle: true },
+                },
+            ],
+            [
+                DocumentType.FUNCPLOT,
+                {
+                    layout: { view: FunctionPlotBox, dataField: defaultDataKey },
+                    options: { nativeDimModifiable: true, links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.BUTTON,
+                {
+                    layout: { view: LabelBox, dataField: 'onClick' },
+                    options: { links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.SLIDER,
+                {
+                    layout: { view: SliderBox, dataField: defaultDataKey },
+                    options: { links: '@links(self)', treeViewGrowsHorizontally: true },
+                },
+            ],
+            [
+                DocumentType.PRES,
+                {
+                    layout: { view: PresBox, dataField: defaultDataKey },
+                    options: { links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.FONTICON,
+                {
+                    layout: { view: FontIconBox, dataField: defaultDataKey },
+                    options: { hideLinkButton: true, _width: 40, _height: 40, borderRounding: '100%', links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.WEBCAM,
+                {
+                    layout: { view: RecordingBox, dataField: defaultDataKey },
+                    options: { links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.PRESELEMENT,
+                {
+                    layout: { view: PresElementBox, dataField: defaultDataKey },
+                },
+            ],
+            [
+                DocumentType.MARKER,
+                {
+                    layout: { view: CollectionView, dataField: defaultDataKey },
+                    options: { links: '@links(self)', hideLinkButton: true, pointerEvents: 'none' },
+                },
+            ],
+            [
+                DocumentType.INK,
+                {
+                    // NOTE: this is unused!! ink fields are filled in directly within the InkDocument() method
+                    layout: { view: InkingStroke, dataField: defaultDataKey },
+                    options: { links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.SCREENSHOT,
+                {
+                    layout: { view: ScreenshotBox, dataField: defaultDataKey },
+                    options: { links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.COMPARISON,
+                {
+                    layout: { view: ComparisonBox, dataField: defaultDataKey },
+                    options: { clipWidth: 50, nativeDimModifiable: true, backgroundColor: 'gray', targetDropAction: 'alias', links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.GROUPDB,
+                {
+                    data: new List<Doc>(),
+                    layout: { view: EmptyBox, dataField: defaultDataKey },
+                    options: { childDropAction: 'alias', title: 'Global Group Database' },
+                },
+            ],
+            [
+                DocumentType.GROUP,
+                {
+                    layout: { view: EmptyBox, dataField: defaultDataKey },
+                    options: { links: '@links(self)' },
+                },
+            ],
+            [
+                DocumentType.DATAVIZ,
+                {
+                    layout: { view: DataVizBox, dataField: defaultDataKey },
+                    options: { _fitWidth: true, nativeDimModifiable: true, links: '@links(self)' },
+                },
+            ],
         ]);
 
-        const suffix = "Proto";
+        const suffix = 'Proto';
 
         /**
          * This function loads or initializes the prototype for each docment type.
-         * 
+         *
          * This is an asynchronous function because it has to attempt
          * to fetch the prototype documents from the server.
-         * 
+         *
          * Once we have this object that maps the prototype ids to a potentially
          * undefined document, we either initialize our private prototype
          * variables with the document returned from the server or, if prototypes
@@ -526,13 +673,15 @@ export namespace Docs {
             ProxyField.initPlugin();
             ComputedField.initPlugin();
             // non-guid string ids for each document prototype
-            const 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
             const actualProtos = Docs.newAccount ? {} : await DocServer.GetRefFields(prototypeIds);
             // update this object to include any default values: DocumentOptions for all prototypes
             prototypeIds.map(id => {
                 const existing = actualProtos[id] as Doc;
-                const type = id.replace(suffix, "") as DocumentType;
+                const type = id.replace(suffix, '') as DocumentType;
                 // get or create prototype of the specified type...
                 const target = existing || buildPrototype(type, id);
                 // ...and set it if not undefined (can be undefined only if TemplateMap does not contain
@@ -545,30 +694,38 @@ export namespace Docs {
          * Retrieves the prototype for the given document type, or
          * undefined if that type's proto doesn't have a configuration
          * in the template map.
-         * @param type 
+         * @param type
          */
         const PrototypeMap: PrototypeMap = new Map();
-        export function get(type: DocumentType): Doc { return PrototypeMap.get(type)!; }
+        export function get(type: DocumentType): Doc {
+            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.LINKDB); }
+        export function MainLinkDocument() {
+            return Prototypes.get(DocumentType.LINKDB);
+        }
 
         /**
          * A collection of all scripts in the database
          */
-        export function MainScriptDocument() { return Prototypes.get(DocumentType.SCRIPTDB); }
+        export function MainScriptDocument() {
+            return Prototypes.get(DocumentType.SCRIPTDB);
+        }
 
         /**
          * A collection of all user acl groups in the database
          */
-        export function MainGroupDocument() { return Prototypes.get(DocumentType.GROUPDB); }
+        export function MainGroupDocument() {
+            return Prototypes.get(DocumentType.GROUPDB);
+        }
 
         /**
          * This is a convenience method that is used to initialize
          * prototype documents for the first time.
-         * 
+         *
          * @param protoId the id of the prototype, indicating the specific prototype
          * to initialize (see the *protoId list at the top of the namespace)
          * @param title the prototype document's title, follows *-PROTO
@@ -590,11 +747,21 @@ export namespace Docs {
             // synthesize the default options, the type and title from computed values and
             // whatever options pertain to this specific prototype
             const options: DocumentOptions = {
-                system: true, _layoutKey: "layout", title, type, baseProto: true, x: 0, y: 0, _width: 300, ...(template.options || {}),
-                layout: layout.view?.LayoutString(layout.dataField), data: template.data, layout_keyValue: KeyValueBox.LayoutString("")
+                system: true,
+                _layoutKey: 'layout',
+                title,
+                type,
+                baseProto: true,
+                x: 0,
+                y: 0,
+                _width: 300,
+                ...(template.options || {}),
+                layout: layout.view?.LayoutString(layout.dataField),
+                data: template.data,
+                layout_keyValue: KeyValueBox.LayoutString(''),
             };
             Object.entries(options).map(pair => {
-                if (typeof pair[1] === "string" && pair[1].startsWith("@")) {
+                if (typeof pair[1] === 'string' && pair[1].startsWith('@')) {
                     (options as any)[pair[0]] = ComputedField.MakeFunction(pair[1].substring(1));
                 }
             });
@@ -607,16 +774,15 @@ export namespace Docs {
      * delegated from top-level prototypes
      */
     export namespace Create {
-
         /**
          * This function receives the relevant document prototype and uses
          * it to create a new of that base-level prototype, or the
-         * underlying data document, which it then delegates again 
+         * underlying data document, which it then delegates again
          * to create the view document.
-         * 
+         *
          * It also takes the opportunity to register the user
          * that created the document and the time of creation.
-         * 
+         *
          * @param proto the specific document prototype off of which to model
          * this new instance (textProto, imageProto, etc.)
          * @param data the Field to store at this new instance's data key
@@ -626,45 +792,48 @@ export namespace Docs {
          * only when creating a DockDocument from the current user's already existing
          * main document.
          */
-        function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = "data", protoId?: string) {
-            const viewKeys = ["x", "y", "system"]; // keys that should be addded to the view document even though they don't begin with an "_"
-            const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, "^_");
+        function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = 'data', protoId?: string) {
+            const viewKeys = ['x', 'y', 'system']; // keys that should be addded to the view document even though they don't begin with an "_"
+            const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, '^_');
 
-            dataProps["acl-Override"] = "None";
-            dataProps["acl-Public"] = options["acl-Public"] ? options["acl-Public"] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            dataProps['acl-Override'] = 'None';
+            dataProps['acl-Public'] = options['acl-Public'] ? options['acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
 
             dataProps.system = viewProps.system;
             dataProps.isPrototype = true;
             dataProps.author = Doc.CurrentUserEmail;
-            dataProps.creationDate = new DateField;
-            dataProps[`${fieldKey}-lastModified`] = new DateField;
+            dataProps.creationDate = new DateField();
+            dataProps[`${fieldKey}-lastModified`] = new DateField();
 
             dataProps[fieldKey] = data;
 
             // so that the list of annotations is already initialised, prevents issues in addonly.
             // without this, if a doc has no annotations but the user has AddOnly privileges, they won't be able to add an annotation because they would have needed to create the field's list which they don't have permissions to do.
-            dataProps[fieldKey + "-annotations"] = new List<Doc>();
-            dataProps[fieldKey + "-sidebar"] = new List<Doc>();
+            dataProps[fieldKey + '-annotations'] = new List<Doc>();
+            dataProps[fieldKey + '-sidebar'] = new List<Doc>();
             const dataDoc = Doc.assign(Doc.MakeDelegate(proto, protoId), dataProps, undefined, true);
 
             const viewFirstProps: { [id: string]: any } = {};
-            viewFirstProps["acl-Public"] = options["_acl-Public"] ? options["_acl-Public"] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
-            viewFirstProps["acl-Override"] = "None";
+            viewFirstProps['acl-Public'] = options['_acl-Public'] ? options['_acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            viewFirstProps['acl-Override'] = 'None';
             viewFirstProps.author = Doc.CurrentUserEmail;
             const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true);
             Doc.assign(viewDoc, viewProps, true, true);
             ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc);
 
-            !Doc.IsSystem(dataDoc) && ![DocumentType.MARKER, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(proto.type as any) &&
-                !dataDoc.isFolder && !dataProps.annotationOn && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, undefined, dataDoc);
+            !Doc.IsSystem(dataDoc) &&
+                ![DocumentType.MARKER, DocumentType.KVP, DocumentType.LINK, DocumentType.LINKANCHOR].includes(proto.type as any) &&
+                !dataDoc.isFolder &&
+                !dataProps.annotationOn &&
+                Doc.AddDocToList(Doc.MyFileOrphans, undefined, dataDoc);
 
             updateCachedAcls(dataDoc);
             updateCachedAcls(viewDoc);
             return viewDoc;
         }
 
-        export function ImageDocument(url: string|ImageField, options: DocumentOptions = {}) {
-            const imgField =  url instanceof ImageField ? url : new ImageField(url);
+        export function ImageDocument(url: string | ImageField, options: DocumentOptions = {}) {
+            const imgField = url instanceof ImageField ? url : new ImageField(url);
             return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField.url.href), ...options });
         }
 
@@ -672,9 +841,8 @@ export namespace Docs {
             return InstanceFromProto(Prototypes.get(DocumentType.PRES), new List<Doc>(), options);
         }
 
-        export function ScriptingDocument(script: Opt<ScriptField>|null, options: DocumentOptions = {}, fieldKey?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script ? script: undefined,
-                { ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined });
+        export function ScriptingDocument(script: Opt<ScriptField> | null, options: DocumentOptions = {}, fieldKey?: string) {
+            return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script ? script : undefined, { ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined });
         }
 
         export function VideoDocument(url: string, options: DocumentOptions = {}) {
@@ -686,24 +854,23 @@ export namespace Docs {
         }
 
         export function WebCamDocument(url: string, options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.WEBCAM), "", options);
+            return InstanceFromProto(Prototypes.get(DocumentType.WEBCAM), '', options);
         }
 
         export function ScreenshotDocument(options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.SCREENSHOT), "", options);
+            return InstanceFromProto(Prototypes.get(DocumentType.SCREENSHOT), '', options);
         }
 
-        export function ComparisonDocument(options: DocumentOptions = { title: "Comparison Box" }) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), "", options);
+        export function ComparisonDocument(options: DocumentOptions = { title: 'Comparison Box' }) {
+            return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), '', options);
         }
 
         export function AudioDocument(url: string, options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(url),
-                { ...options, backgroundColor: ComputedField.MakeFunction("this._mediaState === 'playing' ? 'green':'gray'") as any });
+            return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(url), { ...options, backgroundColor: ComputedField.MakeFunction("this._mediaState === 'playing' ? 'green':'gray'") as any });
         }
 
         export function RecordingDocument(url: string, options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.REC), "", options);
+            return InstanceFromProto(Prototypes.get(DocumentType.REC), '', options);
         }
 
         export function SearchDocument(options: DocumentOptions = {}) {
@@ -711,34 +878,46 @@ export namespace Docs {
         }
 
         export function ColorDocument(options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COLOR), "", options);
+            return InstanceFromProto(Prototypes.get(DocumentType.COLOR), '', options);
         }
 
-        export function RTFDocument(field: RichTextField, options: DocumentOptions = {}, fieldKey: string = "text") {
+        export function RTFDocument(field: RichTextField, options: DocumentOptions = {}, fieldKey: string = 'text') {
             return InstanceFromProto(Prototypes.get(DocumentType.RTF), field, options, undefined, fieldKey);
         }
-        export function TextDocument(text: string, options: DocumentOptions = {}, fieldKey: string = "text") {
+        export function TextDocument(text: string, options: DocumentOptions = {}, fieldKey: string = 'text') {
             const rtf = {
                 doc: {
-                    type: "doc", content: [{
-                        type: "paragraph",
-                        content: [{
-                            type: "text",
-                            text
-                        }]
-                    }]
+                    type: 'doc',
+                    content: [
+                        {
+                            type: 'paragraph',
+                            content: [
+                                {
+                                    type: 'text',
+                                    text,
+                                },
+                            ],
+                        },
+                    ],
                 },
-                selection: { type: "text", anchor: 1, head: 1 },
-                storedMarks: []
+                selection: { type: 'text', anchor: 1, head: 1 },
+                storedMarks: [],
             };
             const field = text ? new RichTextField(JSON.stringify(rtf), text) : undefined;
             return InstanceFromProto(Prototypes.get(DocumentType.RTF), field, options, undefined, fieldKey);
         }
 
-        export function LinkDocument(source: { doc: Doc, ctx?: Doc }, target: { doc: Doc, ctx?: Doc }, options: DocumentOptions = {}, id?: string) {
-            const linkDoc = InstanceFromProto(Prototypes.get(DocumentType.LINK), undefined, {
-                anchor1: source.doc, anchor2: target.doc, ...options
-            }, id);
+        export function LinkDocument(source: { doc: Doc; ctx?: Doc }, target: { doc: Doc; ctx?: Doc }, options: DocumentOptions = {}, id?: string) {
+            const linkDoc = InstanceFromProto(
+                Prototypes.get(DocumentType.LINK),
+                undefined,
+                {
+                    anchor1: source.doc,
+                    anchor2: target.doc,
+                    ...options,
+                },
+                id
+            );
 
             LinkManager.Instance.addLink(linkDoc);
 
@@ -749,9 +928,9 @@ export namespace Docs {
             const I = new Doc();
             I[Initializing] = true;
             I.type = DocumentType.INK;
-            I.layout = InkingStroke.LayoutString("data");
+            I.layout = InkingStroke.LayoutString('data');
             I.color = color;
-            I.hideDecorationTitle = true;  // don't show title when selected
+            I.hideDecorationTitle = true; // don't show title when selected
             // I.hideOpenButton = true; // don't show open full screen button when selected
             I.fillColor = fillColor;
             I.strokeWidth = strokeWidth;
@@ -760,20 +939,20 @@ export namespace Docs {
             I.strokeEndMarker = arrowEnd;
             I.strokeDash = dash;
             I.tool = tool;
-            I["text-align"] = "center";
-            I.title = "ink";
+            I['text-align'] = 'center';
+            I.title = 'ink';
             I.x = options.x as number;
             I.y = options.y as number;
             I._width = options._width as number;
             I._height = options._height as number;
-            I._fontFamily = "cursive";
+            I._fontFamily = 'cursive';
             I.author = Doc.CurrentUserEmail;
             I.rotation = 0;
             I.data = new InkField(points);
-            I.creationDate = new DateField;
-            I["acl-Public"] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
-            I["acl-Override"] = "None";
-            I.links = ComputedField.MakeFunction("links(self)");
+            I.creationDate = new DateField();
+            I['acl-Public'] = Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment;
+            I['acl-Override'] = 'None';
+            I.links = ComputedField.MakeFunction('links(self)');
             I[Initializing] = false;
             return I;
         }
@@ -783,7 +962,7 @@ export namespace Docs {
             const height = options._height || undefined;
             const nwid = options._nativeWidth || undefined;
             const nhght = options._nativeHeight || undefined;
-            if (!nhght && width && height && nwid) options._nativeHeight = Number(nwid) * Number(height) / Number(width);
+            if (!nhght && width && height && nwid) options._nativeHeight = (Number(nwid) * Number(height)) / Number(width);
             return InstanceFromProto(Prototypes.get(DocumentType.PDF), new PdfField(url), options);
         }
 
@@ -792,8 +971,8 @@ export namespace Docs {
             const height = options._height || undefined;
             const nwid = options._nativeWidth || undefined;
             const nhght = options._nativeHeight || undefined;
-            if (!nhght && width && height && nwid) options._nativeHeight = Number(nwid) * Number(height) / Number(width);
-            return InstanceFromProto(Prototypes.get(DocumentType.WEB), new WebField(url ? url : "http://www.bing.com/"), options);
+            if (!nhght && width && height && nwid) options._nativeHeight = (Number(nwid) * Number(height)) / Number(width);
+            return InstanceFromProto(Prototypes.get(DocumentType.WEB), new WebField(url ? url : 'http://www.bing.com/'), options);
         }
 
         export function HtmlDocument(html: string, options: DocumentOptions = {}) {
@@ -809,12 +988,12 @@ export namespace Docs {
         }
 
         export function KVPDocument(document: Doc, options: DocumentOptions = {}) {
-            return InstanceFromProto(Prototypes.get(DocumentType.KVP), document, { title: document.title + ".kvp", ...options });
+            return InstanceFromProto(Prototypes.get(DocumentType.KVP), document, { title: document.title + '.kvp', ...options });
         }
 
         export function FreeformDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
             const inst = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _xPadding: 20, _yPadding: 20, ...options, _viewType: CollectionViewType.Freeform }, id);
-            documents.map(d => d.context = inst);
+            documents.map(d => (d.context = inst));
             return inst;
         }
 
@@ -831,7 +1010,7 @@ export namespace Docs {
         }
 
         export function PileDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _overflow: "visible", _forceActive: true, _noAutoscroll: true, ...options, _viewType: CollectionViewType.Pile }, id);
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { _overflow: 'visible', _forceActive: true, _noAutoscroll: true, ...options, _viewType: CollectionViewType.Pile }, id);
         }
 
         export function LinearDocument(documents: Array<Doc>, options: DocumentOptions, id?: string) {
@@ -886,7 +1065,7 @@ export namespace Docs {
         }
 
         export function ButtonDocument(options?: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.BUTTON), undefined, { ...(options || {}), "onClick-rawScript": "-script-" });
+            return InstanceFromProto(Prototypes.get(DocumentType.BUTTON), undefined, { ...(options || {}), 'onClick-rawScript': '-script-' });
         }
 
         export function SliderDocument(options?: DocumentOptions) {
@@ -905,11 +1084,11 @@ export namespace Docs {
         }
 
         export function DataVizDocument(url: string, options?: DocumentOptions) {
-            return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: "Data Viz", ...options });
+            return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: 'Data Viz', ...options });
         }
 
         export function DockDocument(documents: Array<Doc>, config: string, options: DocumentOptions, id?: string) {
-            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { freezeChildren: "remove|add", ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
+            return InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { freezeChildren: 'remove|add', ...options, _viewType: CollectionViewType.Docking, dockingConfig: config }, id);
         }
 
         export function DirectoryImportDocument(options: DocumentOptions = {}) {
@@ -917,23 +1096,26 @@ export namespace Docs {
         }
 
         export type DocConfig = {
-            doc: Doc,
-            initialWidth?: number,
-            path?: Doc[]
+            doc: Doc;
+            initialWidth?: number;
+            path?: Doc[];
         };
 
-        export function StandardCollectionDockingDocument(configs: Array<DocConfig>, options: DocumentOptions, id?: string, type: string = "row") {
+        export function StandardCollectionDockingDocument(configs: Array<DocConfig>, options: DocumentOptions, id?: string, type: string = 'row') {
             const layoutConfig = {
                 content: [
                     {
                         type: type,
-                        content: [
-                            ...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, undefined, config.initialWidth))
-                        ]
-                    }
-                ]
+                        content: [...configs.map(config => CollectionDockingView.makeDocumentConfig(config.doc, undefined, config.initialWidth))],
+                    },
+                ],
             };
-            return DockDocument(configs.map(c => c.doc), JSON.stringify(layoutConfig), options, id);
+            return DockDocument(
+                configs.map(c => c.doc),
+                JSON.stringify(layoutConfig),
+                options,
+                id
+            );
         }
 
         export function DelegateDocument(proto: Doc, options: DocumentOptions = {}) {
@@ -944,9 +1126,9 @@ export namespace Docs {
 
 export namespace DocUtils {
     export function Excluded(d: Doc, docFilters: string[]) {
-        const filterFacets: { [key: string]: { [value: string]: string } } = {};  // maps each filter key to an object with value=>modifier fields
+        const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields
         docFilters.forEach(filter => {
-            const fields = filter.split(":");
+            const fields = filter.split(':');
             const key = fields[0];
             const value = fields[1];
             const modifiers = fields[2];
@@ -959,7 +1141,7 @@ export namespace DocUtils {
         if (d.z) return false;
         for (const facetKey of Object.keys(filterFacets)) {
             const facet = filterFacets[facetKey];
-            const xs = Object.keys(facet).filter(value => facet[value] === "x");
+            const xs = Object.keys(facet).filter(value => facet[value] === 'x');
             const failsNotEqualFacets = xs?.some(value => Doc.matchFieldValue(d, facetKey, value));
             if (failsNotEqualFacets) {
                 return true;
@@ -968,21 +1150,21 @@ export namespace DocUtils {
         return false;
     }
     /**
-     * @param docs 
-     * @param docFilters 
-     * @param docRangeFilters 
-     * @param viewSpecScript 
-     * Given a list of docs and docFilters, @returns the list of Docs that match those filters 
+     * @param docs
+     * @param docFilters
+     * @param docRangeFilters
+     * @param viewSpecScript
+     * Given a list of docs and docFilters, @returns the list of Docs that match those filters
      */
     export function FilterDocs(docs: Doc[], docFilters: string[], docRangeFilters: string[], viewSpecScript?: ScriptField, parentCollection?: Doc) {
         const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
         if (!docFilters?.length && !docRangeFilters?.length) {
-            return childDocs.filter(d => !d.cookies);  // remove documents that need a cookie if there are no filters to provide one
+            return childDocs.filter(d => !d.cookies); // remove documents that need a cookie if there are no filters to provide one
         }
 
-        const filterFacets: { [key: string]: { [value: string]: string } } = {};  // maps each filter key to an object with value=>modifier fields
+        const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields
         docFilters.forEach(filter => {
-            const fields = filter.split(":");
+            const fields = filter.split(':');
             const key = fields[0];
             const value = fields[1];
             const modifiers = fields[2];
@@ -992,63 +1174,67 @@ export namespace DocUtils {
             filterFacets[key][value] = modifiers;
         });
 
-        const filteredDocs = docFilters.length ? childDocs.filter(d => {
-            if (d.z) return true;
-            // if the document needs a cookie but no filter provides the cookie, then the document does not pass the filter
-            if (d.cookies && (!filterFacets.cookies || !Object.keys(filterFacets.cookies).some(key => d.cookies === key))) {
-                return false;
-            }
-
-            for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== "cookies")) {
-                const facet = filterFacets[facetKey];
-
-                // facets that match some value in the field of the document (e.g. some text field)
-                const matches = Object.keys(facet).filter(value => value !== "cookies" && facet[value] === "match");
-
-                // facets that have a check next to them
-                const checks = Object.keys(facet).filter(value => facet[value] === "check");
-
-                // metadata facets that exist
-                const exists = Object.keys(facet).filter(value => facet[value] === "exists");
-
-                // metadata facets that exist
-                const unsets = Object.keys(facet).filter(value => facet[value] === "unset");
-
-                // facets that have an x next to them
-                const xs = Object.keys(facet).filter(value => facet[value] === "x");
-
-                if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true;
-                const failsNotEqualFacets = !xs.length ? false : xs.some(value => Doc.matchFieldValue(d, facetKey, value));
-                const satisfiesCheckFacets = !checks.length ? true : checks.some(value => Doc.matchFieldValue(d, facetKey, value));
-                const satisfiesExistsFacets = !exists.length ? true : exists.some(value => d[facetKey] !== undefined);
-                const satisfiesUnsetsFacets = !unsets.length ? true : unsets.some(value => d[facetKey] === undefined);
-                const satisfiesMatchFacets = !matches.length ? true : matches.some(value => {
-                    if (facetKey.startsWith("*")) { //  fields starting with a '*' are used to match families of related fields.  ie, *lastModified will match text-lastModified, data-lastModified, etc
-                        const allKeys = Array.from(Object.keys(d));
-                        allKeys.push(...Object.keys(Doc.GetProto(d)));
-                        const keys = allKeys.filter(key => key.includes(facetKey.substring(1)));
-                        return keys.some(key => Field.toString(d[key] as Field).includes(value));
-                    }
-                    return Field.toString(d[facetKey] as Field).includes(value);
-                });
-                // if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria
-                if ((parentCollection?.currentFilter as Doc)?.filterBoolean === "OR") {
-                    if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true;
-                }
-                // if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria
-                else {
-                    if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false;
-                }
-
-            }
-            return (parentCollection?.currentFilter as Doc)?.filterBoolean === "OR" ? false : true;
-        }) : childDocs;
+        const filteredDocs = docFilters.length
+            ? childDocs.filter(d => {
+                  if (d.z) return true;
+                  // if the document needs a cookie but no filter provides the cookie, then the document does not pass the filter
+                  if (d.cookies && (!filterFacets.cookies || !Object.keys(filterFacets.cookies).some(key => d.cookies === key))) {
+                      return false;
+                  }
+
+                  for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== 'cookies')) {
+                      const facet = filterFacets[facetKey];
+
+                      // facets that match some value in the field of the document (e.g. some text field)
+                      const matches = Object.keys(facet).filter(value => value !== 'cookies' && facet[value] === 'match');
+
+                      // facets that have a check next to them
+                      const checks = Object.keys(facet).filter(value => facet[value] === 'check');
+
+                      // metadata facets that exist
+                      const exists = Object.keys(facet).filter(value => facet[value] === 'exists');
+
+                      // metadata facets that exist
+                      const unsets = Object.keys(facet).filter(value => facet[value] === 'unset');
+
+                      // facets that have an x next to them
+                      const xs = Object.keys(facet).filter(value => facet[value] === 'x');
+
+                      if (!unsets.length && !exists.length && !xs.length && !checks.length && !matches.length) return true;
+                      const failsNotEqualFacets = !xs.length ? false : xs.some(value => Doc.matchFieldValue(d, facetKey, value));
+                      const satisfiesCheckFacets = !checks.length ? true : checks.some(value => Doc.matchFieldValue(d, facetKey, value));
+                      const satisfiesExistsFacets = !exists.length ? true : exists.some(value => d[facetKey] !== undefined);
+                      const satisfiesUnsetsFacets = !unsets.length ? true : unsets.some(value => d[facetKey] === undefined);
+                      const satisfiesMatchFacets = !matches.length
+                          ? true
+                          : matches.some(value => {
+                                if (facetKey.startsWith('*')) {
+                                    //  fields starting with a '*' are used to match families of related fields.  ie, *lastModified will match text-lastModified, data-lastModified, etc
+                                    const allKeys = Array.from(Object.keys(d));
+                                    allKeys.push(...Object.keys(Doc.GetProto(d)));
+                                    const keys = allKeys.filter(key => key.includes(facetKey.substring(1)));
+                                    return keys.some(key => Field.toString(d[key] as Field).includes(value));
+                                }
+                                return Field.toString(d[facetKey] as Field).includes(value);
+                            });
+                      // if we're ORing them together, the default return is false, and we return true for a doc if it satisfies any one set of criteria
+                      if ((parentCollection?.currentFilter as Doc)?.filterBoolean === 'OR') {
+                          if (satisfiesUnsetsFacets && satisfiesExistsFacets && satisfiesCheckFacets && !failsNotEqualFacets && satisfiesMatchFacets) return true;
+                      }
+                      // if we're ANDing them together, the default return is true, and we return false for a doc if it doesn't satisfy any set of criteria
+                      else {
+                          if (!satisfiesUnsetsFacets || !satisfiesExistsFacets || !satisfiesCheckFacets || failsNotEqualFacets || (matches.length && !satisfiesMatchFacets)) return false;
+                      }
+                  }
+                  return (parentCollection?.currentFilter as Doc)?.filterBoolean === 'OR' ? false : true;
+              })
+            : childDocs;
         const rangeFilteredDocs = filteredDocs.filter(d => {
             for (let i = 0; i < docRangeFilters.length; i += 3) {
                 const key = docRangeFilters[i];
                 const min = Number(docRangeFilters[i + 1]);
                 const max = Number(docRangeFilters[i + 2]);
-                const val = typeof d[key] === "string" ? (Number(StrCast(d[key])).toString() === StrCast(d[key]) ? Number(StrCast(d[key])) : undefined) : Cast(d[key], "number", null);
+                const val = typeof d[key] === 'string' ? (Number(StrCast(d[key])).toString() === StrCast(d[key]) ? Number(StrCast(d[key])) : undefined) : Cast(d[key], 'number', null);
                 if (val === undefined) {
                     //console.log("Should 'undefined' pass range filter or not?")
                 } else if (val < min || val > max) return false;
@@ -1059,7 +1245,7 @@ export namespace DocUtils {
     }
 
     export function Publish(promoteDoc: Doc, targetID: string, addDoc: any, remDoc: any) {
-        targetID = targetID.replace(/^-/, "").replace(/\([0-9]*\)$/, "");
+        targetID = targetID.replace(/^-/, '').replace(/\([0-9]*\)$/, '');
         DocServer.GetRefField(targetID).then(doc => {
             if (promoteDoc !== doc) {
                 let copy = doc as Doc;
@@ -1073,16 +1259,17 @@ export namespace DocUtils {
                 remDoc && remDoc(promoteDoc);
                 if (!doc) {
                     DocListCastAsync(promoteDoc.links).then(links => {
-                        links && links.map(async link => {
-                            if (link) {
-                                const a1 = await Cast(link.anchor1, Doc);
-                                if (a1 && Doc.AreProtosEqual(a1, promoteDoc)) link.anchor1 = copy;
-                                const a2 = await Cast(link.anchor2, Doc);
-                                if (a2 && Doc.AreProtosEqual(a2, promoteDoc)) link.anchor2 = copy;
-                                LinkManager.Instance.deleteLink(link);
-                                LinkManager.Instance.addLink(link);
-                            }
-                        });
+                        links &&
+                            links.map(async link => {
+                                if (link) {
+                                    const a1 = await Cast(link.anchor1, Doc);
+                                    if (a1 && Doc.AreProtosEqual(a1, promoteDoc)) link.anchor1 = copy;
+                                    const a2 = await Cast(link.anchor2, Doc);
+                                    if (a2 && Doc.AreProtosEqual(a2, promoteDoc)) link.anchor2 = copy;
+                                    LinkManager.Instance.deleteLink(link);
+                                    LinkManager.Instance.addLink(link);
+                                }
+                            });
                     });
                 }
             }
@@ -1093,19 +1280,19 @@ export namespace DocUtils {
         options?.afterFocus?.(false);
     }
 
-    export let ActiveRecordings: { props: FieldViewProps, getAnchor: () => Doc }[] = [];
+    export let ActiveRecordings: { props: FieldViewProps; getAnchor: () => Doc }[] = [];
 
     export function MakeLinkToActiveAudio(getSourceDoc: () => Doc, broadcastEvent = true) {
-        broadcastEvent && runInAction(() => DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1);
+        broadcastEvent && runInAction(() => (DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1));
         return DocUtils.ActiveRecordings.map(audio => {
-            const link = DocUtils.MakeLink({ doc: getSourceDoc() }, { doc: audio.getAnchor() || audio.props.Document }, "recording annotation:linked recording", "recording timeline");
-            link && (link.followLinkLocation = "add:right");
+            const link = DocUtils.MakeLink({ doc: getSourceDoc() }, { doc: audio.getAnchor() || audio.props.Document }, 'recording annotation:linked recording', 'recording timeline');
+            link && (link.followLinkLocation = 'add:right');
             return link;
         });
     }
 
-    export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = "", description: string = "", id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) {
-        if (!linkRelationship) linkRelationship = target.doc.type === DocumentType.RTF ? "Commentary:Comments On" : "link";
+    export function MakeLink(source: { doc: Doc }, target: { doc: Doc }, linkRelationship: string = '', description: string = '', id?: string, allowParCollectionLink?: boolean, showPopup?: number[]) {
+        if (!linkRelationship) linkRelationship = target.doc.type === DocumentType.RTF ? 'Commentary:Comments On' : 'link';
         const sv = DocumentManager.Instance.getDocumentView(source.doc);
         if (!allowParCollectionLink && sv?.props.ContainingCollectionDoc === target.doc) return;
         if (target.doc === Doc.UserDoc()) return undefined;
@@ -1114,7 +1301,7 @@ export namespace DocUtils {
             if (showPopup) {
                 LinkManager.currentLink = linkDoc;
 
-                TaskCompletionBox.textDisplayed = "Link Created";
+                TaskCompletionBox.textDisplayed = 'Link Created';
                 TaskCompletionBox.popupX = showPopup[0];
                 TaskCompletionBox.popupY = showPopup[1] - 33;
                 TaskCompletionBox.taskCompleted = true;
@@ -1133,25 +1320,75 @@ export namespace DocUtils {
                     TaskCompletionBox.popupY -= 40;
                 }
 
-                setTimeout(action(() => TaskCompletionBox.taskCompleted = false), 2500);
+                setTimeout(
+                    action(() => (TaskCompletionBox.taskCompleted = false)),
+                    2500
+                );
             }
             return linkDoc;
         });
 
-        return makeLink(Docs.Create.LinkDocument(source, target, {
-            title: ComputedField.MakeFunction("generateLinkTitle(self)") as any,
-            "anchor1-useLinkSmallAnchor": source.doc.useLinkSmallAnchor ? true : undefined,
-            "anchor2-useLinkSmallAnchor": target.doc.useLinkSmallAnchor ? true : undefined,
-            "acl-Public": SharingPermissions.Augment,
-            "_acl-Public": SharingPermissions.Augment,
-            linkDisplay: true,
-            _hidden: true,
-            _linkAutoMove: true,
-            linkRelationship,
-            _showCaption: "description",
-            _showTitle: "linkRelationship",
-            description
-        }, id), showPopup);
+        return makeLink(
+            Docs.Create.LinkDocument(
+                source,
+                target,
+                {
+                    title: ComputedField.MakeFunction('generateLinkTitle(self)') as any,
+                    'anchor1-useLinkSmallAnchor': source.doc.useLinkSmallAnchor ? true : undefined,
+                    'anchor2-useLinkSmallAnchor': target.doc.useLinkSmallAnchor ? true : undefined,
+                    'acl-Public': SharingPermissions.Augment,
+                    '_acl-Public': SharingPermissions.Augment,
+                    linkDisplay: true,
+                    _hidden: true,
+                    _linkAutoMove: true,
+                    linkRelationship,
+                    _showCaption: 'description',
+                    _showTitle: 'linkRelationship',
+                    description,
+                },
+                id
+            ),
+            showPopup
+        );
+    }
+
+    export function AssignScripts(doc: Doc, scripts?: { [key: string]: string }, funcs?: { [key: string]: string }) {
+        scripts &&
+            Object.keys(scripts).map(key => {
+                if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && scripts[key]) {
+                    doc[key] = ScriptField.MakeScript(scripts[key], { dragData: DragManager.DocumentDragData.name, value: 'any', scriptContext: 'any', documentView: Doc.name }, { _readOnly_: true });
+                }
+            });
+        funcs &&
+            Object.keys(funcs).map(key => {
+                const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
+                if (ScriptCast(cfield)?.script.originalScript !== funcs[key] && funcs[key]) {
+                    doc[key] = ComputedField.MakeFunction(funcs[key], { dragData: DragManager.DocumentDragData.name }, { _readOnly_: true });
+                }
+            });
+        return doc;
+    }
+    export function AssignOpts(doc: Doc | undefined, reqdOpts: DocumentOptions, items?: Doc[]) {
+        if (doc) {
+            const compareValues = (val1: any, val2: any) => {
+                if (val1 instanceof List && val2 instanceof List && val1.length === val2.length) {
+                    return !val1.some(v => !val2.includes(v)) || !val2.some(v => val1.includes(v));
+                }
+                return val1 === val2;
+            };
+            Object.entries(reqdOpts).forEach(pair => {
+                const targetDoc = pair[0].startsWith('_') ? doc : Doc.GetProto(doc as Doc);
+                if (!Object.getOwnPropertyNames(targetDoc).includes(pair[0].replace(/^_/, '')) || !compareValues(pair[1], targetDoc[pair[0]])) {
+                    targetDoc[pair[0]] = pair[1];
+                }
+            });
+            items?.forEach(item => !DocListCast(doc.data).includes(item) && Doc.AddDocToList(Doc.GetProto(doc), 'data', item));
+            items && DocListCast(doc.data).forEach(item => !items.includes(item) && Doc.RemoveDocFromList(Doc.GetProto(doc), 'data', item));
+        }
+        return doc;
+    }
+    export function AssignDocField(doc: Doc, field: string, creator: (reqdOpts: DocumentOptions, items?: Doc[]) => Doc, reqdOpts: DocumentOptions, items?: Doc[], scripts?: { [key: string]: string }, funcs?: { [key: string]: string }) {
+        return DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts, items) ?? (doc[field] = creator(reqdOpts, items)), scripts, funcs);
     }
 
     export function DocumentFromField(target: Doc, fieldKey: string, proto?: Doc, options?: DocumentOptions): Doc | undefined {
@@ -1160,24 +1397,24 @@ export namespace DocUtils {
         const field = target[fieldKey];
         const resolved = options || {};
         if (field instanceof ImageField) {
-            created = Docs.Create.ImageDocument((field).url.href, resolved);
+            created = Docs.Create.ImageDocument(field.url.href, resolved);
             layout = ImageBox.LayoutString;
         } else if (field instanceof Doc) {
             created = field;
         } else if (field instanceof VideoField) {
-            created = Docs.Create.VideoDocument((field).url.href, resolved);
+            created = Docs.Create.VideoDocument(field.url.href, resolved);
             layout = VideoBox.LayoutString;
         } else if (field instanceof PdfField) {
-            created = Docs.Create.PdfDocument((field).url.href, resolved);
+            created = Docs.Create.PdfDocument(field.url.href, resolved);
             layout = PDFBox.LayoutString;
         } else if (field instanceof AudioField) {
-            created = Docs.Create.AudioDocument((field).url.href, resolved);
+            created = Docs.Create.AudioDocument(field.url.href, resolved);
             layout = AudioBox.LayoutString;
         } else if (field instanceof RecordingField) {
-            created = Docs.Create.RecordingDocument((field).url.href, resolved);
+            created = Docs.Create.RecordingDocument(field.url.href, resolved);
             layout = RecordingBox.LayoutString;
         } else if (field instanceof InkField) {
-            created = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), (field).inkData, resolved);
+            created = Docs.Create.InkDocument(ActiveInkColor(), Doc.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), field.inkData, resolved);
             layout = InkingStroke.LayoutString;
         } else if (field instanceof List && field[0] instanceof Doc) {
             created = Docs.Create.StackingDocument(DocListCast(field), resolved);
@@ -1185,9 +1422,8 @@ export namespace DocUtils {
         } else if (field instanceof MapField) {
             created = Docs.Create.MapDocument(DocListCast(field), resolved);
             layout = MapBox.LayoutString;
-        }
-        else {
-            created = Docs.Create.TextDocument("", { ...{ _width: 200, _height: 25, _autoHeight: true }, ...resolved });
+        } else {
+            created = Docs.Create.TextDocument('', { ...{ _width: 200, _height: 25, _autoHeight: true }, ...resolved });
             layout = FormattedTextBox.LayoutString;
         }
         if (created) {
@@ -1199,28 +1435,28 @@ export namespace DocUtils {
     }
 
     export async function DocumentFromType(type: string, path: string, options: DocumentOptions): Promise<Opt<Doc>> {
-        let ctor: ((path: string, options: DocumentOptions) => (Doc | Promise<Doc | undefined>)) | undefined = undefined;
-        if (type.indexOf("image") !== -1) {
+        let ctor: ((path: string, options: DocumentOptions) => Doc | Promise<Doc | undefined>) | undefined = undefined;
+        if (type.indexOf('image') !== -1) {
             ctor = Docs.Create.ImageDocument;
             if (!options._width) options._width = 300;
         }
-        if (type.indexOf("video") !== -1) {
+        if (type.indexOf('video') !== -1) {
             ctor = Docs.Create.VideoDocument;
             if (!options._width) options._width = 600;
-            if (!options._height) options._height = (options._width as number) * 2 / 3;
+            if (!options._height) options._height = ((options._width as number) * 2) / 3;
         }
-        if (type.indexOf("audio") !== -1) {
+        if (type.indexOf('audio') !== -1) {
             ctor = Docs.Create.AudioDocument;
         }
-        if (type.indexOf("pdf") !== -1) {
+        if (type.indexOf('pdf') !== -1) {
             ctor = Docs.Create.PdfDocument;
             if (!options._width) options._width = 400;
-            if (!options._height) options._height = (options._width as number) * 1200 / 927;
+            if (!options._height) options._height = ((options._width as number) * 1200) / 927;
         }
-        if (type.indexOf("csv") !== -1) {
+        if (type.indexOf('csv') !== -1) {
             ctor = Docs.Create.DataVizDocument;
             if (!options._width) options._width = 400;
-            if (!options._height) options._height = (options._width as number) * 1200 / 927;
+            if (!options._height) options._height = ((options._width as number) * 1200) / 927;
         }
         //TODO:al+glr
         // if (type.indexOf("map") !== -1) {
@@ -1228,15 +1464,15 @@ export namespace DocUtils {
         //     if (!options._width) options._width = 800;
         //     if (!options._height) options._height = (options._width as number) * 3 / 4;
         // }
-        if (type.indexOf("html") !== -1) {
+        if (type.indexOf('html') !== -1) {
             if (path.includes(window.location.hostname)) {
                 const s = path.split('/');
                 const id = s[s.length - 1];
                 return DocServer.GetRefField(id).then(field => {
                     if (field instanceof Doc) {
                         const alias = Doc.MakeAlias(field);
-                        alias.x = options.x as number || 0;
-                        alias.y = options.y as number || 0;
+                        alias.x = (options.x as number) || 0;
+                        alias.y = (options.y as number) || 0;
                         alias._width = (options._width as number) || 300;
                         alias._height = (options._height as number) || (options._width as number) || 300;
                         return alias;
@@ -1245,55 +1481,63 @@ export namespace DocUtils {
                 });
             }
             ctor = Docs.Create.WebDocument;
-            options = { ...options, _width: 400, _height: 512, title: path, };
+            options = { ...options, _width: 400, _height: 512, title: path };
         }
-        
+
         return ctor ? ctor(path, options) : undefined;
     }
 
     export function addDocumentCreatorMenuItems(docTextAdder: (d: Doc) => void, docAdder: (d: Doc) => void, x: number, y: number, simpleMenu: boolean = false): void {
-        !simpleMenu && ContextMenu.Instance.addItem({
-            description: "Quick Notes",
-            subitems: DocListCast((Doc.UserDoc()["template-notes"] as Doc).data).map((note, i) => ({
-                description: ":" + StrCast(note.title),
-                event: undoBatch((args: { x: number, y: number }) => {
-                    const textDoc = Docs.Create.TextDocument("", {
-                        _width: 200, x, y, _autoHeight: note._autoHeight !== false,
-                        title: StrCast(note.title) + "#" + (note.aliasCount = NumCast(note.aliasCount) + 1)
-                    });
-                    textDoc.layoutKey = "layout_" + note.title;
-                    textDoc[textDoc.layoutKey] = note;
-                    docTextAdder(textDoc);
+        !simpleMenu &&
+            ContextMenu.Instance.addItem({
+                description: 'Quick Notes',
+                subitems: DocListCast((Doc.UserDoc()['template-notes'] as Doc).data).map((note, i) => ({
+                    description: ':' + StrCast(note.title),
+                    event: undoBatch((args: { x: number; y: number }) => {
+                        const textDoc = Docs.Create.TextDocument('', {
+                            _width: 200,
+                            x,
+                            y,
+                            _autoHeight: note._autoHeight !== false,
+                            title: StrCast(note.title) + '#' + (note.aliasCount = NumCast(note.aliasCount) + 1),
+                        });
+                        textDoc.layoutKey = 'layout_' + note.title;
+                        textDoc[textDoc.layoutKey] = note;
+                        docTextAdder(textDoc);
+                    }),
+                    icon: StrCast(note.icon) as IconProp,
+                })) as ContextMenuProps[],
+                icon: 'sticky-note',
+            });
+        const documentList: ContextMenuProps[] = DocListCast(DocListCast(Doc.MyTools?.data)[0]?.data)
+            .filter(btnDoc => !btnDoc.hidden)
+            .map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null))
+            .filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation)
+            .map((dragDoc, i) => ({
+                description: ':' + StrCast(dragDoc.title).replace('Untitled ', ''),
+                event: undoBatch((args: { x: number; y: number }) => {
+                    const newDoc = DocUtils.copyDragFactory(dragDoc);
+                    if (newDoc) {
+                        newDoc.author = Doc.CurrentUserEmail;
+                        newDoc.x = x;
+                        newDoc.y = y;
+                        EquationBox.SelectOnLoad = newDoc[Id];
+                        if (newDoc.type === DocumentType.RTF) FormattedTextBox.SelectOnLoad = newDoc[Id];
+                        docAdder?.(newDoc);
+                    }
                 }),
-                icon: StrCast(note.icon) as IconProp
-            })) as ContextMenuProps[],
-            icon: "sticky-note"
-        });
-        const documentList: ContextMenuProps[] = DocListCast(DocListCast(CurrentUserUtils.MyTools?.data)[0]?.data).filter(btnDoc => !btnDoc.hidden).map(btnDoc => Cast(btnDoc?.dragFactory, Doc, null)).filter(doc => doc && doc !== Doc.UserDoc().emptyPresentation).map((dragDoc, i) => ({
-            description: ":" + StrCast(dragDoc.title).replace("Untitled ",""),
-            event: undoBatch((args: { x: number, y: number }) => {
-                const newDoc = DocUtils.copyDragFactory(dragDoc);
-                if (newDoc) {
-                    newDoc.author = Doc.CurrentUserEmail;
-                    newDoc.x = x;
-                    newDoc.y = y;
-                    EquationBox.SelectOnLoad = newDoc[Id];
-                    if (newDoc.type === DocumentType.RTF) FormattedTextBox.SelectOnLoad = newDoc[Id];
-                    docAdder?.(newDoc);
-                }
-            }),
-            icon: Doc.toIcon(dragDoc),
-        })) as ContextMenuProps[];
+                icon: Doc.toIcon(dragDoc),
+            })) as ContextMenuProps[];
         ContextMenu.Instance.addItem({
-            description: "Create document",
+            description: 'Create document',
             subitems: documentList,
-            icon: "file"
+            icon: 'file',
         });
-    }// applies a custom template to a document.  the template is identified by it's short name (e.g, slideView not layout_slideView)
-    export function makeCustomViewClicked(doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = "custom", docLayoutTemplate?: Doc) {
-        const batch = UndoManager.StartBatch("makeCustomViewClicked");
+    } // applies a custom template to a document.  the template is identified by it's short name (e.g, slideView not layout_slideView)
+    export function makeCustomViewClicked(doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', docLayoutTemplate?: Doc) {
+        const batch = UndoManager.StartBatch('makeCustomViewClicked');
         runInAction(() => {
-            doc.layoutKey = "layout_" + templateSignature;
+            doc.layoutKey = 'layout_' + templateSignature;
             createCustomView(doc, creator, templateSignature, docLayoutTemplate);
         });
         batch.end();
@@ -1301,44 +1545,49 @@ export namespace DocUtils {
     }
     export function findTemplate(templateName: string, type: string, signature: string) {
         let docLayoutTemplate: Opt<Doc>;
-        const iconViews = DocListCast(Cast(Doc.UserDoc()["template-icons"], Doc, null)?.data);
-        const templBtns = DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data);
-        const noteTypes = DocListCast(Cast(Doc.UserDoc()["template-notes"], Doc, null)?.data);
+        const iconViews = DocListCast(Cast(Doc.UserDoc()['template-icons'], Doc, null)?.data);
+        const templBtns = DocListCast(Cast(Doc.UserDoc()['template-buttons'], Doc, null)?.data);
+        const noteTypes = DocListCast(Cast(Doc.UserDoc()['template-notes'], Doc, null)?.data);
         const clickFuncs = DocListCast(Cast(Doc.UserDoc().clickFuncs, Doc, null)?.data);
-        const allTemplates = iconViews.concat(templBtns).concat(noteTypes).concat(clickFuncs).map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc).filter(doc => doc.isTemplateDoc);
+        const allTemplates = iconViews
+            .concat(templBtns)
+            .concat(noteTypes)
+            .concat(clickFuncs)
+            .map(btnDoc => (btnDoc.dragFactory as Doc) || btnDoc)
+            .filter(doc => doc.isTemplateDoc);
         // bcz: this is hacky -- want to have different templates be applied depending on the "type" of a document.  but type is not reliable and there could be other types of template searches so this should be generalized
         // first try to find a template that matches the specific document type (<typeName>_<templateName>).  otherwise, fallback to a general match on <templateName>
-        !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName + "_" + type && (docLayoutTemplate = tempDoc));
+        !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName + '_' + type && (docLayoutTemplate = tempDoc));
         !docLayoutTemplate && allTemplates.forEach(tempDoc => StrCast(tempDoc.title) === templateName && (docLayoutTemplate = tempDoc));
         return docLayoutTemplate;
     }
-    export function createCustomView(doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = "custom", docLayoutTemplate?: Doc) {
-        const templateName = templateSignature.replace(/\(.*\)/, "");
-        docLayoutTemplate = docLayoutTemplate || findTemplate(templateName, StrCast(doc._isGroup && doc.transcription ? "transcription" : doc.type), templateSignature);
+    export function createCustomView(doc: Doc, creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, templateSignature: string = 'custom', docLayoutTemplate?: Doc) {
+        const templateName = templateSignature.replace(/\(.*\)/, '');
+        docLayoutTemplate = docLayoutTemplate || findTemplate(templateName, StrCast(doc._isGroup && doc.transcription ? 'transcription' : doc.type), templateSignature);
 
-        const customName = "layout_" + templateSignature;
+        const customName = 'layout_' + templateSignature;
         const _width = NumCast(doc._width);
         const _height = NumCast(doc._height);
-        const options = { title: "data", backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: - _height / 2, _showSidebar: false };
+        const options = { title: 'data', backgroundColor: StrCast(doc.backgroundColor), _autoHeight: true, _width, x: -_width / 2, y: -_height / 2, _showSidebar: false };
 
         if (docLayoutTemplate) {
-            if (docLayoutTemplate !== doc[customName]) { 
+            if (docLayoutTemplate !== doc[customName]) {
                 Doc.ApplyTemplateTo(docLayoutTemplate, doc, customName, undefined);
             }
         } else {
             let fieldTemplate: Opt<Doc>;
-            if (doc.data instanceof RichTextField || typeof (doc.data) === "string") {
-                fieldTemplate = Docs.Create.TextDocument("", options);
+            if (doc.data instanceof RichTextField || typeof doc.data === 'string') {
+                fieldTemplate = Docs.Create.TextDocument('', options);
             } else if (doc.data instanceof PdfField) {
-                fieldTemplate = Docs.Create.PdfDocument("http://www.msn.com", options);
+                fieldTemplate = Docs.Create.PdfDocument('http://www.msn.com', options);
             } else if (doc.data instanceof VideoField) {
-                fieldTemplate = Docs.Create.VideoDocument("http://www.cs.brown.edu", options);
+                fieldTemplate = Docs.Create.VideoDocument('http://www.cs.brown.edu', options);
             } else if (doc.data instanceof AudioField) {
-                fieldTemplate = Docs.Create.AudioDocument("http://www.cs.brown.edu", options);
+                fieldTemplate = Docs.Create.AudioDocument('http://www.cs.brown.edu', options);
             } else if (doc.data instanceof ImageField) {
-                fieldTemplate = Docs.Create.ImageDocument("http://www.cs.brown.edu", options);
+                fieldTemplate = Docs.Create.ImageDocument('http://www.cs.brown.edu', options);
             }
-            const docTemplate = creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + "(" + doc.title + ")", isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) });
+            const docTemplate = creator?.(fieldTemplate ? [fieldTemplate] : [], { title: customName + '(' + doc.title + ')', isTemplateDoc: true, _width: _width + 20, _height: Math.max(100, _height + 45) });
             fieldTemplate && Doc.MakeMetadataFieldTemplate(fieldTemplate, docTemplate ? Doc.GetProto(docTemplate) : docTemplate);
             docTemplate && Doc.ApplyTemplateTo(docTemplate, doc, customName, undefined);
         }
@@ -1350,13 +1599,14 @@ export namespace DocUtils {
         }
     }
     export function iconify(doc: Doc) {
-        const layoutKey = Cast(doc.layoutKey, "string", null);
-        DocUtils.makeCustomViewClicked(doc, Docs.Create.StackingDocument, "icon", undefined);
-        if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") doc.deiconifyLayout = layoutKey.replace("layout_", "");
+        const layoutKey = Cast(doc.layoutKey, 'string', null);
+        DocUtils.makeCustomViewClicked(doc, Docs.Create.StackingDocument, 'icon', undefined);
+        if (layoutKey && layoutKey !== 'layout' && layoutKey !== 'layout_icon') doc.deiconifyLayout = layoutKey.replace('layout_', '');
     }
 
     export function pileup(docList: Doc[], x?: number, y?: number, size: number = 55, create: boolean = true) {
-        let w = 0, h = 0;
+        let w = 0,
+            h = 0;
         runInAction(() => {
             docList.forEach(d => {
                 DocUtils.iconify(d);
@@ -1364,19 +1614,23 @@ export namespace DocUtils {
                 h = Math.max(NumCast(d._height), h);
             });
             docList.forEach((d, i) => {
-                d.x = Math.cos(Math.PI * 2 * i / docList.length) * size;
-                d.y = Math.sin(Math.PI * 2 * i / docList.length) * size;
-                d._timecodeToShow = undefined;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+                d.x = Math.cos((Math.PI * 2 * i) / docList.length) * size;
+                d.y = Math.sin((Math.PI * 2 * i) / docList.length) * size;
+                d._timecodeToShow = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
             });
-            const aggBounds = aggregateBounds(docList.map(d => ({ x: NumCast(d.x), y: NumCast(d.y), width: NumCast(d._width), height: NumCast(d._height) })), 0, 0);
+            const aggBounds = aggregateBounds(
+                docList.map(d => ({ x: NumCast(d.x), y: NumCast(d.y), width: NumCast(d._width), height: NumCast(d._height) })),
+                0,
+                0
+            );
             docList.forEach((d, i) => {
-                d.x = NumCast(d.x) - ((aggBounds.r + aggBounds.x) / 2);
-                d.y = NumCast(d.y) - ((aggBounds.b + aggBounds.y) / 2);
-                d._timecodeToShow = undefined;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+                d.x = NumCast(d.x) - (aggBounds.r + aggBounds.x) / 2;
+                d.y = NumCast(d.y) - (aggBounds.b + aggBounds.y) / 2;
+                d._timecodeToShow = undefined; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
             });
         });
         if (create) {
-            const newCollection = Docs.Create.PileDocument(docList, { title: "pileup", x: (x || 0) - size, y: (y || 0) - size, _width: size * 2, _height: size * 2, });
+            const newCollection = Docs.Create.PileDocument(docList, { title: 'pileup', x: (x || 0) - size, y: (y || 0) - size, _width: size * 2, _height: size * 2 });
             newCollection.x = NumCast(newCollection.x) + NumCast(newCollection._width) / 2 - size;
             newCollection.y = NumCast(newCollection.y) + NumCast(newCollection._height) / 2 - size;
             newCollection._width = newCollection._height = size * 2;
@@ -1388,18 +1642,25 @@ export namespace DocUtils {
     export function LeavePushpin(doc: Doc, annotationField: string) {
         if (doc.isPushpin) return undefined;
         const context = Cast(doc.context, Doc, null) ?? Cast(doc.annotationOn, Doc, null);
-        const hasContextAnchor = DocListCast(doc.links).
-            some(l =>
-                (l.anchor2 === doc && Cast(l.anchor1, Doc, null)?.annotationOn === context) ||
-                (l.anchor1 === doc && Cast(l.anchor2, Doc, null)?.annotationOn === context));
+        const hasContextAnchor = DocListCast(doc.links).some(l => (l.anchor2 === doc && Cast(l.anchor1, Doc, null)?.annotationOn === context) || (l.anchor1 === doc && Cast(l.anchor2, Doc, null)?.annotationOn === context));
         if (context && !hasContextAnchor && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) {
             const pushpin = Docs.Create.FontIconDocument({
-                title: "pushpin", label: "", annotationOn: Cast(doc.annotationOn, Doc, null), isPushpin: true,
-                icon: "map-pin", x: Cast(doc.x, "number", null), y: Cast(doc.y, "number", null), backgroundColor: "#ACCEF7",
-                _width: 15, _height: 15, _xPadding: 0, _isLinkButton: true, _timecodeToShow: Cast(doc._timecodeToShow, "number", null)
+                title: 'pushpin',
+                label: '',
+                annotationOn: Cast(doc.annotationOn, Doc, null),
+                isPushpin: true,
+                icon: 'map-pin',
+                x: Cast(doc.x, 'number', null),
+                y: Cast(doc.y, 'number', null),
+                backgroundColor: '#ACCEF7',
+                _width: 15,
+                _height: 15,
+                _xPadding: 0,
+                _isLinkButton: true,
+                _timecodeToShow: Cast(doc._timecodeToShow, 'number', null),
             });
             Doc.AddDocToList(context, annotationField, pushpin);
-            const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, "pushpin", "");
+            const pushpinLink = DocUtils.MakeLink({ doc: pushpin }, { doc: doc }, 'pushpin', '');
             doc._timecodeToShow = undefined;
             return pushpin;
         }
@@ -1407,7 +1668,7 @@ export namespace DocUtils {
     }
 
     // /**
-    //  * 
+    //  *
     //  * @param dms Degree Minute Second format exif gps data
     //  * @param ref ref that determines negativity of decimal coordinates
     //  * @returns a decimal format of gps latitude / longitude
@@ -1427,7 +1688,7 @@ export namespace DocUtils {
 
     function ConvertDMSToDD(degrees: number, minutes: number, seconds: number, direction: string) {
         var dd = degrees + minutes / 60 + seconds / (60 * 60);
-        if (direction === "S" || direction === "W") {
+        if (direction === 'S' || direction === 'W') {
             dd = dd * -1;
         } // Don't do anything for N or E
         return dd;
@@ -1444,15 +1705,18 @@ export namespace DocUtils {
         if (doc) {
             const proto = Doc.GetProto(doc);
             proto.text = result.rawText;
-            proto.fileUpload = pathname.replace(/.*\//, "").replace("upload_", "").replace(/\.[a-z0-9]*$/, "");
+            proto.fileUpload = pathname
+                .replace(/.*\//, '')
+                .replace('upload_', '')
+                .replace(/\.[a-z0-9]*$/, '');
             if (Upload.isImageInformation(result)) {
                 const maxNativeDim = Math.min(Math.max(result.nativeHeight, result.nativeWidth), defaultNativeImageDim);
-                proto["data-nativeOrientation"] = result.exifData?.data?.image?.Orientation ?? ((StrCast((result.exifData?.data as any)?.Orientation).includes("Rotate 90")) ? 5 : undefined);
-                proto["data-nativeWidth"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim * result.nativeWidth / result.nativeHeight : maxNativeDim;
-                proto["data-nativeHeight"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
-                if (NumCast(proto["data-nativeOrientation"]) >= 5) {
-                    proto["data-nativeHeight"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim * result.nativeWidth / result.nativeHeight : maxNativeDim;
-                    proto["data-nativeWidth"] = (result.nativeWidth < result.nativeHeight) ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
+                proto['data-nativeOrientation'] = result.exifData?.data?.image?.Orientation ?? (StrCast((result.exifData?.data as any)?.Orientation).includes('Rotate 90') ? 5 : undefined);
+                proto['data-nativeWidth'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
+                proto['data-nativeHeight'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
+                if (NumCast(proto['data-nativeOrientation']) >= 5) {
+                    proto['data-nativeHeight'] = result.nativeWidth < result.nativeHeight ? (maxNativeDim * result.nativeWidth) / result.nativeHeight : maxNativeDim;
+                    proto['data-nativeWidth'] = result.nativeWidth < result.nativeHeight ? maxNativeDim : maxNativeDim / (result.nativeWidth / result.nativeHeight);
                 }
                 proto.contentSize = result.contentSize;
                 // exif gps data coordinates are stored in DMS (Degrees Minutes Seconds), the following operation converts that to decimal coordinates
@@ -1464,15 +1728,41 @@ export namespace DocUtils {
                     proto.lat = ConvertDMSToDD(latitude[0], latitude[1], latitude[2], latitudeDirection);
                     proto.lng = ConvertDMSToDD(longitude[0], longitude[1], longitude[2], longitudeDirection);
                 }
-
             }
             generatedDocuments.push(doc);
         }
     }
 
+    export function GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) {
+        const tbox = Docs.Create.TextDocument('', {
+            _xMargin: noMargins ? 0 : undefined,
+            _yMargin: noMargins ? 0 : undefined,
+            annotationOn,
+            docMaxAutoHeight: maxHeight,
+            backgroundColor: backgroundColor,
+            _width: width || 200,
+            _height: 35,
+            x: x,
+            y: y,
+            _fitWidth: true,
+            _autoHeight: true,
+            title,
+        });
+        const template = Doc.UserDoc().defaultTextLayout;
+        if (template instanceof Doc) {
+            tbox._width = NumCast(template._width);
+            tbox.layoutKey = 'layout_' + StrCast(template.title);
+            Doc.GetProto(tbox)[StrCast(tbox.layoutKey)] = template;
+        }
+        return tbox;
+    }
+
     export async function uploadYoutubeVideo(videoId: string, options: DocumentOptions) {
         const generatedDocuments: Doc[] = [];
-        for (const { source: { name, type }, result } of await Networking.UploadYoutubeToServer(videoId)) {
+        for (const {
+            source: { name, type },
+            result,
+        } of await Networking.UploadYoutubeToServer(videoId)) {
             name && type && processFileupload(generatedDocuments, name, type, result, options);
         }
         return generatedDocuments;
@@ -1480,7 +1770,10 @@ export namespace DocUtils {
     export async function uploadFilesToDocs(files: File[], options: DocumentOptions) {
         const generatedDocuments: Doc[] = [];
         const upfiles = await Networking.UploadFilesToServer(files);
-        for (const { source: { name, type }, result } of upfiles) {
+        for (const {
+            source: { name, type },
+            result,
+        } of upfiles) {
             name && type && processFileupload(generatedDocuments, name, type, result, options);
         }
         return generatedDocuments;
@@ -1490,36 +1783,43 @@ export namespace DocUtils {
     export function copyDragFactory(dragFactory: Doc) {
         if (!dragFactory) return undefined;
         const ndoc = dragFactory.isTemplateDoc ? Doc.ApplyTemplate(dragFactory) : Doc.MakeCopy(dragFactory, true);
-        ndoc && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, "data", Doc.GetProto(ndoc));
-        if (ndoc && dragFactory["dragFactory-count"] !== undefined) {
-            dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1;
-            Doc.SetInPlace(ndoc, "title", ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString(), true);
+        ndoc && Doc.AddDocToList(Doc.MyFileOrphans, 'data', Doc.GetProto(ndoc));
+        if (ndoc && dragFactory['dragFactory-count'] !== undefined) {
+            dragFactory['dragFactory-count'] = NumCast(dragFactory['dragFactory-count']) + 1;
+            Doc.SetInPlace(ndoc, 'title', ndoc.title + ' ' + NumCast(dragFactory['dragFactory-count']).toString(), true);
         }
 
-        if (ndoc && CurrentUserUtils.ActiveDashboard) inheritParentAcls(CurrentUserUtils.ActiveDashboard, ndoc);
+        if (ndoc && Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, ndoc);
 
         return ndoc;
     }
     export function delegateDragFactory(dragFactory: Doc) {
         const ndoc = Doc.MakeDelegateWithProto(dragFactory);
-        if (ndoc && dragFactory["dragFactory-count"] !== undefined) {
-            dragFactory["dragFactory-count"] = NumCast(dragFactory["dragFactory-count"]) + 1;
-            Doc.GetProto(ndoc).title = ndoc.title + " " + NumCast(dragFactory["dragFactory-count"]).toString();
+        if (ndoc && dragFactory['dragFactory-count'] !== undefined) {
+            dragFactory['dragFactory-count'] = NumCast(dragFactory['dragFactory-count']) + 1;
+            Doc.GetProto(ndoc).title = ndoc.title + ' ' + NumCast(dragFactory['dragFactory-count']).toString();
         }
         return ndoc;
     }
 }
 
-ScriptingGlobals.add("Docs", Docs);
-ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) { return DocUtils.copyDragFactory(dragFactory); });
-ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) { return DocUtils.delegateDragFactory(dragFactory); });
-ScriptingGlobals.add(function makeDelegate(proto: any) { const d = Docs.Create.DelegateDocument(proto, { title: "child of " + proto.title }); return d; });
+ScriptingGlobals.add('Docs', Docs);
+ScriptingGlobals.add(function copyDragFactory(dragFactory: Doc) {
+    return DocUtils.copyDragFactory(dragFactory);
+});
+ScriptingGlobals.add(function delegateDragFactory(dragFactory: Doc) {
+    return DocUtils.delegateDragFactory(dragFactory);
+});
+ScriptingGlobals.add(function makeDelegate(proto: any) {
+    const d = Docs.Create.DelegateDocument(proto, { title: 'child of ' + proto.title });
+    return d;
+});
 ScriptingGlobals.add(function generateLinkTitle(self: Doc) {
-    const anchor1title = self.anchor1 && self.anchor1 !== self ? Cast(self.anchor1, Doc, null).title : "<?>";
-    const anchor2title = self.anchor2 && self.anchor2 !== self ? Cast(self.anchor2, Doc, null).title : "<?>";
-    const relation = self.linkRelationship || "to";
+    const anchor1title = self.anchor1 && self.anchor1 !== self ? Cast(self.anchor1, Doc, null).title : '<?>';
+    const anchor2title = self.anchor2 && self.anchor2 !== self ? Cast(self.anchor2, Doc, null).title : '<?>';
+    const relation = self.linkRelationship || 'to';
     return `${anchor1title} (${relation}) ${anchor2title}`;
 });
 ScriptingGlobals.add(function openTabAlias(tab: Doc) {
-    CollectionDockingView.AddSplit(Doc.MakeAlias(tab), "right");
-});
\ No newline at end of file
+    CollectionDockingView.AddSplit(Doc.MakeAlias(tab), 'right');
+});
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 84efcb966..02d43088d 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -2,7 +2,6 @@ import { computed, observable, reaction } from "mobx";
 import * as rp from 'request-promise';
 import { DataSym, Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc";
 import { Id } from "../../fields/FieldSymbols";
-import { InkTool } from "../../fields/InkField";
 import { List } from "../../fields/List";
 import { PrefetchProxy } from "../../fields/Proxy";
 import { RichTextField } from "../../fields/RichTextField";
@@ -14,11 +13,12 @@ import { SharingPermissions } from "../../fields/util";
 import { OmitKeys, Utils } from "../../Utils";
 import { DocServer } from "../DocServer";
 import { Docs, DocumentOptions, DocUtils, FInfo } from "../documents/Documents";
-import { DocumentType } from "../documents/DocumentTypes";
+import { CollectionViewType, DocumentType } from "../documents/DocumentTypes";
 import { CollectionDockingView } from "../views/collections/CollectionDockingView";
 import { TreeViewType } from "../views/collections/CollectionTreeView";
-import { CollectionView, CollectionViewType } from "../views/collections/CollectionView";
+import { CollectionView } from "../views/collections/CollectionView";
 import { TreeView } from "../views/collections/TreeView";
+import { DashboardView } from "../views/DashboardView";
 import { Colors } from "../views/global/globalEnums";
 import { MainView } from "../views/MainView";
 import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox";
@@ -57,59 +57,6 @@ interface Button {
 
 export let resolvedPorts: { server: number, socket: number };
 export class CurrentUserUtils {
-    private static curr_id: string;
-    //TODO tfs: these should be temporary...
-    private static mainDocId: string | undefined;
-
-    public static get id() { return this.curr_id; }
-    public static get MainDocId() { return this.mainDocId; }
-    public static set MainDocId(id: string | undefined) { this.mainDocId = id; }
-    @computed public static get UserDocument() { return Doc.UserDoc(); }
-
-    @observable public static GuestTarget: Doc | undefined;
-    @observable public static GuestDashboard: Doc | undefined;
-    @observable public static GuestMobile: Doc | undefined;
-    @observable public static propertiesWidth: number = 0;
-    @observable public static headerBarHeight: number = 0;
-    @observable public static searchPanelWidth: number = 0;
-
-    static AssignScripts(doc:Doc, scripts?:{ [key: string]: string;}, funcs?:{[key:string]: string}) {
-        scripts && Object.keys(scripts).map(key => {
-            if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && scripts[key]) {
-                doc[key] = ScriptField.MakeScript(scripts[key], { dragData: DragManager.DocumentDragData.name, value:"any", scriptContext: "any", documentView:Doc.name}, {"_readOnly_": true});
-            }
-        }); 
-        funcs && Object.keys(funcs).map(key => {
-            const cfield = ComputedField.WithoutComputed(() => FieldValue(doc[key]));
-            if (ScriptCast(cfield)?.script.originalScript !== funcs[key] && funcs[key]) {
-                doc[key] = ComputedField.MakeFunction(funcs[key], { dragData: DragManager.DocumentDragData.name }, {"_readOnly_": true});
-            }
-        });
-        return doc;
-    }
-    static AssignOpts(doc:Doc|undefined, reqdOpts:DocumentOptions, items?:Doc[]) {
-        if (doc) {
-            const compareValues = (val1:any, val2:any) => {
-                if (val1 instanceof List && val2 instanceof List && val1.length === val2.length) {
-                    return !val1.some(v => !val2.includes(v)) || !val2.some(v => val1.includes(v));
-                }
-                return val1 === val2;
-            }
-            Object.entries(reqdOpts).forEach(pair => {
-                const targetDoc = pair[0].startsWith("_") ? doc : Doc.GetProto(doc as Doc);
-                if (!Object.getOwnPropertyNames(targetDoc).includes(pair[0].replace(/^_/,"")) ||
-                    !compareValues(pair[1], targetDoc[pair[0]])) {
-                    targetDoc[pair[0]] = pair[1];
-                }
-            });
-            items?.forEach(item => !DocListCast(doc.data).includes(item) && Doc.AddDocToList(Doc.GetProto(doc), "data", item));
-            items && DocListCast(doc.data).forEach(item => !items.includes(item) && Doc.RemoveDocFromList(Doc.GetProto(doc), "data", item));
-        }
-        return doc;
-    }
-    static AssignDocField(doc:Doc, field:string, creator:(reqdOpts:DocumentOptions, items?:Doc[]) => Doc, reqdOpts:DocumentOptions, items?: Doc[], scripts?:{[key:string]:string}, funcs?:{[key:string]:string}) {
-        return this.AssignScripts(this.AssignOpts(DocCast(doc[field]), reqdOpts, items) ?? (doc[field] = creator(reqdOpts, items)), scripts, funcs);
-    }
 
     // initializes experimental advanced template views - slideView,  headerView
     static setupExperimentalTemplateButtons(doc: Doc, tempDocs?:Doc) {
@@ -139,12 +86,12 @@ export class CurrentUserUtils {
             const reqdScripts = { onDragStart: '{ return copyDragFactory(this.dragFactory); }' };
             const assignBtnAndTempOpts = (templateBtn:Opt<Doc>, btnOpts:DocumentOptions, templateOptions:DocumentOptions) => {
                 if (templateBtn) {
-                    this.AssignOpts(templateBtn,btnOpts);
-                    this.AssignDocField(templateBtn, "dragFactory", opts => template(opts), templateOptions);
+                    DocUtils.AssignOpts(templateBtn,btnOpts);
+                    DocUtils.AssignDocField(templateBtn, "dragFactory", opts => template(opts), templateOptions);
                 }
                 return templateBtn;
             };
-            return this.AssignScripts(assignBtnAndTempOpts(tempBtn, btnOpts, templateOpts) ?? this.createToolButton( {...btnOpts, dragFactory: MakeTemplate(template(templateOpts))}), reqdScripts);
+            return DocUtils.AssignScripts(assignBtnAndTempOpts(tempBtn, btnOpts, templateOpts) ?? this.createToolButton( {...btnOpts, dragFactory: MakeTemplate(template(templateOpts))}), reqdScripts);
         });
 
         const reqdOpts:DocumentOptions = {
@@ -154,7 +101,7 @@ export class CurrentUserUtils {
         };
         const reqdScripts = { dropConverter : "convertToButtons(dragData)" };
         const reqdFuncs = { hidden: "IsNoviceMode()" };
-        return this.AssignScripts(this.AssignOpts(tempDocs, reqdOpts, requiredTypes) ?? Docs.Create.MasonryDocument(requiredTypes, reqdOpts), reqdScripts, reqdFuncs);
+        return DocUtils.AssignScripts(DocUtils.AssignOpts(tempDocs, reqdOpts, requiredTypes) ?? Docs.Create.MasonryDocument(requiredTypes, reqdOpts), reqdScripts, reqdFuncs);
     }
 
     /// Initializes templates that can be applied to notes
@@ -167,16 +114,16 @@ export class CurrentUserUtils {
         const reqdNoteList = reqdTempOpts.map(opts => {
             const reqdOpts = {...opts, title: "text", system: true};
             const noteType = tempNotes ? DocListCast(tempNotes.data).find(doc => doc.noteType === opts.noteType): undefined;
-            return this.AssignOpts(noteType, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts), true, opts.noteType??"Note");
+            return DocUtils.AssignOpts(noteType, reqdOpts) ?? MakeTemplate(Docs.Create.TextDocument("",reqdOpts), true, opts.noteType??"Note");
         });
 
         const reqdOpts:DocumentOptions = { title: "Note Layouts", _height: 75, system: true };
-        return this.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts));
+        return DocUtils.AssignOpts(tempNotes, reqdOpts, reqdNoteList) ?? (doc[field] = Docs.Create.TreeDocument(reqdNoteList, reqdOpts));
     }
 
     /// Initializes collection of templates for notes and click functions
     static setupDocTemplates(doc: Doc, field="myTemplates") {
-        this.AssignDocField(doc, "presElement", opts => Docs.Create.PresElementBoxDocument(opts), { title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"});
+        DocUtils.AssignDocField(doc, "presElement", opts => Docs.Create.PresElementBoxDocument(opts), { title: "pres element template", type: DocumentType.PRESELEMENT, _fitWidth: true, _xMargin: 0, isTemplateDoc: true, isTemplateForField: "data"});
         const templates = [
             DocCast(doc.presElement),
             CurrentUserUtils.setupNoteTemplates(doc),
@@ -184,13 +131,13 @@ export class CurrentUserUtils {
         ];
         const reqdOpts = { title: "template layouts", _xMargin: 0, system: true,  };
         const reqdScripts = {  dropConverter: "convertToButtons(dragData)"  };
-        return this.AssignDocField(doc, field, (opts,items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, templates, reqdScripts);
+        return DocUtils.AssignDocField(doc, field, (opts,items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, templates, reqdScripts);
     }
 
     // setup templates for different document types when they are iconified from Document Decorations
     static setupDefaultIconTemplates(doc: Doc, field="template-icons") {
         const reqdOpts = { title: "icon templates", _height: 75, system: true };
-        const templateIconsDoc = this.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
+        const templateIconsDoc = DocUtils.AssignOpts(DocCast(doc[field]), reqdOpts) ?? (doc[field] = Docs.Create.TreeDocument([], reqdOpts));
 
         const makeIconTemplate = (type: DocumentType | undefined, templateField: string, opts:DocumentOptions) => {
             const iconFieldName = "icon" + (type ? "_" + type : "");
@@ -201,8 +148,8 @@ export class CurrentUserUtils {
                 case DocumentType.FONTICON: creator = fontBox; break;
             }
             const allopts = {system: true, ...opts};
-            return this.AssignScripts(  (curIcon?.iconTemplate === opts.iconTemplate ? 
-                      this.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts), true, iconFieldName, templateField))),
+            return DocUtils.AssignScripts(  (curIcon?.iconTemplate === opts.iconTemplate ? 
+                      DocUtils.AssignOpts(curIcon, allopts):undefined) ?? ((templateIconsDoc[iconFieldName] = MakeTemplate(creator(allopts), true, iconFieldName, templateField))),
                 {onClick:"deiconifyView(documentView)"});
         };
         const labelBox = (opts: DocumentOptions, data?:string) => Docs.Create.LabelDocument({
@@ -224,7 +171,7 @@ export class CurrentUserUtils {
             makeIconTemplate("transcription" as any, "transcription", { iconTemplate:DocumentType.LABEL, backgroundColor: "orange" }),
             //makeIconTemplate(DocumentType.PDF, "icon", {iconTemplate:DocumentType.IMG}, (opts) => imageBox("http://www.cs.brown.edu/~bcz/noImage.png", opts))
         ].filter(d => d).map(d => d!);
-        this.AssignOpts(DocCast(doc[field]), {}, iconTemplates);
+        DocUtils.AssignOpts(DocCast(doc[field]), {}, iconTemplates);
     }
 
     /// initalizes the set of "empty<DocType>" versions of each document type with default fields.  e.g.,. emptyNote, emptyPresentation
@@ -297,7 +244,7 @@ export class CurrentUserUtils {
                                                                                             }, funcs: {title: 'self.text?.Text'}},
             ];
 
-        emptyThings.forEach(thing => this.AssignDocField(doc, "empty"+thing.key, (opts) => thing.creator(opts), {...standardOps(thing.key), ...thing.opts}, undefined, undefined, thing.funcs));
+        emptyThings.forEach(thing => DocUtils.AssignDocField(doc, "empty"+thing.key, (opts) => thing.creator(opts), {...standardOps(thing.key), ...thing.opts}, undefined, undefined, thing.funcs));
         
         return [
             { toolTip: "Tap or drag to create a note",            title: "Note", icon: "sticky-note",   dragFactory: doc.emptyNote as Doc, },
@@ -326,7 +273,7 @@ export class CurrentUserUtils {
                 btnType: ButtonType.ToolButton, backgroundColor: reqdOpts.backgroundColor ?? Colors.DARK_GRAY, color: Colors.WHITE, system: true,
                 _removeDropProperties: new List<string>(["_stayInCollection"]),
                 };
-            return this.AssignScripts(this.AssignOpts(btn, opts) ?? Docs.Create.FontIconDocument(opts), reqdOpts.scripts, reqdOpts.funcs);
+            return DocUtils.AssignScripts(DocUtils.AssignOpts(btn, opts) ?? Docs.Create.FontIconDocument(opts), reqdOpts.scripts, reqdOpts.funcs);
         });
 
         const reqdOpts:DocumentOptions = {
@@ -335,7 +282,7 @@ export class CurrentUserUtils {
             childDocumentsActive: true
         };
         const reqdScripts = { dropConverter: "convertToButtons(dragData)" };
-        return this.AssignScripts(this.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts);
+        return DocUtils.AssignScripts(DocUtils.AssignOpts(dragCreatorDoc, reqdOpts, creatorBtns) ?? Docs.Create.MasonryDocument(creatorBtns, reqdOpts), reqdScripts);
     }
 
     /// returns descriptions needed to buttons for the left sidebar to open up panes displaying different collections of documents
@@ -348,7 +295,7 @@ export class CurrentUserUtils {
             { title: "Tools",           target: this.setupToolsBtnPanel(doc, "myTools"),           icon: "wrench",      funcs: {hidden: "IsNoviceMode()"} },
             { title: "Imports",         target: this.setupImportSidebar(doc, "myImports"),         icon: "upload",      },
             { title: "Recently Closed", target: this.setupRecentlyClosed(doc, "myRecentlyClosed"), icon: "archive",     },
-            { title: "Shared Docs",     target: this.MySharedDocs,                                 icon: "users",       funcs:{badgeValue:badgeValue}},
+            { title: "Shared Docs",     target: Doc.MySharedDocs,                                  icon: "users",       funcs:{badgeValue:badgeValue}},
             { title: "Trails",          target: this.setupTrails(doc, "myTrails"),                 icon: "pres-trail",  },
             { title: "User Doc View",   target: this.setupUserDocView(doc, "myUserDocView"),       icon: "address-card",funcs: {hidden: "IsNoviceMode()"} },
         ].map(tuple => ({...tuple, scripts:{onClick: 'selectMainMenu(self)'}}));
@@ -356,7 +303,7 @@ export class CurrentUserUtils {
 
     /// the empty panel that is filled with whichever left menu button's panel has been selected
     static setupLeftSidebarPanel(doc: Doc, field="myLeftSidebarPanel") {
-        this.AssignDocField(doc, field, (opts) => ((doc:Doc) => {doc.system = true; return doc;})(new Doc()), {system:true});
+        DocUtils.AssignDocField(doc, field, (opts) => ((doc:Doc) => {doc.system = true; return doc;})(new Doc()), {system:true});
     }
 
     /// Initializes the left sidebar menu buttons and the panels they open up
@@ -370,20 +317,20 @@ export class CurrentUserUtils {
                 _width: 60, _height: 60,  _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, _dropAction: "alias",
                 _removeDropProperties: new List<string>(["dropAction", "_stayInCollection"]),
             };
-            return this.AssignScripts(this.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs);
+            return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), scripts, funcs);
         });
 
         const reqdStackOpts:DocumentOptions ={
             title: "menuItemPanel", childDropAction: "alias", backgroundColor: Colors.DARK_GRAY, boxShadow: "rgba(0,0,0,0)",  dontRegisterView: true, ignoreClick: true,
             _chromeHidden: true, _gridGap: 0, _yMargin: 0, _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true
         };
-        return this.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdStackOpts, menuBtns, { dropConverter: "convertToButtons(dragData)" });
+        return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdStackOpts, menuBtns, { dropConverter: "convertToButtons(dragData)" });
     }
 
     // Sets up mobile menu if it is undefined creates a new one, otherwise returns existing menu
     static setupActiveMobileMenu(doc: Doc, field="activeMobileMenu") {
         const reqdOpts = { _width: 980, ignoreClick: true, _lockedPosition: false, title: "home", _yMargin: 100, system: true, _chromeHidden: true,};
-        this.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(this.setupMobileButtons(), opts), reqdOpts);
+        DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(this.setupMobileButtons(), opts), reqdOpts);
     }
 
     // Sets up mobile buttons for inside mobile menu
@@ -436,42 +383,6 @@ export class CurrentUserUtils {
     }) as any as Doc
 
 
-    static setupThumbButtons(doc: Doc) {
-        const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, pointerDown?: string, pointerUp?: string, clipboard?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [
-            { title: "use pen", icon: "pen-nib", pointerUp: "resetPen()", pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: "blue" },
-            { title: "use highlighter", icon: "highlighter", pointerUp: "resetPen()", pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: "yellow" },
-            { title: "notepad", icon: "clipboard", pointerUp: "GestureOverlay.Instance.closeFloatingDoc()", pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)', clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300, system: true }), backgroundColor: "orange" },
-            { title: "interpret text", icon: "font", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('inktotext')", backgroundColor: "orange" },
-            { title: "ignore gestures", icon: "signature", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('ignoregesture')", backgroundColor: "green" },
-        ];
-        return docProtoData.map(data => Docs.Create.FontIconDocument({
-            _nativeWidth: 10, _nativeHeight: 10, _width: 10, _height: 10, title: data.title, icon: data.icon,
-            _dropAction: data.pointerDown ? "copy" : undefined,
-            ignoreClick: data.ignoreClick,
-            onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined,
-            clipboard: data.clipboard,
-            onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined,
-            onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined,
-            backgroundColor: data.backgroundColor,
-            _removeDropProperties: new List<string>(["dropAction"]),
-            dragFactory: data.dragFactory,
-            system: true
-        }));
-    }
-
-    static setupThumbDoc(userDoc: Doc) {
-        if (!userDoc.thumbDoc) {
-            const thumbDoc = Docs.Create.LinearDocument(CurrentUserUtils.setupThumbButtons(userDoc), {
-                _width: 100, _height: 50, ignoreClick: true, _lockedPosition: true, title: "buttons",
-                _autoHeight: true, _yMargin: 5, linearViewIsExpanded: true, backgroundColor: "white", system: true
-            });
-            thumbDoc.inkToTextDoc = Docs.Create.LinearDocument([], {
-                _width: 300, _height: 25, _autoHeight: true, linearViewIsExpanded: true, flexDirection: "column", system: true
-            });
-            userDoc.thumbDoc = thumbDoc;
-        }
-        return Cast(userDoc.thumbDoc, Doc);
-    }
 
     static setupMobileInkingDoc(userDoc: Doc) {
         return Docs.Create.FreeformDocument([], { title: "Mobile Inking", backgroundColor: "white", system: true });
@@ -492,7 +403,7 @@ export class CurrentUserUtils {
 
     /// Search option on the left side button panel
     static setupSearcher(doc: Doc, field:string) {
-        return this.AssignDocField(doc, field, (opts, items) => Docs.Create.SearchDocument(opts), {
+        return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.SearchDocument(opts), {
                 dontRegisterView: true, backgroundColor: "dimgray", ignoreClick: true, title: "Search Panel", system: true, childDropAction: "alias", 
                 _lockedPosition: true, _viewType: CollectionViewType.Schema, _searchDoc: true,  });
     }
@@ -506,7 +417,7 @@ export class CurrentUserUtils {
             title: "My Tools", system: true, ignoreClick: true, boxShadow: "0 0",
             _showTitle: "title", _width: 500, _yMargin: 20, _lockedPosition: true, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, 
         };
-        return this.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, templateBtns]);
+        return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.StackingDocument(items??[], opts), reqdToolOps, [creatorBtns, templateBtns]);
     }
 
     /// initializes the left sidebar dashboard pane
@@ -517,7 +428,7 @@ export class CurrentUserUtils {
         const reqdBtnOpts:DocumentOptions = { _forceActive: true,  _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true,
              title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", system: true };
         const reqdBtnScript = {onClick: newDashboard,}
-        const newDashboardButton = this.AssignScripts(this.AssignOpts(DocCast(myDashboards?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
+        const newDashboardButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myDashboards?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
        
         const reqdOpts:DocumentOptions = {
             title: "My Dashboards", childHideLinkButton: true, freezeChildren: "remove|add", treeViewHideTitle: true, boxShadow: "0 0", childDontRegisterViews: true,
@@ -530,7 +441,7 @@ export class CurrentUserUtils {
             childContextMenuIcons: new List<string>(["chalkboard", "tv", "camera", "users", "times"]), // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters
             explainer: "This is your collection of dashboards. A dashboard represents the tab configuration of your workspace. To manage documents as folders, go to the Files."
         };
-        myDashboards = this.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts);
+        myDashboards = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts);
         const toggleDarkTheme = `this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`;
         const contextMenuScripts = [newDashboard];
         const childContextMenuScripts = [toggleDarkTheme, `toggleComicMode()`, `snapshotDashboard()`, `shareDashboard(self)`, 'removeDashboard(self)']; // entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuFilters
@@ -553,7 +464,7 @@ export class CurrentUserUtils {
         const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30,  _stayInCollection: true, _hideContextMenu: true, 
             title: "New trail",  toolTip: "Create new trail", btnType: ButtonType.ClickButton, buttonText: "New trail", icon: "plus", system: true };
         const reqdBtnScript = {onClick: `createNewPresentation()`};
-        const newTrailButton = this.AssignScripts(this.AssignOpts(DocCast(myTrails?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
+        const newTrailButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myTrails?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript);
         
         const reqdOpts:DocumentOptions = {
             title: "My Trails", _showTitle: "title", _height: 100,
@@ -564,7 +475,7 @@ export class CurrentUserUtils {
             _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true,
             explainer: "All of the trails that you have created will appear here."
         };
-        myTrails = this.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts);
+        myTrails = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts);
         const contextMenuScripts = [reqdBtnScript.onClick];
         if (Cast(myTrails.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) {
             myTrails.contextMenuScripts = new List<ScriptField>(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!));
@@ -575,7 +486,7 @@ export class CurrentUserUtils {
     /// initializes the left sidebar File system pane
     static setupFilesystem(doc: Doc, field:string) {
         var myFilesystem = DocCast(doc[field]);
-        const myFileOrphans = this.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
+        const myFileOrphans = DocUtils.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
         
         const newFolder = `makeTopLevelFolder()`;
         const newFolderOpts: DocumentOptions = {
@@ -583,7 +494,7 @@ export class CurrentUserUtils {
             title: "New folder", btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", system: true
         };
         const newFolderScript = { onClick: newFolder};
-        const newFolderButton = this.AssignScripts(this.AssignOpts(DocCast(myFilesystem?.buttonMenuDoc), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript);
+        const newFolderButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myFilesystem?.buttonMenuDoc), newFolderOpts) ?? Docs.Create.FontIconDocument(newFolderOpts), newFolderScript);
         
         const reqdOpts:DocumentOptions = { _showTitle: "title", _height: 100, _gridGap: 5, _forceActive: true, _lockedPosition: true, 
             title: "My Documents", buttonMenu: true, buttonMenuDoc: newFolderButton, treeViewHideTitle: true, targetDropAction: "proto", system: true,
@@ -593,7 +504,7 @@ export class CurrentUserUtils {
             childContextMenuIcons: new List<string>(["plus"]),
             explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard."
         };
-        myFilesystem = this.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [myFileOrphans]);
+        myFilesystem = DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [myFileOrphans]);
         const childContextMenuScripts = [newFolder];
         if (Cast(myFilesystem.childContextMenuScripts, listSpec(ScriptField), null)?.length !== childContextMenuScripts.length) {
             myFilesystem.childContextMenuScripts = new List<ScriptField>(childContextMenuScripts.map(script => ScriptField.MakeFunction(script)!));
@@ -610,13 +521,13 @@ export class CurrentUserUtils {
             contextMenuIcons:new List<string>(["trash"]),
             explainer: "Recently closed documents appear in this menu. They will only be deleted if you explicity empty this list."
         };
-        const recentlyClosed = this.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts);
+        const recentlyClosed = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts);
 
         const clearAll = (target:string) => `getProto(${target}).data = new List([])`;
         const clearBtnsOpts:DocumentOptions = { _width: 30, _height: 30, _forceActive: true, _stayInCollection: true, _hideContextMenu: true, 
             title: "Empty", target: recentlyClosed, btnType: ButtonType.ClickButton,  buttonText: "Empty", icon: "trash", system: true,
             toolTip: "Empty recently closed",};
-        const clearDocsButton = this.AssignDocField(recentlyClosed, "clearDocsBtn", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")});
+        const clearDocsButton = DocUtils.AssignDocField(recentlyClosed, "clearDocsBtn", (opts) => Docs.Create.FontIconDocument(opts), clearBtnsOpts, undefined, {onClick: clearAll("self.target")});
 
         if (recentlyClosed.buttonMenuDoc !== clearDocsButton) Doc.GetProto(recentlyClosed).buttonMenuDoc = clearDocsButton;
         
@@ -626,19 +537,6 @@ export class CurrentUserUtils {
         return recentlyClosed;
     }
 
-    /// creates a new, empty filter doc
-    static createFilterDoc() {
-        const clearAll = `getProto(self).data = new List([])`;
-        const reqdOpts:DocumentOptions = {
-            _lockedPosition: true,  _autoHeight: true, _fitWidth: true, _height: 150, _xPadding: 5, _yPadding: 5, _gridGap: 5, _forceActive: true, 
-            title: "Unnamed Filter", filterBoolean: "AND", boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, system: true,
-            childDropAction: "none", treeViewHideTitle: true, treeViewTruncateTitleWidth: 150, 
-            childContextMenuLabels: new List<string>(["Clear All"]),
-            childContextMenuScripts: new List<ScriptField>([ScriptField.MakeFunction(clearAll)!]),
-        };
-        return Docs.Create.FilterDocument(reqdOpts);
-    }
-
     /// initializes the left sidebar panel view of the UserDoc
     static setupUserDocView(doc: Doc, field:string) {
         const reqdOpts:DocumentOptions = {
@@ -646,8 +544,8 @@ export class CurrentUserUtils {
             boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", ignoreClick: true, system: true,
             treeViewHideTitle: true, treeViewTruncateTitleWidth: 150
         };
-        if (!doc[field]) this.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" });
-        return this.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [doc]);
+        if (!doc[field]) DocUtils.AssignOpts(doc, {treeViewOpen: true, treeViewExpandedView: "fields" });
+        return DocUtils.AssignDocField(doc, field, (opts, items) => Docs.Create.TreeDocument(items??[], opts), reqdOpts, [doc]);
     }
 
     static linearButtonList = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.LinearDocument(docs, {
@@ -666,7 +564,7 @@ export class CurrentUserUtils {
     static setupDockedButtons(doc: Doc, field="myDockedBtns") {
         const dockedBtns = DocCast(doc[field]);
         const dockBtn = (opts: DocumentOptions, scripts: {[key:string]:string}) =>
-            this.AssignScripts(this.AssignOpts(DocListCast(dockedBtns?.data)?.find(doc => doc.title === opts.title), opts) ?? 
+            DocUtils.AssignScripts(DocUtils.AssignOpts(DocListCast(dockedBtns?.data)?.find(doc => doc.title === opts.title), opts) ?? 
                             CurrentUserUtils.createToolButton(opts), scripts);
         
         const btnDescs = [// setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet
@@ -680,7 +578,7 @@ export class CurrentUserUtils {
         };
         reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "redo")!).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true });
         reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(btns.find(btn => btn.title === "undo")!).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true });
-        return this.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), dockBtnsReqdOpts, btns);
+        return DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), dockBtnsReqdOpts, btns);
     }
 
     static textTools():Button[] {
@@ -768,13 +666,13 @@ export class CurrentUserUtils {
             ...params.funcs,
             backgroundColor: params.scripts?.onClick /// a bit hacky.  if onClick is set, then we assume it returns a color value when queried with '_readOnly_'.  This will be true for toggle buttons, but not generally
         }
-        return this.AssignScripts(this.AssignOpts(btnDoc, reqdOpts) ?? Docs.Create.FontIconDocument(reqdOpts), params.scripts, reqdFuncs);
+        return DocUtils.AssignScripts(DocUtils.AssignOpts(btnDoc, reqdOpts) ?? Docs.Create.FontIconDocument(reqdOpts), params.scripts, reqdFuncs);
     }
 
     /// Initializes all the default buttons for the top bar context menu
     static setupContextMenuButtons(doc: Doc, field="myContextMenuBtns") {
         const reqdCtxtOpts = { title: "context menu buttons", flexGap: 0, childDontRegisterViews: true,  linearViewIsExpanded: true, ignoreClick: true, linearViewExpandable: false, _height: 35 };
-        const ctxtMenuBtnsDoc = this.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined);
+        const ctxtMenuBtnsDoc = DocUtils.AssignDocField(doc, field, (opts, items) => this.linearButtonList(opts, items??[]), reqdCtxtOpts, undefined);
         const ctxtMenuBtns = CurrentUserUtils.contextMenuTools().map(params => {
             const menuBtnDoc = DocListCast(ctxtMenuBtnsDoc?.data).find(doc => doc.title === params.title);
             if (!params.subMenu) {
@@ -786,20 +684,20 @@ export class CurrentUserUtils {
                 const items = params.subMenu?.map(sub =>
                     this.setupContextMenuButton(sub, DocListCast(menuBtnDoc?.data).find(doc => doc.title === sub.title))
                 );
-                return this.AssignScripts(
-                    this.AssignDocField(ctxtMenuBtnsDoc, StrCast(params.title), (opts) => this.linearButtonList(opts, items??[]), reqdSubMenuOpts, items), undefined, params.funcs);
+                return DocUtils.AssignScripts(
+                    DocUtils.AssignDocField(ctxtMenuBtnsDoc, StrCast(params.title), (opts) => this.linearButtonList(opts, items??[]), reqdSubMenuOpts, items), undefined, params.funcs);
             }
         });
-        return this.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts, ctxtMenuBtns);
+        return DocUtils.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts, ctxtMenuBtns);
     }
 
     /// collection of documents rendered in the overlay layer above all tabs and other UI
     static setupOverlays(doc: Doc, field = "myOverlayDocs") {
-        return this.AssignDocField(doc, field, (opts) => Docs.Create.FreeformDocument([], opts), { title: "overlay documents", backgroundColor: "#aca3a6", system: true });
+        return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.FreeformDocument([], opts), { title: "overlay documents", backgroundColor: "#aca3a6", system: true });
     }
 
     static setupPublished(doc:Doc, field = "myPublishedDocs") {
-        return this.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), { title: "published docs", backgroundColor: "#aca3a6", system: true });
+        return DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), { title: "published docs", backgroundColor: "#aca3a6", system: true });
     }
     
     /// The database of all links on all documents
@@ -838,7 +736,7 @@ export class CurrentUserUtils {
             explainer: "This is where documents or dashboards that other users have shared with you will appear. To share a document or dashboard right click and select 'Share'"
         };
         
-        this.AssignDocField(doc, "mySharedDocs", opts => Docs.Create.TreeDocument([], opts, sharingDocumentId + "layout", sharingDocumentId), sharedDocOpts, undefined, sharedScripts);
+        DocUtils.AssignDocField(doc, "mySharedDocs", opts => Docs.Create.TreeDocument([], opts, sharingDocumentId + "layout", sharingDocumentId), sharedDocOpts, undefined, sharedScripts);
     }
 
     /// Import option on the left side button panel 
@@ -849,12 +747,12 @@ export class CurrentUserUtils {
             childDropAction: "copy", _autoHeight: true, _yMargin: 50, _gridGap: 15, boxShadow: "0 0", _lockedPosition: true, system: true, _chromeHidden: true,
             dontRegisterView: true, explainer: "This is where documents that are Imported into Dash will go."
         };
-        const myImports = this.AssignDocField(doc, field, (opts) => Docs.Create.StackingDocument([], opts), reqdOpts);
+        const myImports = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.StackingDocument([], opts), reqdOpts);
     
         const reqdBtnOpts:DocumentOptions = { _forceActive: true, toolTip: "Import from computer",
             _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ClickButton, 
             buttonText: "Import", icon: "upload", system: true };
-        this.AssignDocField(myImports, "buttonMenuDoc", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" });
+        DocUtils.AssignDocField(myImports, "buttonMenuDoc", (opts) => Docs.Create.FontIconDocument(opts), reqdBtnOpts, undefined, { onClick: "importDocument()" });
         return myImports;
     }
 
@@ -909,7 +807,7 @@ export class CurrentUserUtils {
     /// written to the server if the code hasn't changed.  However, choices need to be made for each Doc/field
     /// whether to revert to "default" values, or to leave them as the user/system last set them.
     static updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) {
-        this.AssignDocField(doc, "globalGroupDatabase", () => Docs.Prototypes.MainGroupDocument(), {});
+        DocUtils.AssignDocField(doc, "globalGroupDatabase", () => Docs.Prototypes.MainGroupDocument(), {});
         reaction(() => DateCast(DocCast(doc.globalGroupDatabase)["data-lastModified"]),
             async () => {
                 const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase).data);
@@ -948,8 +846,8 @@ export class CurrentUserUtils {
         this.setupLeftSidebarMenu(doc); // the left-side column of buttons that open their contents in a flyout panel on the left
         this.setupDocTemplates(doc); // sets up the template menu of templates
         this.setupFieldInfos(doc); // sets up the collection of field info descriptions for each possible DocumentOption
-        this.AssignDocField(doc, "globalScriptDatabase", (opts) =>  Docs.Prototypes.MainScriptDocument(), {});
-        this.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents
+        DocUtils.AssignDocField(doc, "globalScriptDatabase", (opts) =>  Docs.Prototypes.MainScriptDocument(), {});
+        DocUtils.AssignDocField(doc, "myHeaderBar", (opts) => Docs.Create.MulticolumnDocument([], opts), { title: "header bar", system: true }); // drop down panel at top of dashboard for stashing documents
         
         if (doc.activeDashboard instanceof Doc) {
             // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
@@ -962,7 +860,7 @@ export class CurrentUserUtils {
     }
     static setupFieldInfos(doc:Doc, field="fieldInfos") {
         const fieldInfoOpts = { title: "Field Infos", system: true}; // bcz: all possible document options have associated field infos which are stored onn the FieldInfos document **except for title and system which are used as part of the definition of the fieldInfos object
-        const infos = this.AssignDocField(doc, field, opts => Doc.assign(new Doc(), opts as any), fieldInfoOpts);
+        const infos = DocUtils.AssignDocField(doc, field, opts => Doc.assign(new Doc(), opts as any), fieldInfoOpts);
         const entries = Object.entries(new DocumentOptions());
         entries.forEach(pair => {
             if (!Array.from(Object.keys(fieldInfoOpts)).includes(pair[0])) {
@@ -974,7 +872,7 @@ export class CurrentUserUtils {
                     case Doc.name: opts.fieldValues = new List<Doc>(options.values as any); break;
                     default: opts.fieldValues = new List<string>(options.values as any); break;// string, pointerEvents, dimUnit, dropActionType
                 }
-                this.AssignDocField(infos, pair[0], opts => Doc.assign(new Doc(), OmitKeys(opts,["values"]).omit), opts);
+                DocUtils.AssignDocField(infos, pair[0], opts => Doc.assign(new Doc(), OmitKeys(opts,["values"]).omit), opts);
             }
         });
     }
@@ -995,7 +893,6 @@ export class CurrentUserUtils {
     }
 
     public static async loadUserDocument(id: string) {
-        this.curr_id = id;
         await rp.get(Utils.prepend("/getUserDocumentIds")).then(ids => {
             const { userDocumentId, sharingDocumentId, linkDatabaseId } = JSON.parse(ids);
             if (userDocumentId !== "guest") {
@@ -1012,46 +909,6 @@ export class CurrentUserUtils {
         });
     }
 
-    public static _urlState: HistoryUtil.DocUrl;
-
-    /// opens a dashboard as the ActiveDashboard (and adds the dashboard to the users list of dashboards if it's not already there).
-    /// this also sets the readonly state of the dashboard based on the current mode of dash (from its url)
-    public static openDashboard = (doc: Doc|undefined, fromHistory = false) => {
-        if (!doc) return false;
-        CurrentUserUtils.MainDocId = doc[Id];
-        Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", doc);
-
-        // this has the side-effect of setting the main container since we're assigning the active/guest dashboard
-        Doc.UserDoc() ? (CurrentUserUtils.ActiveDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc);
-            
-        const state = CurrentUserUtils._urlState;
-        if (state.sharing === true && !Doc.UserDoc()) {
-            DocServer.Control.makeReadOnly();
-        } else {
-            fromHistory || HistoryUtil.pushState({
-                type: "doc",
-                docId: doc[Id],
-                readonly: state.readonly,
-                nro: state.nro,
-                sharing: false,
-            });
-            if (state.readonly === true || state.readonly === null) {
-                DocServer.Control.makeReadOnly();
-            } else if (state.safe) {
-                if (!state.nro) {
-                    DocServer.Control.makeReadOnly();
-                }
-                CollectionView.SetSafeMode(true);
-            } else if (state.nro || state.nro === null || state.readonly === false) {
-            } else if (doc.readOnly) {
-                DocServer.Control.makeReadOnly();
-            } else {
-                DocServer.Control.makeEditable();
-            }
-        }
-
-        return true;
-    }
 
     public static importDocument = () => {
         const input = document.createElement("input");
@@ -1068,7 +925,7 @@ export class CurrentUserUtils {
                 //     setTimeout(() => SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs =>
                 //         docs.docs.forEach(d => LinkManager.Instance.addLink(d))), 2000); // need to give solr some time to update so that this query will find any link docs we've added.
                 // }
-                const list = Cast(CurrentUserUtils.MyImports.data, listSpec(Doc), null);
+                const list = Cast(Doc.MyImports.data, listSpec(Doc), null);
                 doc instanceof Doc && list?.splice(0, 0, doc);
             } else if (input.files && input.files.length !== 0) {
                 const disposer = OverlayView.ShowSpinner();
@@ -1076,7 +933,7 @@ export class CurrentUserUtils {
                 if (results.length !== input.files?.length) {
                     alert("Error uploading files - possibly due to unsupported file types");
                 }
-                const list = Cast(CurrentUserUtils.MyImports.data, listSpec(Doc), null);
+                const list = Cast(Doc.MyImports.data, listSpec(Doc), null);
                 list?.splice(0, 0, ...results);
                 disposer();
             } else {
@@ -1085,104 +942,20 @@ export class CurrentUserUtils {
         };
         input.click();
     }
-    
-    public static snapshotDashboard() { return CollectionDockingView.TakeSnapshot(CurrentUserUtils.ActiveDashboard);  }
-
-    public static closeActiveDashboard = () => { CurrentUserUtils.ActiveDashboard = undefined; }
-
-    public static removeDashboard = async (dashboard:Doc) => {
-        const dashboards = await DocListCastAsync(CurrentUserUtils.MyDashboards.data);
-        if (dashboards?.length) {
-            if (dashboard === CurrentUserUtils.ActiveDashboard) CurrentUserUtils.openDashboard(dashboards.find(doc => doc !== dashboard));
-            Doc.RemoveDocFromList(CurrentUserUtils.MyDashboards, "data", dashboard);
-            if (!dashboards.length) CurrentUserUtils.ActivePage = "home";
-        }
-    }
-    public static createNewDashboard = (id?: string, name?: string) => {
-        const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true);
-        const dashboards = CurrentUserUtils.MyDashboards;
-        const dashboardCount = DocListCast(dashboards.data).length + 1;
-        const freeformOptions: DocumentOptions = {
-            x: 0,
-            y: 400,
-            _width: 1500,
-            _height: 1000,
-            _fitWidth: true,
-            _backgroundGridShow: true,
-            title: `Untitled Tab 1`,
-        };
-        const title = name ? name : `Dashboard ${dashboardCount}`
-        const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
-        const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: title }, id, "row");
-        freeformDoc.context = dashboardDoc;
-
-        // switching the tabs from the datadoc to the regular doc
-        const dashboardTabs = DocListCast(dashboardDoc[DataSym].data);
-        dashboardDoc.data = new List<Doc>(dashboardTabs);
-        dashboardDoc["pane-count"] = 1;
-
-        CurrentUserUtils.ActivePresentation = presentation;
-
-        Doc.AddDocToList(dashboards, "data", dashboardDoc);
-        // open this new dashboard 
-        CurrentUserUtils.ActiveDashboard = dashboardDoc;
-        CurrentUserUtils.ActivePage = "dashboard";
-    }
-
-    public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number, backgroundColor?: string) {
-        const tbox = Docs.Create.TextDocument("", {
-            _xMargin: noMargins ? 0 : undefined, _yMargin: noMargins ? 0 : undefined, annotationOn, docMaxAutoHeight: maxHeight, backgroundColor: backgroundColor,
-            _width: width || 200, _height: 35, x: x, y: y, _fitWidth: true, _autoHeight: true, title
-        });
-        const template = Doc.UserDoc().defaultTextLayout;
-        if (template instanceof Doc) {
-            tbox._width = NumCast(template._width);
-            tbox.layoutKey = "layout_" + StrCast(template.title);
-            Doc.GetProto(tbox)[StrCast(tbox.layoutKey)] = template;
-        }
-        return tbox;
-    }
-
-    public static get MyUserDocView() { return DocCast(Doc.UserDoc().myUserDocView); }
-    public static get MyDockedBtns() { return DocCast(Doc.UserDoc().myDockedBtns); }
-    public static get MySearcher() { return DocCast(Doc.UserDoc().mySearcher); }
-    public static get MyFilesystem() { return DocCast(Doc.UserDoc().myFilesystem); }
-    public static get MyHeaderBar() { return DocCast(Doc.UserDoc().myHeaderBar); }
-    public static get MyTools() { return DocCast(Doc.UserDoc().myTools); }
-    public static get MyDashboards() { return DocCast(Doc.UserDoc().myDashboards); }
-    public static get MyFileOrphans() { return DocCast(Doc.UserDoc().myFileOrphans); }
-    public static get MyTemplates() { return DocCast(Doc.UserDoc().myTemplates); }
-    public static get MyLeftSidebarMenu() { return DocCast(Doc.UserDoc().myLeftSidebarMenu); }
-    public static get MyLeftSidebarPanel() { return DocCast(Doc.UserDoc().myLeftSidebarPanel); }
-    public static get MySharedDocs() { return DocCast(Doc.UserDoc().mySharedDocs); }
-    public static get MyTrails() { return DocCast(Doc.UserDoc().myTrails); }
-    public static get MyImports() { return DocCast(Doc.UserDoc().myImports); }
-    public static get MyContextMenuBtns() { return DocCast(Doc.UserDoc().myContextMenuBtns); }
-    public static get MyRecentlyClosed() { return DocCast(Doc.UserDoc().myRecentlyClosed); }
-    public static get MyOverlayDocs() { return DocCast(Doc.UserDoc().myOverlayDocs); }
-    public static get MyPublishedDocs() { return DocCast(Doc.UserDoc().myPublishedDocs); }
-    public static get ActiveDashboard() { return DocCast(Doc.UserDoc().activeDashboard); }
-    public static set ActiveDashboard(val:Doc|undefined) { Doc.UserDoc().activeDashboard = val; }
-    public static get ActivePresentation() { return DocCast(Doc.UserDoc().activePresentation); }
-    public static set ActivePresentation(val) { Doc.UserDoc().activePresentation = val; }
-    public static get ActivePage() { return StrCast(Doc.UserDoc().activePage); }
-    public static set ActivePage(val) { Doc.UserDoc().activePage = val; }
-    public static set ActiveTool(tool: InkTool) { Doc.UserDoc().activeTool = tool; }
-    public static get ActiveTool(): InkTool { return StrCast(Doc.UserDoc().activeTool, InkTool.None) as InkTool; }
 }
 
-ScriptingGlobals.add(function MySharedDocs() { return CurrentUserUtils.MySharedDocs; }, "document containing all shared Docs");
+ScriptingGlobals.add(function MySharedDocs() { return Doc.MySharedDocs; }, "document containing all shared Docs");
 ScriptingGlobals.add(function IsNoviceMode() { return Doc.noviceMode; }, "is Dash in novice mode");
 ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering");
-ScriptingGlobals.add(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(); }, "creates a snapshot copy of a dashboard");
-ScriptingGlobals.add(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(); }, "creates a new dashboard when called");
+ScriptingGlobals.add(function snapshotDashboard() { DashboardView.snapshotDashboard(); }, "creates a snapshot copy of a dashboard");
+ScriptingGlobals.add(function createNewDashboard() { return DashboardView.createNewDashboard(); }, "creates a new dashboard when called");
 ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance.createNewPresentation(); }, "creates a new presentation when called");
 ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called");
 ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, "returns all the links to the document or its annotations", "(doc: any)");
 ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar");
 ScriptingGlobals.add(function shareDashboard(dashboard: Doc) { SharingManager.Instance.open(undefined, dashboard); }, "opens sharing dialog for Dashboard");
-ScriptingGlobals.add(function removeDashboard(dashboard: Doc) { CurrentUserUtils.removeDashboard(dashboard); }, "Remove Dashboard from Dashboards");
-ScriptingGlobals.add(function addToDashboards(dashboard: Doc) {  CurrentUserUtils.openDashboard( Doc.MakeAlias(dashboard)); },  "adds Dashboard to set of Dashboards");
+ScriptingGlobals.add(function removeDashboard(dashboard: Doc) { DashboardView.removeDashboard(dashboard); }, "Remove Dashboard from Dashboards");
+ScriptingGlobals.add(function addToDashboards(dashboard: Doc) {  DashboardView.openDashboard( Doc.MakeAlias(dashboard)); },  "adds Dashboard to set of Dashboards");
 ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkContext?: boolean) {
     let selected = (sel => checkContext ? DocCast(sel?.context) : sel)(SelectionManager.SelectedSchemaDoc() ?? SelectionManager.Docs().lastElement());
     return docType ? selected?.type === docType : colType ? selected?.viewType === colType : true;
@@ -1190,5 +963,5 @@ ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colTy
 ScriptingGlobals.add(function makeTopLevelFolder() {
     TreeView._editTitleOnLoad = { id: Utils.GenerateGuid(), parent: undefined };
     const opts = { title: "Untitled folder", _stayInCollection: true, isFolder: true };
-    return Doc.AddDocToList(CurrentUserUtils.MyFilesystem, "data", Docs.Create.TreeDocument([], opts, TreeView._editTitleOnLoad.id));
+    return Doc.AddDocToList(Doc.MyFilesystem, "data", Docs.Create.TreeDocument([], opts, TreeView._editTitleOnLoad.id));
 });
\ No newline at end of file
diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts
index 8473ce703..d3ac2f03f 100644
--- a/src/client/util/DocumentManager.ts
+++ b/src/client/util/DocumentManager.ts
@@ -4,44 +4,43 @@ import { Id } from '../../fields/FieldSymbols';
 import { Cast } from '../../fields/Types';
 import { returnFalse } from '../../Utils';
 import { DocumentType } from '../documents/DocumentTypes';
-import { CollectionDockingView } from '../views/collections/CollectionDockingView';
-import { CollectionFreeFormView } from '../views/collections/collectionFreeForm';
-import { CollectionView } from '../views/collections/CollectionView';
 import { LightboxView } from '../views/LightboxView';
 import { DocumentView, ViewAdjustment } from '../views/nodes/DocumentView';
 import { LinkAnchorBox } from '../views/nodes/LinkAnchorBox';
-import { CurrentUserUtils } from './CurrentUserUtils';
+import { CollectionDockingView } from '../views/collections/CollectionDockingView';
+import { CollectionFreeFormView } from '../views/collections/collectionFreeForm';
+import { CollectionView } from '../views/collections/CollectionView';
 import { ScriptingGlobals } from './ScriptingGlobals';
 import { SelectionManager } from './SelectionManager';
 
 export class DocumentManager {
-
     //global holds all of the nodes (regardless of which collection they're in)
     @observable public DocumentViews = new Set<DocumentView>();
     @observable public LinkAnchorBoxViews: DocumentView[] = [];
     @observable public RecordingEvent = 0;
-    @observable public LinkedDocumentViews: { a: DocumentView, b: DocumentView, l: Doc }[] = [];
+    @observable public LinkedDocumentViews: { a: DocumentView; b: DocumentView; l: Doc }[] = [];
 
     private static _instance: DocumentManager;
-    public static get Instance(): DocumentManager { return this._instance || (this._instance = new this()); }
+    public static get Instance(): DocumentManager {
+        return this._instance || (this._instance = new this());
+    }
 
     //private constructor so no other class can create a nodemanager
-    private constructor() { }
+    private constructor() {}
 
     @action
     public AddView = (view: DocumentView) => {
         //console.log("MOUNT " + view.props.Document.title + "/" + view.props.LayoutTemplateString);
         if (view.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
-            const viewAnchorIndex = view.props.LayoutTemplateString.includes("anchor2") ? "anchor2" : "anchor1";
+            const viewAnchorIndex = view.props.LayoutTemplateString.includes('anchor2') ? 'anchor2' : 'anchor1';
             DocListCast(view.rootDoc.links).forEach(link => {
-                this.LinkAnchorBoxViews?.filter(dv => Doc.AreProtosEqual(dv.rootDoc, link) && !dv.props.LayoutTemplateString?.includes(viewAnchorIndex)).
-                    forEach(otherView => this.LinkedDocumentViews.push(
-                        {
-                            a: viewAnchorIndex === "anchor2" ? otherView : view,
-                            b: viewAnchorIndex === "anchor2" ? view : otherView,
-                            l: link
-                        })
-                    );
+                this.LinkAnchorBoxViews?.filter(dv => Doc.AreProtosEqual(dv.rootDoc, link) && !dv.props.LayoutTemplateString?.includes(viewAnchorIndex)).forEach(otherView =>
+                    this.LinkedDocumentViews.push({
+                        a: viewAnchorIndex === 'anchor2' ? otherView : view,
+                        b: viewAnchorIndex === 'anchor2' ? view : otherView,
+                        l: link,
+                    })
+                );
             });
             this.LinkAnchorBoxViews.push(view);
             // this.LinkedDocumentViews.forEach(view => console.log("  LV = " + view.a.props.Document.title + "/" + view.a.props.LayoutTemplateString + " --> " +
@@ -49,14 +48,16 @@ export class DocumentManager {
         } else {
             this.DocumentViews.add(view);
         }
-    }
+    };
     public RemoveView = action((view: DocumentView) => {
-        this.LinkedDocumentViews.slice().forEach(action(pair => {
-            if (pair.a === view || pair.b === view) {
-                const li = this.LinkedDocumentViews.indexOf(pair);
-                li !== -1 && this.LinkedDocumentViews.splice(li, 1);
-            }
-        }));
+        this.LinkedDocumentViews.slice().forEach(
+            action(pair => {
+                if (pair.a === view || pair.b === view) {
+                    const li = this.LinkedDocumentViews.indexOf(pair);
+                    li !== -1 && this.LinkedDocumentViews.splice(li, 1);
+                }
+            })
+        );
 
         if (view.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
             const index = this.LinkAnchorBoxViews.indexOf(view);
@@ -125,11 +126,11 @@ export class DocumentManager {
         const views: DocumentView[] = [];
         Array.from(DocumentManager.Instance.DocumentViews).map(view => LightboxView.IsLightboxDocView(view.docViewPath) && Doc.AreProtosEqual(view.rootDoc, toFind) && views.push(view));
         return views?.find(view => view.ContentDiv?.getBoundingClientRect().width && view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse) || (views.length ? views[0] : undefined);
-    }
+    };
     public getFirstDocumentView = (toFind: Doc, originatingDoc: Opt<Doc> = undefined): DocumentView | undefined => {
         const views = this.getDocumentViews(toFind).filter(view => view.rootDoc !== originatingDoc);
         return views?.find(view => view.ContentDiv?.getBoundingClientRect().width && view.props.focus !== returnFalse) || views?.find(view => view.props.focus !== returnFalse) || (views.length ? views[0] : undefined);
-    }
+    };
     public getDocumentViews(toFind: Doc): DocumentView[] {
         const toReturn: DocumentView[] = [];
         const docViews = Array.from(DocumentManager.Instance.DocumentViews).filter(view => !LightboxView.IsLightboxDocView(view.docViewPath));
@@ -146,17 +147,16 @@ export class DocumentManager {
         return toReturn;
     }
 
-
     static addView = (doc: Doc, finished?: () => void) => {
-        CollectionDockingView.AddSplit(doc, "right");
+        CollectionDockingView.AddSplit(doc, 'right');
         finished?.();
-    }
+    };
     public jumpToDocument = async (
-        targetDoc: Doc,        // document to display
-        willZoom: boolean,     // whether to zoom doc to take up most of screen
+        targetDoc: Doc, // document to display
+        willZoom: boolean, // whether to zoom doc to take up most of screen
         createViewFunc = DocumentManager.addView, // how to create a view of the doc if it doesn't exist
-        docContext: Doc[],  // context to load that should contain the target
-        linkDoc?: Doc,   // link that's being followed
+        docContext: Doc[], // context to load that should contain the target
+        linkDoc?: Doc, // link that's being followed
         closeContextIfNotFound: boolean = false, // after opening a context where the document should be, this determines whether the context should be closed if the Doc isn't actually there
         originatingDoc: Opt<Doc> = undefined, // doc that initiated the display of the target odoc
         finished?: () => void,
@@ -172,14 +172,15 @@ export class DocumentManager {
         var wasHidden = resolvedTarget.hidden;
         if (wasHidden) {
             runInAction(() => {
-                resolvedTarget.hidden = false;  // if the target is hidden, un-hide it here.
+                resolvedTarget.hidden = false; // if the target is hidden, un-hide it here.
                 docView?.props.bringToFront(resolvedTarget);
             });
         }
         const focusAndFinish = (didFocus: boolean) => {
             const finalTargetDoc = resolvedTarget;
             if (originatingDoc?.isPushpin) {
-                if (!didFocus && !wasHidden) {  // don't toggle the hidden state if the doc was already un-hidden as part of this document traversal
+                if (!didFocus && !wasHidden) {
+                    // don't toggle the hidden state if the doc was already un-hidden as part of this document traversal
                     finalTargetDoc.hidden = !finalTargetDoc.hidden;
                 }
             } else {
@@ -192,77 +193,92 @@ export class DocumentManager {
         const contextDocs = docContext.length ? await DocListCastAsync(docContext[0].data) : undefined;
         const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext.lastElement() : undefined;
         const targetDocContext = contextDoc || annotatedDoc;
-        const targetDocContextView = (targetDocContext && getFirstDocView(targetDocContext)) ||
-            (wasHidden && annoContainerView);// if we have an annotation container and the target was hidden, then try again because we just un-hid the document above
+        const targetDocContextView = (targetDocContext && getFirstDocView(targetDocContext)) || (wasHidden && annoContainerView); // if we have an annotation container and the target was hidden, then try again because we just un-hid the document above
         const focusView = !docView && targetDoc.type === DocumentType.MARKER && annoContainerView ? annoContainerView : docView;
         if (annoContainerView) {
-            if (annoContainerView.props.Document.layoutKey === "layout_icon") {
-                annoContainerView.iconify(() => annoContainerView.focus(targetDoc, {
-                    originalTarget, willZoom, scale: presZoom, afterFocus: (didFocus: boolean) =>
-                        new Promise<ViewAdjustment>(res => {
-                            focusAndFinish(true);
-                            res(ViewAdjustment.doNothing);
-                        })
-                }));
+            if (annoContainerView.props.Document.layoutKey === 'layout_icon') {
+                annoContainerView.iconify(() =>
+                    annoContainerView.focus(targetDoc, {
+                        originalTarget,
+                        willZoom,
+                        scale: presZoom,
+                        afterFocus: (didFocus: boolean) =>
+                            new Promise<ViewAdjustment>(res => {
+                                focusAndFinish(true);
+                                res(ViewAdjustment.doNothing);
+                            }),
+                    })
+                );
                 return;
             } else if (!docView && targetDoc.type !== DocumentType.MARKER) {
-                annoContainerView.focus(targetDoc);  // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below
+                annoContainerView.focus(targetDoc); // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below
             }
         }
         if (focusView) {
             !noSelect && Doc.linkFollowHighlight(focusView.rootDoc); //TODO:glr make this a setting in PresBox
-            const doFocus = (forceDidFocus: boolean) => focusView.focus(originalTarget ?? targetDoc, {
-                originalTarget, willZoom, scale: presZoom, afterFocus: (didFocus: boolean) =>
-                    new Promise<ViewAdjustment>(res => {
-                        focusAndFinish(forceDidFocus || didFocus);
-                        res(ViewAdjustment.doNothing);
-                    })
-            });
-            if (focusView.props.Document.layoutKey === "layout_icon") {
+            const doFocus = (forceDidFocus: boolean) =>
+                focusView.focus(originalTarget ?? targetDoc, {
+                    originalTarget,
+                    willZoom,
+                    scale: presZoom,
+                    afterFocus: (didFocus: boolean) =>
+                        new Promise<ViewAdjustment>(res => {
+                            focusAndFinish(forceDidFocus || didFocus);
+                            res(ViewAdjustment.doNothing);
+                        }),
+                });
+            if (focusView.props.Document.layoutKey === 'layout_icon') {
                 focusView.iconify(() => doFocus(true));
             } else {
                 doFocus(false);
             }
         } else {
-            if (!targetDocContext) { // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default
+            if (!targetDocContext) {
+                // we don't have a view and there's no context specified ... create a new view of the target using the dockFunc or default
                 createViewFunc(Doc.BrushDoc(targetDoc), finished); // bcz: should we use this?: Doc.MakeAlias(targetDoc)));
-            } else {  // otherwise try to get a view of the context of the target
-                if (targetDocContextView) { // we found a context view and aren't forced to create a new one ... focus on the context first..
+            } else {
+                // otherwise try to get a view of the context of the target
+                if (targetDocContextView) {
+                    // we found a context view and aren't forced to create a new one ... focus on the context first..
                     wasHidden = wasHidden || targetDocContextView.rootDoc.hidden;
                     targetDocContextView.rootDoc.hidden = false; // make sure context isn't hidden
-                    targetDocContext._viewTransition = "transform 500ms";
+                    targetDocContext._viewTransition = 'transform 500ms';
                     targetDocContextView.props.focus(targetDocContextView.rootDoc, {
-                        willZoom, afterFocus: async () => {
+                        willZoom,
+                        afterFocus: async () => {
                             targetDocContext._viewTransition = undefined;
-                            if (targetDocContext.layoutKey === "layout_icon") {
-                                targetDocContextView.iconify(() => this.jumpToDocument(
-                                    resolvedTarget ?? targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc,
-                                    finished, originalTarget, noSelect, presZoom));
+                            if (targetDocContext.layoutKey === 'layout_icon') {
+                                targetDocContextView.iconify(() => this.jumpToDocument(resolvedTarget ?? targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc, finished, originalTarget, noSelect, presZoom));
                             }
                             return ViewAdjustment.doNothing;
-                        }
+                        },
                     });
 
                     // now find the target document within the context
-                    if (targetDoc._timecodeToShow) {  // if the target has a timecode, it should show up once the (presumed) video context scrubs to the display timecode;
+                    if (targetDoc._timecodeToShow) {
+                        // if the target has a timecode, it should show up once the (presumed) video context scrubs to the display timecode;
                         targetDocContext._currentTimecode = targetDoc.anchorTimecodeToShow;
                         finished?.();
-                    } else { // no timecode means we need to find the context view and focus on our target
+                    } else {
+                        // no timecode means we need to find the context view and focus on our target
                         const findView = (delay: number) => {
-                            const retryDocView = getFirstDocView(resolvedTarget);  // test again for the target view snce we presumably created the context above by focusing on it
-                            if (retryDocView) {   // we found the target in the context. 
+                            const retryDocView = getFirstDocView(resolvedTarget); // test again for the target view snce we presumably created the context above by focusing on it
+                            if (retryDocView) {
+                                // we found the target in the context.
                                 Doc.linkFollowHighlight(retryDocView.rootDoc);
                                 retryDocView.focus(targetDoc, {
-                                    willZoom, afterFocus: (didFocus: boolean) =>
+                                    willZoom,
+                                    afterFocus: (didFocus: boolean) =>
                                         new Promise<ViewAdjustment>(res => {
                                             !noSelect && focusAndFinish(true);
                                             res(ViewAdjustment.doNothing);
-                                        })
+                                        }),
                                 }); // focus on the target in the context
                             } else if (delay > 1000) {
                                 // we didn't find the target, so it must have moved out of the context.  Go back to just creating it.
                                 if (closeContextIfNotFound) targetDocContextView.props.removeDocument?.(targetDocContextView.rootDoc);
-                                if (targetDoc.layout) { // there will no layout for a TEXTANCHOR type document
+                                if (targetDoc.layout) {
+                                    // there will no layout for a TEXTANCHOR type document
                                     createViewFunc(Doc.BrushDoc(targetDoc), finished); //  create a new view of the target
                                 }
                             } else {
@@ -272,22 +288,24 @@ export class DocumentManager {
                         setTimeout(() => findView(0), 0);
                     }
                 } else {
-                    if (docContext.length && docContext[0]?.layoutKey === "layout_icon") {
+                    if (docContext.length && docContext[0]?.layoutKey === 'layout_icon') {
                         const docContextView = this.getFirstDocumentView(docContext[0]);
                         if (docContextView) {
-                            return docContextView.iconify(() => this.jumpToDocument(
-                                targetDoc, willZoom, createViewFunc, docContext.slice(1, docContext.length), linkDoc, closeContextIfNotFound, originatingDoc,
-                                finished, originalTarget, noSelect, presZoom));
+                            return docContextView.iconify(() =>
+                                this.jumpToDocument(targetDoc, willZoom, createViewFunc, docContext.slice(1, docContext.length), linkDoc, closeContextIfNotFound, originatingDoc, finished, originalTarget, noSelect, presZoom)
+                            );
                         }
                     }
                     // there's no context view so we need to create one first and try again when that finishes
                     const finishFunc = () => this.jumpToDocument(targetDoc, true, createViewFunc, docContext, linkDoc, true /* if we don't find the target, we want to get rid of the context just created */, undefined, finished, originalTarget);
-                    createViewFunc(targetDocContext, // after creating the context, this calls the finish function that will retry looking for the target
-                        finishFunc);
+                    createViewFunc(
+                        targetDocContext, // after creating the context, this calls the finish function that will retry looking for the target
+                        finishFunc
+                    );
                 }
             }
         }
-    }
+    };
 }
 export function DocFocusOrOpen(doc: Doc, collectionDoc?: Doc) {
     const cv = collectionDoc && DocumentManager.Instance.getDocumentView(collectionDoc);
@@ -295,14 +313,12 @@ export function DocFocusOrOpen(doc: Doc, collectionDoc?: Doc) {
     if (dv && Doc.AreProtosEqual(dv.props.Document, doc)) {
         dv.props.focus(dv.props.Document, { willZoom: true });
         Doc.linkFollowHighlight(dv?.props.Document, false);
-    }
-    else {
-        const context = doc.context !== CurrentUserUtils.MyFilesystem && Cast(doc.context, Doc, null);
+    } else {
+        const context = doc.context !== Doc.MyFilesystem && Cast(doc.context, Doc, null);
         const showDoc = context || doc;
         const bestAlias = showDoc === Doc.GetProto(showDoc) ? DocListCast(showDoc.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail) : showDoc;
 
-        CollectionDockingView.AddSplit(bestAlias ? bestAlias : Doc.MakeAlias(showDoc), "right") && context &&
-            setTimeout(() => DocumentManager.Instance.getDocumentView(Doc.GetProto(doc))?.focus(doc));
+        CollectionDockingView.AddSplit(bestAlias ? bestAlias : Doc.MakeAlias(showDoc), 'right') && context && setTimeout(() => DocumentManager.Instance.getDocumentView(Doc.GetProto(doc))?.focus(doc));
     }
 }
-ScriptingGlobals.add(DocFocusOrOpen);
\ No newline at end of file
+ScriptingGlobals.add(DocFocusOrOpen);
diff --git a/src/client/util/History.ts b/src/client/util/History.ts
index 7dcff9c56..18aee6444 100644
--- a/src/client/util/History.ts
+++ b/src/client/util/History.ts
@@ -1,8 +1,8 @@
-import { Doc } from "../../fields/Doc";
-import { DocServer } from "../DocServer";
 import * as qs from 'query-string';
-import { Utils, OmitKeys } from "../../Utils";
-import { CurrentUserUtils } from "./CurrentUserUtils";
+import { Doc } from '../../fields/Doc';
+import { OmitKeys, Utils } from '../../Utils';
+import { DocServer } from '../DocServer';
+import { DashboardView } from '../views/DashboardView';
 
 export namespace HistoryUtil {
     export interface DocInitializerList {
@@ -10,7 +10,7 @@ export namespace HistoryUtil {
     }
 
     export interface DocUrl {
-        type: "doc";
+        type: 'doc';
         docId: string;
         initializers?: {
             [docId: string]: DocInitializerList;
@@ -25,11 +25,11 @@ export namespace HistoryUtil {
 
     // const handlers: ((state: ParsedUrl | null) => void)[] = [];
     function onHistory(e: PopStateEvent) {
-        if (window.location.pathname !== "/home") {
-            const url = e.state as ParsedUrl || parseUrl(window.location);
+        if (window.location.pathname !== '/home') {
+            const url = (e.state as ParsedUrl) || parseUrl(window.location);
             if (url) {
                 switch (url.type) {
-                    case "doc":
+                    case 'doc':
                         onDocUrl(url);
                         break;
                 }
@@ -43,13 +43,13 @@ export namespace HistoryUtil {
     let _lastStatePush = 0;
     export function pushState(state: ParsedUrl) {
         if (Date.now() - _lastStatePush > 1000) {
-            history.pushState(state, "", createUrl(state));
+            history.pushState(state, '', createUrl(state));
         }
         _lastStatePush = Date.now();
     }
 
     export function replaceState(state: ParsedUrl) {
-        history.replaceState(state, "", createUrl(state));
+        history.replaceState(state, '', createUrl(state));
     }
 
     function copyState(state: ParsedUrl): ParsedUrl {
@@ -61,7 +61,7 @@ export namespace HistoryUtil {
         if (state) {
             state.initializers = state.initializers || {};
         }
-        return state ?? {initializers:{}};
+        return state ?? { initializers: {} };
     }
 
     // export function addHandler(handler: (state: ParsedUrl | null) => void) {
@@ -78,10 +78,10 @@ export namespace HistoryUtil {
     const parsers: { [type: string]: (pathname: string[], opts: qs.ParsedQuery) => ParsedUrl | undefined } = {};
     const stringifiers: { [type: string]: (state: ParsedUrl) => string } = {};
 
-    type ParserValue = true | "none" | "json" | ((value: string) => any);
+    type ParserValue = true | 'none' | 'json' | ((value: string) => any);
 
     type Parser = {
-        [key: string]: ParserValue
+        [key: string]: ParserValue;
     };
 
     function addParser(type: string, requiredFields: Parser, optionalFields: Parser, customParser?: (pathname: string[], opts: qs.ParsedQuery, current: ParsedUrl) => ParsedUrl | null | undefined) {
@@ -90,9 +90,9 @@ export namespace HistoryUtil {
                 return value;
             }
             if (Array.isArray(value)) {
-            } else if (parser === true || parser === "json") {
+            } else if (parser === true || parser === 'json') {
                 value = JSON.parse(value);
-            } else if (parser === "none") {
+            } else if (parser === 'none') {
             } else {
                 value = parser(value);
             }
@@ -142,29 +142,28 @@ export namespace HistoryUtil {
             }
             const queryObj = OmitKeys(state, keys).extract;
             const query: any = {};
-            Object.keys(queryObj).forEach(key => query[key] = queryObj[key] === null ? null : JSON.stringify(queryObj[key]));
+            Object.keys(queryObj).forEach(key => (query[key] = queryObj[key] === null ? null : JSON.stringify(queryObj[key])));
             const queryString = qs.stringify(query);
-            return path + (queryString ? `?${queryString}` : "");
+            return path + (queryString ? `?${queryString}` : '');
         };
     }
 
-    addParser("doc", {}, { readonly: true, initializers: true, nro: true, sharing: true }, (pathname, opts, current) => {
+    addParser('doc', {}, { readonly: true, initializers: true, nro: true, sharing: true }, (pathname, opts, current) => {
         if (pathname.length !== 2) return undefined;
 
         current.initializers = current.initializers || {};
         const docId = pathname[1];
         current.docId = docId;
     });
-    addStringifier("doc", ["initializers", "readonly", "nro"], (state, current) => {
+    addStringifier('doc', ['initializers', 'readonly', 'nro'], (state, current) => {
         return `${current}/${state.docId}`;
     });
 
-
     export function parseUrl(location: Location | URL): ParsedUrl | undefined {
         const pathname = location.pathname.substring(1);
         const search = location.search;
         const opts = search.length ? qs.parse(search, { sort: false }) : {};
-        const pathnameSplit = pathname.split("/");
+        const pathnameSplit = pathname.split('/');
 
         const type = pathnameSplit[0];
 
@@ -179,7 +178,7 @@ export namespace HistoryUtil {
         if (params.type in stringifiers) {
             return stringifiers[params.type](params);
         }
-        return "";
+        return '';
     }
 
     export async function initDoc(id: string, initializer: DocInitializerList) {
@@ -197,7 +196,7 @@ export namespace HistoryUtil {
             await Promise.all(Object.keys(init).map(id => initDoc(id, init[id])));
         }
         if (field instanceof Doc) {
-            CurrentUserUtils.openDashboard(field, true);
+            DashboardView.openDashboard(field, true);
         }
     }
 
diff --git a/src/client/util/LinkFollower.ts b/src/client/util/LinkFollower.ts
new file mode 100644
index 000000000..f75ac24f5
--- /dev/null
+++ b/src/client/util/LinkFollower.ts
@@ -0,0 +1,112 @@
+import { action, observable, observe } from 'mobx';
+import { computedFn } from 'mobx-utils';
+import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../fields/Doc';
+import { List } from '../../fields/List';
+import { ProxyField } from '../../fields/Proxy';
+import { BoolCast, Cast, StrCast } from '../../fields/Types';
+import { LightboxView } from '../views/LightboxView';
+import { DocumentViewSharedProps, ViewAdjustment } from '../views/nodes/DocumentView';
+import { DocumentManager } from './DocumentManager';
+import { UndoManager } from './UndoManager';
+
+type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void;
+/*
+ * link doc:
+ * - anchor1: doc
+ * - anchor2: doc
+ *
+ * group doc:
+ * - type: string representing the group type/name/category
+ * - metadata: doc representing the metadata kvps
+ *
+ * metadata doc:
+ * - user defined kvps
+ */
+export class LinkFollower {
+    // follows a link - if the target is on screen, it highlights/pans to it.
+    // if the target isn't onscreen, then it will open up the target in the lightbox, or in place
+    // depending on the followLinkLocation property of the source (or the link itself as a fallback);
+    public static FollowLink = (linkDoc: Opt<Doc>, sourceDoc: Doc, docViewProps: DocumentViewSharedProps, altKey: boolean, zoom: boolean = false) => {
+        const batch = UndoManager.StartBatch('follow link click');
+        // open up target if it's not already in view ...
+        const createViewFunc = (doc: Doc, followLoc: string, finished?: Opt<() => void>) => {
+            const createTabForTarget = (didFocus: boolean) =>
+                new Promise<ViewAdjustment>(res => {
+                    const where = LightboxView.LightboxDoc ? 'lightbox' : StrCast(sourceDoc.followLinkLocation, followLoc);
+                    docViewProps.addDocTab(doc, where);
+                    setTimeout(() => {
+                        const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView;
+                        const targDocView = getFirstDocView(doc); // get first document view available within the lightbox if that's open, or anywhere otherwise.
+                        if (targDocView) {
+                            targDocView.props.focus(doc, {
+                                willZoom: BoolCast(sourceDoc.followLinkZoom, false),
+                                afterFocus: (didFocus: boolean) => {
+                                    finished?.();
+                                    res(ViewAdjustment.resetView);
+                                    return new Promise<ViewAdjustment>(res2 => res2(ViewAdjustment.doNothing));
+                                },
+                            });
+                        } else {
+                            res(where !== 'inPlace' ? ViewAdjustment.resetView : ViewAdjustment.doNothing); // for 'inPlace'  resetting the initial focus&zoom would negate the zoom into the target
+                        }
+                    });
+                });
+
+            if (!sourceDoc.followLinkZoom) {
+                createTabForTarget(false);
+            } else {
+                // first focus & zoom onto this (the clicked document).  Then execute the function to focus on the target
+                docViewProps.focus(sourceDoc, { willZoom: BoolCast(sourceDoc.followLinkZoom, true), scale: 1, afterFocus: createTabForTarget });
+            }
+        };
+        LinkFollower.traverseLink(linkDoc, sourceDoc, createViewFunc, BoolCast(sourceDoc.followLinkZoom, zoom), docViewProps.ContainingCollectionDoc, batch.end, altKey ? true : undefined);
+    };
+
+    public static traverseLink(link: Opt<Doc>, sourceDoc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) {
+        const linkDocs = link ? [link] : DocListCast(sourceDoc.links);
+        const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor1
+        const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor2
+        const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0);
+        const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0);
+        const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView || backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView;
+        const linkDocList = linkWithoutTargetDoc ? [linkWithoutTargetDoc] : traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs;
+        const followLinks = sourceDoc.isPushpin ? linkDocList : linkDocList.slice(0, 1);
+        var count = 0;
+        const allFinished = () => ++count === followLinks.length && finished?.();
+        followLinks.forEach(async linkDoc => {
+            if (linkDoc) {
+                const target = (
+                    sourceDoc === linkDoc.anchor1
+                        ? linkDoc.anchor2
+                        : sourceDoc === linkDoc.anchor2
+                        ? linkDoc.anchor1
+                        : Doc.AreProtosEqual(sourceDoc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc)
+                        ? linkDoc.anchor2
+                        : linkDoc.anchor1
+                ) as Doc;
+                if (target) {
+                    if (target.TourMap) {
+                        const fieldKey = Doc.LayoutFieldKey(target);
+                        const tour = DocListCast(target[fieldKey]).reverse();
+                        LightboxView.SetLightboxDoc(currentContext, undefined, tour);
+                        setTimeout(LightboxView.Next);
+                        allFinished();
+                    } else {
+                        const containerAnnoDoc = Cast(target.annotationOn, Doc, null);
+                        const containerDoc = containerAnnoDoc || target;
+                        var containerDocContext = containerDoc?.context ? [Cast(containerDoc?.context, Doc, null)] : ([] as Doc[]);
+                        while (containerDocContext.length && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && containerDocContext[0].context) {
+                            containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
+                        }
+                        const targetContexts = LightboxView.LightboxDoc ? [containerAnnoDoc || containerDocContext[0]].filter(a => a) : containerDocContext;
+                        DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, 'lightbox'), finished), targetContexts, linkDoc, undefined, sourceDoc, allFinished);
+                    }
+                } else {
+                    allFinished();
+                }
+            } else {
+                allFinished();
+            }
+        });
+    }
+}
diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts
index d51cd350d..7a12a8580 100644
--- a/src/client/util/LinkManager.ts
+++ b/src/client/util/LinkManager.ts
@@ -1,35 +1,30 @@
-import { action, observable, observe } from "mobx";
-import { computedFn } from "mobx-utils";
-import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from "../../fields/Doc";
-import { List } from "../../fields/List";
-import { ProxyField } from "../../fields/Proxy";
-import { BoolCast, Cast, StrCast } from "../../fields/Types";
-import { LightboxView } from "../views/LightboxView";
-import { DocumentViewSharedProps, ViewAdjustment } from "../views/nodes/DocumentView";
-import { DocumentManager } from "./DocumentManager";
-import { UndoManager } from "./UndoManager";
-
-type CreateViewFunc = (doc: Doc, followLinkLocation: string, finished?: () => void) => void;
-/* 
- * link doc: 
- * - anchor1: doc 
+import { action, observable, observe } from 'mobx';
+import { computedFn } from 'mobx-utils';
+import { DirectLinksSym, Doc, DocListCast, DocListCastAsync, Field, Opt } from '../../fields/Doc';
+import { List } from '../../fields/List';
+import { ProxyField } from '../../fields/Proxy';
+import { Cast, StrCast } from '../../fields/Types';
+/*
+ * link doc:
+ * - anchor1: doc
  * - anchor2: doc
- * 
+ *
  * group doc:
  * - type: string representing the group type/name/category
  * - metadata: doc representing the metadata kvps
- * 
+ *
  * metadata doc:
- * - user defined kvps 
+ * - user defined kvps
  */
 export class LinkManager {
-
     @observable static _instance: LinkManager;
     @observable static userLinkDBs: Doc[] = [];
     public static currentLink: Opt<Doc>;
-    public static get Instance() { return LinkManager._instance; }
+    public static get Instance() {
+        return LinkManager._instance;
+    }
     public static addLinkDB = (linkDb: any) => LinkManager.userLinkDBs.push(linkDb);
-    public static AutoKeywords = "keywords:Usages";
+    public static AutoKeywords = 'keywords:Usages';
     static links: Doc[] = [];
     constructor() {
         LinkManager._instance = this;
@@ -38,50 +33,61 @@ export class LinkManager {
         const addLinkToDoc = (link: Doc) => {
             const a1Prom = link?.anchor1;
             const a2Prom = link?.anchor2;
-            Promise.all([a1Prom, a2Prom]).then((all) => {
+            Promise.all([a1Prom, a2Prom]).then(all => {
                 const a1 = all[0];
                 const a2 = all[1];
                 const a1ProtoProm = (link?.anchor1 as Doc)?.proto;
                 const a2ProtoProm = (link?.anchor2 as Doc)?.proto;
-                Promise.all([a1ProtoProm, a2ProtoProm]).then(action((all) => {
-                    if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
-                        Doc.GetProto(a1)[DirectLinksSym].add(link);
-                        Doc.GetProto(a2)[DirectLinksSym].add(link);
-                        Doc.GetProto(link)[DirectLinksSym].add(link);
-                    }
-                }));
+                Promise.all([a1ProtoProm, a2ProtoProm]).then(
+                    action(all => {
+                        if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
+                            Doc.GetProto(a1)[DirectLinksSym].add(link);
+                            Doc.GetProto(a2)[DirectLinksSym].add(link);
+                            Doc.GetProto(link)[DirectLinksSym].add(link);
+                        }
+                    })
+                );
             });
         };
         const remLinkFromDoc = (link: Doc) => {
             const a1 = link?.anchor1;
             const a2 = link?.anchor2;
-            Promise.all([a1, a2]).then(action(() => {
-                if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
-                    Doc.GetProto(a1)[DirectLinksSym].delete(link);
-                    Doc.GetProto(a2)[DirectLinksSym].delete(link);
-                    Doc.GetProto(link)[DirectLinksSym].delete(link);
-                }
-            }));
+            Promise.all([a1, a2]).then(
+                action(() => {
+                    if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) {
+                        Doc.GetProto(a1)[DirectLinksSym].delete(link);
+                        Doc.GetProto(a2)[DirectLinksSym].delete(link);
+                        Doc.GetProto(link)[DirectLinksSym].delete(link);
+                    }
+                })
+            );
         };
         const watchUserLinkDB = (userLinkDBDoc: Doc) => {
             LinkManager.links.push(...DocListCast(userLinkDBDoc.data));
-            const toRealField = (field: Field) => field instanceof ProxyField ? field.value() : field;  // see List.ts.  data structure is not a simple list of Docs, but a list of ProxyField/Fields
-            observe(userLinkDBDoc.data as Doc, change => {  // observe pushes/splices on a user link DB 'data' field (should only happen for local changes)
-                switch (change.type as any) {
-                    case "splice":
-                        (change as any).added.forEach((link: any) => addLinkToDoc(toRealField(link)));
-                        (change as any).removed.forEach((link: any) => remLinkFromDoc(toRealField(link)));
-                        break;
-                    case "update": //let oldValue = change.oldValue;
-                }
-            }, true);
-            observe(userLinkDBDoc, "data",  // obsever when a new array of links is assigned as the link DB 'data' field (should happen whenever a remote user adds/removes a link)
+            const toRealField = (field: Field) => (field instanceof ProxyField ? field.value() : field); // see List.ts.  data structure is not a simple list of Docs, but a list of ProxyField/Fields
+            observe(
+                userLinkDBDoc.data as Doc,
                 change => {
+                    // observe pushes/splices on a user link DB 'data' field (should only happen for local changes)
                     switch (change.type as any) {
-                        case "update":
-                            Promise.all([...(change.oldValue as any as Doc[] || []), ...(change.newValue as any as Doc[] || [])]).then(doclist => {
-                                const oldDocs = doclist.slice(0, (change.oldValue as any as Doc[] || []).length);
-                                const newDocs = doclist.slice((change.oldValue as any as Doc[] || []).length, doclist.length);
+                        case 'splice':
+                            (change as any).added.forEach((link: any) => addLinkToDoc(toRealField(link)));
+                            (change as any).removed.forEach((link: any) => remLinkFromDoc(toRealField(link)));
+                            break;
+                        case 'update': //let oldValue = change.oldValue;
+                    }
+                },
+                true
+            );
+            observe(
+                userLinkDBDoc,
+                'data', // obsever when a new array of links is assigned as the link DB 'data' field (should happen whenever a remote user adds/removes a link)
+                change => {
+                    switch (change.type as any) {
+                        case 'update':
+                            Promise.all([...((change.oldValue as any as Doc[]) || []), ...((change.newValue as any as Doc[]) || [])]).then(doclist => {
+                                const oldDocs = doclist.slice(0, ((change.oldValue as any as Doc[]) || []).length);
+                                const newDocs = doclist.slice(((change.oldValue as any as Doc[]) || []).length, doclist.length);
 
                                 const added = newDocs?.filter(link => !(oldDocs || []).includes(link));
                                 const removed = oldDocs?.filter(link => !(newDocs || []).includes(link));
@@ -89,38 +95,54 @@ export class LinkManager {
                                 removed?.forEach((link: any) => remLinkFromDoc(toRealField(link)));
                             });
                     }
-                }, true);
+                },
+                true
+            );
         };
-        observe(LinkManager.userLinkDBs, change => {
-            switch (change.type as any) {
-                case "splice": (change as any).added.forEach(watchUserLinkDB); break;
-                case "update": //let oldValue = change.oldValue;
-            }
-        }, true);
+        observe(
+            LinkManager.userLinkDBs,
+            change => {
+                switch (change.type as any) {
+                    case 'splice':
+                        (change as any).added.forEach(watchUserLinkDB);
+                        break;
+                    case 'update': //let oldValue = change.oldValue;
+                }
+            },
+            true
+        );
         LinkManager.addLinkDB(Doc.LinkDBDoc());
-        DocListCastAsync(Doc.LinkDBDoc()?.data).then(dblist => dblist?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager
-            const a1 = await Cast(link?.anchor1, Doc, null);
-            const a2 = await Cast(link?.anchor2, Doc, null);
-        }));
+        DocListCastAsync(Doc.LinkDBDoc()?.data).then(dblist =>
+            dblist?.forEach(async link => {
+                // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager
+                const a1 = await Cast(link?.anchor1, Doc, null);
+                const a2 = await Cast(link?.anchor2, Doc, null);
+            })
+        );
     }
 
-
     public createLinkrelationshipLists = () => {
         //create new lists for link relations and their associated colors if the lists don't already exist
         !Doc.UserDoc().linkRelationshipList && (Doc.UserDoc().linkRelationshipList = new List<string>());
         !Doc.UserDoc().linkColorList && (Doc.UserDoc().linkColorList = new List<string>());
         !Doc.UserDoc().linkRelationshipSizes && (Doc.UserDoc().linkRelationshipSizes = new List<number>());
-    }
+    };
 
     public addLink(linkDoc: Doc, checkExists = false) {
         if (!checkExists || !DocListCast(Doc.LinkDBDoc().data).includes(linkDoc)) {
-            Doc.AddDocToList(Doc.LinkDBDoc(), "data", linkDoc);
+            Doc.AddDocToList(Doc.LinkDBDoc(), 'data', linkDoc);
         }
     }
-    public deleteLink(linkDoc: Doc) { return Doc.RemoveDocFromList(Doc.LinkDBDoc(), "data", linkDoc); }
-    public deleteAllLinksOnAnchor(anchor: Doc) { LinkManager.Instance.relatedLinker(anchor).forEach(linkDoc => LinkManager.Instance.deleteLink(linkDoc)); }
+    public deleteLink(linkDoc: Doc) {
+        return Doc.RemoveDocFromList(Doc.LinkDBDoc(), 'data', linkDoc);
+    }
+    public deleteAllLinksOnAnchor(anchor: Doc) {
+        LinkManager.Instance.relatedLinker(anchor).forEach(linkDoc => LinkManager.Instance.deleteLink(linkDoc));
+    }
 
-    public getAllRelatedLinks(anchor: Doc) { return this.relatedLinker(anchor); } // finds all links that contain the given anchor
+    public getAllRelatedLinks(anchor: Doc) {
+        return this.relatedLinker(anchor);
+    } // finds all links that contain the given anchor
     public getAllDirectLinks(anchor: Doc): Doc[] {
         // FIXME:glr Why is Doc undefined?
         if (Doc.GetProto(anchor)[DirectLinksSym]) {
@@ -133,18 +155,16 @@ export class LinkManager {
     relatedLinker = computedFn(function relatedLinker(this: any, anchor: Doc): Doc[] {
         const lfield = Doc.LayoutFieldKey(anchor);
         if (!anchor || anchor instanceof Promise || Doc.GetProto(anchor) instanceof Promise) {
-            console.log("WAITING FOR DOC/PROTO IN LINKMANAGER");
+            console.log('WAITING FOR DOC/PROTO IN LINKMANAGER');
             return [];
         }
         const dirLinks = Doc.GetProto(anchor)[DirectLinksSym];
-        const annos = DocListCast(anchor[lfield + "-annotations"]);
-        const timelineAnnos = DocListCast(anchor[lfield + "-annotations-timeline"]);
+        const annos = DocListCast(anchor[lfield + '-annotations']);
+        const timelineAnnos = DocListCast(anchor[lfield + '-annotations-timeline']);
         if (!annos || !timelineAnnos) {
             debugger;
         }
-        const related = [...annos, ...timelineAnnos].reduce((list, anno) =>
-            [...list, ...LinkManager.Instance.relatedLinker(anno)],
-            Array.from(dirLinks).slice());
+        const related = [...annos, ...timelineAnnos].reduce((list, anno) => [...list, ...LinkManager.Instance.relatedLinker(anno)], Array.from(dirLinks).slice());
         return related;
     }, true);
 
@@ -152,15 +172,15 @@ export class LinkManager {
     public getRelatedGroupedLinks(anchor: Doc): Map<string, Array<Doc>> {
         const anchorGroups = new Map<string, Array<Doc>>();
         this.relatedLinker(anchor).forEach(link => {
-            if (link.linkRelationship && link.linkRelationship !== "-ungrouped-") {
+            if (link.linkRelationship && link.linkRelationship !== '-ungrouped-') {
                 const relation = StrCast(link.linkRelationship);
-                const anchorRelation = relation.indexOf(":") !== -1 ? relation.split(":")[Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), anchor) ? 0 : 1] : relation;
+                const anchorRelation = relation.indexOf(':') !== -1 ? relation.split(':')[Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), anchor) ? 0 : 1] : relation;
                 const group = anchorGroups.get(anchorRelation);
                 anchorGroups.set(anchorRelation, group ? [...group, link] : [link]);
             } else {
                 // if link is in no groups then put it in default group
-                const group = anchorGroups.get("*");
-                anchorGroups.set("*", group ? [...group, link] : [link]);
+                const group = anchorGroups.get('*');
+                anchorGroups.set('*', group ? [...group, link] : [link]);
             }
         });
         return anchorGroups;
@@ -178,85 +198,4 @@ export class LinkManager {
         if (Doc.AreProtosEqual(anchor, a2.annotationOn as Doc)) return a1;
         if (Doc.AreProtosEqual(anchor, linkDoc)) return linkDoc;
     }
-
-
-    // follows a link - if the target is on screen, it highlights/pans to it.
-    // if the target isn't onscreen, then it will open up the target in the lightbox, or in place
-    // depending on the followLinkLocation property of the source (or the link itself as a fallback);
-    public static FollowLink = (linkDoc: Opt<Doc>, sourceDoc: Doc, docViewProps: DocumentViewSharedProps, altKey: boolean, zoom: boolean = false) => {
-        const batch = UndoManager.StartBatch("follow link click");
-        // open up target if it's not already in view ...
-        const createViewFunc = (doc: Doc, followLoc: string, finished?: Opt<() => void>) => {
-            const createTabForTarget = (didFocus: boolean) => new Promise<ViewAdjustment>(res => {
-                const where = LightboxView.LightboxDoc ? "lightbox" : StrCast(sourceDoc.followLinkLocation, followLoc);
-                docViewProps.addDocTab(doc, where);
-                setTimeout(() => {
-                    const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView;
-                    const targDocView = getFirstDocView(doc); // get first document view available within the lightbox if that's open, or anywhere otherwise.
-                    if (targDocView) {
-                        targDocView.props.focus(doc, {
-                            willZoom: BoolCast(sourceDoc.followLinkZoom, false),
-                            afterFocus: (didFocus: boolean) => {
-                                finished?.();
-                                res(ViewAdjustment.resetView);
-                                return new Promise<ViewAdjustment>(res2 => res2(ViewAdjustment.doNothing));
-                            }
-                        });
-                    } else {
-                        res(where !== "inPlace" ? ViewAdjustment.resetView : ViewAdjustment.doNothing); // for 'inPlace'  resetting the initial focus&zoom would negate the zoom into the target 
-                    }
-                });
-            });
-
-            if (!sourceDoc.followLinkZoom) {
-                createTabForTarget(false);
-            } else {
-                // first focus & zoom onto this (the clicked document).  Then execute the function to focus on the target
-                docViewProps.focus(sourceDoc, { willZoom: BoolCast(sourceDoc.followLinkZoom, true), scale: 1, afterFocus: createTabForTarget });
-            }
-        };
-        LinkManager.traverseLink(linkDoc, sourceDoc, createViewFunc, BoolCast(sourceDoc.followLinkZoom, zoom), docViewProps.ContainingCollectionDoc, batch.end, altKey ? true : undefined);
-    }
-
-    public static traverseLink(link: Opt<Doc>, sourceDoc: Doc, createViewFunc: CreateViewFunc, zoom = false, currentContext?: Doc, finished?: () => void, traverseBacklink?: boolean) {
-        const linkDocs = link ? [link] : DocListCast(sourceDoc.links);
-        const firstDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor1 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor1
-        const secondDocs = linkDocs.filter(linkDoc => Doc.AreProtosEqual(linkDoc.anchor2 as Doc, sourceDoc) || Doc.AreProtosEqual((linkDoc.anchor2 as Doc).annotationOn as Doc, sourceDoc)); // link docs where 'doc' is anchor2
-        const fwdLinkWithoutTargetView = firstDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor2 as Doc).length === 0);
-        const backLinkWithoutTargetView = secondDocs.find(d => DocumentManager.Instance.getDocumentViews(d.anchor1 as Doc).length === 0);
-        const linkWithoutTargetDoc = traverseBacklink === undefined ? fwdLinkWithoutTargetView || backLinkWithoutTargetView : traverseBacklink ? backLinkWithoutTargetView : fwdLinkWithoutTargetView;
-        const linkDocList = linkWithoutTargetDoc ? [linkWithoutTargetDoc] : (traverseBacklink === undefined ? firstDocs.concat(secondDocs) : traverseBacklink ? secondDocs : firstDocs);
-        const followLinks = sourceDoc.isPushpin ? linkDocList : linkDocList.slice(0, 1);
-        var count = 0;
-        const allFinished = () => ++count === followLinks.length && finished?.();
-        followLinks.forEach(async linkDoc => {
-            if (linkDoc) {
-                const target = (sourceDoc === linkDoc.anchor1 ? linkDoc.anchor2 : sourceDoc === linkDoc.anchor2 ? linkDoc.anchor1 :
-                    (Doc.AreProtosEqual(sourceDoc, linkDoc.anchor1 as Doc) || Doc.AreProtosEqual((linkDoc.anchor1 as Doc).annotationOn as Doc, sourceDoc) ? linkDoc.anchor2 : linkDoc.anchor1)) as Doc;
-                if (target) {
-                    if (target.TourMap) {
-                        const fieldKey = Doc.LayoutFieldKey(target);
-                        const tour = DocListCast(target[fieldKey]).reverse();
-                        LightboxView.SetLightboxDoc(currentContext, undefined, tour);
-                        setTimeout(LightboxView.Next);
-                        allFinished();
-                    } else {
-                        const containerAnnoDoc = Cast(target.annotationOn, Doc, null);
-                        const containerDoc = containerAnnoDoc || target;
-                        var containerDocContext = containerDoc?.context ? [Cast(containerDoc?.context, Doc, null)] : [] as Doc[];
-                        while (containerDocContext.length && !DocumentManager.Instance.getDocumentView(containerDocContext[0]) && containerDocContext[0].context) {
-                            containerDocContext = [Cast(containerDocContext[0].context, Doc, null), ...containerDocContext];
-                        }
-                        const targetContexts = LightboxView.LightboxDoc ? [containerAnnoDoc || containerDocContext[0]].filter(a => a) : containerDocContext;
-                        DocumentManager.Instance.jumpToDocument(target, zoom, (doc, finished) => createViewFunc(doc, StrCast(linkDoc.followLinkLocation, "lightbox"), finished), targetContexts, linkDoc, undefined, sourceDoc, allFinished);
-                    }
-                } else {
-                    allFinished();
-                }
-            } else {
-                allFinished();
-            }
-        });
-    }
-
-}
\ No newline at end of file
+}
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index b71086561..d67ce6f6a 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,15 +1,11 @@
-import { action, observable, ObservableMap } from "mobx";
-import { computedFn } from "mobx-utils";
-import { Doc, Opt } from "../../fields/Doc";
-import { DocumentType } from "../documents/DocumentTypes";
-import { CollectionViewType } from "../views/collections/CollectionView";
-import { DocumentView } from "../views/nodes/DocumentView";
-import { CurrentUserUtils } from "./CurrentUserUtils";
+import { action, observable, ObservableMap } from 'mobx';
+import { computedFn } from 'mobx-utils';
+import { Doc, Opt } from '../../fields/Doc';
+import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
+import { DocumentView } from '../views/nodes/DocumentView';
 
 export namespace SelectionManager {
-
     class Manager {
-
         @observable IsDragging: boolean = false;
         SelectedViews: ObservableMap<DocumentView, Doc> = new ObservableMap();
         @observable SelectedSchemaDocument: Doc | undefined;
@@ -63,16 +59,21 @@ export namespace SelectionManager {
         manager.SelectSchemaViewDoc(document);
     }
 
-    const IsSelectedCache = computedFn(function isSelected(doc: DocumentView) {  // wrapping get() in a computedFn only generates mobx() invalidations when the return value of the function for the specific get parameters has changed
+    const IsSelectedCache = computedFn(function isSelected(doc: DocumentView) {
+        // wrapping get() in a computedFn only generates mobx() invalidations when the return value of the function for the specific get parameters has changed
         return manager.SelectedViews.get(doc) ? true : false;
     });
     // computed functions, such as used in IsSelected generate errors if they're called outside of a
     // reaction context.  Specifying the context with 'outsideReaction' allows an efficiency feature
     // to avoid unnecessary mobx invalidations when running inside a reaction.
     export function IsSelected(doc: DocumentView | undefined, outsideReaction?: boolean): boolean {
-        return !doc ? false : outsideReaction ?
-            manager.SelectedViews.get(doc) ? true : false : // get() accesses a hashtable -- setting anything in the hashtable generates a mobx invalidation for every get()
-            IsSelectedCache(doc);
+        return !doc
+            ? false
+            : outsideReaction
+            ? manager.SelectedViews.get(doc)
+                ? true
+                : false // get() accesses a hashtable -- setting anything in the hashtable generates a mobx invalidation for every get()
+            : IsSelectedCache(doc);
     }
 
     export function DeselectAll(except?: Doc): void {
@@ -96,4 +97,4 @@ export namespace SelectionManager {
     export function Docs(): Doc[] {
         return Array.from(manager.SelectedViews.values()).filter(doc => doc?._viewType !== CollectionViewType.Docking);
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 080237649..12d1793af 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -1,29 +1,28 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import { ColorState, SketchPicker } from "react-color";
-import { Doc } from "../../fields/Doc";
-import { BoolCast, StrCast, Cast } from "../../fields/Types";
-import { addStyleSheet, addStyleSheetRule, Utils } from "../../Utils";
-import { GoogleAuthenticationManager } from "../apis/GoogleAuthenticationManager";
-import { DocServer } from "../DocServer";
-import { Networking } from "../Network";
-import { MainViewModal } from "../views/MainViewModal";
-import { FontIconBox } from "../views/nodes/button/FontIconBox";
-import { CurrentUserUtils } from "./CurrentUserUtils";
-import { DragManager } from "./DragManager";
-import { GroupManager } from "./GroupManager";
-import "./SettingsManager.scss";
-import { undoBatch } from "./UndoManager";
-const higflyout = require("@hig/flyout");
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { ColorState, SketchPicker } from 'react-color';
+import { Doc } from '../../fields/Doc';
+import { BoolCast, Cast, StrCast } from '../../fields/Types';
+import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils';
+import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
+import { DocServer } from '../DocServer';
+import { Networking } from '../Network';
+import { MainViewModal } from '../views/MainViewModal';
+import { FontIconBox } from '../views/nodes/button/FontIconBox';
+import { DragManager } from './DragManager';
+import { GroupManager } from './GroupManager';
+import './SettingsManager.scss';
+import { undoBatch } from './UndoManager';
+const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
 export enum ColorScheme {
-    Dark = "-Dark",
-    Light = "-Light",
-    System = "-MatchSystem"
+    Dark = '-Dark',
+    Light = '-Light',
+    System = '-MatchSystem',
 }
 
 @observer
@@ -31,67 +30,77 @@ export class SettingsManager extends React.Component<{}> {
     public static Instance: SettingsManager;
     static _settingsStyle = addStyleSheet();
     @observable private isOpen = false;
-    @observable private passwordResultText = "";
+    @observable private passwordResultText = '';
     @observable private playgroundMode = false;
 
-    @observable private curr_password = "";
-    @observable private new_password = "";
-    @observable private new_confirm = "";
-    @observable activeTab = "Accounts";
+    @observable private curr_password = '';
+    @observable private new_password = '';
+    @observable private new_confirm = '';
+    @observable activeTab = 'Accounts';
 
-    @computed get backgroundColor() { return Doc.UserDoc().activeCollectionBackground; }
-    @computed get colorScheme() { return CurrentUserUtils.ActiveDashboard?.colorScheme; }
+    @observable public static propertiesWidth: number = 0;
+    @observable public static headerBarHeight: number = 0;
+
+    @computed get backgroundColor() {
+        return Doc.UserDoc().activeCollectionBackground;
+    }
+    @computed get colorScheme() {
+        return Doc.ActiveDashboard?.colorScheme;
+    }
 
     constructor(props: {}) {
         super(props);
         SettingsManager.Instance = this;
     }
 
-    public close = action(() => this.isOpen = false);
-    public open = action(() => this.isOpen = true);
+    public close = action(() => (this.isOpen = false));
+    public open = action(() => (this.isOpen = true));
 
     private googleAuthorize = action(() => GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken(true));
     private changePassword = async () => {
         if (!(this.curr_password && this.new_password && this.new_confirm)) {
-            runInAction(() => this.passwordResultText = "Error: Hey, we're missing some fields!");
+            runInAction(() => (this.passwordResultText = "Error: Hey, we're missing some fields!"));
         } else {
             const passwordBundle = { curr_pass: this.curr_password, new_pass: this.new_password, new_confirm: this.new_confirm };
             const { error } = await Networking.PostToServer('/internalResetPassword', passwordBundle);
-            runInAction(() => this.passwordResultText = error ? "Error: " + error[0].msg + "..." : "Password successfully updated!");
+            runInAction(() => (this.passwordResultText = error ? 'Error: ' + error[0].msg + '...' : 'Password successfully updated!'));
         }
-    }
-
-    @undoBatch selectUserMode = action((e: React.ChangeEvent) => Doc.noviceMode = (e.currentTarget as any)?.value === "Novice");
-    @undoBatch changeShowTitle = action((e: React.ChangeEvent) => Doc.UserDoc().showTitle = (e.currentTarget as any).value ? "title" : undefined);
-    @undoBatch changeFontFamily = action((e: React.ChangeEvent) => Doc.UserDoc().fontFamily = (e.currentTarget as any).value);
-    @undoBatch changeFontSize = action((e: React.ChangeEvent) => Doc.UserDoc().fontSize = (e.currentTarget as any).value);
-    @undoBatch switchActiveBackgroundColor = action((color: ColorState) => Doc.UserDoc().activeCollectionBackground = String(color.hex));
-    @undoBatch switchUserColor = action((color: ColorState) => { Doc.SharingDoc().userColor = undefined; Doc.GetProto(Doc.SharingDoc()).userColor = String(color.hex); });
+    };
+
+    @undoBatch selectUserMode = action((e: React.ChangeEvent) => (Doc.noviceMode = (e.currentTarget as any)?.value === 'Novice'));
+    @undoBatch changeShowTitle = action((e: React.ChangeEvent) => (Doc.UserDoc().showTitle = (e.currentTarget as any).value ? 'title' : undefined));
+    @undoBatch changeFontFamily = action((e: React.ChangeEvent) => (Doc.UserDoc().fontFamily = (e.currentTarget as any).value));
+    @undoBatch changeFontSize = action((e: React.ChangeEvent) => (Doc.UserDoc().fontSize = (e.currentTarget as any).value));
+    @undoBatch switchActiveBackgroundColor = action((color: ColorState) => (Doc.UserDoc().activeCollectionBackground = String(color.hex)));
+    @undoBatch switchUserColor = action((color: ColorState) => {
+        Doc.SharingDoc().userColor = undefined;
+        Doc.GetProto(Doc.SharingDoc()).userColor = String(color.hex);
+    });
     @undoBatch playgroundModeToggle = action(() => {
         this.playgroundMode = !this.playgroundMode;
         if (this.playgroundMode) {
             DocServer.Control.makeReadOnly();
-            addStyleSheetRule(SettingsManager._settingsStyle, "topbar-inner-container", { background: "red !important" });
-        }
-        else DocServer.Control.makeEditable();
+            addStyleSheetRule(SettingsManager._settingsStyle, 'topbar-inner-container', { background: 'red !important' });
+        } else DocServer.Control.makeEditable();
     });
 
     @undoBatch
     @action
     changeColorScheme = action((e: React.ChangeEvent) => {
-        const activeDashboard= CurrentUserUtils.ActiveDashboard;
+        const activeDashboard = Doc.ActiveDashboard;
         if (!activeDashboard) return;
         const scheme: ColorScheme = (e.currentTarget as any).value;
         switch (scheme) {
             case ColorScheme.Light:
                 activeDashboard.colorScheme = undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
-                addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "#d3d3d3 !important" });
+                addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: '#d3d3d3 !important' });
                 break;
             case ColorScheme.Dark:
                 activeDashboard.colorScheme = ColorScheme.Dark;
-                addStyleSheetRule(SettingsManager._settingsStyle, "lm_header", { background: "black !important" });
+                addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: 'black !important' });
                 break;
-            case ColorScheme.System: default:
+            case ColorScheme.System:
+            default:
                 window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
                     activeDashboard.colorScheme = e.matches ? ColorScheme.Dark : undefined; // undefined means ColorScheme.Light until all CSS is updated with values for each color scheme (e.g., see MainView.scss, DocumentDecorations.scss)
                 });
@@ -99,223 +108,285 @@ export class SettingsManager extends React.Component<{}> {
         }
     });
 
-
     @computed get colorsContent() {
-        const colorBox = (func: (color: ColorState) => void) => <SketchPicker onChange={func} color={StrCast(this.backgroundColor)}
-            presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
-                '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
-                '#FFFFFF', '#f1efeb', 'transparent']} />;
-
-        const colorFlyout = <div className="colorFlyout">
-            <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={colorBox(this.switchActiveBackgroundColor)}>
-                <div className="colorFlyout-button" style={{ backgroundColor: StrCast(this.backgroundColor) }} onPointerDown={e => e.stopPropagation()} >
-                    <FontAwesomeIcon icon="palette" size="sm" color={StrCast(this.backgroundColor)} />
-                </div>
-            </Flyout>
-        </div>;
-        const userColorFlyout = <div className="colorFlyout">
-            <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={colorBox(this.switchUserColor)}>
-                <div className="colorFlyout-button" style={{ backgroundColor: StrCast(this.backgroundColor) }} onPointerDown={e => e.stopPropagation()} >
-                    <FontAwesomeIcon icon="palette" size="sm" color={StrCast(this.backgroundColor)} />
-                </div>
-            </Flyout>
-        </div>;
+        const colorBox = (func: (color: ColorState) => void) => (
+            <SketchPicker
+                onChange={func}
+                color={StrCast(this.backgroundColor)}
+                presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
+            />
+        );
+
+        const colorFlyout = (
+            <div className="colorFlyout">
+                <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={colorBox(this.switchActiveBackgroundColor)}>
+                    <div className="colorFlyout-button" style={{ backgroundColor: StrCast(this.backgroundColor) }} onPointerDown={e => e.stopPropagation()}>
+                        <FontAwesomeIcon icon="palette" size="sm" color={StrCast(this.backgroundColor)} />
+                    </div>
+                </Flyout>
+            </div>
+        );
+        const userColorFlyout = (
+            <div className="colorFlyout">
+                <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={colorBox(this.switchUserColor)}>
+                    <div className="colorFlyout-button" style={{ backgroundColor: StrCast(this.backgroundColor) }} onPointerDown={e => e.stopPropagation()}>
+                        <FontAwesomeIcon icon="palette" size="sm" color={StrCast(this.backgroundColor)} />
+                    </div>
+                </Flyout>
+            </div>
+        );
 
         const colorSchemes = [ColorScheme.Light, ColorScheme.Dark, ColorScheme.System];
-        const schemeMap = ["Light", "Dark", "Match system"];
+        const schemeMap = ['Light', 'Dark', 'Match system'];
 
-        return <div className="colors-content">
-            <div className="preferences-color">
-                <div className="preferences-color-text">Background Color</div>
-                {colorFlyout}
-            </div>
-            <div className="preferences-color">
-                <div className="preferences-color-text">Border/Header Color</div>
-                {userColorFlyout}
-            </div>
-            <div className="preferences-colorScheme">
-                <div className="preferences-color-text">Color Scheme</div>
-                <div className="preferences-color-controls">
-                    <select className="scheme-select" onChange={this.changeColorScheme} defaultValue={StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme)}>
-                        {colorSchemes.map((scheme, i) => <option key={scheme} value={scheme}> {schemeMap[i]} </option>)}
-                    </select>
+        return (
+            <div className="colors-content">
+                <div className="preferences-color">
+                    <div className="preferences-color-text">Background Color</div>
+                    {colorFlyout}
+                </div>
+                <div className="preferences-color">
+                    <div className="preferences-color-text">Border/Header Color</div>
+                    {userColorFlyout}
+                </div>
+                <div className="preferences-colorScheme">
+                    <div className="preferences-color-text">Color Scheme</div>
+                    <div className="preferences-color-controls">
+                        <select className="scheme-select" onChange={this.changeColorScheme} defaultValue={StrCast(Doc.ActiveDashboard?.colorScheme)}>
+                            {colorSchemes.map((scheme, i) => (
+                                <option key={scheme} value={scheme}>
+                                    {' '}
+                                    {schemeMap[i]}{' '}
+                                </option>
+                            ))}
+                        </select>
+                    </div>
                 </div>
             </div>
-        </div>;
+        );
     }
 
     @computed get formatsContent() {
-        return <div className="prefs-content">
-            <div>
-                <input type="checkbox" onChange={e => Doc.UserDoc().showTitle = Doc.UserDoc().showTitle ? undefined : "creationDate"} checked={Doc.UserDoc().showTitle !== undefined} />
-                <div className="preferences-check">Show doc header</div>
-            </div>
-            <div>
-                <input type="checkbox" onChange={e => Doc.UserDoc()["documentLinksButton-fullMenu"] = !Doc.UserDoc()["documentLinksButton-fullMenu"]}
-                    checked={BoolCast(Doc.UserDoc()["documentLinksButton-fullMenu"])} />
-                <div className="preferences-check">Show full toolbar</div>
-            </div>
-            <div>
-                <input type="checkbox" onChange={e => DragManager.SetRaiseWhenDragged(!DragManager.GetRaiseWhenDragged())}
-                    checked={DragManager.GetRaiseWhenDragged()} />
-                <div className="preferences-check">Raise on drag</div>
-            </div>
-            <div>
-                <input type="checkbox" onChange={e => FontIconBox.SetShowLabels(!FontIconBox.GetShowLabels())}
-                    checked={FontIconBox.GetShowLabels()} />
-                <div className="preferences-check">Show button labels</div>
+        return (
+            <div className="prefs-content">
+                <div>
+                    <input type="checkbox" onChange={e => (Doc.UserDoc().showTitle = Doc.UserDoc().showTitle ? undefined : 'creationDate')} checked={Doc.UserDoc().showTitle !== undefined} />
+                    <div className="preferences-check">Show doc header</div>
+                </div>
+                <div>
+                    <input type="checkbox" onChange={e => (Doc.UserDoc()['documentLinksButton-fullMenu'] = !Doc.UserDoc()['documentLinksButton-fullMenu'])} checked={BoolCast(Doc.UserDoc()['documentLinksButton-fullMenu'])} />
+                    <div className="preferences-check">Show full toolbar</div>
+                </div>
+                <div>
+                    <input type="checkbox" onChange={e => DragManager.SetRaiseWhenDragged(!DragManager.GetRaiseWhenDragged())} checked={DragManager.GetRaiseWhenDragged()} />
+                    <div className="preferences-check">Raise on drag</div>
+                </div>
+                <div>
+                    <input type="checkbox" onChange={e => FontIconBox.SetShowLabels(!FontIconBox.GetShowLabels())} checked={FontIconBox.GetShowLabels()} />
+                    <div className="preferences-check">Show button labels</div>
+                </div>
             </div>
-        </div>;
+        );
     }
 
     @computed get appearanceContent() {
-
-        return <div className="tab-content appearances-content">
-            <div className="tab-column">
-                <div className="tab-column-title">Colors</div>
-                <div className="tab-column-content">{this.colorsContent}</div>
-            </div>
-            <div className="tab-column">
-                <div className="tab-column-title">Formats</div>
-                <div className="tab-column-content">{this.formatsContent}</div>
+        return (
+            <div className="tab-content appearances-content">
+                <div className="tab-column">
+                    <div className="tab-column-title">Colors</div>
+                    <div className="tab-column-content">{this.colorsContent}</div>
+                </div>
+                <div className="tab-column">
+                    <div className="tab-column-title">Formats</div>
+                    <div className="tab-column-content">{this.formatsContent}</div>
+                </div>
             </div>
-        </div>;
+        );
     }
 
     @computed get textContent() {
-
-        const fontFamilies = ["Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text", "Roboto"];
-        const fontSizes = ["7px", "8px", "9px", "10px", "12px", "14px", "16px", "18px", "20px", "24px", "32px", "48px", "72px"];
+        const fontFamilies = ['Times New Roman', 'Arial', 'Georgia', 'Comic Sans MS', 'Tahoma', 'Impact', 'Crimson Text', 'Roboto'];
+        const fontSizes = ['7px', '8px', '9px', '10px', '12px', '14px', '16px', '18px', '20px', '24px', '32px', '48px', '72px'];
 
         return (
             <div className="tab-content appearances-content">
                 <div className="preferences-font">
                     <div className="preferences-font-text">Default Font</div>
                     <div className="preferences-font-controls">
-                        <select className="size-select" onChange={this.changeFontSize} value={StrCast(Doc.UserDoc().fontSize, "7px")}>
-                            {fontSizes.map(size => <option key={size} value={size} defaultValue={StrCast(Doc.UserDoc().fontSize)}> {size} </option>)}
+                        <select className="size-select" onChange={this.changeFontSize} value={StrCast(Doc.UserDoc().fontSize, '7px')}>
+                            {fontSizes.map(size => (
+                                <option key={size} value={size} defaultValue={StrCast(Doc.UserDoc().fontSize)}>
+                                    {' '}
+                                    {size}{' '}
+                                </option>
+                            ))}
                         </select>
-                        <select className="font-select" onChange={this.changeFontFamily} value={StrCast(Doc.UserDoc().fontFamily, "Times New Roman")} >
-                            {fontFamilies.map(font => <option key={font} value={font} defaultValue={StrCast(Doc.UserDoc().fontFamily)}> {font} </option>)}
+                        <select className="font-select" onChange={this.changeFontFamily} value={StrCast(Doc.UserDoc().fontFamily, 'Times New Roman')}>
+                            {fontFamilies.map(font => (
+                                <option key={font} value={font} defaultValue={StrCast(Doc.UserDoc().fontFamily)}>
+                                    {' '}
+                                    {font}{' '}
+                                </option>
+                            ))}
                         </select>
                     </div>
                 </div>
-            </div>);
+            </div>
+        );
     }
 
     @action
     changeVal = (e: React.ChangeEvent, pass: string) => {
         const value = (e.target as any).value;
         switch (pass) {
-            case "curr": this.curr_password = value; break;
-            case "new": this.new_password = value; break;
-            case "conf": this.new_confirm = value; break;
+            case 'curr':
+                this.curr_password = value;
+                break;
+            case 'new':
+                this.new_password = value;
+                break;
+            case 'conf':
+                this.new_confirm = value;
+                break;
         }
-    }
+    };
 
     @computed get passwordContent() {
-        return <div className="password-content">
-            <div className="password-content-inputs">
-                <input className="password-inputs" type="password" placeholder="current password" onChange={e => this.changeVal(e, "curr")} />
-                <input className="password-inputs" type="password" placeholder="new password" onChange={e => this.changeVal(e, "new")} />
-                <input className="password-inputs" type="password" placeholder="confirm new password" onChange={e => this.changeVal(e, "conf")} />
-            </div>
-            <div className="password-content-buttons">
-                {!this.passwordResultText ? (null) : <div className={`${this.passwordResultText.startsWith("Error") ? "error" : "success"}-text`}>{this.passwordResultText}</div>}
-                <a className="password-forgot" href="/forgotPassword">forgot password?</a>
-                <button className="password-submit" onClick={this.changePassword}>submit</button>
+        return (
+            <div className="password-content">
+                <div className="password-content-inputs">
+                    <input className="password-inputs" type="password" placeholder="current password" onChange={e => this.changeVal(e, 'curr')} />
+                    <input className="password-inputs" type="password" placeholder="new password" onChange={e => this.changeVal(e, 'new')} />
+                    <input className="password-inputs" type="password" placeholder="confirm new password" onChange={e => this.changeVal(e, 'conf')} />
+                </div>
+                <div className="password-content-buttons">
+                    {!this.passwordResultText ? null : <div className={`${this.passwordResultText.startsWith('Error') ? 'error' : 'success'}-text`}>{this.passwordResultText}</div>}
+                    <a className="password-forgot" href="/forgotPassword">
+                        forgot password?
+                    </a>
+                    <button className="password-submit" onClick={this.changePassword}>
+                        submit
+                    </button>
+                </div>
             </div>
-        </div>;
+        );
     }
 
     @computed get accountOthersContent() {
-        return <div className="account-others-content">
-            <button onClick={this.googleAuthorize} value="data">Authorize Google Acc</button>
-        </div>;
+        return (
+            <div className="account-others-content">
+                <button onClick={this.googleAuthorize} value="data">
+                    Authorize Google Acc
+                </button>
+            </div>
+        );
     }
 
     @computed get accountsContent() {
-        return <div className="tab-content accounts-content">
-            <div className="tab-column">
-                <div className="tab-column-title">Password</div>
-                <div className="tab-column-content">{this.passwordContent}</div>
-            </div>
-            <div className="tab-column">
-                <div className="tab-column-title">Others</div>
-                <div className="tab-column-content">{this.accountOthersContent}</div>
+        return (
+            <div className="tab-content accounts-content">
+                <div className="tab-column">
+                    <div className="tab-column-title">Password</div>
+                    <div className="tab-column-content">{this.passwordContent}</div>
+                </div>
+                <div className="tab-column">
+                    <div className="tab-column-title">Others</div>
+                    <div className="tab-column-content">{this.accountOthersContent}</div>
+                </div>
             </div>
-        </div>;
+        );
     }
 
     @computed get modesContent() {
-        return <div className="tab-content modes-content">
-            <div className="tab-column">
-                <div className="tab-column-title">Modes</div>
-                <div className="tab-column-content">
-                    <select className="modes-select" onChange={this.selectUserMode} defaultValue={Doc.noviceMode ? "Novice" : "Developer"}>
-                        <option key={"Novice"} value={"Novice"}> Novice </option>
-                        <option key={"Developer"} value={"Developer"}> Developer</option>
-                    </select>
-                    <div className="modes-playground">
-                        <input className="playground-check" type="checkbox" checked={this.playgroundMode} onChange={this.playgroundModeToggle} />
-                        <div className="playground-text">Playground Mode</div>
+        return (
+            <div className="tab-content modes-content">
+                <div className="tab-column">
+                    <div className="tab-column-title">Modes</div>
+                    <div className="tab-column-content">
+                        <select className="modes-select" onChange={this.selectUserMode} defaultValue={Doc.noviceMode ? 'Novice' : 'Developer'}>
+                            <option key={'Novice'} value={'Novice'}>
+                                {' '}
+                                Novice{' '}
+                            </option>
+                            <option key={'Developer'} value={'Developer'}>
+                                {' '}
+                                Developer
+                            </option>
+                        </select>
+                        <div className="modes-playground">
+                            <input className="playground-check" type="checkbox" checked={this.playgroundMode} onChange={this.playgroundModeToggle} />
+                            <div className="playground-text">Playground Mode</div>
+                        </div>
                     </div>
                 </div>
-            </div>
-            <div className="tab-column">
-                <div className="tab-column-title">Permissions</div>
-                <div className="tab-column-content">
-                    <button onClick={() => GroupManager.Instance?.open()}>Manage groups</button>
-                    <div className="default-acl">
-                        <input className="acl-check" type="checkbox" checked={BoolCast(Doc.defaultAclPrivate)}
-                            onChange={action(() => Doc.defaultAclPrivate = !Doc.defaultAclPrivate)} />
-                        <div className="acl-text">Default access private</div>
+                <div className="tab-column">
+                    <div className="tab-column-title">Permissions</div>
+                    <div className="tab-column-content">
+                        <button onClick={() => GroupManager.Instance?.open()}>Manage groups</button>
+                        <div className="default-acl">
+                            <input className="acl-check" type="checkbox" checked={BoolCast(Doc.defaultAclPrivate)} onChange={action(() => (Doc.defaultAclPrivate = !Doc.defaultAclPrivate))} />
+                            <div className="acl-text">Default access private</div>
+                        </div>
                     </div>
                 </div>
             </div>
-
-        </div>;
+        );
     }
 
-
     private get settingsInterface() {
         // const pairs = [{ title: "Password", ele: this.passwordContent }, { title: "Modes", ele: this.modesContent },
         // { title: "Accounts", ele: this.accountsContent }, { title: "Preferences", ele: this.preferencesContent }];
 
-        const tabs = [{ title: "Accounts", ele: this.accountsContent }, { title: "Modes", ele: this.modesContent },
-        { title: "Appearance", ele: this.appearanceContent }, { title: "Text", ele: this.textContent }];
+        const tabs = [
+            { title: 'Accounts', ele: this.accountsContent },
+            { title: 'Modes', ele: this.modesContent },
+            { title: 'Appearance', ele: this.appearanceContent },
+            { title: 'Text', ele: this.textContent },
+        ];
 
-        return <div className="settings-interface">
-            <div className="settings-panel">
-                <div className="settings-tabs">
-                    {tabs.map(tab => <div key={tab.title} className={"tab-control " + (this.activeTab === tab.title ? "active" : "inactive")} onClick={action(() => this.activeTab = tab.title)}>{tab.title}</div>)}
-                </div>
+        return (
+            <div className="settings-interface">
+                <div className="settings-panel">
+                    <div className="settings-tabs">
+                        {tabs.map(tab => (
+                            <div key={tab.title} className={'tab-control ' + (this.activeTab === tab.title ? 'active' : 'inactive')} onClick={action(() => (this.activeTab = tab.title))}>
+                                {tab.title}
+                            </div>
+                        ))}
+                    </div>
 
-                <div className="settings-user">
-                    <div className="settings-username">{Doc.CurrentUserEmail}</div>
-                    <button className="logout-button" onClick={() => window.location.assign(Utils.prepend("/logout"))} >
-                        {CurrentUserUtils.GuestDashboard ? "Exit" : "Log Out"}
-                    </button>
+                    <div className="settings-user">
+                        <div className="settings-username">{Doc.CurrentUserEmail}</div>
+                        <button className="logout-button" onClick={() => window.location.assign(Utils.prepend('/logout'))}>
+                            {Doc.GuestDashboard ? 'Exit' : 'Log Out'}
+                        </button>
+                    </div>
                 </div>
-            </div>
 
-            <div className="close-button" onClick={this.close}>
-                <FontAwesomeIcon icon={"times"} color="black" size={"lg"} />
-            </div>
+                <div className="close-button" onClick={this.close}>
+                    <FontAwesomeIcon icon={'times'} color="black" size={'lg'} />
+                </div>
 
-            <div className="settings-content">
-                {tabs.map(tab => <div key={tab.title} className={"tab-section " + (this.activeTab === tab.title ? "active" : "inactive")}>{tab.ele}</div>)}
+                <div className="settings-content">
+                    {tabs.map(tab => (
+                        <div key={tab.title} className={'tab-section ' + (this.activeTab === tab.title ? 'active' : 'inactive')}>
+                            {tab.ele}
+                        </div>
+                    ))}
+                </div>
             </div>
-        </div>;
-
+        );
     }
 
     render() {
-        return <MainViewModal
-            contents={this.settingsInterface}
-            isDisplayed={this.isOpen}
-            interactive={true}
-            closeOnExternalClick={this.close}
-            dialogueBoxStyle={{ width: "500px", height: "300px", background: Cast(Doc.SharingDoc().userColor, "string", null) }} />;
+        return (
+            <MainViewModal
+                contents={this.settingsInterface}
+                isDisplayed={this.isOpen}
+                interactive={true}
+                closeOnExternalClick={this.close}
+                dialogueBoxStyle={{ width: '500px', height: '300px', background: Cast(Doc.SharingDoc().userColor, 'string', null) }}
+            />
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 1acb3ab38..793027ea1 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -17,7 +17,6 @@ import { MainViewModal } from '../views/MainViewModal';
 import { DocumentView } from '../views/nodes/DocumentView';
 import { TaskCompletionBox } from '../views/nodes/TaskCompletedBox';
 import { SearchBox } from '../views/search/SearchBox';
-import { CurrentUserUtils } from './CurrentUserUtils';
 import { DocumentManager } from './DocumentManager';
 import { GroupManager, UserOptions } from './GroupManager';
 import { GroupMemberView } from './GroupMemberView';
@@ -180,7 +179,7 @@ export class SharingManager extends React.Component<{}> {
         const target = targetDoc || this.targetDoc!;
         const acl = `acl-${normalizeEmail(user.email)}`;
         const myAcl = `acl-${Doc.CurrentUserEmailNormalized}`;
-        const isDashboard = DocListCast(CurrentUserUtils.MyDashboards.data).indexOf(target) !== -1;
+        const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
         return !docs
@@ -211,7 +210,7 @@ export class SharingManager extends React.Component<{}> {
         const target = targetDoc || this.targetDoc!;
         const key = normalizeEmail(StrCast(group.title));
         const acl = `acl-${key}`;
-        const isDashboard = DocListCast(CurrentUserUtils.MyDashboards.data).indexOf(target) !== -1;
+        const isDashboard = DocListCast(Doc.MyDashboards.data).indexOf(target) !== -1;
 
         const docs = SelectionManager.Views().length < 2 ? [target] : SelectionManager.Views().map(docView => docView.props.Document);
 
@@ -279,7 +278,7 @@ export class SharingManager extends React.Component<{}> {
                 else this.setInternalGroupSharing(GroupManager.Instance.getGroup(shareWith)!, permission, doc);
             });
         } else {
-            const dashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
+            const dashboards = DocListCast(Doc.MyDashboards.data);
             docs.forEach(doc => {
                 const isDashboard = dashboards.indexOf(doc) !== -1;
                 if (GetEffectiveAcl(doc) === AclAdmin) distributeAcls(`acl-${shareWith}`, permission, doc, undefined, undefined, isDashboard);
@@ -291,7 +290,7 @@ export class SharingManager extends React.Component<{}> {
      * Sets the background of the Dashboard if it has been shared as a visual indicator
      */
     setDashboardBackground = async (doc: Doc, permission: SharingPermissions) => {
-        if (Doc.IndexOf(doc, DocListCast(CurrentUserUtils.MyDashboards.data)) !== -1) {
+        if (Doc.IndexOf(doc, DocListCast(Doc.MyDashboards.data)) !== -1) {
             if (permission !== SharingPermissions.None) {
                 doc.isShared = true;
                 doc.backgroundColor = 'green';
@@ -329,7 +328,7 @@ export class SharingManager extends React.Component<{}> {
      */
     removeGroup = (group: Doc) => {
         if (group.docsShared) {
-            const dashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
+            const dashboards = DocListCast(Doc.MyDashboards.data);
             DocListCast(group.docsShared).forEach(doc => {
                 const acl = `acl-${StrCast(group.title)}`;
                 const isDashboard = dashboards.indexOf(doc) !== -1;
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 868d63a90..c59c37488 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -1,164 +1,290 @@
-import { action, computed, observable } from "mobx";
-import { extname } from 'path';
-import { observer } from "mobx-react";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Doc, DocListCast } from "../../fields/Doc";
-import { Id } from "../../fields/FieldSymbols";
-import { Cast, ImageCast, StrCast } from "../../fields/Types";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { undoBatch, UndoManager } from "../util/UndoManager";
-import "./DashboardView.scss"
-import { MainViewModal } from "./MainViewModal";
-import { ContextMenu } from "./ContextMenu";
-import { DocumentManager } from "../util/DocumentManager";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { ContextMenuProps } from "./ContextMenuItem";
-import { simulateMouseClick } from "../../Utils";
-import { SharingManager } from "../util/SharingManager";
-import { CollectionViewType } from "./collections/CollectionView";
+import { DataSym, Doc, DocListCast, DocListCastAsync } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { List } from '../../fields/List';
+import { Cast, ImageCast, StrCast } from '../../fields/Types';
+import { DocServer } from '../DocServer';
+import { Docs, DocumentOptions } from '../documents/Documents';
+import { CollectionViewType } from '../documents/DocumentTypes';
+import { HistoryUtil } from '../util/History';
+import { SharingManager } from '../util/SharingManager';
+import { undoBatch } from '../util/UndoManager';
+import { CollectionDockingView } from './collections/CollectionDockingView';
+import { CollectionView } from './collections/CollectionView';
+import { ContextMenu } from './ContextMenu';
+import './DashboardView.scss';
+import { MainViewModal } from './MainViewModal';
 
 enum DashboardGroup {
-      MyDashboards, SharedDashboards
+    MyDashboards,
+    SharedDashboards,
 }
 
 // DashboardView is the view with the dashboard previews, rendered when the app first loads
 
 @observer
 export class DashboardView extends React.Component {
+    //TODO: delete dashboard, share dashboard, etc.
 
-      //TODO: delete dashboard, share dashboard, etc.
+    public static _urlState: HistoryUtil.DocUrl;
 
-      @observable private selectedDashboardGroup = DashboardGroup.MyDashboards;
+    @observable private selectedDashboardGroup = DashboardGroup.MyDashboards;
 
-      @observable private newDashboardName: string | undefined = undefined;
-      @action abortCreateNewDashboard = () => { this.newDashboardName = undefined }
-      @action setNewDashboardName(name: string) { this.newDashboardName = name }
+    @observable private newDashboardName: string | undefined = undefined;
+    @action abortCreateNewDashboard = () => {
+        this.newDashboardName = undefined;
+    };
+    @action setNewDashboardName(name: string) {
+        this.newDashboardName = name;
+    }
 
-      @action
-      selectDashboardGroup = (group: DashboardGroup) => {
-            this.selectedDashboardGroup = group
-      }
+    @action
+    selectDashboardGroup = (group: DashboardGroup) => {
+        this.selectedDashboardGroup = group;
+    };
 
-      clickDashboard = async (e: React.MouseEvent, dashboard: Doc) => {
-            if (e.detail === 2) {
-                  Doc.AddDocToList(CurrentUserUtils.MySharedDocs, "viewed", dashboard)
-                  CurrentUserUtils.ActiveDashboard = dashboard;
-                  CurrentUserUtils.ActivePage = "dashboard";
+    clickDashboard = async (e: React.MouseEvent, dashboard: Doc) => {
+        if (e.detail === 2) {
+            Doc.AddDocToList(Doc.MySharedDocs, 'viewed', dashboard);
+            Doc.ActiveDashboard = dashboard;
+            Doc.ActivePage = 'dashboard';
+        }
+    };
+
+    getDashboards = () => {
+        const allDashboards = DocListCast(Doc.MyDashboards.data);
+        if (this.selectedDashboardGroup === DashboardGroup.MyDashboards) {
+            return allDashboards.filter(dashboard => Doc.GetProto(dashboard).author === Doc.CurrentUserEmail);
+        } else {
+            const sharedDashboards = DocListCast(Doc.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking);
+            return sharedDashboards;
+        }
+    };
+
+    isUnviewedSharedDashboard = (dashboard: Doc): boolean => {
+        // const sharedDashboards = DocListCast(Doc.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking);
+        return !DocListCast(Doc.MySharedDocs.viewed).includes(dashboard);
+    };
+
+    getSharedDashboards = () => {
+        const sharedDashs = DocListCast(Doc.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking);
+        return sharedDashs.filter(dashboard => !DocListCast(Doc.MySharedDocs.viewed).includes(dashboard));
+    };
+
+    @undoBatch
+    createNewDashboard = async (name: string) => {
+        DashboardView.createNewDashboard(undefined, name);
+        this.abortCreateNewDashboard();
+    };
+
+    @computed
+    get namingInterface() {
+        return (
+            <div>
+                <input className="password-inputs" placeholder="Untitled Dashboard" onChange={e => this.setNewDashboardName((e.target as any).value)} />
+                <button className="password-submit" onClick={this.abortCreateNewDashboard}>
+                    Cancel
+                </button>
+                <button
+                    className="password-submit"
+                    onClick={() => {
+                        this.createNewDashboard(this.newDashboardName!);
+                    }}>
+                    Create
+                </button>
+            </div>
+        );
+    }
+
+    _downX: number = 0;
+    _downY: number = 0;
+    @action
+    onContextMenu = (dashboard: Doc, e?: React.MouseEvent, pageX?: number, pageY?: number) => {
+        // the touch onContextMenu is button 0, the pointer onContextMenu is button 2
+        if (e) {
+            e.preventDefault();
+            e.stopPropagation();
+            e.persist();
+
+            if (!navigator.userAgent.includes('Mozilla') && (Math.abs(this._downX - e?.clientX) > 3 || Math.abs(this._downY - e?.clientY) > 3)) {
+                return;
             }
-      }
+            const cm = ContextMenu.Instance;
+            cm.addItem({
+                description: 'Share Dashboard',
+                event: async () => {
+                    SharingManager.Instance.open(undefined, dashboard);
+                },
+                icon: 'edit',
+            });
+            cm.addItem({
+                description: 'Delete Dashboard',
+                event: async () => {
+                    DashboardView.removeDashboard(dashboard);
+                },
+                icon: 'trash',
+            });
+            cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15);
+        }
+    };
+
+    render() {
+        return (
+            <>
+                <div className="dashboard-view">
+                    <div className="left-menu">
+                        <div
+                            className="text-button"
+                            onClick={() => {
+                                this.setNewDashboardName('');
+                            }}>
+                            New
+                        </div>
+                        <div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.MyDashboards && 'selected'}`} onClick={() => this.selectDashboardGroup(DashboardGroup.MyDashboards)}>
+                            My Dashboards
+                        </div>
+                        <div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.SharedDashboards && 'selected'}`} onClick={() => this.selectDashboardGroup(DashboardGroup.SharedDashboards)}>
+                            Shared Dashboards
+                        </div>
+                    </div>
+                    <div className="all-dashboards">
+                        {this.getDashboards().map(dashboard => {
+                            const href = ImageCast((dashboard.thumb as Doc)?.data)?.url.href;
+                            return (
+                                <div
+                                    className="dashboard-container"
+                                    key={dashboard[Id]}
+                                    onContextMenu={e => {
+                                        this.onContextMenu(dashboard, e);
+                                    }}
+                                    onClick={e => this.clickDashboard(e, dashboard)}>
+                                    <img
+                                        src={
+                                            href ?? 'https://media.istockphoto.com/photos/hot-air-balloons-flying-over-the-botan-canyon-in-turkey-picture-id1297349747?b=1&k=20&m=1297349747&s=170667a&w=0&h=oH31fJty_4xWl_JQ4OIQWZKP8C6ji9Mz7L4XmEnbqRU='
+                                        }></img>
+                                    <div className="info">
+                                        <div className="title"> {StrCast(dashboard.title)} </div>
+                                        {this.selectedDashboardGroup === DashboardGroup.SharedDashboards && this.isUnviewedSharedDashboard(dashboard) ? <div>unviewed</div> : <div></div>}
+                                        <div
+                                            className="more"
+                                            onPointerDown={e => {
+                                                this._downX = e.clientX;
+                                                this._downY = e.clientY;
+                                            }}
+                                            onClick={e => {
+                                                this.onContextMenu(dashboard, e);
+                                            }}>
+                                            <FontAwesomeIcon color="black" size="lg" icon="bars" />
+                                        </div>
+                                    </div>
+                                </div>
+                            );
+                        })}
+                    </div>
+                </div>
+                <MainViewModal
+                    contents={this.namingInterface}
+                    isDisplayed={this.newDashboardName !== undefined}
+                    interactive={true}
+                    closeOnExternalClick={this.abortCreateNewDashboard}
+                    dialogueBoxStyle={{ width: '500px', height: '300px', background: Cast(Doc.SharingDoc().userColor, 'string', null) }}
+                />
+                ;
+            </>
+        );
+    }
+
+    public static closeActiveDashboard() {
+        Doc.ActiveDashboard = undefined;
+    }
+    public static snapshotDashboard() {
+        return CollectionDockingView.TakeSnapshot(Doc.ActiveDashboard);
+    }
 
-      getDashboards = () => {
-            const allDashboards = DocListCast(CurrentUserUtils.MyDashboards.data);
-            if (this.selectedDashboardGroup === DashboardGroup.MyDashboards) {
-                  return allDashboards.filter((dashboard) => Doc.GetProto(dashboard).author === Doc.CurrentUserEmail)
+    /// opens a dashboard as the ActiveDashboard (and adds the dashboard to the users list of dashboards if it's not already there).
+    /// this also sets the readonly state of the dashboard based on the current mode of dash (from its url)
+    public static openDashboard = (doc: Doc | undefined, fromHistory = false) => {
+        if (!doc) return false;
+        Doc.MainDocId = doc[Id];
+        Doc.AddDocToList(Doc.MyDashboards, 'data', doc);
+
+        // this has the side-effect of setting the main container since we're assigning the active/guest dashboard
+        Doc.UserDoc() ? (Doc.ActiveDashboard = doc) : (Doc.GuestDashboard = doc);
+
+        const state = DashboardView._urlState;
+        if (state.sharing === true && !Doc.UserDoc()) {
+            DocServer.Control.makeReadOnly();
+        } else {
+            fromHistory ||
+                HistoryUtil.pushState({
+                    type: 'doc',
+                    docId: doc[Id],
+                    readonly: state.readonly,
+                    nro: state.nro,
+                    sharing: false,
+                });
+            if (state.readonly === true || state.readonly === null) {
+                DocServer.Control.makeReadOnly();
+            } else if (state.safe) {
+                if (!state.nro) {
+                    DocServer.Control.makeReadOnly();
+                }
+                CollectionView.SetSafeMode(true);
+            } else if (state.nro || state.nro === null || state.readonly === false) {
+            } else if (doc.readOnly) {
+                DocServer.Control.makeReadOnly();
             } else {
-                  const sharedDashboards = DocListCast(CurrentUserUtils.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking);
-                  return sharedDashboards
+                DocServer.Control.makeEditable();
             }
-      }
-
-      isUnviewedSharedDashboard = (dashboard: Doc): boolean => {
-            // const sharedDashboards = DocListCast(CurrentUserUtils.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking);
-            return !DocListCast(CurrentUserUtils.MySharedDocs.viewed).includes(dashboard)
-      }
-
-      getSharedDashboards = () => {
-            const sharedDashs = DocListCast(CurrentUserUtils.MySharedDocs.data).filter(doc => doc._viewType === CollectionViewType.Docking);
-            return sharedDashs.filter((dashboard) => !DocListCast(CurrentUserUtils.MySharedDocs.viewed).includes(dashboard))
-      }
-
-      @undoBatch
-      createNewDashboard = async (name: string) => { 
-            CurrentUserUtils.createNewDashboard(undefined, name);
-            this.abortCreateNewDashboard();
-      }
-
-      @computed
-      get namingInterface() {
-            return <div>
-                  <input className="password-inputs" placeholder="Untitled Dashboard" onChange={e => this.setNewDashboardName((e.target as any).value)} />
-                  <button className="password-submit" onClick={this.abortCreateNewDashboard}>Cancel</button>
-                  <button className="password-submit" onClick={() => { this.createNewDashboard(this.newDashboardName!) }}>Create</button>
-            </div>;
-      }
-      
-      _downX: number = 0;
-      _downY: number = 0;
-      @action
-      onContextMenu = (dashboard: Doc, e?: React.MouseEvent, pageX?: number, pageY?: number) => {
-          // the touch onContextMenu is button 0, the pointer onContextMenu is button 2
-          if (e) {
-              e.preventDefault();
-              e.stopPropagation();
-              e.persist();
-  
-              if (!navigator.userAgent.includes("Mozilla") && (Math.abs(this._downX - e?.clientX) > 3 || Math.abs(this._downY - e?.clientY) > 3)) {
-                  return;
-              }
-              const cm = ContextMenu.Instance;
-             cm.addItem({
-                    description: "Share Dashboard", event: async () => {
-                        SharingManager.Instance.open(undefined, dashboard)
-                    }, icon: "edit"
-              });
-              cm.addItem({
-                    description: "Delete Dashboard", event: async () => {
-                          CurrentUserUtils.removeDashboard(dashboard)
-                    }, icon: "trash"
-              });
-              cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15);
-          }
         }
-  
-
-      render() {
-            return <>
-                  <div className="dashboard-view">
-                        <div className="left-menu">
-                              <div className="text-button" onClick={() => { this.setNewDashboardName("") }}>New</div>
-                              <div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.MyDashboards && 'selected'}`} onClick={() => this.selectDashboardGroup(DashboardGroup.MyDashboards)}>My Dashboards</div>
-                              <div className={`text-button ${this.selectedDashboardGroup === DashboardGroup.SharedDashboards && 'selected'}`} onClick={() => this.selectDashboardGroup(DashboardGroup.SharedDashboards)}>Shared Dashboards</div>
-                        </div>
-                        <div className="all-dashboards">
-                              {this.getDashboards().map((dashboard) => {
-                                    const href = ImageCast((dashboard.thumb as Doc)?.data)?.url.href;
-                                    return <div className="dashboard-container" key={dashboard[Id]}
-                                              onContextMenu={(e) => {this.onContextMenu(dashboard, e)}}  
-                                              onClick={e => this.clickDashboard(e, dashboard)}>
-                                          <img src={href ?? "https://media.istockphoto.com/photos/hot-air-balloons-flying-over-the-botan-canyon-in-turkey-picture-id1297349747?b=1&k=20&m=1297349747&s=170667a&w=0&h=oH31fJty_4xWl_JQ4OIQWZKP8C6ji9Mz7L4XmEnbqRU="}></img>
-                                          <div className="info">
-                                                <div className="title"> {StrCast(dashboard.title)} </div>
-                                                {this.selectedDashboardGroup === DashboardGroup.SharedDashboards && this.isUnviewedSharedDashboard(dashboard) ?
-                                                 <div>unviewed</div> : <div></div>
-                                                }
-                                                <div className="more" onPointerDown={e => {
-                                                        this._downX = e.clientX;
-                                                        this._downY = e.clientY;
-                                                    }} 
-                                                    onClick={(e) => {this.onContextMenu(dashboard, e)}}
-                                                >
-                                                      <FontAwesomeIcon color="black" size="lg" icon="bars" />
-                                                </div>
-                                          </div>
 
-                                    </div>
+        return true;
+    };
 
-                              })}
-                            </div>
+    public static removeDashboard = async (dashboard: Doc) => {
+        const dashboards = await DocListCastAsync(Doc.MyDashboards.data);
+        if (dashboards?.length) {
+            if (dashboard === Doc.ActiveDashboard) DashboardView.openDashboard(dashboards.find(doc => doc !== dashboard));
+            Doc.RemoveDocFromList(Doc.MyDashboards, 'data', dashboard);
+            if (!dashboards.length) Doc.ActivePage = 'home';
+        }
+    };
 
-                  </div>
-                  <MainViewModal
-                        contents={this.namingInterface}
-                        isDisplayed={this.newDashboardName !== undefined}
-                        interactive={true}
-                        closeOnExternalClick={this.abortCreateNewDashboard}
-                        dialogueBoxStyle={{ width: "500px", height: "300px", background: Cast(Doc.SharingDoc().userColor, "string", null) }} />;
-            </>
+    public static createNewDashboard = (id?: string, name?: string) => {
+        const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true);
+        const dashboards = Doc.MyDashboards;
+        const dashboardCount = DocListCast(dashboards.data).length + 1;
+        const freeformOptions: DocumentOptions = {
+            x: 0,
+            y: 400,
+            _width: 1500,
+            _height: 1000,
+            _fitWidth: true,
+            _backgroundGridShow: true,
+            title: `Untitled Tab 1`,
+        };
+        const title = name ? name : `Dashboard ${dashboardCount}`;
+        const freeformDoc = Doc.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
+        const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: title }, id, 'row');
+        freeformDoc.context = dashboardDoc;
 
-      }
+        // switching the tabs from the datadoc to the regular doc
+        const dashboardTabs = DocListCast(dashboardDoc[DataSym].data);
+        dashboardDoc.data = new List<Doc>(dashboardTabs);
+        dashboardDoc['pane-count'] = 1;
+
+        Doc.ActivePresentation = presentation;
+
+        Doc.AddDocToList(dashboards, 'data', dashboardDoc);
+        // open this new dashboard
+        Doc.ActiveDashboard = dashboardDoc;
+        Doc.ActivePage = 'dashboard';
+    };
 }
 
 export function AddToList(MySharedDocs: Doc, arg1: string, dash: any) {
-      throw new Error("Function not implemented.");
+    throw new Error('Function not implemented.');
 }
-
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 169bd3873..280ca8a8c 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -8,13 +8,11 @@ import { Cast, ScriptCast } from '../../fields/Types';
 import { denormalizeEmail, distributeAcls, GetEffectiveAcl, inheritParentAcls, SharingPermissions } from '../../fields/util';
 import { returnFalse } from '../../Utils';
 import { DocUtils } from '../documents/Documents';
-import { CurrentUserUtils } from '../util/CurrentUserUtils';
 import { InteractionUtils } from '../util/InteractionUtils';
 import { UndoManager } from '../util/UndoManager';
 import { DocumentView } from './nodes/DocumentView';
 import { Touchable } from './Touchable';
 
-
 ///  DocComponent returns a generic React base class used by views that don't have 'fieldKey' props (e.g.,CollectionFreeFormDocumentView, DocumentView)
 export interface DocComponentProps {
     Document: Doc;
@@ -24,13 +22,21 @@ export interface DocComponentProps {
 export function DocComponent<P extends DocComponentProps>() {
     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() { return this.props.Document; }
+        @computed get Document() {
+            return this.props.Document;
+        }
         // This is the "The Document" -- it encapsulates, data, layout, and any templates
-        @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; }
+        @computed get rootDoc() {
+            return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document;
+        }
         // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info
-        @computed get layoutDoc() { return this.props.LayoutTemplateString ? this.props.Document : Doc.Layout(this.props.Document, this.props.LayoutTemplate?.()); }
+        @computed get layoutDoc() {
+            return this.props.LayoutTemplateString ? this.props.Document : Doc.Layout(this.props.Document, this.props.LayoutTemplate?.());
+        }
         // This is the data part of a document -- ie, the data that is constant across all views of the document
-        @computed get dataDoc() { return this.props.Document[DataSym] as Doc; }
+        @computed get dataDoc() {
+            return this.props.Document[DataSym] as Doc;
+        }
 
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
     }
@@ -55,32 +61,39 @@ export function ViewBoxBaseComponent<P extends ViewBoxBaseProps>() {
         //@computed get Document(): T { return schemaCtor(this.props.Document); }
 
         // This is the "The Document" -- it encapsulates, data, layout, and any templates
-        @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; }
+        @computed get rootDoc() {
+            return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document;
+        }
         // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info
-        @computed get layoutDoc() { return Doc.Layout(this.props.Document); }
+        @computed get layoutDoc() {
+            return Doc.Layout(this.props.Document);
+        }
         // This is the data part of a document -- ie, the data that is constant across all views of the document
-        @computed get dataDoc() { return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym]; }
+        @computed get dataDoc() {
+            return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym];
+        }
         // key where data is stored
-        @computed get fieldKey() { return this.props.fieldKey; }
-
-        isContentActive = (outsideReaction?: boolean) => (
-            this.props.isContentActive?.() === false ? false :
-                (CurrentUserUtils.ActiveTool !== InkTool.None ||
-                    (this.props.isContentActive?.() || this.props.Document.forceActive ||
-                        this.props.isSelected(outsideReaction) ||
-                        this.props.rootSelected(outsideReaction)) ? true : undefined))
+        @computed get fieldKey() {
+            return this.props.fieldKey;
+        }
+
+        isContentActive = (outsideReaction?: boolean) =>
+            this.props.isContentActive?.() === false
+                ? false
+                : Doc.ActiveTool !== InkTool.None || this.props.isContentActive?.() || this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this.props.rootSelected(outsideReaction)
+                ? true
+                : undefined;
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
     }
     return Component;
 }
 
-
 ///  DocAnnotatbleComponent -return a base class for React views of document fields that are annotatable *and* interactive when selected (e.g., pdf, image)
 export interface ViewBoxAnnotatableProps {
     Document: Doc;
     DataDoc?: Doc;
     fieldKey: string;
-    filterAddDocument?: (doc: Doc[]) => boolean;  // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
+    filterAddDocument?: (doc: Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
     isContentActive: () => boolean | undefined;
     select: (isCtrlPressed: boolean) => void;
     whenChildContentsActiveChanged: (isActive: boolean) => void;
@@ -91,19 +104,29 @@ export interface ViewBoxAnnotatableProps {
 }
 export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>() {
     class Component extends Touchable<P> {
-        @observable _annotationKeySuffix = () => "annotations";
+        @observable _annotationKeySuffix = () => 'annotations';
         @observable _isAnyChildContentActive = false;
         //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then
-        @computed get Document() { return this.props.Document; }
+        @computed get Document() {
+            return this.props.Document;
+        }
         // This is the "The Document" -- it encapsulates, data, layout, and any templates
-        @computed get rootDoc() { return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document; }
+        @computed get rootDoc() {
+            return Cast(this.props.Document.rootDocument, Doc, null) || this.props.Document;
+        }
         // This is the rendering data of a document -- it may be "The Document", or it may be some template document that holds the rendering info
-        @computed get layoutDoc() { return Doc.Layout(this.props.Document); }
+        @computed get layoutDoc() {
+            return Doc.Layout(this.props.Document);
+        }
         // This is the data part of a document -- ie, the data that is constant across all views of the document
-        @computed get dataDoc() { return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym]; }
+        @computed get dataDoc() {
+            return this.props.DataDoc && (this.props.Document.isTemplateForField || this.props.Document.isTemplateDoc) ? this.props.DataDoc : this.props.Document[DataSym];
+        }
 
         // key where data is stored
-        @computed get fieldKey() { return this.props.fieldKey; }
+        @computed get fieldKey() {
+            return this.props.fieldKey;
+        }
 
         isAnyChildContentActive = () => this._isAnyChildContentActive;
 
@@ -111,20 +134,23 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
 
         styleFromLayoutString = (scale: number) => {
             const style: { [key: string]: any } = {};
-            const divKeys = ["width", "height", "fontSize", "transform", "left", "background", "left", "right", "top", "bottom", "pointerEvents", "position"];
-            const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a property expression string:  { script }  into a value
-                return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result?.toString() ?? "";
+            const divKeys = ['width', 'height', 'fontSize', 'transform', 'left', 'background', 'left', 'right', 'top', 'bottom', 'pointerEvents', 'position'];
+            const replacer = (match: any, expr: string, offset: any, string: any) => {
+                // bcz: this executes a script to convert a property expression string:  { script }  into a value
+                return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: 'number' })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result?.toString() ?? '';
             };
             divKeys.map((prop: string) => {
                 const p = (this.props as any)[prop];
-                typeof p === "string" && (style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer));
+                typeof p === 'string' && (style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer));
             });
             return style;
-        }
+        };
 
         protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
 
-        @computed public get annotationKey() { return this.fieldKey + (this._annotationKeySuffix() ? "-" + this._annotationKeySuffix() : ""); }
+        @computed public get annotationKey() {
+            return this.fieldKey + (this._annotationKeySuffix() ? '-' + this._annotationKeySuffix() : '');
+        }
 
         @action.bound
         removeDocument(doc: Doc | Doc[], annotationKey?: string, leavePushpin?: boolean): boolean {
@@ -132,23 +158,26 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             const indocs = doc instanceof Doc ? [doc] : doc;
             const docs = indocs.filter(doc => [AclEdit, AclAdmin].includes(effectiveAcl) || GetEffectiveAcl(doc) === AclAdmin);
             if (docs.length) {
-                setTimeout(() => docs.map(doc => { // this allows 'addDocument' to see the annotationOn field in order to create a pushin
-                    Doc.SetInPlace(doc, "isPushpin", undefined, true);
-                    doc.annotationOn === this.props.Document && Doc.SetInPlace(doc, "annotationOn", undefined, true);
-                }));
+                setTimeout(() =>
+                    docs.map(doc => {
+                        // this allows 'addDocument' to see the annotationOn field in order to create a pushin
+                        Doc.SetInPlace(doc, 'isPushpin', undefined, true);
+                        doc.annotationOn === this.props.Document && Doc.SetInPlace(doc, 'annotationOn', undefined, true);
+                    })
+                );
                 const targetDataDoc = this.dataDoc;
                 const value = DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]);
                 const toRemove = value.filter(v => docs.includes(v));
 
                 if (toRemove.length !== 0) {
-                    const recent = CurrentUserUtils.MyRecentlyClosed;
+                    const recent = Doc.MyRecentlyClosed;
                     toRemove.forEach(doc => {
                         leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey);
                         Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
                         doc.context = undefined;
                         if (recent) {
-                            Doc.RemoveDocFromList(recent, "data", doc);
-                            Doc.AddDocToList(recent, "data", doc, undefined, true, true);
+                            Doc.RemoveDocFromList(recent, 'data', doc);
+                            Doc.AddDocToList(recent, 'data', doc, undefined, true, true);
                         }
                     });
                     this.isAnyChildContentActive() && this.props.select(false);
@@ -172,12 +201,11 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
                 return UndoManager.RunInTempBatch(() => this.removeDocument(doc, annotationKey, true) && addDocument(doc, annotationKey));
             }
             return false;
-        }
+        };
         @action.bound
         addDocument = (doc: Doc | Doc[], annotationKey?: string): boolean => {
             const docs = doc instanceof Doc ? [doc] : doc;
-            if (this.props.filterAddDocument?.(docs) === false ||
-                docs.find(doc => Doc.AreProtosEqual(doc, this.props.Document) && Doc.LayoutField(doc) === Doc.LayoutField(this.props.Document))) {
+            if (this.props.filterAddDocument?.(docs) === false || docs.find(doc => Doc.AreProtosEqual(doc, this.props.Document) && Doc.LayoutField(doc) === Doc.LayoutField(this.props.Document))) {
                 return false;
             }
             const targetDataDoc = this.props.Document[DataSym];
@@ -188,8 +216,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
             if (added.length) {
                 if (effectiveAcl === AclPrivate || effectiveAcl === AclReadonly) {
                     return false;
-                }
-                else {
+                } else {
                     if (this.props.Document[AclSym] && Object.keys(this.props.Document[AclSym]).length) {
                         added.forEach(d => {
                             for (const [key, value] of Object.entries(this.props.Document[AclSym])) {
@@ -200,32 +227,34 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>()
 
                     if (effectiveAcl === AclAugment) {
                         added.map(doc => {
-                            if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)) && CurrentUserUtils.ActiveDashboard) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
+                            if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)) && Doc.ActiveDashboard) inheritParentAcls(Doc.ActiveDashboard, doc);
                             doc.context = this.props.Document;
                             if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
                             Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc);
                         });
-                    }
-                    else {
-                        added.filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))).map(doc => {  // only make a pushpin if we have acl's to edit the document
-                            //DocUtils.LeavePushpin(doc);
-                            doc._stayInCollection = undefined;
-                            doc.context = this.props.Document;
-                            if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
+                    } else {
+                        added
+                            .filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc)))
+                            .map(doc => {
+                                // only make a pushpin if we have acl's to edit the document
+                                //DocUtils.LeavePushpin(doc);
+                                doc._stayInCollection = undefined;
+                                doc.context = this.props.Document;
+                                if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document;
 
-                            CurrentUserUtils.ActiveDashboard && inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc);
-                        });
+                                Doc.ActiveDashboard && inheritParentAcls(Doc.ActiveDashboard, doc);
+                            });
                         const annoDocs = targetDataDoc[annotationKey ?? this.annotationKey] as List<Doc>;
                         if (annoDocs instanceof List) annoDocs.push(...added);
                         else targetDataDoc[annotationKey ?? this.annotationKey] = new List<Doc>(added);
-                        targetDataDoc[(annotationKey ?? this.annotationKey) + "-lastModified"] = new DateField(new Date(Date.now()));
+                        targetDataDoc[(annotationKey ?? this.annotationKey) + '-lastModified'] = new DateField(new Date(Date.now()));
                     }
                 }
             }
             return true;
-        }
+        };
 
-        whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive));
+        whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive)));
     }
     return Component;
-}
\ No newline at end of file
+}
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 9b8f7238d..bac51a11d 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -1,57 +1,54 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
-import { action, computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, DocCastAsync } from "../../fields/Doc";
+import { action, computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc } from '../../fields/Doc';
 import { RichTextField } from '../../fields/RichTextField';
-import { Cast, NumCast, StrCast } from "../../fields/Types";
-import { emptyFunction, setupMoveUpEvents, simulateMouseClick } from "../../Utils";
+import { Cast, NumCast } from '../../fields/Types';
+import { emptyFunction, setupMoveUpEvents, simulateMouseClick } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils';
 import { Docs } from '../documents/Documents';
-import { DocumentType } from '../documents/DocumentTypes';
-import { CurrentUserUtils } from '../util/CurrentUserUtils';
 import { DragManager } from '../util/DragManager';
 import { SelectionManager } from '../util/SelectionManager';
+import { SettingsManager } from '../util/SettingsManager';
 import { SharingManager } from '../util/SharingManager';
+import { undoBatch } from '../util/UndoManager';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { TabDocView } from './collections/TabDocView';
 import './DocumentButtonBar.scss';
+import { Colors } from './global/globalEnums';
 import { MetadataEntryMenu } from './MetadataEntryMenu';
 import { DocumentLinksButton } from './nodes/DocumentLinksButton';
 import { DocumentView } from './nodes/DocumentView';
-import { GoogleRef } from "./nodes/formattedText/FormattedTextBox";
-import { TemplateMenu } from "./TemplateMenu";
-import React = require("react");
-import { PresBox } from './nodes/trails/PresBox';
-import { undoBatch } from '../util/UndoManager';
-import { CollectionViewType } from './collections/CollectionView';
-import { Colors } from './global/globalEnums';
 import { DashFieldView } from './nodes/formattedText/DashFieldView';
-const higflyout = require("@hig/flyout");
+import { GoogleRef } from './nodes/formattedText/FormattedTextBox';
+import { TemplateMenu } from './TemplateMenu';
+import React = require('react');
+const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
-const cloud: IconProp = "cloud-upload-alt";
-const fetch: IconProp = "sync-alt";
+const cloud: IconProp = 'cloud-upload-alt';
+const fetch: IconProp = 'sync-alt';
 
 enum UtilityButtonState {
     Default,
     OpenRight,
-    OpenExternally
+    OpenExternally,
 }
 
 @observer
-export class DocumentButtonBar extends React.Component<{ views: () => (DocumentView | undefined)[], stack?: any }, {}> {
+export class DocumentButtonBar extends React.Component<{ views: () => (DocumentView | undefined)[]; stack?: any }, {}> {
     private _dragRef = React.createRef<HTMLDivElement>();
     private _pullAnimating = false;
     private _pushAnimating = false;
     private _pullColorAnimating = false;
 
-    @observable private pushIcon: IconProp = "arrow-alt-circle-up";
-    @observable private pullIcon: IconProp = "arrow-alt-circle-down";
-    @observable private pullColor: string = "white";
+    @observable private pushIcon: IconProp = 'arrow-alt-circle-up';
+    @observable private pullIcon: IconProp = 'arrow-alt-circle-down';
+    @observable private pullColor: string = 'white';
     @observable public isAnimatingFetch = false;
     @observable public isAnimatingPulse = false;
 
@@ -63,17 +60,21 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
 
     constructor(props: { views: () => (DocumentView | undefined)[] }) {
         super(props);
-        runInAction(() => DocumentButtonBar.Instance = this);
+        runInAction(() => (DocumentButtonBar.Instance = this));
     }
 
     public startPullOutcome = action((success: boolean) => {
         if (!this._pullAnimating) {
             this._pullAnimating = true;
-            this.pullIcon = success ? "check-circle" : "stop-circle";
-            setTimeout(() => runInAction(() => {
-                this.pullIcon = "arrow-alt-circle-down";
-                this._pullAnimating = false;
-            }), 1000);
+            this.pullIcon = success ? 'check-circle' : 'stop-circle';
+            setTimeout(
+                () =>
+                    runInAction(() => {
+                        this.pullIcon = 'arrow-alt-circle-down';
+                        this._pullAnimating = false;
+                    }),
+                1000
+            );
         }
     });
 
@@ -81,11 +82,15 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         this.isAnimatingPulse = false;
         if (!this._pushAnimating) {
             this._pushAnimating = true;
-            this.pushIcon = success ? "check-circle" : "stop-circle";
-            setTimeout(() => runInAction(() => {
-                this.pushIcon = "arrow-alt-circle-up";
-                this._pushAnimating = false;
-            }), 1000);
+            this.pushIcon = success ? 'check-circle' : 'stop-circle';
+            setTimeout(
+                () =>
+                    runInAction(() => {
+                        this.pushIcon = 'arrow-alt-circle-up';
+                        this._pushAnimating = false;
+                    }),
+                1000
+            );
         }
     });
 
@@ -93,165 +98,241 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
         this.isAnimatingFetch = false;
         if (!this._pullColorAnimating) {
             this._pullColorAnimating = true;
-            this.pullColor = unchanged ? "lawngreen" : "red";
+            this.pullColor = unchanged ? 'lawngreen' : 'red';
             setTimeout(this.clearPullColor, 1000);
         }
     });
 
     private clearPullColor = action(() => {
-        this.pullColor = "white";
+        this.pullColor = 'white';
         this._pullColorAnimating = false;
     });
 
-    get view0() { return this.props.views()?.[0]; }
+    get view0() {
+        return this.props.views()?.[0];
+    }
 
     @computed
     get considerGoogleDocsPush() {
         const targetDoc = this.view0?.props.Document;
         const published = targetDoc && Doc.GetProto(targetDoc)[GoogleRef] !== undefined;
-        const animation = this.isAnimatingPulse ? "shadow-pulse 1s linear infinite" : "none";
-        return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{`${published ? "Push" : "Publish"} to Google Docs`}</div></>}>
-            <div
-                className="documentButtonBar-button"
-                style={{ animation }}
-                onClick={async () => {
-                    await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
-                    !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></Tooltip>;
+        const animation = this.isAnimatingPulse ? 'shadow-pulse 1s linear infinite' : 'none';
+        return !targetDoc ? null : (
+            <Tooltip
+                title={
+                    <>
+                        <div className="dash-tooltip">{`${published ? 'Push' : 'Publish'} to Google Docs`}</div>
+                    </>
+                }>
+                <div
+                    className="documentButtonBar-button"
+                    style={{ animation }}
+                    onClick={async () => {
+                        await GoogleAuthenticationManager.Instance.fetchOrGenerateAccessToken();
+                        !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>
+            </Tooltip>
+        );
     }
 
     @computed
     get considerGoogleDocsPull() {
         const targetDoc = this.view0?.props.Document;
         const dataDoc = targetDoc && Doc.GetProto(targetDoc);
-        const animation = this.isAnimatingFetch ? "spin 0.5s linear infinite" : "none";
+        const animation = this.isAnimatingFetch ? 'spin 0.5s linear infinite' : 'none';
 
         const title = (() => {
             switch (this.openHover) {
                 default:
-                case UtilityButtonState.Default: return `${!dataDoc?.googleDocUnchanged ? "Pull from" : "Fetch"} Google Docs`;
-                case UtilityButtonState.OpenRight: return "Open in Right Split";
-                case UtilityButtonState.OpenExternally: return "Open in new Browser Tab";
+                case UtilityButtonState.Default:
+                    return `${!dataDoc?.googleDocUnchanged ? 'Pull from' : 'Fetch'} Google Docs`;
+                case UtilityButtonState.OpenRight:
+                    return 'Open in Right Split';
+                case UtilityButtonState.OpenExternally:
+                    return 'Open in new Browser Tab';
             }
         })();
 
-        return !targetDoc || !dataDoc || !dataDoc[GoogleRef] ? (null) : <Tooltip
-            title={<><div className="dash-tooltip">{title}</div></>}>
-            <div className="documentButtonBar-button"
-                style={{ backgroundColor: this.pullColor }}
-                onPointerEnter={action(e => {
-                    if (e.altKey) {
-                        this.openHover = UtilityButtonState.OpenExternally;
-                    } else if (e.shiftKey) {
-                        this.openHover = UtilityButtonState.OpenRight;
-                    }
-                })}
-                onPointerLeave={action(() => this.openHover = UtilityButtonState.Default)}
-                onClick={async e => {
-                    const googleDocUrl = `https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`;
-                    if (e.shiftKey) {
-                        e.preventDefault();
-                        let googleDoc = await Cast(dataDoc.googleDoc, Doc);
-                        if (!googleDoc) {
-                            const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, useCors: false };
-                            googleDoc = Docs.Create.WebDocument(googleDocUrl, options);
-                            dataDoc.googleDoc = googleDoc;
+        return !targetDoc || !dataDoc || !dataDoc[GoogleRef] ? null : (
+            <Tooltip
+                title={
+                    <>
+                        <div className="dash-tooltip">{title}</div>
+                    </>
+                }>
+                <div
+                    className="documentButtonBar-button"
+                    style={{ backgroundColor: this.pullColor }}
+                    onPointerEnter={action(e => {
+                        if (e.altKey) {
+                            this.openHover = UtilityButtonState.OpenExternally;
+                        } else if (e.shiftKey) {
+                            this.openHover = UtilityButtonState.OpenRight;
                         }
-                        CollectionDockingView.AddSplit(googleDoc, "right");
-                    } else if (e.altKey) {
-                        e.preventDefault();
-                        window.open(googleDocUrl);
-                    } else {
-                        this.clearPullColor();
-                        DocumentButtonBar.hasPulledHack = false;
-                        targetDoc[Pulls] = NumCast(targetDoc[Pulls]) + 1;
-                        dataDoc.googleDocUnchanged && runInAction(() => this.isAnimatingFetch = true);
-                    }
-                }}>
-                <FontAwesomeIcon className="documentdecorations-icon" size="sm"
-                    style={{ WebkitAnimation: animation, MozAnimation: animation }}
-                    icon={(() => {
-                        switch (this.openHover) {
-                            default:
-                            case UtilityButtonState.Default: return dataDoc.googleDocUnchanged === false ? (this.pullIcon as any) : fetch;
-                            case UtilityButtonState.OpenRight: return "arrow-alt-circle-right";
-                            case UtilityButtonState.OpenExternally: return "share";
+                    })}
+                    onPointerLeave={action(() => (this.openHover = UtilityButtonState.Default))}
+                    onClick={async e => {
+                        const googleDocUrl = `https://docs.google.com/document/d/${dataDoc[GoogleRef]}/edit`;
+                        if (e.shiftKey) {
+                            e.preventDefault();
+                            let googleDoc = await Cast(dataDoc.googleDoc, Doc);
+                            if (!googleDoc) {
+                                const options = { _width: 600, _nativeWidth: 960, _nativeHeight: 800, useCors: false };
+                                googleDoc = Docs.Create.WebDocument(googleDocUrl, options);
+                                dataDoc.googleDoc = googleDoc;
+                            }
+                            CollectionDockingView.AddSplit(googleDoc, 'right');
+                        } else if (e.altKey) {
+                            e.preventDefault();
+                            window.open(googleDocUrl);
+                        } else {
+                            this.clearPullColor();
+                            DocumentButtonBar.hasPulledHack = false;
+                            targetDoc[Pulls] = NumCast(targetDoc[Pulls]) + 1;
+                            dataDoc.googleDocUnchanged && runInAction(() => (this.isAnimatingFetch = true));
                         }
-                    })()}
-                />
-            </div></Tooltip>;
+                    }}>
+                    <FontAwesomeIcon
+                        className="documentdecorations-icon"
+                        size="sm"
+                        style={{ WebkitAnimation: animation, MozAnimation: animation }}
+                        icon={(() => {
+                            switch (this.openHover) {
+                                default:
+                                case UtilityButtonState.Default:
+                                    return dataDoc.googleDocUnchanged === false ? (this.pullIcon as any) : fetch;
+                                case UtilityButtonState.OpenRight:
+                                    return 'arrow-alt-circle-right';
+                                case UtilityButtonState.OpenExternally:
+                                    return 'share';
+                            }
+                        })()}
+                    />
+                </div>
+            </Tooltip>
+        );
     }
     @computed
     get followLinkButton() {
         const targetDoc = this.view0?.props.Document;
-        return !targetDoc ? (null) : <Tooltip title={
-            <div className="dash-tooltip">{"Set onClick to follow primary link"}</div>}>
-            <div className="documentButtonBar-icon"
-                style={{ backgroundColor: targetDoc.isLinkButton ? Colors.LIGHT_BLUE : Colors.DARK_GRAY, color: targetDoc.isLinkButton ? Colors.BLACK : Colors.WHITE }}
-                onClick={undoBatch(e => this.props.views().map(view => view?.docView?.toggleFollowLink(undefined, undefined, false)))}>
-                <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="hand-point-right" />
-            </div>
-        </Tooltip>;
+        return !targetDoc ? null : (
+            <Tooltip title={<div className="dash-tooltip">{'Set onClick to follow primary link'}</div>}>
+                <div
+                    className="documentButtonBar-icon"
+                    style={{ backgroundColor: targetDoc.isLinkButton ? Colors.LIGHT_BLUE : Colors.DARK_GRAY, color: targetDoc.isLinkButton ? Colors.BLACK : Colors.WHITE }}
+                    onClick={undoBatch(e => this.props.views().map(view => view?.docView?.toggleFollowLink(undefined, undefined, false)))}>
+                    <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="hand-point-right" />
+                </div>
+            </Tooltip>
+        );
     }
     @computed
     get pinButton() {
         const targetDoc = this.view0?.props.Document;
-        return !targetDoc ? (null) : <Tooltip title={
-            <div className="dash-tooltip">{SelectionManager.Views().length > 1 ? "Pin multiple documents to presentation" : "Pin to presentation"}</div>}>
-            <div className="documentButtonBar-icon"
-                style={{ color: "white" }}
-                onClick={(e => TabDocView.PinDoc(this.props.views().filter(v => v).map(dv => dv!.rootDoc), {pinDocView: true}))}
-             >
-                <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
-            </div>
-        </Tooltip>;
+        return !targetDoc ? null : (
+            <Tooltip title={<div className="dash-tooltip">{SelectionManager.Views().length > 1 ? 'Pin multiple documents to presentation' : 'Pin to presentation'}</div>}>
+                <div
+                    className="documentButtonBar-icon"
+                    style={{ color: 'white' }}
+                    onClick={e =>
+                        TabDocView.PinDoc(
+                            this.props
+                                .views()
+                                .filter(v => v)
+                                .map(dv => dv!.rootDoc),
+                            { pinDocView: true }
+                        )
+                    }>
+                    <FontAwesomeIcon className="documentdecorations-icon" size="sm" icon="map-pin" />
+                </div>
+            </Tooltip>
+        );
     }
 
     @computed
     get shareButton() {
         const targetDoc = this.view0?.props.Document;
-        return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Open Sharing Manager"}</div></>}>
-            <div className="documentButtonBar-icon" style={{ color: "white" }} onClick={e => SharingManager.Instance.open(this.view0, targetDoc)}>
-                <FontAwesomeIcon className="documentdecorations-icon" icon="users" />
-            </div></Tooltip >;
+        return !targetDoc ? null : (
+            <Tooltip
+                title={
+                    <>
+                        <div className="dash-tooltip">{'Open Sharing Manager'}</div>
+                    </>
+                }>
+                <div className="documentButtonBar-icon" style={{ color: 'white' }} onClick={e => SharingManager.Instance.open(this.view0, targetDoc)}>
+                    <FontAwesomeIcon className="documentdecorations-icon" icon="users" />
+                </div>
+            </Tooltip>
+        );
     }
 
     @computed
     get menuButton() {
         const targetDoc = this.view0?.props.Document;
-        return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{`Open Context Menu`}</div></>}>
-            <div className="documentButtonBar-icon" style={{ color: "white", cursor: "pointer" }} onClick={e => this.openContextMenu(e)}>
-                <FontAwesomeIcon className="documentdecorations-icon" icon="bars" />
-            </div></Tooltip >;
+        return !targetDoc ? null : (
+            <Tooltip
+                title={
+                    <>
+                        <div className="dash-tooltip">{`Open Context Menu`}</div>
+                    </>
+                }>
+                <div className="documentButtonBar-icon" style={{ color: 'white', cursor: 'pointer' }} onClick={e => this.openContextMenu(e)}>
+                    <FontAwesomeIcon className="documentdecorations-icon" icon="bars" />
+                </div>
+            </Tooltip>
+        );
     }
 
     @computed
     get moreButton() {
         const targetDoc = this.view0?.props.Document;
-        return !targetDoc ? (null) : <Tooltip title={<><div className="dash-tooltip">{`${CurrentUserUtils.propertiesWidth > 0 ? "Close" : "Open"} Properties Panel`}</div></>}>
-            <div className="documentButtonBar-icon" style={{ color: "white", cursor: "e-resize" }} onClick={action(e =>
-                CurrentUserUtils.propertiesWidth = CurrentUserUtils.propertiesWidth > 0 ? 0 : 250)}>
-                <FontAwesomeIcon className="documentdecorations-icon" icon="ellipsis-h"
-                />
-            </div></Tooltip >;
+        return !targetDoc ? null : (
+            <Tooltip
+                title={
+                    <>
+                        <div className="dash-tooltip">{`${SettingsManager.propertiesWidth > 0 ? 'Close' : 'Open'} Properties Panel`}</div>
+                    </>
+                }>
+                <div className="documentButtonBar-icon" style={{ color: 'white', cursor: 'e-resize' }} onClick={action(e => (SettingsManager.propertiesWidth = SettingsManager.propertiesWidth > 0 ? 0 : 250))}>
+                    <FontAwesomeIcon className="documentdecorations-icon" icon="ellipsis-h" />
+                </div>
+            </Tooltip>
+        );
     }
 
     @computed
     get metadataButton() {
         const view0 = this.view0;
-        return !view0 ? (null) : <Tooltip title={<><div className="dash-tooltip">Show metadata panel</div></>}>
-            <div className="documentButtonBar-linkFlyout">
-                <Flyout anchorPoint={anchorPoints.LEFT_TOP}
-                    content={<MetadataEntryMenu docs={this.props.views().filter(dv => dv).map(dv => dv!.props.Document)} suggestWithFunction />  /* tfs: @bcz This might need to be the data document? */}>
-                    <div className={"documentButtonBar-linkButton-" + "empty"} onPointerDown={e => e.stopPropagation()} >
-                        {<FontAwesomeIcon className="documentdecorations-icon" icon="tag" />}
-                    </div>
-                </Flyout>
-            </div></Tooltip>;
+        return !view0 ? null : (
+            <Tooltip
+                title={
+                    <>
+                        <div className="dash-tooltip">Show metadata panel</div>
+                    </>
+                }>
+                <div className="documentButtonBar-linkFlyout">
+                    <Flyout
+                        anchorPoint={anchorPoints.LEFT_TOP}
+                        content={
+                            <MetadataEntryMenu
+                                docs={this.props
+                                    .views()
+                                    .filter(dv => dv)
+                                    .map(dv => dv!.props.Document)}
+                                suggestWithFunction
+                            /> /* tfs: @bcz This might need to be the data document? */
+                        }>
+                        <div className={'documentButtonBar-linkButton-' + 'empty'} onPointerDown={e => e.stopPropagation()}>
+                            {<FontAwesomeIcon className="documentdecorations-icon" icon="tag" />}
+                        </div>
+                    </Flyout>
+                </div>
+            </Tooltip>
+        );
     }
     @observable _aliasDown = false;
     onAliasButtonDown = action((e: React.PointerEvent): void => {
@@ -264,13 +345,13 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             const dragDocView = this.view0!;
             const dragData = new DragManager.DocumentDragData([dragDocView.props.Document]);
             const [left, top] = dragDocView.props.ScreenToLocalTransform().inverse().transformPoint(0, 0);
-            dragData.defaultDropAction = "alias";
+            dragData.defaultDropAction = 'alias';
             dragData.canEmbed = true;
             DragManager.StartDocumentDrag([dragDocView.ContentDiv!], dragData, left, top, { hideSource: false });
             return true;
         }
         return false;
-    }
+    };
 
     _ref = React.createRef<HTMLDivElement>();
     @observable _tooltipOpen: boolean = false;
@@ -278,49 +359,60 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
     get templateButton() {
         const view0 = this.view0;
         const views = this.props.views();
-        return !view0 ? (null) :
-            <Tooltip title={<div className="dash-tooltip">Tap to Customize Layout. Drag an embeddable alias</div>} open={this._tooltipOpen} onClose={action(() => this._tooltipOpen = false)} placement="bottom">
-                <div className="documentButtonBar-linkFlyout" ref={this._dragRef}
-                    onPointerEnter={action(() => !this._ref.current?.getBoundingClientRect().width && (this._tooltipOpen = true))} >
-
-                    <Flyout anchorPoint={anchorPoints.LEFT_TOP} onOpen={action(() => this._aliasDown = true)} onClose={action(() => this._aliasDown = false)}
-                        content={!this._aliasDown ? (null) :
-                            <div ref={this._ref}> <TemplateMenu docViews={views.filter(v => v).map(v => v as DocumentView)} /></div>}>
-                        <div className={"documentButtonBar-linkButton-empty"} ref={this._dragRef} onPointerDown={this.onAliasButtonDown} >
+        return !view0 ? null : (
+            <Tooltip title={<div className="dash-tooltip">Tap to Customize Layout. Drag an embeddable alias</div>} open={this._tooltipOpen} onClose={action(() => (this._tooltipOpen = false))} placement="bottom">
+                <div className="documentButtonBar-linkFlyout" ref={this._dragRef} onPointerEnter={action(() => !this._ref.current?.getBoundingClientRect().width && (this._tooltipOpen = true))}>
+                    <Flyout
+                        anchorPoint={anchorPoints.LEFT_TOP}
+                        onOpen={action(() => (this._aliasDown = true))}
+                        onClose={action(() => (this._aliasDown = false))}
+                        content={
+                            !this._aliasDown ? null : (
+                                <div ref={this._ref}>
+                                    {' '}
+                                    <TemplateMenu docViews={views.filter(v => v).map(v => v as DocumentView)} />
+                                </div>
+                            )
+                        }>
+                        <div className={'documentButtonBar-linkButton-empty'} ref={this._dragRef} onPointerDown={this.onAliasButtonDown}>
                             {<FontAwesomeIcon className="documentdecorations-icon" icon="edit" size="sm" />}
                         </div>
                     </Flyout>
                 </div>
-            </Tooltip>;
+            </Tooltip>
+        );
     }
 
     openContextMenu = (e: React.MouseEvent) => {
         let child = SelectionManager.Views()[0].ContentDiv!.children[0];
         while (child.children.length) {
-            const next = Array.from(child.children).find(c => c.className?.toString().includes("SVGAnimatedString") || typeof (c.className) === "string");
+            const next = Array.from(child.children).find(c => c.className?.toString().includes('SVGAnimatedString') || typeof c.className === 'string');
             if (next?.className?.toString().includes(DocumentView.ROOT_DIV)) break;
             if (next?.className?.toString().includes(DashFieldView.name)) break;
             if (next) child = next;
             else break;
         }
         simulateMouseClick(child, e.clientX, e.clientY - 30, e.screenX, e.screenY - 30);
-    }
+    };
 
     render() {
-        if (!this.view0) return (null);
+        if (!this.view0) return null;
 
         const isText = this.view0.props.Document[this.view0.LayoutFieldKey] instanceof RichTextField;
         const doc = this.view0?.props.Document;
         const considerPull = isText && this.considerGoogleDocsPull;
         const considerPush = isText && this.considerGoogleDocsPush;
-        return <div className="documentButtonBar">
-            <div className="documentButtonBar-button">
-                <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={true} />
-            </div>
-            {(DocumentLinksButton.StartLink || Doc.UserDoc()["documentLinksButton-fullMenu"]) && DocumentLinksButton.StartLink !== doc ? <div className="documentButtonBar-button">
-                <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={false} />
-            </div> : (null)}
-            {/*!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button">
+        return (
+            <div className="documentButtonBar">
+                <div className="documentButtonBar-button">
+                    <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={true} />
+                </div>
+                {(DocumentLinksButton.StartLink || Doc.UserDoc()['documentLinksButton-fullMenu']) && DocumentLinksButton.StartLink !== doc ? (
+                    <div className="documentButtonBar-button">
+                        <DocumentLinksButton View={this.view0} AlwaysOn={true} InMenu={true} StartLink={false} />
+                    </div>
+                ) : null}
+                {/*!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button">
                 {this.templateButton}
             </div>
             /*<div className="documentButtonBar-button">
@@ -329,27 +421,22 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
             <div className="documentButtonBar-button">
                 {this.contextButton}
             </div> */}
-            {!SelectionManager.Views()?.some(v => v.allLinks.length) ? (null) : <div className="documentButtonBar-button">
-                {this.followLinkButton}
-            </div>}
-            <div className="documentButtonBar-button">
-                {this.pinButton}
-            </div>
-            {!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button">
-                {this.shareButton}
-            </div>}
-            {!Doc.UserDoc()["documentLinksButton-fullMenu"] ? (null) : <div className="documentButtonBar-button" style={{ display: !considerPush ? "none" : "" }}>
-                {this.considerGoogleDocsPush}
-            </div>}
-            <div className="documentButtonBar-button" style={{ display: !considerPull ? "none" : "" }}>
-                {this.considerGoogleDocsPull}
-            </div>
-            <div className="documentButtonBar-button">
-                {this.menuButton}
-            </div>
-            {/* {Doc.noviceMode ? (null) : <div className="documentButtonBar-button">
+                {!SelectionManager.Views()?.some(v => v.allLinks.length) ? null : <div className="documentButtonBar-button">{this.followLinkButton}</div>}
+                <div className="documentButtonBar-button">{this.pinButton}</div>
+                {!Doc.UserDoc()['documentLinksButton-fullMenu'] ? null : <div className="documentButtonBar-button">{this.shareButton}</div>}
+                {!Doc.UserDoc()['documentLinksButton-fullMenu'] ? null : (
+                    <div className="documentButtonBar-button" style={{ display: !considerPush ? 'none' : '' }}>
+                        {this.considerGoogleDocsPush}
+                    </div>
+                )}
+                <div className="documentButtonBar-button" style={{ display: !considerPull ? 'none' : '' }}>
+                    {this.considerGoogleDocsPull}
+                </div>
+                <div className="documentButtonBar-button">{this.menuButton}</div>
+                {/* {Doc.noviceMode ? (null) : <div className="documentButtonBar-button">
                 {this.moreButton}
             </div>} */}
-        </div>;
+            </div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 9544c588b..c55daca3f 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -7,13 +7,12 @@ import { DateField } from '../../fields/DateField';
 import { AclAdmin, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, WidthSym } from '../../fields/Doc';
 import { Document } from '../../fields/documentSchemas';
 import { InkField } from '../../fields/InkField';
-import { ComputedField, ScriptField } from '../../fields/ScriptField';
-import { Cast, FieldValue, NumCast, StrCast } from '../../fields/Types';
+import { ScriptField } from '../../fields/ScriptField';
+import { Cast, NumCast, StrCast } from '../../fields/Types';
 import { GetEffectiveAcl } from '../../fields/util';
-import { emptyFunction, returnFalse, setupMoveUpEvents, numberValue, numbersAlmostEqual } from '../../Utils';
+import { emptyFunction, numberValue, returnFalse, setupMoveUpEvents } from '../../Utils';
 import { Docs } from '../documents/Documents';
 import { DocumentType } from '../documents/DocumentTypes';
-import { CurrentUserUtils } from '../util/CurrentUserUtils';
 import { DragManager } from '../util/DragManager';
 import { SelectionManager } from '../util/SelectionManager';
 import { SnappingManager } from '../util/SnappingManager';
@@ -22,7 +21,7 @@ import { CollectionDockingView } from './collections/CollectionDockingView';
 import { CollectionFreeFormView } from './collections/collectionFreeForm';
 import { DocumentButtonBar } from './DocumentButtonBar';
 import './DocumentDecorations.scss';
-import { KeyManager } from './GlobalKeyHandler';
+import { Colors } from './global/globalEnums';
 import { InkingStroke } from './InkingStroke';
 import { InkStrokeProperties } from './InkStrokeProperties';
 import { LightboxView } from './LightboxView';
@@ -30,7 +29,6 @@ import { DocumentView } from './nodes/DocumentView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { ImageBox } from './nodes/ImageBox';
 import React = require('react');
-import { Colors } from './global/globalEnums';
 
 @observer
 export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> {
@@ -105,10 +103,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                         if (titleFieldKey === 'title') {
                             d.dataDoc['title-custom'] = !this._accumulatedTitle.startsWith('-');
                             if (StrCast(d.rootDoc.title).startsWith('@') && !this._accumulatedTitle.startsWith('@')) {
-                                Doc.RemoveDocFromList(CurrentUserUtils.MyPublishedDocs, undefined, d.rootDoc);
+                                Doc.RemoveDocFromList(Doc.MyPublishedDocs, undefined, d.rootDoc);
                             }
                             if (!StrCast(d.rootDoc.title).startsWith('@') && this._accumulatedTitle.startsWith('@')) {
-                                Doc.AddDocToList(CurrentUserUtils.MyPublishedDocs, undefined, d.rootDoc);
+                                Doc.AddDocToList(Doc.MyPublishedDocs, undefined, d.rootDoc);
                             }
                         }
                         //@ts-ignore
@@ -266,7 +264,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
     };
 
     onSelectorClick = () => SelectionManager.Views()?.[0]?.props.ContainingCollectionView?.props.select(false);
- 
+
     /**
      * Handles setting up events when user clicks on the border radius editor
      * @param e PointerEvent
@@ -284,21 +282,20 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                 const y = this.Bounds.y + 3;
                 const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2);
                 let dist = Math.sqrt((e.clientX - x) * (e.clientX - x) + (e.clientY - y) * (e.clientY - y));
-                if (e.clientX < x && e.clientY < y) dist = 0
+                if (e.clientX < x && e.clientY < y) dist = 0;
                 SelectionManager.Views()
                     .map(dv => dv.props.Document)
                     .map(doc => {
-                        const docMax = Math.min(NumCast(doc.width)/2, NumCast(doc.height)/2);
-                        const ratio = dist/maxDist;
+                        const docMax = Math.min(NumCast(doc.width) / 2, NumCast(doc.height) / 2);
+                        const ratio = dist / maxDist;
                         const radius = Math.min(1, ratio) * docMax;
                         doc.borderRounding = `${radius}px`;
-                    }
-                    );
+                    });
                 return false;
             }, // moveEvent
             action(e => {
                 this._isRounding = false;
-                this._resizeUndo?.end()
+                this._resizeUndo?.end();
             }), // upEvent
             e => {} // clickEvent
         );
@@ -325,8 +322,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                         // Rotation between -360 and 360
                         let newRotation = (oldRotation - (angle * 180) / Math.PI) % 360;
 
-                        const diff = Math.round(newRotation / 45) - newRotation / 45
-                        if (diff < .05) {
+                        const diff = Math.round(newRotation / 45) - newRotation / 45;
+                        if (diff < 0.05) {
                             console.log('show lines');
                         }
                         dv.rootDoc._jitterRotation = newRotation;
@@ -337,12 +334,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             action(() => {
                 SelectionManager.Views().forEach(dv => {
                     const oldRotation = NumCast(dv.rootDoc._jitterRotation);
-                    const diff = Math.round(oldRotation / 45) - oldRotation / 45
-                    if (diff < .05) {
+                    const diff = Math.round(oldRotation / 45) - oldRotation / 45;
+                    if (diff < 0.05) {
                         let newRotation = Math.round(oldRotation / 45) * 45;
                         dv.rootDoc._jitterRotation = newRotation;
                     }
-                })
+                });
                 this._isRotating = false;
                 rotateUndo?.end();
                 UndoManager.FilterBatches(['data', 'x', 'y', 'width', 'height']);
@@ -599,13 +596,16 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         // hide the decorations if the parent chooses to hide it or if the document itself hides it
         const hideResizers = seldoc.props.hideResizeHandles || seldoc.rootDoc.hideResizeHandles || seldoc.rootDoc._isGroup || this._isRounding || this._isRotating;
         const hideTitle = seldoc.props.hideDecorationTitle || seldoc.rootDoc.hideDecorationTitle || this._isRounding || this._isRotating;
-        const hideDocumentButtonBar = seldoc.props.hideDocumentButtonBar || seldoc.rootDoc.hideDocumentButtonBar || this._isRounding || 
-        this._isRotating;
+        const hideDocumentButtonBar = seldoc.props.hideDocumentButtonBar || seldoc.rootDoc.hideDocumentButtonBar || this._isRounding || this._isRotating;
         // if multiple documents have been opened at the same time, then don't show open button
         const hideOpenButton =
-            seldoc.props.hideOpenButton || seldoc.rootDoc.hideOpenButton || SelectionManager.Views().some(docView => docView.props.Document._stayInCollection || docView.props.Document.isGroup || docView.props.Document.hideOpenButton) || this._isRounding || this._isRotating;
+            seldoc.props.hideOpenButton ||
+            seldoc.rootDoc.hideOpenButton ||
+            SelectionManager.Views().some(docView => docView.props.Document._stayInCollection || docView.props.Document.isGroup || docView.props.Document.hideOpenButton) ||
+            this._isRounding ||
+            this._isRotating;
         const hideDeleteButton =
-            this._isRounding || 
+            this._isRounding ||
             this._isRotating ||
             seldoc.props.hideDeleteButton ||
             seldoc.rootDoc.hideDeleteButton ||
@@ -635,7 +635,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
             </Tooltip>
         );
 
-        const colorScheme = StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme);
+        const colorScheme = StrCast(Doc.ActiveDashboard?.colorScheme);
         const titleArea = hideTitle ? null : this._editingTitle ? (
             <input
                 ref={this._keyinput}
@@ -665,14 +665,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
         // Rotation constants: Only allow rotation on ink and images
         const useRotation = seldoc.ComponentView instanceof InkingStroke || seldoc.ComponentView instanceof ImageBox;
         const rotation = NumCast(seldoc.rootDoc._jitterRotation);
-        
 
         const resizerScheme = colorScheme ? 'documentDecorations-resizer' + colorScheme : '';
 
         // Radius constants
         const useRounding = seldoc.ComponentView instanceof ImageBox || seldoc.ComponentView instanceof FormattedTextBox;
         const borderRadius = numberValue(StrCast(seldoc.rootDoc.borderRounding));
-        const docMax = Math.min(NumCast(seldoc.rootDoc.width)/2, NumCast(seldoc.rootDoc.height)/2);
+        const docMax = Math.min(NumCast(seldoc.rootDoc.width) / 2, NumCast(seldoc.rootDoc.height) / 2);
         const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2);
         const radiusHandle = (borderRadius / docMax) * maxDist;
         const radiusHandleLocation = Math.min(radiusHandle, maxDist);
@@ -722,7 +721,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                                     <div key="b" className={`documentDecorations-bottomResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} />
                                     <div key="br" className={`documentDecorations-bottomRightResizer ${resizerScheme}`} onPointerDown={this.onPointerDown} onContextMenu={e => e.preventDefault()} />
 
-                                    {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? null : topBtn('selector', 'arrow-alt-circle-up', undefined, this.onSelectorClick, 'tap to select containing document')}   
+                                    {seldoc.props.renderDepth <= 1 || !seldoc.props.ContainingCollectionView ? null : topBtn('selector', 'arrow-alt-circle-up', undefined, this.onSelectorClick, 'tap to select containing document')}
                                 </>
                             )}
 
@@ -733,11 +732,16 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                             )}
 
                             {useRounding && (
-                                <div key="rad" style={{
+                                <div
+                                    key="rad"
+                                    style={{
                                         background: `${this._isRounding ? Colors.MEDIUM_BLUE : undefined}`,
-                                        left:`${radiusHandleLocation + 3}`,
-                                        top:`${radiusHandleLocation + 23}`
-                                    }} className={`documentDecorations-borderRadius`} onPointerDown={this.onRadiusDown} onContextMenu={e => e.preventDefault()} 
+                                        left: `${radiusHandleLocation + 3}`,
+                                        top: `${radiusHandleLocation + 23}`,
+                                    }}
+                                    className={`documentDecorations-borderRadius`}
+                                    onPointerDown={this.onRadiusDown}
+                                    onContextMenu={e => e.preventDefault()}
                                 />
                             )}
 
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index e960f5cca..5a68e9091 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -1,37 +1,52 @@
-import React = require("react");
+import React = require('react');
 import * as fitCurve from 'fit-curve';
-import { action, computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc } from "../../fields/Doc";
-import { InkData, InkTool } from "../../fields/InkField";
-import { Cast, FieldValue, NumCast } from "../../fields/Types";
-import MobileInkOverlay from "../../mobile/MobileInkOverlay";
-import { GestureUtils } from "../../pen-gestures/GestureUtils";
-import { MobileInkOverlayContent } from "../../server/Message";
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from "../../Utils";
-import { CognitiveServices } from "../cognitive_services/CognitiveServices";
-import { DocUtils } from "../documents/Documents";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { InteractionUtils } from "../util/InteractionUtils";
-import { ScriptingGlobals } from "../util/ScriptingGlobals";
-import { SelectionManager } from "../util/SelectionManager";
-import { Transform } from "../util/Transform";
-import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu";
-import "./GestureOverlay.scss";
-import { ActiveArrowEnd, ActiveArrowScale, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, SetActiveArrowStart, SetActiveDash, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from "./InkingStroke";
-import { checkInksToGroup, createInkGroup } from "./nodes/button/FontIconBox";
-import { DocumentView } from "./nodes/DocumentView";
-import { RadialMenu } from "./nodes/RadialMenu";
-import HorizontalPalette from "./Palette";
-import { Touchable } from "./Touchable";
-import TouchScrollableMenu, { TouchScrollableMenuItem } from "./TouchScrollableMenu";
-import { InkTranscription } from "./InkTranscription";
+import { action, computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc } from '../../fields/Doc';
+import { InkData, InkTool } from '../../fields/InkField';
+import { List } from '../../fields/List';
+import { ScriptField } from '../../fields/ScriptField';
+import { Cast, FieldValue, NumCast } from '../../fields/Types';
+import MobileInkOverlay from '../../mobile/MobileInkOverlay';
+import { GestureUtils } from '../../pen-gestures/GestureUtils';
+import { MobileInkOverlayContent } from '../../server/Message';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
+import { CognitiveServices } from '../cognitive_services/CognitiveServices';
+import { Docs, DocUtils } from '../documents/Documents';
+import { InteractionUtils } from '../util/InteractionUtils';
+import { ScriptingGlobals } from '../util/ScriptingGlobals';
+import { SelectionManager } from '../util/SelectionManager';
+import { Transform } from '../util/Transform';
+import { CollectionFreeFormViewChrome } from './collections/CollectionMenu';
+import './GestureOverlay.scss';
+import {
+    ActiveArrowEnd,
+    ActiveArrowScale,
+    ActiveArrowStart,
+    ActiveDash,
+    ActiveFillColor,
+    ActiveInkBezierApprox,
+    ActiveInkColor,
+    ActiveInkWidth,
+    SetActiveArrowStart,
+    SetActiveDash,
+    SetActiveFillColor,
+    SetActiveInkColor,
+    SetActiveInkWidth,
+} from './InkingStroke';
+import { InkTranscription } from './InkTranscription';
+import { checkInksToGroup } from './nodes/button/FontIconBox';
+import { DocumentView } from './nodes/DocumentView';
+import { RadialMenu } from './nodes/RadialMenu';
+import HorizontalPalette from './Palette';
+import { Touchable } from './Touchable';
+import TouchScrollableMenu, { TouchScrollableMenuItem } from './TouchScrollableMenu';
 
 @observer
 export class GestureOverlay extends Touchable {
     static Instance: GestureOverlay;
 
-    @observable public InkShape: string = "";
+    @observable public InkShape: string = '';
     @observable public SavedColor?: string;
     @observable public SavedWidth?: number;
     @observable public Tool: ToolglassTools = ToolglassTools.None;
@@ -42,14 +57,18 @@ export class GestureOverlay extends Touchable {
     @observable private _menuX: number = -300;
     @observable private _menuY: number = -300;
     @observable private _pointerY?: number;
-    @observable private _points: { X: number, Y: number }[] = [];
+    @observable private _points: { X: number; Y: number }[] = [];
     @observable private _strokes: InkData[] = [];
     @observable private _palette?: JSX.Element;
     @observable private _clipboardDoc?: JSX.Element;
     @observable private _possibilities: JSX.Element[] = [];
 
-    @computed private get height(): number { return 2 * Math.max(this._pointerY && this._thumbY ? this._thumbY - this._pointerY : 100, 100); }
-    @computed private get showBounds() { return this.Tool !== ToolglassTools.None; }
+    @computed private get height(): number {
+        return 2 * Math.max(this._pointerY && this._thumbY ? this._thumbY - this._pointerY : 100, 100);
+    }
+    @computed private get showBounds() {
+        return this.Tool !== ToolglassTools.None;
+    }
 
     @observable private showMobileInkOverlay: boolean = false;
 
@@ -70,10 +89,73 @@ export class GestureOverlay extends Touchable {
         GestureOverlay.Instance = this;
     }
 
+    static setupThumbButtons(doc: Doc) {
+        const docProtoData: { title: string; icon: string; drag?: string; ignoreClick?: boolean; pointerDown?: string; pointerUp?: string; clipboard?: Doc; backgroundColor?: string; dragFactory?: Doc }[] = [
+            { title: 'use pen', icon: 'pen-nib', pointerUp: 'resetPen()', pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: 'blue' },
+            { title: 'use highlighter', icon: 'highlighter', pointerUp: 'resetPen()', pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: 'yellow' },
+            {
+                title: 'notepad',
+                icon: 'clipboard',
+                pointerUp: 'GestureOverlay.Instance.closeFloatingDoc()',
+                pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)',
+                clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300, system: true }),
+                backgroundColor: 'orange',
+            },
+            { title: 'interpret text', icon: 'font', pointerUp: "setToolglass('none')", pointerDown: "setToolglass('inktotext')", backgroundColor: 'orange' },
+            { title: 'ignore gestures', icon: 'signature', pointerUp: "setToolglass('none')", pointerDown: "setToolglass('ignoregesture')", backgroundColor: 'green' },
+        ];
+        return docProtoData.map(data =>
+            Docs.Create.FontIconDocument({
+                _nativeWidth: 10,
+                _nativeHeight: 10,
+                _width: 10,
+                _height: 10,
+                title: data.title,
+                icon: data.icon,
+                _dropAction: data.pointerDown ? 'copy' : undefined,
+                ignoreClick: data.ignoreClick,
+                onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined,
+                clipboard: data.clipboard,
+                onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined,
+                onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined,
+                backgroundColor: data.backgroundColor,
+                _removeDropProperties: new List<string>(['dropAction']),
+                dragFactory: data.dragFactory,
+                system: true,
+            })
+        );
+    }
+
+    static setupThumbDoc(userDoc: Doc) {
+        if (!userDoc.thumbDoc) {
+            const thumbDoc = Docs.Create.LinearDocument(GestureOverlay.setupThumbButtons(userDoc), {
+                _width: 100,
+                _height: 50,
+                ignoreClick: true,
+                _lockedPosition: true,
+                title: 'buttons',
+                _autoHeight: true,
+                _yMargin: 5,
+                linearViewIsExpanded: true,
+                backgroundColor: 'white',
+                system: true,
+            });
+            thumbDoc.inkToTextDoc = Docs.Create.LinearDocument([], {
+                _width: 300,
+                _height: 25,
+                _autoHeight: true,
+                linearViewIsExpanded: true,
+                flexDirection: 'column',
+                system: true,
+            });
+            userDoc.thumbDoc = thumbDoc;
+        }
+        return Cast(userDoc.thumbDoc, Doc);
+    }
     componentDidMount = () => {
-        this._thumbDoc = FieldValue(Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc));
+        this._thumbDoc = FieldValue(Cast(GestureOverlay.setupThumbDoc(Doc.UserDoc()), Doc));
         this._inkToTextDoc = FieldValue(Cast(this._thumbDoc?.inkToTextDoc, Doc));
-    }
+    };
 
     // TODO: nda - add dragging groups with one finger drag and have to click into group to scroll within the group
 
@@ -84,24 +166,24 @@ export class GestureOverlay extends Touchable {
         const ntt: (React.Touch | Touch)[] = Array.from(e.targetTouches);
         const nct: (React.Touch | Touch)[] = Array.from(e.changedTouches);
         const nt: (React.Touch | Touch)[] = Array.from(e.touches);
-        this._hands.forEach((hand) => {
+        this._hands.forEach(hand => {
             for (let i = 0; i < e.targetTouches.length; i++) {
                 const pt = e.targetTouches.item(i);
-                if (pt && hand.some((finger) => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) {
+                if (pt && hand.some(finger => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) {
                     ntt.splice(ntt.indexOf(pt), 1);
                 }
             }
 
             for (let i = 0; i < e.changedTouches.length; i++) {
                 const pt = e.changedTouches.item(i);
-                if (pt && hand.some((finger) => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) {
+                if (pt && hand.some(finger => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) {
                     nct.splice(nct.indexOf(pt), 1);
                 }
             }
 
             for (let i = 0; i < e.touches.length; i++) {
                 const pt = e.touches.item(i);
-                if (pt && hand.some((finger) => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) {
+                if (pt && hand.some(finger => finger.screenX === pt.screenX && finger.screenY === pt.screenY)) {
                     nt.splice(nt.indexOf(pt), 1);
                 }
             }
@@ -110,8 +192,8 @@ export class GestureOverlay extends Touchable {
     }
 
     onReactTouchStart = (te: React.TouchEvent) => {
-        document.removeEventListener("touchmove", this.onReactHoldTouchMove);
-        document.removeEventListener("touchend", this.onReactHoldTouchEnd);
+        document.removeEventListener('touchmove', this.onReactHoldTouchMove);
+        document.removeEventListener('touchend', this.onReactHoldTouchEnd);
         if (RadialMenu.Instance?._display === true) {
             te.preventDefault();
             te.stopPropagation();
@@ -129,7 +211,7 @@ export class GestureOverlay extends Touchable {
             // and this seems to be the only way of differentiating pen and touch on touch events
             if (pt.radiusX > 1 && pt.radiusY > 1) {
                 InkTranscription.Instance.createInkGroup();
-                CurrentUserUtils.ActiveTool = InkTool.None;
+                Doc.ActiveTool = InkTool.None;
                 this.prevPoints.set(pt.identifier, pt);
             }
         }
@@ -147,18 +229,16 @@ export class GestureOverlay extends Touchable {
         if (nts.nt.length < 5) {
             const target = document.elementFromPoint(te.changedTouches.item(0).clientX, te.changedTouches.item(0).clientY);
             target?.dispatchEvent(
-                new CustomEvent<InteractionUtils.MultiTouchEvent<React.TouchEvent>>("dashOnTouchStart",
-                    {
-                        bubbles: true,
-                        detail: {
-                            fingers: this.prevPoints.size,
-                            targetTouches: nts.ntt,
-                            touches: nts.nt,
-                            changedTouches: nts.nct,
-                            touchEvent: te
-                        }
-                    }
-                )
+                new CustomEvent<InteractionUtils.MultiTouchEvent<React.TouchEvent>>('dashOnTouchStart', {
+                    bubbles: true,
+                    detail: {
+                        fingers: this.prevPoints.size,
+                        targetTouches: nts.ntt,
+                        touches: nts.nt,
+                        changedTouches: nts.nct,
+                        touchEvent: te,
+                    },
+                })
             );
             if (nts.nt.length === 1) {
                 // -- radial menu code --
@@ -167,45 +247,41 @@ export class GestureOverlay extends Touchable {
                     const pt: any = te.touches[te.touches?.length - 1];
                     if (nts.nt.length === 1 && pt.radiusX > 1 && pt.radiusY > 1) {
                         target?.dispatchEvent(
-                            new CustomEvent<InteractionUtils.MultiTouchEvent<React.TouchEvent>>("dashOnTouchHoldStart",
-                                {
-                                    bubbles: true,
-                                    detail: {
-                                        fingers: this.prevPoints.size,
-                                        targetTouches: nts.ntt,
-                                        touches: nts.nt,
-                                        changedTouches: nts.nct,
-                                        touchEvent: te
-                                    }
-                                }
-                            )
+                            new CustomEvent<InteractionUtils.MultiTouchEvent<React.TouchEvent>>('dashOnTouchHoldStart', {
+                                bubbles: true,
+                                detail: {
+                                    fingers: this.prevPoints.size,
+                                    targetTouches: nts.ntt,
+                                    touches: nts.nt,
+                                    changedTouches: nts.nct,
+                                    touchEvent: te,
+                                },
+                            })
                         );
                         this._holdTimer = undefined;
-                        document.removeEventListener("touchmove", this.onReactTouchMove);
-                        document.removeEventListener("touchend", this.onReactTouchEnd);
-                        document.removeEventListener("touchmove", this.onReactHoldTouchMove);
-                        document.removeEventListener("touchend", this.onReactHoldTouchEnd);
-                        document.addEventListener("touchmove", this.onReactHoldTouchMove);
-                        document.addEventListener("touchend", this.onReactHoldTouchEnd);
+                        document.removeEventListener('touchmove', this.onReactTouchMove);
+                        document.removeEventListener('touchend', this.onReactTouchEnd);
+                        document.removeEventListener('touchmove', this.onReactHoldTouchMove);
+                        document.removeEventListener('touchend', this.onReactHoldTouchEnd);
+                        document.addEventListener('touchmove', this.onReactHoldTouchMove);
+                        document.addEventListener('touchend', this.onReactHoldTouchEnd);
                     }
-
-                }, (500));
-            }
-            else {
+                }, 500);
+            } else {
                 this._holdTimer && clearTimeout(this._holdTimer);
             }
-            document.removeEventListener("touchmove", this.onReactTouchMove);
-            document.removeEventListener("touchend", this.onReactTouchEnd);
-            document.addEventListener("touchmove", this.onReactTouchMove);
-            document.addEventListener("touchend", this.onReactTouchEnd);
+            document.removeEventListener('touchmove', this.onReactTouchMove);
+            document.removeEventListener('touchend', this.onReactTouchEnd);
+            document.addEventListener('touchmove', this.onReactTouchMove);
+            document.addEventListener('touchend', this.onReactTouchEnd);
         }
         // otherwise, handle as a hand event
         else {
             this.handleHandDown(te);
-            document.removeEventListener("touchmove", this.onReactTouchMove);
-            document.removeEventListener("touchend", this.onReactTouchEnd);
+            document.removeEventListener('touchmove', this.onReactTouchMove);
+            document.removeEventListener('touchend', this.onReactTouchEnd);
         }
-    }
+    };
 
     onReactTouchMove = (e: TouchEvent) => {
         const nts: any = this.getNewTouches(e);
@@ -213,19 +289,18 @@ export class GestureOverlay extends Touchable {
         this._holdTimer = undefined;
 
         document.dispatchEvent(
-            new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>("dashOnTouchMove",
-                {
-                    bubbles: true,
-                    detail: {
-                        fingers: this.prevPoints.size,
-                        targetTouches: nts.ntt,
-                        touches: nts.nt,
-                        changedTouches: nts.nct,
-                        touchEvent: e
-                    }
-                })
+            new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>('dashOnTouchMove', {
+                bubbles: true,
+                detail: {
+                    fingers: this.prevPoints.size,
+                    targetTouches: nts.ntt,
+                    touches: nts.nt,
+                    changedTouches: nts.nct,
+                    touchEvent: e,
+                },
+            })
         );
-    }
+    };
 
     onReactTouchEnd = (e: TouchEvent) => {
         const nts: any = this.getNewTouches(e);
@@ -233,17 +308,16 @@ export class GestureOverlay extends Touchable {
         this._holdTimer = undefined;
 
         document.dispatchEvent(
-            new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>("dashOnTouchEnd",
-                {
-                    bubbles: true,
-                    detail: {
-                        fingers: this.prevPoints.size,
-                        targetTouches: nts.ntt,
-                        touches: nts.nt,
-                        changedTouches: nts.nct,
-                        touchEvent: e
-                    }
-                })
+            new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>('dashOnTouchEnd', {
+                bubbles: true,
+                detail: {
+                    fingers: this.prevPoints.size,
+                    targetTouches: nts.ntt,
+                    touches: nts.nt,
+                    changedTouches: nts.nct,
+                    touchEvent: e,
+                },
+            })
         );
 
         // cleanup any lingering pointers
@@ -257,11 +331,11 @@ export class GestureOverlay extends Touchable {
         }
 
         if (this.prevPoints.size === 0) {
-            document.removeEventListener("touchmove", this.onReactTouchMove);
-            document.removeEventListener("touchend", this.onReactTouchEnd);
+            document.removeEventListener('touchmove', this.onReactTouchMove);
+            document.removeEventListener('touchend', this.onReactTouchEnd);
         }
         e.stopPropagation();
-    }
+    };
 
     handleHandDown = async (e: React.TouchEvent) => {
         this._holdTimer && clearTimeout(this._holdTimer);
@@ -285,17 +359,17 @@ export class GestureOverlay extends Touchable {
         }
 
         // this chunk of code determines whether this is a left hand or a right hand, as well as which pointer is the thumb and pointer
-        const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]);
+        const thumb = fingers.reduce((a, v) => (a.clientY > v.clientY ? a : v), fingers[0]);
         const rightMost = Math.max(...fingers.map(f => f.clientX));
         const leftMost = Math.min(...fingers.map(f => f.clientX));
         let pointer: React.Touch | undefined;
         // left hand
         if (thumb.clientX === rightMost) {
-            pointer = fingers.reduce((a, v) => a.clientX > v.clientX || v.identifier === thumb.identifier ? a : v);
+            pointer = fingers.reduce((a, v) => (a.clientX > v.clientX || v.identifier === thumb.identifier ? a : v));
         }
         // right hand
         else if (thumb.clientX === leftMost) {
-            pointer = fingers.reduce((a, v) => a.clientX < v.clientX || v.identifier === thumb.identifier ? a : v);
+            pointer = fingers.reduce((a, v) => (a.clientX < v.clientX || v.identifier === thumb.identifier ? a : v));
         }
         this.pointerIdentifier = pointer?.identifier;
 
@@ -316,7 +390,7 @@ export class GestureOverlay extends Touchable {
         const minY = Math.min(...others.map(f => f.clientY));
 
         // load up the palette collection around the thumb
-        const thumbDoc = await Cast(CurrentUserUtils.setupThumbDoc(CurrentUserUtils.UserDocument), Doc);
+        const thumbDoc = await Cast(GestureOverlay.setupThumbDoc(Doc.UserDoc()), Doc);
         if (thumbDoc) {
             runInAction(() => {
                 RadialMenu.Instance._display = false;
@@ -331,11 +405,11 @@ export class GestureOverlay extends Touchable {
         }
 
         this.removeMoveListeners();
-        document.removeEventListener("touchmove", this.handleHandMove);
-        document.addEventListener("touchmove", this.handleHandMove);
-        document.removeEventListener("touchend", this.handleHandUp);
-        document.addEventListener("touchend", this.handleHandUp);
-    }
+        document.removeEventListener('touchmove', this.handleHandMove);
+        document.addEventListener('touchmove', this.handleHandMove);
+        document.removeEventListener('touchend', this.handleHandUp);
+        document.addEventListener('touchend', this.handleHandUp);
+    };
 
     @action
     handleHandMove = (e: TouchEvent) => {
@@ -348,18 +422,20 @@ export class GestureOverlay extends Touchable {
                     const tPt = e.targetTouches.item(j);
                     if (tPt?.screenX === pt?.screenX && tPt?.screenY === pt?.screenY) {
                         if (pt && this.prevPoints.has(pt.identifier)) {
-                            this._hands.forEach(hand => hand.some(f => {
-                                if (f.identifier === pt.identifier) {
-                                    fingers.push(pt);
-                                }
-                            }));
+                            this._hands.forEach(hand =>
+                                hand.some(f => {
+                                    if (f.identifier === pt.identifier) {
+                                        fingers.push(pt);
+                                    }
+                                })
+                            );
                         }
                     }
                 }
             }
         }
         // update hand trackers
-        const thumb = fingers.reduce((a, v) => a.clientY > v.clientY ? a : v, fingers[0]);
+        const thumb = fingers.reduce((a, v) => (a.clientY > v.clientY ? a : v), fingers[0]);
         if (thumb?.identifier && thumb?.identifier === this.thumbIdentifier) {
             this._hands.set(thumb.identifier, fingers);
         }
@@ -373,12 +449,11 @@ export class GestureOverlay extends Touchable {
                     // moving a thumb horiz. changes the palette collection selection, moving vert. changes the selection of any menus on the current palette item
                     const yOverX = Math.abs(pt.clientX - this._thumbX) < Math.abs(pt.clientY - this._thumbY);
                     if ((yOverX && this._inkToTextDoc) || this._selectedIndex > -1) {
-                        if (Math.abs(pt.clientY - this._thumbY) > (10 * window.devicePixelRatio)) {
-                            this._selectedIndex = Math.min(Math.max(-1, (-Math.ceil((pt.clientY - this._thumbY) / (10 * window.devicePixelRatio)) - 1)), this._possibilities.length - 1);
+                        if (Math.abs(pt.clientY - this._thumbY) > 10 * window.devicePixelRatio) {
+                            this._selectedIndex = Math.min(Math.max(-1, -Math.ceil((pt.clientY - this._thumbY) / (10 * window.devicePixelRatio)) - 1), this._possibilities.length - 1);
                         }
-                    }
-                    else if (this._thumbDoc) {
-                        if (Math.abs(pt.clientX - this._thumbX) > (15 * window.devicePixelRatio)) {
+                    } else if (this._thumbDoc) {
+                        if (Math.abs(pt.clientX - this._thumbX) > 15 * window.devicePixelRatio) {
                             this._thumbDoc.selectedIndex = Math.max(-1, NumCast(this._thumbDoc.selectedIndex) - Math.sign(pt.clientX - this._thumbX));
                             this._thumbX = pt.clientX;
                         }
@@ -390,7 +465,7 @@ export class GestureOverlay extends Touchable {
                 this._pointerY = pt.clientY;
             }
         }
-    }
+    };
 
     @action
     handleHandUp = (e: TouchEvent) => {
@@ -422,38 +497,37 @@ export class GestureOverlay extends Touchable {
             this._strokes = [];
             this._points = [];
             this._possibilities = [];
-            document.removeEventListener("touchend", this.handleHandUp);
+            document.removeEventListener('touchend', this.handleHandUp);
         }
-    }
+    };
 
     /**
      * Code for radial menu
      */
     onReactHoldTouchMove = (e: TouchEvent) => {
-        document.removeEventListener("touchmove", this.onReactTouchMove);
-        document.removeEventListener("touchend", this.onReactTouchEnd);
-        document.removeEventListener("touchmove", this.onReactHoldTouchMove);
-        document.removeEventListener("touchend", this.onReactHoldTouchEnd);
-        document.addEventListener("touchmove", this.onReactHoldTouchMove);
-        document.addEventListener("touchend", this.onReactHoldTouchEnd);
+        document.removeEventListener('touchmove', this.onReactTouchMove);
+        document.removeEventListener('touchend', this.onReactTouchEnd);
+        document.removeEventListener('touchmove', this.onReactHoldTouchMove);
+        document.removeEventListener('touchend', this.onReactHoldTouchEnd);
+        document.addEventListener('touchmove', this.onReactHoldTouchMove);
+        document.addEventListener('touchend', this.onReactHoldTouchEnd);
         const nts: any = this.getNewTouches(e);
         if (this.prevPoints.size === 1 && this._holdTimer) {
             clearTimeout(this._holdTimer);
         }
         document.dispatchEvent(
-            new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>("dashOnTouchHoldMove",
-                {
-                    bubbles: true,
-                    detail: {
-                        fingers: this.prevPoints.size,
-                        targetTouches: nts.ntt,
-                        touches: nts.nt,
-                        changedTouches: nts.nct,
-                        touchEvent: e
-                    }
-                })
+            new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>('dashOnTouchHoldMove', {
+                bubbles: true,
+                detail: {
+                    fingers: this.prevPoints.size,
+                    targetTouches: nts.ntt,
+                    touches: nts.nt,
+                    changedTouches: nts.nct,
+                    touchEvent: e,
+                },
+            })
         );
-    }
+    };
 
     /**
      * Code for radial menu
@@ -465,17 +539,16 @@ export class GestureOverlay extends Touchable {
             this._holdTimer = undefined;
         }
         document.dispatchEvent(
-            new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>("dashOnTouchHoldEnd",
-                {
-                    bubbles: true,
-                    detail: {
-                        fingers: this.prevPoints.size,
-                        targetTouches: nts.ntt,
-                        touches: nts.nt,
-                        changedTouches: nts.nct,
-                        touchEvent: e
-                    }
-                })
+            new CustomEvent<InteractionUtils.MultiTouchEvent<TouchEvent>>('dashOnTouchHoldEnd', {
+                bubbles: true,
+                detail: {
+                    fingers: this.prevPoints.size,
+                    targetTouches: nts.ntt,
+                    touches: nts.nt,
+                    changedTouches: nts.nct,
+                    touchEvent: e,
+                },
+            })
         );
         for (let i = 0; i < e.changedTouches.length; i++) {
             const pt = e.changedTouches.item(i);
@@ -486,32 +559,38 @@ export class GestureOverlay extends Touchable {
             }
         }
 
-        document.removeEventListener("touchmove", this.onReactHoldTouchMove);
-        document.removeEventListener("touchend", this.onReactHoldTouchEnd);
+        document.removeEventListener('touchmove', this.onReactHoldTouchMove);
+        document.removeEventListener('touchend', this.onReactHoldTouchEnd);
 
         e.stopPropagation();
-    }
+    };
 
     @action
     onPointerDown = (e: React.PointerEvent) => {
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
-            setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => {
-                if (doubleTap) {
-                    InkTranscription.Instance.createInkGroup();
-                    CurrentUserUtils.ActiveTool = InkTool.None;
-                    return;
-                }
-            }));
+            setupMoveUpEvents(
+                this,
+                e,
+                returnFalse,
+                returnFalse,
+                action((e: PointerEvent, doubleTap?: boolean) => {
+                    if (doubleTap) {
+                        InkTranscription.Instance.createInkGroup();
+                        Doc.ActiveTool = InkTool.None;
+                        return;
+                    }
+                })
+            );
         }
-        if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
+        if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
             if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
-                CurrentUserUtils.ActiveTool = InkTool.Write;
+                Doc.ActiveTool = InkTool.Write;
             }
             this._points.push({ X: e.clientX, Y: e.clientY });
             setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
-            // if (CurrentUserUtils.ActiveTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)");
+            // if (Doc.ActiveTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)");
         }
-    }
+    };
 
     @action
     onPointerMove = (e: PointerEvent) => {
@@ -519,17 +598,18 @@ export class GestureOverlay extends Touchable {
 
         if (this._points.length > 1) {
             const B = this.svgBounds;
-            const initialPoint = this._points[0.];
+            const initialPoint = this._points[0];
             const xInGlass = initialPoint.X > (this._thumbX ?? Number.MAX_SAFE_INTEGER) && initialPoint.X < (this._thumbX ?? Number.MAX_SAFE_INTEGER) + this.height;
             const yInGlass = initialPoint.Y > (this._thumbY ?? Number.MAX_SAFE_INTEGER) - this.height && initialPoint.Y < (this._thumbY ?? Number.MAX_SAFE_INTEGER);
             if (this.Tool !== ToolglassTools.None && xInGlass && yInGlass) {
                 switch (this.Tool) {
-                    case ToolglassTools.RadialMenu: return true;
+                    case ToolglassTools.RadialMenu:
+                        return true;
                 }
             }
         }
         return false;
-    }
+    };
 
     handleLineGesture = (): boolean => {
         const actionPerformed = false;
@@ -541,19 +621,18 @@ export class GestureOverlay extends Touchable {
         const target1 = document.elementFromPoint(ep1.X, ep1.Y);
         const target2 = document.elementFromPoint(ep2.X, ep2.Y);
 
-        const ge = new CustomEvent<GestureUtils.GestureEvent>("dashOnGesture",
-            {
-                bubbles: true,
-                detail: {
-                    points: this._points,
-                    gesture: GestureUtils.Gestures.Line,
-                    bounds: B
-                }
-            });
+        const ge = new CustomEvent<GestureUtils.GestureEvent>('dashOnGesture', {
+            bubbles: true,
+            detail: {
+                points: this._points,
+                gesture: GestureUtils.Gestures.Line,
+                bounds: B,
+            },
+        });
         target1?.dispatchEvent(ge);
         target2?.dispatchEvent(ge);
         return actionPerformed;
-    }
+    };
 
     @action
     onPointerUp = (e: PointerEvent) => {
@@ -563,9 +642,9 @@ export class GestureOverlay extends Touchable {
             //push first points to so interactionUtil knows pointer is up
             this._points.push({ X: this._points[0].X, Y: this._points[0].Y });
 
-            const initialPoint = this._points[0.];
-            const xInGlass = initialPoint.X > (this._thumbX ?? Number.MAX_SAFE_INTEGER) && initialPoint.X < (this._thumbX ?? Number.MAX_SAFE_INTEGER) + (this.height);
-            const yInGlass = initialPoint.Y > (this._thumbY ?? Number.MAX_SAFE_INTEGER) - (this.height) && initialPoint.Y < (this._thumbY ?? Number.MAX_SAFE_INTEGER);
+            const initialPoint = this._points[0];
+            const xInGlass = initialPoint.X > (this._thumbX ?? Number.MAX_SAFE_INTEGER) && initialPoint.X < (this._thumbX ?? Number.MAX_SAFE_INTEGER) + this.height;
+            const yInGlass = initialPoint.Y > (this._thumbY ?? Number.MAX_SAFE_INTEGER) - this.height && initialPoint.Y < (this._thumbY ?? Number.MAX_SAFE_INTEGER);
 
             // if a toolglass is selected and the stroke starts within the toolglass boundaries
             if (this.Tool !== ToolglassTools.None && xInGlass && yInGlass) {
@@ -573,8 +652,8 @@ export class GestureOverlay extends Touchable {
                     case ToolglassTools.InkToText:
                         this._strokes.push(new Array(...this._points));
                         this._points = [];
-                        CognitiveServices.Inking.Appliers.InterpretStrokes(this._strokes).then((results) => {
-                            const wordResults = results.filter((r: any) => r.category === "line");
+                        CognitiveServices.Inking.Appliers.InterpretStrokes(this._strokes).then(results => {
+                            const wordResults = results.filter((r: any) => r.category === 'line');
                             const possibilities: string[] = [];
                             for (const wR of wordResults) {
                                 if (wR?.recognizedText) {
@@ -588,8 +667,7 @@ export class GestureOverlay extends Touchable {
 
                             // if we receive any word results from cognitive services, display them
                             runInAction(() => {
-                                this._possibilities = possibilities.map(p =>
-                                    <TouchScrollableMenuItem text={p} onClick={() => GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Text, [{ X: l, Y: t }], p)} />);
+                                this._possibilities = possibilities.map(p => <TouchScrollableMenuItem text={p} onClick={() => GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Text, [{ X: l, Y: t }], p)} />);
                             });
                         });
                         break;
@@ -605,47 +683,66 @@ export class GestureOverlay extends Touchable {
                 this.dispatchGesture(GestureUtils.Gestures.Stroke);
                 this._points = [];
                 if (!CollectionFreeFormViewChrome.Instance?._keepPrimitiveMode) {
-                    this.InkShape = "";
-                    CurrentUserUtils.ActiveTool = InkTool.None;
+                    this.InkShape = '';
+                    Doc.ActiveTool = InkTool.None;
                 }
             }
             // if we're not drawing in a toolglass try to recognize as gesture
-            else { // need to decide when to turn gestures back on
+            else {
+                // need to decide when to turn gestures back on
                 const result = points.length > 2 && GestureUtils.GestureRecognizer.Recognize(new Array(points));
                 let actionPerformed = false;
                 if (Doc.UserDoc().recognizeGestures && result && result.Score > 0.7) {
                     switch (result.Name) {
-                        case GestureUtils.Gestures.Box: actionPerformed = this.dispatchGesture(GestureUtils.Gestures.Box); break;
-                        case GestureUtils.Gestures.StartBracket: actionPerformed = this.dispatchGesture(GestureUtils.Gestures.StartBracket); break;
-                        case GestureUtils.Gestures.EndBracket: actionPerformed = this.dispatchGesture("endbracket"); break;
-                        case GestureUtils.Gestures.Line: actionPerformed = this.handleLineGesture(); break;
-                        case GestureUtils.Gestures.Triangle: actionPerformed = this.makePolygon("triangle", true); break;
-                        case GestureUtils.Gestures.Circle: actionPerformed = this.makePolygon("circle", true); break;
-                        case GestureUtils.Gestures.Rectangle: actionPerformed = this.makePolygon("rectangle", true); break;
-                        case GestureUtils.Gestures.Scribble: console.log("scribble"); break;
+                        case GestureUtils.Gestures.Box:
+                            actionPerformed = this.dispatchGesture(GestureUtils.Gestures.Box);
+                            break;
+                        case GestureUtils.Gestures.StartBracket:
+                            actionPerformed = this.dispatchGesture(GestureUtils.Gestures.StartBracket);
+                            break;
+                        case GestureUtils.Gestures.EndBracket:
+                            actionPerformed = this.dispatchGesture('endbracket');
+                            break;
+                        case GestureUtils.Gestures.Line:
+                            actionPerformed = this.handleLineGesture();
+                            break;
+                        case GestureUtils.Gestures.Triangle:
+                            actionPerformed = this.makePolygon('triangle', true);
+                            break;
+                        case GestureUtils.Gestures.Circle:
+                            actionPerformed = this.makePolygon('circle', true);
+                            break;
+                        case GestureUtils.Gestures.Rectangle:
+                            actionPerformed = this.makePolygon('rectangle', true);
+                            break;
+                        case GestureUtils.Gestures.Scribble:
+                            console.log('scribble');
+                            break;
                     }
                 }
 
                 // if no gesture (or if the gesture was unsuccessful), "dry" the stroke into an ink document
                 if (!actionPerformed) {
-                    const newPoints = this._points.reduce((p, pts) => { p.push([pts.X, pts.Y]); return p; }, [] as number[][]);
+                    const newPoints = this._points.reduce((p, pts) => {
+                        p.push([pts.X, pts.Y]);
+                        return p;
+                    }, [] as number[][]);
                     newPoints.pop();
-                    const controlPoints: { X: number, Y: number }[] = [];
+                    const controlPoints: { X: number; Y: number }[] = [];
 
                     const bezierCurves = fitCurve(newPoints, 10);
                     for (const curve of bezierCurves) {
-
                         controlPoints.push({ X: curve[0][0], Y: curve[0][1] });
                         controlPoints.push({ X: curve[1][0], Y: curve[1][1] });
                         controlPoints.push({ X: curve[2][0], Y: curve[2][1] });
                         controlPoints.push({ X: curve[3][0], Y: curve[3][1] });
-
                     }
-                    const dist = Math.sqrt((controlPoints[0].X - controlPoints.lastElement().X) * (controlPoints[0].X - controlPoints.lastElement().X) +
-                        (controlPoints[0].Y - controlPoints.lastElement().Y) * (controlPoints[0].Y - controlPoints.lastElement().Y));
+                    const dist = Math.sqrt(
+                        (controlPoints[0].X - controlPoints.lastElement().X) * (controlPoints[0].X - controlPoints.lastElement().X) + (controlPoints[0].Y - controlPoints.lastElement().Y) * (controlPoints[0].Y - controlPoints.lastElement().Y)
+                    );
                     if (controlPoints.length > 4 && dist < 10) controlPoints[controlPoints.length - 1] = controlPoints[0];
                     this._points = controlPoints;
-                    this.dispatchGesture(GestureUtils.Gestures.Stroke);                    
+                    this.dispatchGesture(GestureUtils.Gestures.Stroke);
                     // TODO: nda - check inks to group here
                     checkInksToGroup();
                 }
@@ -655,7 +752,7 @@ export class GestureOverlay extends Touchable {
             this._points = [];
         }
         CollectionFreeFormViewChrome.Instance?.primCreated();
-    }
+    };
 
     makePolygon = (shape: string, gesture: boolean) => {
         //take off gesture recognition for now
@@ -673,11 +770,15 @@ export class GestureOverlay extends Touchable {
         var lastx = this._points[this._points.length - 2].X;
         var lasty = this._points[this._points.length - 2].Y;
         var fourth = (lastx - firstx) / 4;
-        if (isNaN(fourth) || fourth === 0) { fourth = 0.01; }
+        if (isNaN(fourth) || fourth === 0) {
+            fourth = 0.01;
+        }
         var m = (lasty - firsty) / (lastx - firstx);
-        if (isNaN(m) || m === 0) { m = 0.01; }
+        if (isNaN(m) || m === 0) {
+            m = 0.01;
+        }
         const b = firsty - m * firstx;
-        if (shape === "noRec") {
+        if (shape === 'noRec') {
             return false;
         }
         if (!gesture) {
@@ -687,7 +788,7 @@ export class GestureOverlay extends Touchable {
             left = this._points[0].X;
             bottom = this._points[this._points.length - 2].Y;
             top = this._points[0].Y;
-            if (shape !== "arrow" && shape !== "line" && shape !== "circle") {
+            if (shape !== 'arrow' && shape !== 'line' && shape !== 'circle') {
                 if (left > right) {
                     const temp = right;
                     right = left;
@@ -704,7 +805,7 @@ export class GestureOverlay extends Touchable {
         switch (shape) {
             //must push an extra point in the end so InteractionUtils knows pointer is up.
             //must be (points[0].X,points[0]-1)
-            case "rectangle":
+            case 'rectangle':
                 this._points.push({ X: left, Y: top });
                 this._points.push({ X: left, Y: top });
                 this._points.push({ X: right, Y: top });
@@ -727,7 +828,7 @@ export class GestureOverlay extends Touchable {
 
                 break;
 
-            case "triangle":
+            case 'triangle':
                 this._points.push({ X: left, Y: bottom });
                 this._points.push({ X: left, Y: bottom });
 
@@ -744,9 +845,8 @@ export class GestureOverlay extends Touchable {
                 this._points.push({ X: left, Y: bottom });
                 this._points.push({ X: left, Y: bottom });
 
-
                 break;
-            case "circle":
+            case 'circle':
                 // Approximation of a circle using 4 Bézier curves in which the constant "c" reduces the maximum radial drift to 0.019608%,
                 // making the curves indistinguishable from a circle.
                 // Source: https://spencermortensen.com/articles/bezier-circle/
@@ -755,30 +855,30 @@ export class GestureOverlay extends Touchable {
                 const centerY = (Math.max(top, bottom) + Math.min(top, bottom)) / 2;
                 const radius = Math.max(centerX - Math.min(left, right), centerY - Math.min(top, bottom));
 
-                // Dividing the circle into four equal sections, and fitting each section to a cubic Bézier curve.   
+                // Dividing the circle into four equal sections, and fitting each section to a cubic Bézier curve.
                 this._points.push({ X: centerX, Y: centerY + radius });
-                this._points.push({ X: centerX + (c * radius), Y: centerY + radius });
-                this._points.push({ X: centerX + radius, Y: centerY + (c * radius) });
+                this._points.push({ X: centerX + c * radius, Y: centerY + radius });
+                this._points.push({ X: centerX + radius, Y: centerY + c * radius });
                 this._points.push({ X: centerX + radius, Y: centerY });
 
                 this._points.push({ X: centerX + radius, Y: centerY });
-                this._points.push({ X: centerX + radius, Y: centerY - (c * radius) });
-                this._points.push({ X: centerX + (c * radius), Y: centerY - radius });
+                this._points.push({ X: centerX + radius, Y: centerY - c * radius });
+                this._points.push({ X: centerX + c * radius, Y: centerY - radius });
                 this._points.push({ X: centerX, Y: centerY - radius });
 
                 this._points.push({ X: centerX, Y: centerY - radius });
-                this._points.push({ X: centerX - (c * radius), Y: centerY - radius });
-                this._points.push({ X: centerX - radius, Y: centerY - (c * radius) });
+                this._points.push({ X: centerX - c * radius, Y: centerY - radius });
+                this._points.push({ X: centerX - radius, Y: centerY - c * radius });
                 this._points.push({ X: centerX - radius, Y: centerY });
 
                 this._points.push({ X: centerX - radius, Y: centerY });
-                this._points.push({ X: centerX - radius, Y: centerY + (c * radius) });
-                this._points.push({ X: centerX - (c * radius), Y: centerY + radius });
+                this._points.push({ X: centerX - radius, Y: centerY + c * radius });
+                this._points.push({ X: centerX - c * radius, Y: centerY + radius });
                 this._points.push({ X: centerX, Y: centerY + radius });
 
                 break;
 
-            case "line":
+            case 'line':
                 if (Math.abs(firstx - lastx) < 10 && Math.abs(firsty - lasty) > 10) {
                     lastx = firstx;
                 }
@@ -791,12 +891,12 @@ export class GestureOverlay extends Touchable {
                 this._points.push({ X: lastx, Y: lasty });
                 this._points.push({ X: lastx, Y: lasty });
                 break;
-            case "arrow":
+            case 'arrow':
                 const x1 = left;
                 const y1 = top;
                 const x2 = right;
                 const y2 = bottom;
-                const L1 = Math.sqrt(Math.pow(Math.abs(x1 - x2), 2) + (Math.pow(Math.abs(y1 - y2), 2)));
+                const L1 = Math.sqrt(Math.pow(Math.abs(x1 - x2), 2) + Math.pow(Math.abs(y1 - y2), 2));
                 const L2 = L1 / 5;
                 const angle = 0.785398;
                 const x3 = x2 + (L2 / L1) * ((x1 - x2) * Math.cos(angle) + (y1 - y2) * Math.sin(angle));
@@ -811,24 +911,24 @@ export class GestureOverlay extends Touchable {
             // this._points.push({ X: x1, Y: y1 - 1 });
         }
         return true;
-    }
+    };
 
-    dispatchGesture = (gesture: "box" | "line" | "startbracket" | "endbracket" | "stroke" | "scribble" | "text", stroke?: InkData, data?: any) => {
+    dispatchGesture = (gesture: 'box' | 'line' | 'startbracket' | 'endbracket' | 'stroke' | 'scribble' | 'text', stroke?: InkData, data?: any) => {
         const target = document.elementFromPoint((stroke ?? this._points)[0].X, (stroke ?? this._points)[0].Y);
-        return target?.dispatchEvent(
-            new CustomEvent<GestureUtils.GestureEvent>("dashOnGesture",
-                {
+        return (
+            target?.dispatchEvent(
+                new CustomEvent<GestureUtils.GestureEvent>('dashOnGesture', {
                     bubbles: true,
                     detail: {
                         points: stroke ?? this._points,
                         gesture: gesture as any,
                         bounds: this.getBounds(stroke ?? this._points),
                         text: data,
-                    }
-                }
-            )
-        ) || false;
-    }
+                    },
+                })
+            ) || false
+        );
+    };
 
     getBounds = (stroke: InkData, pad?: boolean) => {
         const padding = pad ? [-20000, 20000] : [];
@@ -839,7 +939,7 @@ export class GestureOverlay extends Touchable {
         const bottom = Math.max(...ys);
         const top = Math.min(...ys);
         return { right, left, bottom, top, width: right - left, height: bottom - top };
-    }
+    };
 
     @computed get svgBounds() {
         return this.getBounds(this._points);
@@ -847,7 +947,7 @@ export class GestureOverlay extends Touchable {
 
     @computed get elements() {
         const selView = SelectionManager.Views().lastElement();
-        const width = Number(ActiveInkWidth()) * NumCast(selView?.rootDoc._viewScale, 1) / (selView?.props.ScreenToLocalTransform().Scale || 1);
+        const width = (Number(ActiveInkWidth()) * NumCast(selView?.rootDoc._viewScale, 1)) / (selView?.props.ScreenToLocalTransform().Scale || 1);
         const rect = this._overlayRef.current?.getBoundingClientRect();
         const B = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 }; //this.getBounds(this._points, true);
         B.left = B.left - width / 2;
@@ -857,30 +957,74 @@ export class GestureOverlay extends Touchable {
         B.width += width;
         B.height += width;
         const fillColor = ActiveFillColor();
-        const strokeColor = fillColor && fillColor !== "transparent" ? fillColor : ActiveInkColor();
+        const strokeColor = fillColor && fillColor !== 'transparent' ? fillColor : ActiveInkColor();
         return [
             this.props.children,
             this._palette,
-            [this._strokes.map((l, i) => {
-                const b = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 };//this.getBounds(l, true);
-                return <svg key={i} width={b.width} height={b.height} style={{ transform: `translate(${b.left}px, ${b.top}px)`, pointerEvents: "none", position: "absolute", zIndex: 30000, overflow: "visible" }}>
-                    {InteractionUtils.CreatePolyline(l, b.left, b.top, strokeColor, width, width, "miter", "round",
-                        ActiveInkBezierApprox(), "none" /*ActiveFillColor()*/, ActiveArrowStart(), ActiveArrowEnd(), ActiveArrowScale(),
-                        ActiveDash(), 1, 1, this.InkShape, "none", 1.0, false)}
-                </svg>;
-            }),
-            this._points.length <= 1 ? (null) : <svg key="svg" width={B.width} height={B.height}
-                style={{ transform: `translate(${B.left}px, ${B.top}px)`, pointerEvents: "none", position: "absolute", zIndex: 30000, overflow: "visible" }}>
-                {InteractionUtils.CreatePolyline(this._points.map(p => ({ X: p.X, Y: p.Y - (rect?.y || 0) })), B.left, B.top, ActiveInkColor(), width, width, "miter", "round", "",
-                    "none" /*ActiveFillColor()*/, ActiveArrowStart(), ActiveArrowEnd(), ActiveArrowScale(), ActiveDash(), 1, 1, this.InkShape, "none", 1.0, false)}
-            </svg>]
+            [
+                this._strokes.map((l, i) => {
+                    const b = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 }; //this.getBounds(l, true);
+                    return (
+                        <svg key={i} width={b.width} height={b.height} style={{ transform: `translate(${b.left}px, ${b.top}px)`, pointerEvents: 'none', position: 'absolute', zIndex: 30000, overflow: 'visible' }}>
+                            {InteractionUtils.CreatePolyline(
+                                l,
+                                b.left,
+                                b.top,
+                                strokeColor,
+                                width,
+                                width,
+                                'miter',
+                                'round',
+                                ActiveInkBezierApprox(),
+                                'none' /*ActiveFillColor()*/,
+                                ActiveArrowStart(),
+                                ActiveArrowEnd(),
+                                ActiveArrowScale(),
+                                ActiveDash(),
+                                1,
+                                1,
+                                this.InkShape,
+                                'none',
+                                1.0,
+                                false
+                            )}
+                        </svg>
+                    );
+                }),
+                this._points.length <= 1 ? null : (
+                    <svg key="svg" width={B.width} height={B.height} style={{ transform: `translate(${B.left}px, ${B.top}px)`, pointerEvents: 'none', position: 'absolute', zIndex: 30000, overflow: 'visible' }}>
+                        {InteractionUtils.CreatePolyline(
+                            this._points.map(p => ({ X: p.X, Y: p.Y - (rect?.y || 0) })),
+                            B.left,
+                            B.top,
+                            ActiveInkColor(),
+                            width,
+                            width,
+                            'miter',
+                            'round',
+                            '',
+                            'none' /*ActiveFillColor()*/,
+                            ActiveArrowStart(),
+                            ActiveArrowEnd(),
+                            ActiveArrowScale(),
+                            ActiveDash(),
+                            1,
+                            1,
+                            this.InkShape,
+                            'none',
+                            1.0,
+                            false
+                        )}
+                    </svg>
+                ),
+            ],
         ];
     }
     screenToLocalTransform = () => new Transform(-(this._thumbX ?? 0), -(this._thumbY ?? 0) + this.height, 1);
     return300 = () => 300;
     @action
     public openFloatingDoc = (doc: Doc) => {
-        this._clipboardDoc =
+        this._clipboardDoc = (
             <DocumentView
                 Document={doc}
                 DataDoc={undefined}
@@ -905,61 +1049,65 @@ export class GestureOverlay extends Touchable {
                 searchFilterDocs={returnEmptyDoclist}
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={undefined}
-            />;
-    }
+            />
+        );
+    };
 
     @action
     public closeFloatingDoc = () => {
         this._clipboardDoc = undefined;
-    }
+    };
 
     @action
     enableMobileInkOverlay = (content: MobileInkOverlayContent) => {
         this.showMobileInkOverlay = content.enableOverlay;
-    }
+    };
 
     render() {
         return (
-            <div className="gestureOverlay-cont" ref={this._overlayRef}
-                onPointerDown={this.onPointerDown} onTouchStart={this.onReactTouchStart}>
+            <div className="gestureOverlay-cont" ref={this._overlayRef} onPointerDown={this.onPointerDown} onTouchStart={this.onReactTouchStart}>
                 {this.showMobileInkOverlay ? <MobileInkOverlay /> : <></>}
                 {this.elements}
 
-                <div className="clipboardDoc-cont" style={{
-                    height: this.height,
-                    width: this.height,
-                    pointerEvents: this._clipboardDoc ? "unset" : "none",
-                    touchAction: this._clipboardDoc ? "unset" : "none",
-                    transform: `translate(${this._thumbX}px, ${(this._thumbY || 0) - this.height} px)`,
-                }}>
+                <div
+                    className="clipboardDoc-cont"
+                    style={{
+                        height: this.height,
+                        width: this.height,
+                        pointerEvents: this._clipboardDoc ? 'unset' : 'none',
+                        touchAction: this._clipboardDoc ? 'unset' : 'none',
+                        transform: `translate(${this._thumbX}px, ${(this._thumbY || 0) - this.height} px)`,
+                    }}>
                     {this._clipboardDoc}
                 </div>
-                <div className="filter-cont" style={{
-                    transform: `translate(${this._thumbX}px, ${(this._thumbY || 0) - this.height}px)`,
-                    height: this.height,
-                    width: this.height,
-                    pointerEvents: "none",
-                    touchAction: "none",
-                    display: this.showBounds ? "unset" : "none",
-                }}>
-                </div>
+                <div
+                    className="filter-cont"
+                    style={{
+                        transform: `translate(${this._thumbX}px, ${(this._thumbY || 0) - this.height}px)`,
+                        height: this.height,
+                        width: this.height,
+                        pointerEvents: 'none',
+                        touchAction: 'none',
+                        display: this.showBounds ? 'unset' : 'none',
+                    }}></div>
                 <TouchScrollableMenu options={this._possibilities} bounds={this.svgBounds} selectedIndex={this._selectedIndex} x={this._menuX} y={this._menuY} />
-            </div>);
+            </div>
+        );
     }
 }
 
 // export class
 
 export enum ToolglassTools {
-    InkToText = "inktotext",
-    IgnoreGesture = "ignoregesture",
-    RadialMenu = "radialmenu",
-    None = "none",
+    InkToText = 'inktotext',
+    IgnoreGesture = 'ignoregesture',
+    RadialMenu = 'radialmenu',
+    None = 'none',
 }
 
-ScriptingGlobals.add("GestureOverlay", GestureOverlay);
+ScriptingGlobals.add('GestureOverlay', GestureOverlay);
 ScriptingGlobals.add(function setToolglass(tool: any) {
-    runInAction(() => GestureOverlay.Instance.Tool = tool);
+    runInAction(() => (GestureOverlay.Instance.Tool = tool));
 });
 ScriptingGlobals.add(function setPen(width: any, color: any, fill: any, arrowStart: any, arrowEnd: any, dash: any) {
     runInAction(() => {
@@ -975,10 +1123,14 @@ ScriptingGlobals.add(function setPen(width: any, color: any, fill: any, arrowSta
 });
 ScriptingGlobals.add(function resetPen() {
     runInAction(() => {
-        SetActiveInkColor(GestureOverlay.Instance.SavedColor ?? "rgb(0, 0, 0)");
-        SetActiveInkWidth(GestureOverlay.Instance.SavedWidth?.toString() ?? "2");
+        SetActiveInkColor(GestureOverlay.Instance.SavedColor ?? 'rgb(0, 0, 0)');
+        SetActiveInkWidth(GestureOverlay.Instance.SavedWidth?.toString() ?? '2');
     });
-}, "resets the pen tool");
-ScriptingGlobals.add(function createText(text: any, x: any, y: any) {
-    GestureOverlay.Instance.dispatchGesture("text", [{ X: x, Y: y }], text);
-}, "creates a text document with inputted text and coordinates", "(text: any, x: any, y: any)");
+}, 'resets the pen tool');
+ScriptingGlobals.add(
+    function createText(text: any, x: any, y: any) {
+        GestureOverlay.Instance.dispatchGesture('text', [{ X: x, Y: y }], text);
+    },
+    'creates a text document with inputted text and coordinates',
+    '(text: any, x: any, y: any)'
+);
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index f5122df3f..73e0c9933 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -1,39 +1,38 @@
-import { random } from "lodash";
-import { action, observable, runInAction } from "mobx";
-import { DateField } from "../../fields/DateField";
-import { Doc, DocListCast } from "../../fields/Doc";
-import { Id } from "../../fields/FieldSymbols";
-import { InkTool } from "../../fields/InkField";
-import { List } from "../../fields/List";
-import { ScriptField } from "../../fields/ScriptField";
-import { Cast, PromiseValue } from "../../fields/Types";
-import { GoogleAuthenticationManager } from "../apis/GoogleAuthenticationManager";
-import { DocServer } from "../DocServer";
-import { DocumentType } from "../documents/DocumentTypes";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { DragManager } from "../util/DragManager";
-import { GroupManager } from "../util/GroupManager";
-import { SelectionManager } from "../util/SelectionManager";
-import { SettingsManager } from "../util/SettingsManager";
-import { SharingManager } from "../util/SharingManager";
-import { SnappingManager } from "../util/SnappingManager";
-import { undoBatch, UndoManager } from "../util/UndoManager";
-import { CollectionDockingView } from "./collections/CollectionDockingView";
-import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu";
-import { CollectionStackedTimeline } from "./collections/CollectionStackedTimeline";
-import { ContextMenu } from "./ContextMenu";
-import { DocumentDecorations } from "./DocumentDecorations";
-import { InkStrokeProperties } from "./InkStrokeProperties";
-import { LightboxView } from "./LightboxView";
-import { MainView } from "./MainView";
-import { DocumentLinksButton } from "./nodes/DocumentLinksButton";
-import { AnchorMenu } from "./pdf/AnchorMenu";
+import { random } from 'lodash';
+import { action, observable, runInAction } from 'mobx';
+import { DateField } from '../../fields/DateField';
+import { Doc, DocListCast } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { InkTool } from '../../fields/InkField';
+import { List } from '../../fields/List';
+import { ScriptField } from '../../fields/ScriptField';
+import { Cast, PromiseValue } from '../../fields/Types';
+import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
+import { DocServer } from '../DocServer';
+import { DocumentType } from '../documents/DocumentTypes';
+import { DragManager } from '../util/DragManager';
+import { GroupManager } from '../util/GroupManager';
+import { SelectionManager } from '../util/SelectionManager';
+import { SettingsManager } from '../util/SettingsManager';
+import { SharingManager } from '../util/SharingManager';
+import { SnappingManager } from '../util/SnappingManager';
+import { undoBatch, UndoManager } from '../util/UndoManager';
+import { CollectionDockingView } from './collections/CollectionDockingView';
+import { CollectionFreeFormViewChrome } from './collections/CollectionMenu';
+import { CollectionStackedTimeline } from './collections/CollectionStackedTimeline';
+import { ContextMenu } from './ContextMenu';
+import { DocumentDecorations } from './DocumentDecorations';
+import { InkStrokeProperties } from './InkStrokeProperties';
+import { LightboxView } from './LightboxView';
+import { MainView } from './MainView';
+import { DocumentLinksButton } from './nodes/DocumentLinksButton';
+import { AnchorMenu } from './pdf/AnchorMenu';
 
-const modifiers = ["control", "meta", "shift", "alt"];
+const modifiers = ['control', 'meta', 'shift', 'alt'];
 type KeyHandler = (keycode: string, e: KeyboardEvent) => KeyControlInfo;
 type KeyControlInfo = {
-    preventDefault: boolean,
-    stopPropagation: boolean
+    preventDefault: boolean;
+    stopPropagation: boolean;
 };
 
 export class KeyManager {
@@ -41,22 +40,22 @@ export class KeyManager {
     private router = new Map<string, KeyHandler>();
 
     constructor() {
-        const 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);
-        this.router.set(isMac ? "0001" : "0100", this.ctrl);
-        this.router.set(isMac ? "0100" : "0010", this.alt);
-        this.router.set(isMac ? "1001" : "1100", this.ctrl_shift);
-        this.router.set("1000", this.shift);
+        this.router.set('0000', this.unmodified);
+        this.router.set(isMac ? '0001' : '0100', this.ctrl);
+        this.router.set(isMac ? '0100' : '0010', this.alt);
+        this.router.set(isMac ? '1001' : '1100', this.ctrl_shift);
+        this.router.set('1000', this.shift);
     }
 
     public unhandle = action((e: KeyboardEvent) => {
-        if (e.key?.toLowerCase() === "shift") runInAction(() => DocumentDecorations.Instance.AddToSelection = false);
+        if (e.key?.toLowerCase() === 'shift') runInAction(() => (DocumentDecorations.Instance.AddToSelection = false));
     });
 
     public handle = action((e: KeyboardEvent) => {
-        if (e.key?.toLowerCase() === "shift") DocumentDecorations.Instance.AddToSelection = true;
+        if (e.key?.toLowerCase() === 'shift') DocumentDecorations.Instance.AddToSelection = true;
         //if (!Doc.noviceMode && e.key.toLocaleLowerCase() === "shift") DocServer.UPDATE_SERVER_CACHE(true);
         const keyname = e.key && e.key.toLowerCase();
         this.handleGreedy(keyname);
@@ -65,7 +64,7 @@ export class KeyManager {
             return;
         }
 
-        const bit = (value: boolean) => value ? "1" : "0";
+        const bit = (value: boolean) => (value ? '1' : '0');
         const modifierIndex = bit(e.shiftKey) + bit(e.ctrlKey) + bit(e.altKey) + bit(e.metaKey);
 
         const handleConstrained = this.router.get(modifierIndex);
@@ -86,33 +85,33 @@ export class KeyManager {
 
     private unmodified = action((keyname: string, e: KeyboardEvent) => {
         switch (keyname) {
-            case "u":
-                if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") {
+            case 'u':
+                if (document.activeElement?.tagName === 'INPUT' || document.activeElement?.tagName === 'TEXTAREA') {
                     return { stopPropagation: false, preventDefault: false };
                 }
 
                 const ungroupings = SelectionManager.Views().slice();
-                UndoManager.RunInBatch(() => ungroupings.map(dv => dv.layoutDoc.group = undefined), "ungroup");
+                UndoManager.RunInBatch(() => ungroupings.map(dv => (dv.layoutDoc.group = undefined)), 'ungroup');
                 SelectionManager.DeselectAll();
                 break;
-            case "g":
-                if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") {
+            case 'g':
+                if (document.activeElement?.tagName === 'INPUT' || document.activeElement?.tagName === 'TEXTAREA') {
                     return { stopPropagation: false, preventDefault: false };
                 }
 
                 const groupings = SelectionManager.Views().slice();
                 const randomGroup = random(0, 1000);
-                UndoManager.RunInBatch(() => groupings.map(dv => dv.layoutDoc.group = randomGroup), "group");
+                UndoManager.RunInBatch(() => groupings.map(dv => (dv.layoutDoc.group = randomGroup)), 'group');
                 SelectionManager.DeselectAll();
                 break;
-            case " ":
+            case ' ':
                 // MarqueeView.DragMarquee = !MarqueeView.DragMarquee; // bcz: this needs a better disclosure UI
                 break;
-            case "escape":
+            case 'escape':
                 DocumentLinksButton.StartLink = undefined;
                 DocumentLinksButton.StartLinkView = undefined;
                 InkStrokeProperties.Instance._controlButton = false;
-                CurrentUserUtils.ActiveTool = InkTool.None;
+                Doc.ActiveTool = InkTool.None;
                 DragManager.CompleteWindowDrag?.(true);
                 var doDeselect = true;
                 if (SnappingManager.GetIsDragging()) {
@@ -138,20 +137,19 @@ export class KeyManager {
                 window.getSelection()?.empty();
                 document.body.focus();
                 break;
-            case "enter": {
+            case 'enter': {
                 DocumentDecorations.Instance.onCloseClick(false);
                 break;
             }
-            case "delete":
-            case "backspace":
-                if (document.activeElement?.tagName !== "INPUT" && document.activeElement?.tagName !== "TEXTAREA") {
+            case 'delete':
+            case 'backspace':
+                if (document.activeElement?.tagName !== 'INPUT' && document.activeElement?.tagName !== 'TEXTAREA') {
                     UndoManager.RunInBatch(() => {
                         if (LightboxView.LightboxDoc) {
                             LightboxView.SetLightboxDoc(undefined);
                             SelectionManager.DeselectAll();
-                        }
-                        else DocumentDecorations.Instance.onCloseClick(true);
-                    }, "backspace");
+                        } else DocumentDecorations.Instance.onCloseClick(true);
+                    }, 'backspace');
                     // const selected = SelectionManager.Views().filter(dv => !dv.topMost);
                     // UndoManager.RunInBatch(() => {
                     //     SelectionManager.DeselectAll();
@@ -160,15 +158,23 @@ export class KeyManager {
                     return { stopPropagation: true, preventDefault: true };
                 }
                 break;
-            case "arrowleft": UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge(-1, 0)), "nudge left"); break;
-            case "arrowright": UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(1, 0)), "nudge right"); break;
-            case "arrowup": UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(0, -1)), "nudge up"); break;
-            case "arrowdown": UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(0, 1)), "nudge down"); break;
+            case 'arrowleft':
+                UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge(-1, 0)), 'nudge left');
+                break;
+            case 'arrowright':
+                UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(1, 0)), 'nudge right');
+                break;
+            case 'arrowup':
+                UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(0, -1)), 'nudge up');
+                break;
+            case 'arrowdown':
+                UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(0, 1)), 'nudge down');
+                break;
         }
 
         return {
             stopPropagation: false,
-            preventDefault: false
+            preventDefault: false,
         };
     });
 
@@ -177,15 +183,23 @@ export class KeyManager {
         const preventDefault = false;
 
         switch (keyname) {
-            case "arrowleft": UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(-10, 0)), "nudge left"); break;
-            case "arrowright": UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(10, 0)), "nudge right"); break;
-            case "arrowup": UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(0, -10)), "nudge up"); break;
-            case "arrowdown": UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(0, 10)), "nudge down"); break;
+            case 'arrowleft':
+                UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(-10, 0)), 'nudge left');
+                break;
+            case 'arrowright':
+                UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(10, 0)), 'nudge right');
+                break;
+            case 'arrowup':
+                UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(0, -10)), 'nudge up');
+                break;
+            case 'arrowdown':
+                UndoManager.RunInBatch(() => SelectionManager.Views().map(dv => dv.props.CollectionFreeFormDocumentView?.().nudge?.(0, 10)), 'nudge down');
+                break;
         }
 
         return {
             stopPropagation: stopPropagation,
-            preventDefault: preventDefault
+            preventDefault: preventDefault,
         };
     });
 
@@ -194,15 +208,15 @@ export class KeyManager {
         const preventDefault = true;
 
         switch (keyname) {
-            case "ƒ":
-            case "f":
+            case 'ƒ':
+            case 'f':
                 const dv = SelectionManager.Views()?.[0];
-                UndoManager.RunInBatch(() => dv.props.CollectionFreeFormDocumentView?.().float(), "float");
+                UndoManager.RunInBatch(() => dv.props.CollectionFreeFormDocumentView?.().float(), 'float');
         }
 
         return {
             stopPropagation: stopPropagation,
-            preventDefault: preventDefault
+            preventDefault: preventDefault,
         };
     });
 
@@ -211,83 +225,97 @@ export class KeyManager {
         let preventDefault = true;
 
         switch (keyname) {
-            case "arrowright":
-                if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") {
+            case 'arrowright':
+                if (document.activeElement?.tagName === 'INPUT' || document.activeElement?.tagName === 'TEXTAREA') {
                     return { stopPropagation: false, preventDefault: false };
                 }
-                MainView.Instance.mainFreeform && CollectionDockingView.AddSplit(MainView.Instance.mainFreeform, "right");
+                MainView.Instance.mainFreeform && CollectionDockingView.AddSplit(MainView.Instance.mainFreeform, 'right');
                 break;
-            case "arrowleft":
-                if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") {
+            case 'arrowleft':
+                if (document.activeElement?.tagName === 'INPUT' || document.activeElement?.tagName === 'TEXTAREA') {
                     return { stopPropagation: false, preventDefault: false };
                 }
                 MainView.Instance.mainFreeform && CollectionDockingView.CloseSplit(MainView.Instance.mainFreeform);
                 break;
-            case "backspace":
-                if (document.activeElement?.tagName === "INPUT" || document.activeElement?.tagName === "TEXTAREA") {
+            case 'backspace':
+                if (document.activeElement?.tagName === 'INPUT' || document.activeElement?.tagName === 'TEXTAREA') {
                     return { stopPropagation: false, preventDefault: false };
                 }
                 break;
-            case "t":
-                PromiseValue(Cast(Doc.UserDoc()["tabs-button-tools"], Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
+            case 't':
+                PromiseValue(Cast(Doc.UserDoc()['tabs-button-tools'], Doc)).then(pv => pv && (pv.onClick as ScriptField).script.run({ this: pv }));
                 break;
-            case "f":
+            case 'f':
                 if (SelectionManager.Views().length === 1 && SelectionManager.Views()[0].ComponentView?.search) {
-                    SelectionManager.Views()[0].ComponentView?.search?.("", false, false);
+                    SelectionManager.Views()[0].ComponentView?.search?.('', false, false);
                 } else {
-                    const searchBtn = CurrentUserUtils.MySearcher;
+                    const searchBtn = Doc.MySearcher;
                     if (searchBtn) {
                         MainView.Instance.selectMenu(searchBtn);
                     }
                 }
                 break;
-            case "e": CurrentUserUtils.ActiveTool = InkTool.Eraser;
+            case 'e':
+                Doc.ActiveTool = InkTool.Eraser;
                 break;
-            case "p": CurrentUserUtils.ActiveTool = InkTool.Pen;
+            case 'p':
+                Doc.ActiveTool = InkTool.Pen;
                 break;
-            case "o":
+            case 'o':
                 const target = SelectionManager.Docs().lastElement();
                 target && CollectionDockingView.OpenFullScreen(target);
                 break;
-            case "r":
+            case 'r':
                 preventDefault = false;
                 break;
-            case "y":
+            case 'y':
                 SelectionManager.DeselectAll();
                 UndoManager.Redo();
                 stopPropagation = false;
                 break;
-            case "z":
+            case 'z':
                 SelectionManager.DeselectAll();
                 UndoManager.Undo();
                 stopPropagation = false;
                 break;
-            case "a":
+            case 'a':
                 if (e.target !== document.body) {
                     stopPropagation = false;
                     preventDefault = false;
                 }
                 break;
-            case "v":
+            case 'v':
                 stopPropagation = false;
                 preventDefault = false;
                 break;
-            case "x":
+            case 'x':
                 if (SelectionManager.Views().length) {
                     const bds = DocumentDecorations.Instance.Bounds;
-                    const pt = SelectionManager.Views()[0].props.ScreenToLocalTransform().transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2);
-                    const text = `__DashDocId(${pt?.[0] || 0},${pt?.[1] || 0}):` + SelectionManager.Views().map(dv => dv.Document[Id]).join(":");
+                    const pt = SelectionManager.Views()[0]
+                        .props.ScreenToLocalTransform()
+                        .transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2);
+                    const text =
+                        `__DashDocId(${pt?.[0] || 0},${pt?.[1] || 0}):` +
+                        SelectionManager.Views()
+                            .map(dv => dv.Document[Id])
+                            .join(':');
                     SelectionManager.Views().length && navigator.clipboard.writeText(text);
                     DocumentDecorations.Instance.onCloseClick(true);
                     stopPropagation = false;
                     preventDefault = false;
                 }
                 break;
-            case "c":
+            case 'c':
                 if (!AnchorMenu.Instance.Active && DocumentDecorations.Instance.Bounds.r - DocumentDecorations.Instance.Bounds.x > 2) {
                     const bds = DocumentDecorations.Instance.Bounds;
-                    const pt = SelectionManager.Views()[0].props.ScreenToLocalTransform().transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2);
-                    const text = `__DashCloneId(${pt?.[0] || 0},${pt?.[1] || 0}):` + SelectionManager.Views().map(dv => dv.Document[Id]).join(":");
+                    const pt = SelectionManager.Views()[0]
+                        .props.ScreenToLocalTransform()
+                        .transformPoint(bds.x + (bds.r - bds.x) / 2, bds.y + (bds.b - bds.y) / 2);
+                    const text =
+                        `__DashCloneId(${pt?.[0] || 0},${pt?.[1] || 0}):` +
+                        SelectionManager.Views()
+                            .map(dv => dv.Document[Id])
+                            .join(':');
                     SelectionManager.Views().length && navigator.clipboard.writeText(text);
                     stopPropagation = false;
                 }
@@ -297,58 +325,63 @@ export class KeyManager {
 
         return {
             stopPropagation: stopPropagation,
-            preventDefault: preventDefault
+            preventDefault: preventDefault,
         };
     });
 
     public paste(e: ClipboardEvent) {
-        const plain = e.clipboardData?.getData("text/plain");
-        const clone = plain?.startsWith("__DashCloneId(");
-        if (plain && (plain.startsWith("__DashDocId(") || clone)) {
+        const plain = e.clipboardData?.getData('text/plain');
+        const clone = plain?.startsWith('__DashCloneId(');
+        if (plain && (plain.startsWith('__DashDocId(') || clone)) {
             const first = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
             if (first?.props.Document.type === DocumentType.COL) {
-                const docids = plain.split(":");
+                const docids = plain.split(':');
                 let count = 1;
                 const list: Doc[] = [];
                 const targetDataDoc = Doc.GetProto(first.props.Document);
                 const fieldKey = first.LayoutFieldKey;
                 const docList = DocListCast(targetDataDoc[fieldKey]);
-                docids.map((did, i) => i && DocServer.GetRefField(did).then(async doc => {
-                    count++;
-                    if (doc instanceof Doc) {
-                        list.push(doc);
-                    }
-                    if (count === docids.length) {
-                        const added = await Promise.all(list.filter(d => !docList.includes(d)).map(async d => clone ? (await Doc.MakeClone(d)).clone : d));
-                        if (added.length) {
-                            added.map(doc => doc.context = targetDataDoc);
-                            undoBatch(() => {
-                                targetDataDoc[fieldKey] = new List<Doc>([...docList, ...added]);
-                                targetDataDoc[fieldKey + "-lastModified"] = new DateField(new Date(Date.now()));
-                            })();
-                        }
-                    }
-                }));
+                docids.map(
+                    (did, i) =>
+                        i &&
+                        DocServer.GetRefField(did).then(async doc => {
+                            count++;
+                            if (doc instanceof Doc) {
+                                list.push(doc);
+                            }
+                            if (count === docids.length) {
+                                const added = await Promise.all(list.filter(d => !docList.includes(d)).map(async d => (clone ? (await Doc.MakeClone(d)).clone : d)));
+                                if (added.length) {
+                                    added.map(doc => (doc.context = targetDataDoc));
+                                    undoBatch(() => {
+                                        targetDataDoc[fieldKey] = new List<Doc>([...docList, ...added]);
+                                        targetDataDoc[fieldKey + '-lastModified'] = new DateField(new Date(Date.now()));
+                                    })();
+                                }
+                            }
+                        })
+                );
             }
         }
     }
 
-    getClipboard() { return navigator.clipboard.readText(); }
+    getClipboard() {
+        return navigator.clipboard.readText();
+    }
 
     private ctrl_shift = action((keyname: string) => {
         const stopPropagation = true;
         const preventDefault = true;
 
         switch (keyname) {
-            case "z":
+            case 'z':
                 UndoManager.Redo();
                 break;
         }
 
         return {
             stopPropagation: stopPropagation,
-            preventDefault: preventDefault
+            preventDefault: preventDefault,
         };
     });
-
 }
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 471ad09e9..821e2f739 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -1,23 +1,23 @@
-import { Bezier } from "bezier-js";
-import { Normalize, Distance } from "../util/bezierFit";
-import { action, observable, reaction } from "mobx";
-import { Doc, NumListCast, Opt } from "../../fields/Doc";
-import { InkData, InkField, InkTool, PointData } from "../../fields/InkField";
-import { List } from "../../fields/List";
-import { listSpec } from "../../fields/Schema";
-import { Cast, NumCast } from "../../fields/Types";
-import { Point } from "../../pen-gestures/ndollar";
-import { DocumentType } from "../documents/DocumentTypes";
-import { FitOneCurve } from "../util/bezierFit";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { DocumentManager } from "../util/DocumentManager";
-import { undoBatch } from "../util/UndoManager";
-import { InkingStroke } from "./InkingStroke";
-import { DocumentView } from "./nodes/DocumentView";
+import { Bezier } from 'bezier-js';
+import { action, observable, reaction } from 'mobx';
+import { Doc, NumListCast, Opt } from '../../fields/Doc';
+import { InkData, InkField, InkTool, PointData } from '../../fields/InkField';
+import { List } from '../../fields/List';
+import { listSpec } from '../../fields/Schema';
+import { Cast, NumCast } from '../../fields/Types';
+import { Point } from '../../pen-gestures/ndollar';
+import { DocumentType } from '../documents/DocumentTypes';
+import { FitOneCurve } from '../util/bezierFit';
+import { DocumentManager } from '../util/DocumentManager';
+import { undoBatch } from '../util/UndoManager';
+import { InkingStroke } from './InkingStroke';
+import { DocumentView } from './nodes/DocumentView';
 
 export class InkStrokeProperties {
     static _Instance: InkStrokeProperties | undefined;
-    public static get Instance() { return this._Instance || new InkStrokeProperties(); }
+    public static get Instance() {
+        return this._Instance || new InkStrokeProperties();
+    }
 
     @observable _lock = false;
     @observable _controlButton = false;
@@ -25,8 +25,14 @@ export class InkStrokeProperties {
 
     constructor() {
         InkStrokeProperties._Instance = this;
-        reaction(() => this._controlButton, button => button && (CurrentUserUtils.ActiveTool = InkTool.None));
-        reaction(() => CurrentUserUtils.ActiveTool, tool => (tool !== InkTool.None) && (this._controlButton = false));
+        reaction(
+            () => this._controlButton,
+            button => button && (Doc.ActiveTool = InkTool.None)
+        );
+        reaction(
+            () => Doc.ActiveTool,
+            tool => tool !== InkTool.None && (this._controlButton = false)
+        );
     }
 
     /**
@@ -34,35 +40,41 @@ export class InkStrokeProperties {
      * @param func The inputted function.
      * @param requireCurrPoint Indicates whether the current selected point is needed.
      */
-    applyFunction = (strokes: Opt<DocumentView | DocumentView[]>, func: (view: DocumentView, ink: InkData, ptsXscale: number, ptsYscale: number, inkStrokeWidth: number) => { X: number, Y: number }[] | undefined, requireCurrPoint: boolean = false) => {
+    applyFunction = (
+        strokes: Opt<DocumentView | DocumentView[]>,
+        func: (view: DocumentView, ink: InkData, ptsXscale: number, ptsYscale: number, inkStrokeWidth: number) => { X: number; Y: number }[] | undefined,
+        requireCurrPoint: boolean = false
+    ) => {
         var appliedFunc = false;
-        (strokes instanceof DocumentView ? [strokes] : strokes)?.forEach(action(inkView => {
-            if (!requireCurrPoint || this._currentPoint !== -1) {
-                const doc = inkView.rootDoc;
-                if (doc.type === DocumentType.INK && doc.width && doc.height) {
-                    const ink = Cast(doc.data, InkField)?.inkData;
-                    if (ink) {
-                        const oldXrange = (xs => ({ coord: NumCast(doc.x), min: Math.min(...xs), max: Math.max(...xs) }))(ink.map(p => p.X));
-                        const oldYrange = (ys => ({ coord: NumCast(doc.y), min: Math.min(...ys), max: Math.max(...ys) }))(ink.map(p => p.Y));
-                        const ptsXscale = ((NumCast(doc._width) - NumCast(doc.strokeWidth)) / ((oldXrange.max - oldXrange.min) || 1)) || 1;
-                        const ptsYscale = ((NumCast(doc._height) - NumCast(doc.strokeWidth)) / ((oldYrange.max - oldYrange.min) || 1)) || 1;
-                        const newPoints = func(inkView, ink, ptsXscale, ptsYscale, NumCast(doc.strokeWidth));
-                        if (newPoints) {
-                            const newXrange = (xs => ({ min: Math.min(...xs), max: Math.max(...xs) }))(newPoints.map(p => p.X));
-                            const newYrange = (ys => ({ min: Math.min(...ys), max: Math.max(...ys) }))(newPoints.map(p => p.Y));
-                            doc._width = (newXrange.max - newXrange.min) * ptsXscale + NumCast(doc.strokeWidth);
-                            doc._height = (newYrange.max - newYrange.min) * ptsYscale + NumCast(doc.strokeWidth);
-                            doc.x = (oldXrange.coord + (newXrange.min - oldXrange.min) * ptsXscale);
-                            doc.y = (oldYrange.coord + (newYrange.min - oldYrange.min) * ptsYscale);
-                            Doc.GetProto(doc).data = new InkField(newPoints);
-                            appliedFunc = true;
+        (strokes instanceof DocumentView ? [strokes] : strokes)?.forEach(
+            action(inkView => {
+                if (!requireCurrPoint || this._currentPoint !== -1) {
+                    const doc = inkView.rootDoc;
+                    if (doc.type === DocumentType.INK && doc.width && doc.height) {
+                        const ink = Cast(doc.data, InkField)?.inkData;
+                        if (ink) {
+                            const oldXrange = (xs => ({ coord: NumCast(doc.x), min: Math.min(...xs), max: Math.max(...xs) }))(ink.map(p => p.X));
+                            const oldYrange = (ys => ({ coord: NumCast(doc.y), min: Math.min(...ys), max: Math.max(...ys) }))(ink.map(p => p.Y));
+                            const ptsXscale = (NumCast(doc._width) - NumCast(doc.strokeWidth)) / (oldXrange.max - oldXrange.min || 1) || 1;
+                            const ptsYscale = (NumCast(doc._height) - NumCast(doc.strokeWidth)) / (oldYrange.max - oldYrange.min || 1) || 1;
+                            const newPoints = func(inkView, ink, ptsXscale, ptsYscale, NumCast(doc.strokeWidth));
+                            if (newPoints) {
+                                const newXrange = (xs => ({ min: Math.min(...xs), max: Math.max(...xs) }))(newPoints.map(p => p.X));
+                                const newYrange = (ys => ({ min: Math.min(...ys), max: Math.max(...ys) }))(newPoints.map(p => p.Y));
+                                doc._width = (newXrange.max - newXrange.min) * ptsXscale + NumCast(doc.strokeWidth);
+                                doc._height = (newYrange.max - newYrange.min) * ptsYscale + NumCast(doc.strokeWidth);
+                                doc.x = oldXrange.coord + (newXrange.min - oldXrange.min) * ptsXscale;
+                                doc.y = oldYrange.coord + (newYrange.min - oldYrange.min) * ptsYscale;
+                                Doc.GetProto(doc).data = new InkField(newPoints);
+                                appliedFunc = true;
+                            }
                         }
                     }
                 }
-            }
-        }));
+            })
+        );
         return appliedFunc;
-    }
+    };
 
     /**
      * Adds a new control point to the ink instance when editing its format.
@@ -72,7 +84,7 @@ export class InkStrokeProperties {
      */
     @undoBatch
     @action
-    addPoints = (inkView: DocumentView, t: number, i: number, controls: { X: number, Y: number }[]) => {
+    addPoints = (inkView: DocumentView, t: number, i: number, controls: { X: number; Y: number }[]) => {
         this.applyFunction(inkView, (view: DocumentView, ink: InkData) => {
             const doc = view.rootDoc;
             const array = [controls[i], controls[i + 1], controls[i + 2], controls[i + 3]];
@@ -81,12 +93,12 @@ export class InkStrokeProperties {
             controls.splice(i, 4, ...splicepts.map(p => ({ X: p.x, Y: p.y })));
 
             // Updating the indices of the control points whose handle tangency has been broken.
-            doc.brokenInkIndices = new List(Cast(doc.brokenInkIndices, listSpec("number"), []).map(control => control > i ? control + 4 : control));
+            doc.brokenInkIndices = new List(Cast(doc.brokenInkIndices, listSpec('number'), []).map(control => (control > i ? control + 4 : control)));
             this._currentPoint = -1;
 
             return controls;
         });
-    }
+    };
 
     /**
      * Scales a handle point of a control point that is adjacent to a newly added one.
@@ -107,15 +119,15 @@ export class InkStrokeProperties {
      * the tangent vector to a control point is equivalent to the first/last (depending on the direction
      * of the curve) leg of the Bézier curve's derivative.
      * (Source: https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/bezier-der.html)
-     * 
+     *
      * @param C The curve represented by all points from the previous control until the newly added point.
      * @param D The curve represented by all points from the newly added point to the next control.
      * @param newControl The newly added control point.
      */
     getNewHandlePoints = (C: PointData[], D: PointData[], newControl: PointData) => {
         const [m, n] = [C.length, D.length];
-        let handleSizeA = Math.sqrt((Math.pow(newControl.X - C[0].X, 2)) + (Math.pow(newControl.Y - C[0].Y, 2)));
-        let handleSizeB = Math.sqrt((Math.pow(D[n - 1].X - newControl.X, 2)) + (Math.pow(D[n - 1].Y - newControl.Y, 2)));
+        let handleSizeA = Math.sqrt(Math.pow(newControl.X - C[0].X, 2) + Math.pow(newControl.Y - C[0].Y, 2));
+        let handleSizeB = Math.sqrt(Math.pow(D[n - 1].X - newControl.X, 2) + Math.pow(D[n - 1].Y - newControl.Y, 2));
         // Scaling adjustments to improve the ratio between the magnitudes of the two handle lines.
         // (Ensures that the new point added doesn't augment the inital shape of the curve much).
         if (handleSizeA < 75 && handleSizeB < 75) {
@@ -131,50 +143,55 @@ export class InkStrokeProperties {
         }
         // Finding the last leg of the derivative curve of C.
         const dC = { X: (handleSizeA / n) * (C[m - 1].X - C[m - 2].X), Y: (handleSizeA / n) * (C[m - 1].Y - C[m - 2].Y) };
-        // Finding the first leg of the derivative curve of D. 
+        // Finding the first leg of the derivative curve of D.
         const dD = { X: (handleSizeB / m) * (D[1].X - D[0].X), Y: (handleSizeB / m) * (D[1].Y - D[0].Y) };
         const handleA = { X: newControl.X - dC.X, Y: newControl.Y - dC.Y };
         const handleB = { X: newControl.X + dD.X, Y: newControl.Y + dD.Y };
         return [handleA, handleB];
-    }
+    };
 
     /**
      * Deletes the current control point of the selected ink instance.
      */
     @undoBatch
     @action
-    deletePoints = (inkView: DocumentView, preserve: boolean) => this.applyFunction(inkView, (view: DocumentView, ink: InkData) => {
-        const doc = view.rootDoc;
-        const newPoints = ink.slice();
-        const brokenIndices = NumListCast(doc.brokenInkIndices);
-        if (preserve || this._currentPoint === 0 || this._currentPoint === ink.length - 1 || brokenIndices.includes(this._currentPoint)) {
-            newPoints.splice(this._currentPoint === 0 ? 0 : this._currentPoint === ink.length - 1 ? this._currentPoint - 3 : this._currentPoint - 2, 4);
-        } else {
-            const start = this._currentPoint === 0 ? 0 : this._currentPoint - 4;
-            const splicedPoints = ink.slice(start, start + (this._currentPoint === 0 || this._currentPoint === ink.length - 1 ? 4 : 8));
-            const samples: Point[] = [];
-            var startDir = { x: 0, y: 0 };
-            var endDir = { x: 0, y: 0 };
-            for (var i = 0; i < splicedPoints.length / 4; i++) {
-                const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y })));
-                if (i === 0) startDir = bez.derivative(0);
-                if (i === splicedPoints.length / 4 - 1) endDir = bez.derivative(1);
-                for (var t = 0; t < (i === splicedPoints.length / 4 - 1 ? 1 + 1e-7 : 1); t += 0.05) {
-                    const pt = bez.compute(t);
-                    samples.push(new Point(pt.x, pt.y));
+    deletePoints = (inkView: DocumentView, preserve: boolean) =>
+        this.applyFunction(
+            inkView,
+            (view: DocumentView, ink: InkData) => {
+                const doc = view.rootDoc;
+                const newPoints = ink.slice();
+                const brokenIndices = NumListCast(doc.brokenInkIndices);
+                if (preserve || this._currentPoint === 0 || this._currentPoint === ink.length - 1 || brokenIndices.includes(this._currentPoint)) {
+                    newPoints.splice(this._currentPoint === 0 ? 0 : this._currentPoint === ink.length - 1 ? this._currentPoint - 3 : this._currentPoint - 2, 4);
+                } else {
+                    const start = this._currentPoint === 0 ? 0 : this._currentPoint - 4;
+                    const splicedPoints = ink.slice(start, start + (this._currentPoint === 0 || this._currentPoint === ink.length - 1 ? 4 : 8));
+                    const samples: Point[] = [];
+                    var startDir = { x: 0, y: 0 };
+                    var endDir = { x: 0, y: 0 };
+                    for (var i = 0; i < splicedPoints.length / 4; i++) {
+                        const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y })));
+                        if (i === 0) startDir = bez.derivative(0);
+                        if (i === splicedPoints.length / 4 - 1) endDir = bez.derivative(1);
+                        for (var t = 0; t < (i === splicedPoints.length / 4 - 1 ? 1 + 1e-7 : 1); t += 0.05) {
+                            const pt = bez.compute(t);
+                            samples.push(new Point(pt.x, pt.y));
+                        }
+                    }
+                    const { finalCtrls, error } = FitOneCurve(samples, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y });
+                    if (error < 100) {
+                        newPoints.splice(this._currentPoint - 4, 8, ...finalCtrls);
+                    } else {
+                        newPoints.splice(this._currentPoint - 2, 4);
+                    }
                 }
-            }
-            const { finalCtrls, error } = FitOneCurve(samples, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y });
-            if (error < 100) {
-                newPoints.splice(this._currentPoint - 4, 8, ...finalCtrls);
-            } else {
-                newPoints.splice(this._currentPoint - 2, 4);
-            }
-        }
-        doc.brokenInkIndices = new List(brokenIndices.map(control => control >= this._currentPoint ? control - 4 : control));
-        this._currentPoint = -1;
-        return newPoints.length < 4 ? undefined : newPoints;
-    }, true)
+                doc.brokenInkIndices = new List(brokenIndices.map(control => (control >= this._currentPoint ? control - 4 : control)));
+                this._currentPoint = -1;
+                return newPoints.length < 4 ? undefined : newPoints;
+            },
+            true
+        );
 
     /**
      * Rotates ink stroke(s) about a point
@@ -188,15 +205,16 @@ export class InkStrokeProperties {
         this.applyFunction(inkStrokes, (view: DocumentView, ink: InkData, xScale: number, yScale: number, inkStrokeWidth: number) => {
             view.rootDoc.rotation = NumCast(view.rootDoc.rotation) + angle;
             const inkCenterPt = view.ComponentView?.ptFromScreen?.(scrpt);
-            return !inkCenterPt ? ink :
-                ink.map(i => {
-                    const pt = { X: i.X - inkCenterPt.X, Y: i.Y - inkCenterPt.Y };
-                    const newX = Math.cos(angle) * pt.X - Math.sin(angle) * pt.Y * yScale / xScale;
-                    const newY = Math.sin(angle) * pt.X * xScale / yScale + Math.cos(angle) * pt.Y;
-                    return { X: newX + inkCenterPt.X, Y: newY + inkCenterPt.Y };
-                });
+            return !inkCenterPt
+                ? ink
+                : ink.map(i => {
+                      const pt = { X: i.X - inkCenterPt.X, Y: i.Y - inkCenterPt.Y };
+                      const newX = Math.cos(angle) * pt.X - (Math.sin(angle) * pt.Y * yScale) / xScale;
+                      const newY = (Math.sin(angle) * pt.X * xScale) / yScale + Math.cos(angle) * pt.Y;
+                      return { X: newX + inkCenterPt.X, Y: newY + inkCenterPt.Y };
+                  });
         });
-    }
+    };
 
     /**
      * Rotates ink stroke(s) about a point
@@ -210,16 +228,17 @@ export class InkStrokeProperties {
         this.applyFunction(inkStrokes, (view: DocumentView, ink: InkData) => {
             const ptFromScreen = view.ComponentView?.ptFromScreen;
             const ptToScreen = view.ComponentView?.ptToScreen;
-            return !ptToScreen || !ptFromScreen ? ink :
-                ink.map(ptToScreen).map(i => {
-                    const pvec = { X: i.X - scrpt.X, Y: i.Y - scrpt.Y };
-                    const svec = pvec.X * scrVec.X * scaling + pvec.Y * scrVec.Y * scaling;
-                    const ovec = -pvec.X * scrVec.Y * (scaleUniformly ? scaling : 1) + pvec.Y * scrVec.X * (scaleUniformly ? scaling : 1);
-                    const newscrpt = { X: scrpt.X + svec * scrVec.X - ovec * scrVec.Y, Y: scrpt.Y + svec * scrVec.Y + ovec * scrVec.X };
-                    return ptFromScreen(newscrpt);
-                });
+            return !ptToScreen || !ptFromScreen
+                ? ink
+                : ink.map(ptToScreen).map(i => {
+                      const pvec = { X: i.X - scrpt.X, Y: i.Y - scrpt.Y };
+                      const svec = pvec.X * scrVec.X * scaling + pvec.Y * scrVec.Y * scaling;
+                      const ovec = -pvec.X * scrVec.Y * (scaleUniformly ? scaling : 1) + pvec.Y * scrVec.X * (scaleUniformly ? scaling : 1);
+                      const newscrpt = { X: scrpt.X + svec * scrVec.X - ovec * scrVec.Y, Y: scrpt.Y + svec * scrVec.Y + ovec * scrVec.X };
+                      return ptFromScreen(newscrpt);
+                  });
         });
-    }
+    };
 
     /**
      * Handles the movement/scaling of a control point.
@@ -230,7 +249,7 @@ export class InkStrokeProperties {
         this.applyFunction(inkView, (view: DocumentView, ink: InkData) => {
             const order = controlIndex % 4;
             const closed = InkingStroke.IsClosed(ink);
-            const brokenIndices = Cast(inkView.props.Document.brokenInkIndices, listSpec("number"), []);
+            const brokenIndices = Cast(inkView.props.Document.brokenInkIndices, listSpec('number'), []);
             if (origInk && this._currentPoint > 0 && this._currentPoint < ink.length - 1 && brokenIndices.findIndex(value => value === controlIndex) === -1) {
                 const cpt_before = ink[controlIndex];
                 const cpt = { X: cpt_before.X + deltaX, Y: cpt_before.Y + deltaY };
@@ -238,7 +257,7 @@ export class InkStrokeProperties {
                 const start = this._currentPoint === 0 ? 0 : this._currentPoint - 4;
                 const splicedPoints = origInk.slice(start, start + (this._currentPoint === 0 || this._currentPoint === ink.length - 1 ? 4 : 8));
                 const { nearestT, nearestSeg } = InkStrokeProperties.nearestPtToStroke(splicedPoints, cpt);
-                if ((nearestSeg === 0 && nearestT < 1e-1) || (nearestSeg === 4 && (1 - nearestT) < 1e-1)) return ink.slice();
+                if ((nearestSeg === 0 && nearestT < 1e-1) || (nearestSeg === 4 && 1 - nearestT < 1e-1)) return ink.slice();
                 const samplesLeft: Point[] = [];
                 const samplesRight: Point[] = [];
                 var startDir = { x: 0, y: 0 };
@@ -247,7 +266,7 @@ export class InkStrokeProperties {
                     const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y })));
                     if (i === 0) startDir = bez.derivative(0);
                     if (i === nearestSeg / 4) endDir = bez.derivative(nearestT);
-                    for (var t = 0; t < (i === nearestSeg / 4 ? nearestT + .05 : 1); t += 0.05) {
+                    for (var t = 0; t < (i === nearestSeg / 4 ? nearestT + 0.05 : 1); t += 0.05) {
                         const pt = bez.compute(i !== nearestSeg / 4 ? t : Math.min(nearestT, t));
                         samplesLeft.push(new Point(pt.x, pt.y));
                     }
@@ -257,7 +276,7 @@ export class InkStrokeProperties {
                     const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y })));
                     if (i === nearestSeg / 4) startDir = bez.derivative(nearestT);
                     if (i === splicedPoints.length / 4 - 1) endDir = bez.derivative(1);
-                    for (var t = i === nearestSeg / 4 ? nearestT : 0; t < (i === nearestSeg / 4 ? 1 + .05 + 1e-7 : 1 + 1e-7); t += 0.05) {
+                    for (var t = i === nearestSeg / 4 ? nearestT : 0; t < (i === nearestSeg / 4 ? 1 + 0.05 + 1e-7 : 1 + 1e-7); t += 0.05) {
                         const pt = bez.compute(Math.min(1, t));
                         samplesRight.push(new Point(pt.x, pt.y));
                     }
@@ -271,12 +290,11 @@ export class InkStrokeProperties {
             return ink.map((pt, i) => {
                 const leftHandlePoint = order === 0 && i === controlIndex + 1;
                 const rightHandlePoint = order === 0 && controlIndex !== 0 && i === controlIndex - 2;
-                if (controlIndex === i ||
-                    (order === 0 && controlIndex !== 0 && i === controlIndex - 1) ||
-                    (order === 3 && i === controlIndex - 1)) {
-                    return ({ X: pt.X + deltaX, Y: pt.Y + deltaY });
+                if (controlIndex === i || (order === 0 && controlIndex !== 0 && i === controlIndex - 1) || (order === 3 && i === controlIndex - 1)) {
+                    return { X: pt.X + deltaX, Y: pt.Y + deltaY };
                 }
-                if (controlIndex === i ||
+                if (
+                    controlIndex === i ||
                     leftHandlePoint ||
                     rightHandlePoint ||
                     (order === 0 && controlIndex !== 0 && i === controlIndex - 1) ||
@@ -284,15 +302,15 @@ export class InkStrokeProperties {
                     (order === 3 && i === controlIndex - 1) ||
                     (order === 3 && controlIndex !== ink.length - 1 && i === controlIndex + 1) ||
                     (order === 3 && controlIndex !== ink.length - 1 && i === controlIndex + 2) ||
-                    ((ink[0].X === ink[ink.length - 1].X) && (ink[0].Y === ink[ink.length - 1].Y) && (i === 0 || i === ink.length - 1) && (controlIndex === 0 || controlIndex === ink.length - 1))) {
-                    return ({ X: pt.X + deltaX, Y: pt.Y + deltaY });
+                    (ink[0].X === ink[ink.length - 1].X && ink[0].Y === ink[ink.length - 1].Y && (i === 0 || i === ink.length - 1) && (controlIndex === 0 || controlIndex === ink.length - 1))
+                ) {
+                    return { X: pt.X + deltaX, Y: pt.Y + deltaY };
                 }
                 return pt;
             });
-        })
-
+        });
 
-    public static nearestPtToStroke(ctrlPoints: { X: number, Y: number }[], refInkSpacePt: { X: number, Y: number }, excludeSegs?: number[]) {
+    public static nearestPtToStroke(ctrlPoints: { X: number; Y: number }[], refInkSpacePt: { X: number; Y: number }, excludeSegs?: number[]) {
         var distance = Number.MAX_SAFE_INTEGER;
         var nearestT = -1;
         var nearestSeg = -1;
@@ -326,16 +344,16 @@ export class InkStrokeProperties {
             if (screenDragPt) {
                 const snapData = this.snapToAllCurves(screenDragPt, inkView, { nearestPt: { X: 0, Y: 0 }, distance: 10 }, ink, controlIndex);
                 if (snapData.distance < 10) {
-                    const deltaX = (snapData.nearestPt.X - ink[controlIndex].X);
-                    const deltaY = (snapData.nearestPt.Y - ink[controlIndex].Y);
+                    const deltaX = snapData.nearestPt.X - ink[controlIndex].X;
+                    const deltaY = snapData.nearestPt.Y - ink[controlIndex].Y;
                     const res = this.moveControlPtHandle(inkView, deltaX, deltaY, controlIndex, ink.slice());
-                    console.log("X = " + snapData.nearestPt.X + " " + snapData.nearestPt.Y);
+                    console.log('X = ' + snapData.nearestPt.X + ' ' + snapData.nearestPt.Y);
                     return res;
                 }
             }
         }
         return false;
-    }
+    };
 
     excludeSelfSnapSegs = (ink: InkData, controlIndex: number) => {
         const closed = InkingStroke.IsClosed(ink);
@@ -346,9 +364,9 @@ export class InkStrokeProperties {
         const nextseg = which > 1 && (closed || controlIndex < ink.length - 1) ? (thisseg + 4) % ink.length : -1;
         const prevseg = which < 2 && (closed || controlIndex > 0) ? (thisseg - 4 + ink.length) % ink.length : -1;
         return [thisseg, prevseg, nextseg];
-    }
+    };
 
-    snapToAllCurves = (screenDragPt: { X: number, Y: number }, inkView: DocumentView, snapData: { nearestPt: { X: number, Y: number }, distance: number }, ink: InkData, controlIndex: number) => {
+    snapToAllCurves = (screenDragPt: { X: number; Y: number }, inkView: DocumentView, snapData: { nearestPt: { X: number; Y: number }; distance: number }, ink: InkData, controlIndex: number) => {
         const containingCollection = inkView.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
         containingCollection?.childDocs
             .filter(doc => doc.type === DocumentType.INK)
@@ -356,8 +374,7 @@ export class InkStrokeProperties {
                 const testInkView = DocumentManager.Instance.getDocumentView(doc, containingCollection?.props.CollectionView);
                 const snapped = testInkView?.ComponentView?.snapPt?.(screenDragPt, doc === inkView.rootDoc ? this.excludeSelfSnapSegs(ink, controlIndex) : []);
                 if (snapped && snapped.distance < snapData.distance) {
-                    const snappedInkPt = doc === inkView.rootDoc ? snapped.nearestPt :
-                        inkView.ComponentView?.ptFromScreen?.(testInkView?.ComponentView?.ptToScreen?.(snapped.nearestPt) ?? { X: 0, Y: 0 });  // convert from snapped ink coordinate system to dragged ink coordinate system by converting to/from screen space
+                    const snappedInkPt = doc === inkView.rootDoc ? snapped.nearestPt : inkView.ComponentView?.ptFromScreen?.(testInkView?.ComponentView?.ptToScreen?.(snapped.nearestPt) ?? { X: 0, Y: 0 }); // convert from snapped ink coordinate system to dragged ink coordinate system by converting to/from screen space
 
                     if (snappedInkPt) {
                         snapData = { nearestPt: snappedInkPt, distance: snapped.distance };
@@ -365,7 +382,7 @@ export class InkStrokeProperties {
                 }
             });
         return snapData;
-    }
+    };
 
     /**
      * Snaps a control point with broken tangency back to synced rotation.
@@ -375,7 +392,7 @@ export class InkStrokeProperties {
     snapHandleTangent = (inkView: DocumentView, controlIndex: number, handleIndexA: number, handleIndexB: number) => {
         this.applyFunction(inkView, (view: DocumentView, ink: InkData) => {
             const doc = view.rootDoc;
-            const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number"), []);
+            const brokenIndices = Cast(doc.brokenInkIndices, listSpec('number'), []);
             const ind = brokenIndices.findIndex(value => value === controlIndex);
             if (ind !== -1) {
                 brokenIndices.splice(ind, 1);
@@ -387,7 +404,7 @@ export class InkStrokeProperties {
                 return inkCopy;
             }
         });
-    }
+    };
 
     /**
      * Rotates the target point about the origin point for a given angle (radians).
@@ -398,11 +415,11 @@ export class InkStrokeProperties {
         const newX = Math.cos(angle) * rotatedTarget.X - Math.sin(angle) * rotatedTarget.Y;
         const newY = Math.sin(angle) * rotatedTarget.X + Math.cos(angle) * rotatedTarget.Y;
         return { X: newX + origin.X, Y: newY + origin.Y };
-    }
+    };
 
     /**
      * Finds the angle (in radians) between two inputted vectors.
-     * 
+     *
      * α = arccos(a·b / |a|·|b|), where a and b are both vectors.
      */
     public static angleBetweenTwoVectors(vectorA: PointData, vectorB: PointData) {
@@ -444,14 +461,13 @@ export class InkStrokeProperties {
             const newHandlePoint = { X: ink[handleIndex].X - deltaX, Y: ink[handleIndex].Y - deltaY };
             const inkCopy = ink.slice();
             inkCopy[handleIndex] = newHandlePoint;
-            const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number"));
+            const brokenIndices = Cast(doc.brokenInkIndices, listSpec('number'));
             const equivIndex = closed ? (controlIndex === 0 ? ink.length - 1 : controlIndex === ink.length - 1 ? 0 : -1) : -1;
             // Rotate opposite handle if user hasn't held 'Alt' key or not first/final control (which have only 1 handle).
-            if ((!brokenIndices || (!brokenIndices?.includes(controlIndex) && !brokenIndices?.includes(equivIndex))) &&
-                (closed || (handleIndex !== 1 && handleIndex !== ink.length - 2))) {
+            if ((!brokenIndices || (!brokenIndices?.includes(controlIndex) && !brokenIndices?.includes(equivIndex))) && (closed || (handleIndex !== 1 && handleIndex !== ink.length - 2))) {
                 const angle = InkStrokeProperties.angleChange(oldHandlePoint, newHandlePoint, controlPoint);
                 inkCopy[oppositeHandleIndex] = this.rotatePoint(oppositeHandlePoint, controlPoint, angle);
             }
             return inkCopy;
-        })
-}
\ No newline at end of file
+        });
+}
diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx
index 5936ea32d..bf0e8081d 100644
--- a/src/client/views/InkTranscription.tsx
+++ b/src/client/views/InkTranscription.tsx
@@ -9,7 +9,6 @@ import { DocumentType } from '../documents/DocumentTypes';
 import { DocumentManager } from '../util/DocumentManager';
 import { CollectionFreeFormView } from './collections/collectionFreeForm';
 import { InkingStroke } from './InkingStroke';
-import { CurrentUserUtils } from '../util/CurrentUserUtils';
 import './InkTranscription.scss';
 
 /**
@@ -256,7 +255,7 @@ export class InkTranscription extends React.Component {
      */
     createInkGroup() {
         // TODO nda - if document being added to is a inkGrouping then we can just add to that group
-        if (CurrentUserUtils.ActiveTool === InkTool.Write) {
+        if (Doc.ActiveTool === InkTool.Write) {
             CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
                 // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
                 const selected = ffView.unprocessedDocs;
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index a1e71b5f4..99d50b4a2 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -1,7 +1,7 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
-import "normalize.css";
+import 'normalize.css';
 import * as React from 'react';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { Cast, NumCast, StrCast } from '../../fields/Types';
@@ -13,7 +13,7 @@ import { SelectionManager } from '../util/SelectionManager';
 import { Transform } from '../util/Transform';
 import { CollectionDockingView } from './collections/CollectionDockingView';
 import { TabDocView } from './collections/TabDocView';
-import "./LightboxView.scss";
+import './LightboxView.scss';
 import { DocumentView } from './nodes/DocumentView';
 import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider';
 
@@ -25,20 +25,21 @@ interface LightboxViewProps {
 
 @observer
 export class LightboxView extends React.Component<LightboxViewProps> {
-
-    @computed public static get LightboxDoc() { return this._doc; }
+    @computed public static get LightboxDoc() {
+        return this._doc;
+    }
     private static LightboxDocTemplate = () => LightboxView._layoutTemplate;
     @observable private static _layoutTemplate: Opt<Doc>;
     @observable private static _doc: Opt<Doc>;
     @observable private static _docTarget: Opt<Doc>;
     @observable private static _docFilters: string[] = []; // filters
-    @observable private static _tourMap: Opt<Doc[]> = [];   // list of all tours available from the current target
-    private static _savedState: Opt<{ panX: Opt<number>, panY: Opt<number>, scale: Opt<number>, scrollTop: Opt<number> }>;
-    private static _history: Opt<{ doc: Doc, target?: Doc }[]> = [];
+    @observable private static _tourMap: Opt<Doc[]> = []; // list of all tours available from the current target
+    private static _savedState: Opt<{ panX: Opt<number>; panY: Opt<number>; scale: Opt<number>; scrollTop: Opt<number> }>;
+    private static _history: Opt<{ doc: Doc; target?: Doc }[]> = [];
     @observable private static _future: Opt<Doc[]> = [];
     private static _docView: Opt<DocumentView>;
     private static openInTabFunc: any;
-    static path: { doc: Opt<Doc>, target: Opt<Doc>, history: Opt<{ doc: Doc, target?: Doc }[]>, future: Opt<Doc[]>, saved: Opt<{ panX: Opt<number>, panY: Opt<number>, scale: Opt<number>, scrollTop: Opt<number> }> }[] = [];
+    static path: { doc: Opt<Doc>; target: Opt<Doc>; history: Opt<{ doc: Doc; target?: Doc }[]>; future: Opt<Doc[]>; saved: Opt<{ panX: Opt<number>; panY: Opt<number>; scale: Opt<number>; scrollTop: Opt<number> }> }[] = [];
     @action public static SetLightboxDoc(doc: Opt<Doc>, target?: Doc, future?: Doc[], layoutTemplate?: Doc) {
         if (this.LightboxDoc && this.LightboxDoc !== doc && this._savedState) {
             this.LightboxDoc._panX = this._savedState.panX;
@@ -53,52 +54,71 @@ export class LightboxView extends React.Component<LightboxViewProps> {
         } else {
             if (doc) {
                 const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement();
-                l && (Cast(l.anchor2, Doc, null).backgroundColor = "lightgreen");
+                l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen');
             }
             //TabDocView.PinDoc(doc, { hidePresBox: true });
-            this._history ? this._history.push({ doc, target }) : this._history = [{ doc, target }];
+            this._history ? this._history.push({ doc, target }) : (this._history = [{ doc, target }]);
             if (doc !== LightboxView.LightboxDoc) {
                 this._savedState = {
-                    panX: Cast(doc._panX, "number", null),
-                    panY: Cast(doc._panY, "number", null),
-                    scale: Cast(doc._viewScale, "number", null),
-                    scrollTop: Cast(doc._scrollTop, "number", null),
+                    panX: Cast(doc._panX, 'number', null),
+                    panY: Cast(doc._panY, 'number', null),
+                    scale: Cast(doc._viewScale, 'number', null),
+                    scrollTop: Cast(doc._scrollTop, 'number', null),
                 };
             }
         }
         if (future) {
-            this._future = [...(this._future ?? []), ...(this.LightboxDoc ? [this.LightboxDoc] : []), ...future.slice().sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)).sort((a, b) => DocListCast(a.links).length - DocListCast(b.links).length),];
+            this._future = [
+                ...(this._future ?? []),
+                ...(this.LightboxDoc ? [this.LightboxDoc] : []),
+                ...future
+                    .slice()
+                    .sort((a, b) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow))
+                    .sort((a, b) => DocListCast(a.links).length - DocListCast(b.links).length),
+            ];
         }
         this._doc = doc;
         this._layoutTemplate = layoutTemplate;
         this._docTarget = target || doc;
-        this._tourMap = DocListCast(doc?.links).map(link => {
-            const opp = LinkManager.getOppositeAnchor(link, doc!);
-            return opp?.TourMap ? opp : undefined;
-        }).filter(m => m).map(m => m!);
+        this._tourMap = DocListCast(doc?.links)
+            .map(link => {
+                const opp = LinkManager.getOppositeAnchor(link, doc!);
+                return opp?.TourMap ? opp : undefined;
+            })
+            .filter(m => m)
+            .map(m => m!);
 
         return true;
     }
-    public static IsLightboxDocView(path: DocumentView[]) { return path.includes(this._docView!); }
-    @computed get leftBorder() { return Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]); }
-    @computed get topBorder() { return Math.min(this.props.PanelHeight / 4, this.props.maxBorder[1]); }
+    public static IsLightboxDocView(path: DocumentView[]) {
+        return path.includes(this._docView!);
+    }
+    @computed get leftBorder() {
+        return Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]);
+    }
+    @computed get topBorder() {
+        return Math.min(this.props.PanelHeight / 4, this.props.maxBorder[1]);
+    }
     lightboxWidth = () => this.props.PanelWidth - this.leftBorder * 2;
     lightboxHeight = () => this.props.PanelHeight - this.topBorder * 2;
     lightboxScreenToLocal = () => new Transform(-this.leftBorder, -this.topBorder, 1);
     navBtn = (left: Opt<string | number>, bottom: Opt<number>, top: number, icon: string, display: () => string, click: (e: React.MouseEvent) => void, color?: string) => {
-        return <div className="lightboxView-navBtn-frame" style={{
-            display: display(),
-            left,
-            width: bottom !== undefined ? undefined : Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]),
-            bottom
-        }}>
-            <div className="lightboxView-navBtn" title={color} style={{ top, color: color ? "red" : "white", background: color ? "white" : undefined }}
-                onClick={click}>
-                <div style={{ height: 10 }}>{color}</div>
-                <FontAwesomeIcon icon={icon as any} size="3x" />
+        return (
+            <div
+                className="lightboxView-navBtn-frame"
+                style={{
+                    display: display(),
+                    left,
+                    width: bottom !== undefined ? undefined : Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]),
+                    bottom,
+                }}>
+                <div className="lightboxView-navBtn" title={color} style={{ top, color: color ? 'red' : 'white', background: color ? 'white' : undefined }} onClick={click}>
+                    <div style={{ height: 10 }}>{color}</div>
+                    <FontAwesomeIcon icon={icon as any} size="3x" />
+                </div>
             </div>
-        </div>;
-    }
+        );
+    };
     public static GetSavedState(doc: Doc) {
         return this.LightboxDoc === doc && this._savedState ? this._savedState : undefined;
     }
@@ -107,27 +127,28 @@ export class LightboxView extends React.Component<LightboxViewProps> {
     @action
     public static SetCookie(cookie: string) {
         if (this.LightboxDoc && cookie) {
-            this._docFilters = (f => this._docFilters ? [this._docFilters.push(f) as any, this._docFilters][1] : [f])(`cookies:${cookie}:provide`);
+            this._docFilters = (f => (this._docFilters ? [this._docFilters.push(f) as any, this._docFilters][1] : [f]))(`cookies:${cookie}:provide`);
         }
     }
     public static AddDocTab = (doc: Doc, location: string, layoutTemplate?: Doc, openInTabFunc?: any) => {
         LightboxView.openInTabFunc = openInTabFunc;
         SelectionManager.DeselectAll();
-        return LightboxView.SetLightboxDoc(doc, undefined,
-            [...DocListCast(doc[Doc.LayoutFieldKey(doc)]),
-            ...DocListCast(doc[Doc.LayoutFieldKey(doc) + "-annotations"]),
-            ...(LightboxView._future ?? [])
-            ].sort((a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)), layoutTemplate);
-    }
+        return LightboxView.SetLightboxDoc(
+            doc,
+            undefined,
+            [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '-annotations']), ...(LightboxView._future ?? [])].sort((a: Doc, b: Doc) => NumCast(b._timecodeToShow) - NumCast(a._timecodeToShow)),
+            layoutTemplate
+        );
+    };
     docFilters = () => LightboxView._docFilters || [];
     addDocTab = LightboxView.AddDocTab;
     @action public static Next() {
         const doc = LightboxView._doc!;
-        const target = LightboxView._docTarget = this._future?.pop();
+        const target = (LightboxView._docTarget = this._future?.pop());
         const targetDocView = target && DocumentManager.Instance.getLightboxDocumentView(target);
         if (targetDocView && target) {
             const l = DocUtils.MakeLinkToActiveAudio(() => targetDocView.ComponentView?.getAnchor?.() || target).lastElement();
-            l && (Cast(l.anchor2, Doc, null).backgroundColor = "lightgreen");
+            l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen');
             targetDocView.focus(target, { originalTarget: target, willZoom: true, scale: 0.9 });
             if (LightboxView._history?.lastElement().target !== target) LightboxView._history?.push({ doc, target });
         } else {
@@ -152,10 +173,13 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                 LightboxView.SetLightboxDoc(target);
             }
         }
-        LightboxView._tourMap = DocListCast(LightboxView._docTarget?.links).map(link => {
-            const opp = LinkManager.getOppositeAnchor(link, LightboxView._docTarget!);
-            return opp?.TourMap ? opp : undefined;
-        }).filter(m => m).map(m => m!);
+        LightboxView._tourMap = DocListCast(LightboxView._docTarget?.links)
+            .map(link => {
+                const opp = LinkManager.getOppositeAnchor(link, LightboxView._docTarget!);
+                return opp?.TourMap ? opp : undefined;
+            })
+            .filter(m => m)
+            .map(m => m!);
     }
 
     @action public static Previous() {
@@ -170,15 +194,17 @@ export class LightboxView extends React.Component<LightboxViewProps> {
             LightboxView._docTarget = target;
             if (!target) docView.ComponentView?.shrinkWrap?.();
             else docView.focus(target, { willZoom: true, scale: 0.9 });
-        }
-        else {
+        } else {
             LightboxView.SetLightboxDoc(doc, target);
         }
         if (LightboxView._future?.lastElement() !== previous.target || previous.doc) LightboxView._future?.push(previous.target || previous.doc);
-        LightboxView._tourMap = DocListCast(LightboxView._docTarget?.links).map(link => {
-            const opp = LinkManager.getOppositeAnchor(link, LightboxView._docTarget!);
-            return opp?.TourMap ? opp : undefined;
-        }).filter(m => m).map(m => m!);
+        LightboxView._tourMap = DocListCast(LightboxView._docTarget?.links)
+            .map(link => {
+                const opp = LinkManager.getOppositeAnchor(link, LightboxView._docTarget!);
+                return opp?.TourMap ? opp : undefined;
+            })
+            .filter(m => m)
+            .map(m => m!);
     }
     @action
     stepInto = () => {
@@ -187,7 +213,7 @@ export class LightboxView extends React.Component<LightboxViewProps> {
             target: LightboxView._docTarget,
             future: LightboxView._future,
             history: LightboxView._history,
-            saved: LightboxView._savedState
+            saved: LightboxView._savedState,
         });
         const tours = LightboxView._tourMap;
         if (tours && tours.length) {
@@ -197,45 +223,58 @@ export class LightboxView extends React.Component<LightboxViewProps> {
             const coll = LightboxView._docTarget;
             if (coll) {
                 const fieldKey = Doc.LayoutFieldKey(coll);
-                const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + "-annotations"])];
-                const links = DocListCast(coll.links).map(link => LinkManager.getOppositeAnchor(link, coll)).filter(doc => doc).map(doc => doc!);
+                const contents = [...DocListCast(coll[fieldKey]), ...DocListCast(coll[fieldKey + '-annotations'])];
+                const links = DocListCast(coll.links)
+                    .map(link => LinkManager.getOppositeAnchor(link, coll))
+                    .filter(doc => doc)
+                    .map(doc => doc!);
                 LightboxView.SetLightboxDoc(coll, undefined, contents.length ? contents : links);
                 TabDocView.PinDoc(coll, { hidePresBox: true });
             }
         }
-    }
+    };
 
     future = () => LightboxView._future;
     tourMap = () => LightboxView._tourMap;
     render() {
-        let downx = 0, downy = 0;
-        return !LightboxView.LightboxDoc ? (null) :
-            <div className="lightboxView-frame"
-                onPointerDown={e => { downx = e.clientX; downy = e.clientY; }}
+        let downx = 0,
+            downy = 0;
+        return !LightboxView.LightboxDoc ? null : (
+            <div
+                className="lightboxView-frame"
+                onPointerDown={e => {
+                    downx = e.clientX;
+                    downy = e.clientY;
+                }}
                 onClick={e => {
                     if (Math.abs(downx - e.clientX) < 4 && Math.abs(downy - e.clientY) < 4) {
                         LightboxView.SetLightboxDoc(undefined);
                     }
-                }}  >
-
-                <div className="lightboxView-contents" style={{
-                    left: this.leftBorder,
-                    top: this.topBorder,
-                    width: this.lightboxWidth(),
-                    height: this.lightboxHeight(),
-                    clipPath: `path('${Doc.UserDoc().renderStyle === "comic" ? wavyBorderPath(this.lightboxWidth(), this.lightboxHeight()) : undefined}')`
                 }}>
+                <div
+                    className="lightboxView-contents"
+                    style={{
+                        left: this.leftBorder,
+                        top: this.topBorder,
+                        width: this.lightboxWidth(),
+                        height: this.lightboxHeight(),
+                        clipPath: `path('${Doc.UserDoc().renderStyle === 'comic' ? wavyBorderPath(this.lightboxWidth(), this.lightboxHeight()) : undefined}')`,
+                    }}>
                     {/* <CollectionMenu /> TODO:glr This is where it would go*/}
-                    <DocumentView ref={action((r: DocumentView | null) => {
-                        LightboxView._docView = r !== null ? r : undefined;
-                        r && setTimeout(action(() => {
-                            const target = LightboxView._docTarget;
-                            const doc = LightboxView._doc;
-                            const targetView = target && DocumentManager.Instance.getLightboxDocumentView(target);
-                            if (doc === r.props.Document && (!target || target === doc)) r.ComponentView?.shrinkWrap?.();
-                            //else target?.focus(target, { willZoom: true, scale: 0.9, instant: true }); // bcz: why was this here?  it breaks smooth navigation in lightbox using 'next' button
-                        }));
-                    })}
+                    <DocumentView
+                        ref={action((r: DocumentView | null) => {
+                            LightboxView._docView = r !== null ? r : undefined;
+                            r &&
+                                setTimeout(
+                                    action(() => {
+                                        const target = LightboxView._docTarget;
+                                        const doc = LightboxView._doc;
+                                        const targetView = target && DocumentManager.Instance.getLightboxDocumentView(target);
+                                        if (doc === r.props.Document && (!target || target === doc)) r.ComponentView?.shrinkWrap?.();
+                                        //else target?.focus(target, { willZoom: true, scale: 0.9, instant: true }); // bcz: why was this here?  it breaks smooth navigation in lightbox using 'next' button
+                                    })
+                                );
+                        })}
                         Document={LightboxView.LightboxDoc}
                         DataDoc={undefined}
                         LayoutTemplate={LightboxView.LightboxDocTemplate}
@@ -259,35 +298,57 @@ export class LightboxView extends React.Component<LightboxViewProps> {
                         searchFilterDocs={returnEmptyDoclist}
                         ContainingCollectionView={undefined}
                         ContainingCollectionDoc={undefined}
-                        renderDepth={0} />
+                        renderDepth={0}
+                    />
                 </div>
 
-                {this.navBtn(0, undefined, this.props.PanelHeight / 2 - 12.50, "chevron-left",
-                    () => LightboxView.LightboxDoc && LightboxView._history?.length ? "" : "none", e => {
+                {this.navBtn(
+                    0,
+                    undefined,
+                    this.props.PanelHeight / 2 - 12.5,
+                    'chevron-left',
+                    () => (LightboxView.LightboxDoc && LightboxView._history?.length ? '' : 'none'),
+                    e => {
                         e.stopPropagation();
                         LightboxView.Previous();
-                    })}
-                {this.navBtn(this.props.PanelWidth - Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]), undefined, this.props.PanelHeight / 2 - 12.50, "chevron-right",
-                    () => LightboxView.LightboxDoc && LightboxView._future?.length ? "" : "none", e => {
+                    }
+                )}
+                {this.navBtn(
+                    this.props.PanelWidth - Math.min(this.props.PanelWidth / 4, this.props.maxBorder[0]),
+                    undefined,
+                    this.props.PanelHeight / 2 - 12.5,
+                    'chevron-right',
+                    () => (LightboxView.LightboxDoc && LightboxView._future?.length ? '' : 'none'),
+                    e => {
                         e.stopPropagation();
                         LightboxView.Next();
-                    }, this.future()?.length.toString())}
+                    },
+                    this.future()?.length.toString()
+                )}
                 <LightboxTourBtn navBtn={this.navBtn} future={this.future} stepInto={this.stepInto} tourMap={this.tourMap} />
-                <div className="lightboxView-tabBtn" title={"open in tab"}
+                <div
+                    className="lightboxView-tabBtn"
+                    title={'open in tab'}
                     onClick={e => {
                         e.stopPropagation();
-                        CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, "");
+                        CollectionDockingView.AddSplit(LightboxView._docTarget || LightboxView._doc!, '');
                         //LightboxView.openInTabFunc(LightboxView._docTarget || LightboxView._doc!, "inPlace");
                         SelectionManager.DeselectAll();
                         LightboxView.SetLightboxDoc(undefined);
                     }}>
-                    <FontAwesomeIcon icon={"file-download"} size="2x" />
+                    <FontAwesomeIcon icon={'file-download'} size="2x" />
                 </div>
-                <div className="lightboxView-navBtn" title={"toggle fit width"}
-                    onClick={e => { e.stopPropagation(); LightboxView.LightboxDoc!._fitWidth = !LightboxView.LightboxDoc!._fitWidth; }}>
-                    <FontAwesomeIcon icon={LightboxView.LightboxDoc?._fitWidth ? "arrows-alt-h" : "arrows-alt-v"} size="2x" />
+                <div
+                    className="lightboxView-navBtn"
+                    title={'toggle fit width'}
+                    onClick={e => {
+                        e.stopPropagation();
+                        LightboxView.LightboxDoc!._fitWidth = !LightboxView.LightboxDoc!._fitWidth;
+                    }}>
+                    <FontAwesomeIcon icon={LightboxView.LightboxDoc?._fitWidth ? 'arrows-alt-h' : 'arrows-alt-v'} size="2x" />
                 </div>
-            </div>;
+            </div>
+        );
     }
 }
 interface LightboxTourBtnProps {
@@ -299,12 +360,17 @@ interface LightboxTourBtnProps {
 @observer
 export class LightboxTourBtn extends React.Component<LightboxTourBtnProps> {
     render() {
-        return this.props.navBtn("50%", 0, 0, "chevron-down",
-            () => LightboxView.LightboxDoc /*&& this.props.future()?.length*/ ? "" : "none", e => {
+        return this.props.navBtn(
+            '50%',
+            0,
+            0,
+            'chevron-down',
+            () => (LightboxView.LightboxDoc /*&& this.props.future()?.length*/ ? '' : 'none'),
+            e => {
                 e.stopPropagation();
                 this.props.stepInto();
             },
             StrCast(this.props.tourMap()?.lastElement()?.TourMap)
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 542f85228..e998f1fb9 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -7,7 +7,7 @@ import * as ReactDOM from 'react-dom';
 import { AssignAllExtensions } from '../../extensions/General/Extensions';
 import { Docs } from '../documents/Documents';
 import { CurrentUserUtils } from '../util/CurrentUserUtils';
-import { LinkManager } from '../util/LinkManager';
+import { LinkManager } from '../util/LinkManager'; // this must come before importing Docs and CurrentUserUtils
 import { ReplayMovements } from '../util/ReplayMovements';
 import { TrackMovements } from '../util/TrackMovements';
 import { CollectionView } from './collections/CollectionView';
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 6c0a67de2..edc16d9a6 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -9,13 +9,13 @@ import * as React from 'react';
 import * as ReactDOM from 'react-dom';
 import { Doc, DocListCast, Opt } from '../../fields/Doc';
 import { ScriptField } from '../../fields/ScriptField';
-import { PromiseValue, StrCast } from '../../fields/Types';
+import { StrCast } from '../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, returnZero, setupMoveUpEvents, simulateMouseClick, Utils } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
 import { DocServer } from '../DocServer';
 import { Docs, DocUtils } from '../documents/Documents';
+import { CollectionViewType } from '../documents/DocumentTypes';
 import { CaptureManager } from '../util/CaptureManager';
-import { CurrentUserUtils } from '../util/CurrentUserUtils';
 import { DocumentManager } from '../util/DocumentManager';
 import { GroupManager } from '../util/GroupManager';
 import { HistoryUtil } from '../util/History';
@@ -31,7 +31,6 @@ import { CollectionDockingView } from './collections/CollectionDockingView';
 import { MarqueeOptionsMenu } from './collections/collectionFreeForm/MarqueeOptionsMenu';
 import { CollectionLinearView } from './collections/collectionLinear';
 import { CollectionMenu } from './collections/CollectionMenu';
-import { CollectionViewType } from './collections/CollectionView';
 import './collections/TreeView.scss';
 import { ComponentDecorations } from './ComponentDecorations';
 import { ContextMenu } from './ContextMenu';
@@ -76,7 +75,7 @@ export class MainView extends React.Component {
     @observable private _dashUIWidth: number = 0; // width of entire main dashboard region including left menu buttons and properties panel (but not including the dashboard selector button row)
     @observable private _dashUIHeight: number = 0; // height of entire main dashboard region including top menu buttons
     @observable private _panelContent: string = 'none';
-    @observable private _sidebarContent: any = CurrentUserUtils.MyLeftSidebarPanel;
+    @observable private _sidebarContent: any = Doc.MyLeftSidebarPanel;
     @observable private _leftMenuFlyoutWidth: number = 0;
 
     @computed private get dashboardTabHeight() {
@@ -104,27 +103,27 @@ export class MainView extends React.Component {
         return Doc.UserDoc();
     }
     @computed private get colorScheme() {
-        return StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme);
+        return StrCast(Doc.ActiveDashboard?.colorScheme);
     }
     @computed private get mainContainer() {
-        return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard;
+        return this.userDoc ? Doc.ActiveDashboard : Doc.GuestDashboard;
     }
     @computed private get headerBarDoc() {
-        return CurrentUserUtils.MyHeaderBar;
+        return Doc.MyHeaderBar;
     }
     @computed public get mainFreeform(): Opt<Doc> {
         return (docs => (docs?.length > 1 ? docs[1] : undefined))(DocListCast(this.mainContainer!.data));
     }
 
     headerBarDocWidth = () => this.mainDocViewWidth();
-    headerBarDocHeight = () => CurrentUserUtils.headerBarHeight ?? 0;
+    headerBarDocHeight = () => SettingsManager.headerBarHeight ?? 0;
     topMenuHeight = () => 35;
     topMenuWidth = returnZero; // value is ignored ...
     leftMenuWidth = () => Number(LEFT_MENU_WIDTH.replace('px', ''));
     leftMenuHeight = () => this._dashUIHeight;
     leftMenuFlyoutWidth = () => this._leftMenuFlyoutWidth;
     leftMenuFlyoutHeight = () => this._dashUIHeight;
-    propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, CurrentUserUtils.propertiesWidth || 0));
+    propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, SettingsManager.propertiesWidth || 0));
     propertiesHeight = () => this._dashUIHeight;
     mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth() - this.leftMenuFlyoutWidth();
     mainDocViewHeight = () => this._dashUIHeight - this.headerBarDocHeight();
@@ -204,7 +203,7 @@ export class MainView extends React.Component {
     constructor(props: Readonly<{}>) {
         super(props);
         MainView.Instance = this;
-        CurrentUserUtils._urlState = HistoryUtil.parseUrl(window.location) || ({} as any);
+        DashboardView._urlState = HistoryUtil.parseUrl(window.location) || ({} as any);
 
         // causes errors to be generated when modifying an observable outside of an action
         configure({ enforceActions: 'observed' });
@@ -212,8 +211,8 @@ export class MainView extends React.Component {
         if (window.location.pathname !== '/home') {
             const pathname = window.location.pathname.substr(1).split('/');
             if (pathname.length > 1 && pathname[0] === 'doc') {
-                CurrentUserUtils.MainDocId = pathname[1];
-                !this.userDoc && DocServer.GetRefField(pathname[1]).then(action(field => field instanceof Doc && (CurrentUserUtils.GuestTarget = field)));
+                Doc.MainDocId = pathname[1];
+                !this.userDoc && DocServer.GetRefField(pathname[1]).then(action(field => field instanceof Doc && (Doc.GuestTarget = field)));
             }
         }
 
@@ -483,18 +482,18 @@ export class MainView extends React.Component {
     };
 
     initAuthenticationRouters = async () => {
-        const received = CurrentUserUtils.MainDocId;
+        const received = Doc.MainDocId;
         if (received && !this.userDoc) {
             reaction(
-                () => CurrentUserUtils.GuestTarget,
-                target => target && CurrentUserUtils.createNewDashboard(),
+                () => Doc.GuestTarget,
+                target => target && DashboardView.createNewDashboard(),
                 { fireImmediately: true }
             );
         }
         // else {
         //     PromiseValue(this.userDoc.activeDashboard).then(dash => {
-        //         if (dash instanceof Doc) CurrentUserUtils.openDashboard(dash);
-        //         else CurrentUserUtils.createNewDashboard();
+        //         if (dash instanceof Doc) DashboardView.openDashboard(dash);
+        //         else Doc.createNewDashboard();
         //     });
         // }
     };
@@ -503,14 +502,14 @@ export class MainView extends React.Component {
     createNewPresentation = async () => {
         const pres = Docs.Create.PresDocument({ title: 'Untitled Trail', _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: 'alias', _chromeHidden: true, boxShadow: '0 0' });
         CollectionDockingView.AddSplit(pres, 'left');
-        CurrentUserUtils.ActivePresentation = pres;
-        Doc.AddDocToList(CurrentUserUtils.MyTrails, 'data', pres);
+        Doc.ActivePresentation = pres;
+        Doc.AddDocToList(Doc.MyTrails, 'data', pres);
     };
 
     @action
     createNewFolder = async () => {
         const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _stayInCollection: true, isFolder: true });
-        Doc.AddDocToList(CurrentUserUtils.MyFilesystem, 'data', folder);
+        Doc.AddDocToList(Doc.MyFilesystem, 'data', folder);
     };
 
     @observable _exploreMode = false;
@@ -612,9 +611,9 @@ export class MainView extends React.Component {
         setupMoveUpEvents(
             this,
             e,
-            action(e => ((CurrentUserUtils.propertiesWidth = Math.max(0, this._dashUIWidth - e.clientX)) ? false : false)),
-            action(() => CurrentUserUtils.propertiesWidth < 5 && (CurrentUserUtils.propertiesWidth = 0)),
-            action(() => (CurrentUserUtils.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._dashUIWidth - 50, 250) : 0)),
+            action(e => ((SettingsManager.propertiesWidth = Math.max(0, this._dashUIWidth - e.clientX)) ? false : false)),
+            action(() => SettingsManager.propertiesWidth < 5 && (SettingsManager.propertiesWidth = 0)),
+            action(() => (SettingsManager.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._dashUIWidth - 50, 250) : 0)),
             false
         );
     };
@@ -635,10 +634,10 @@ export class MainView extends React.Component {
     addDocTabFunc = (doc: Doc, location: string): boolean => {
         const locationFields = doc._viewType === CollectionViewType.Docking ? ['dashboard'] : location.split(':');
         const locationParams = locationFields.length > 1 ? locationFields[1] : '';
-        if (doc.dockingConfig) return CurrentUserUtils.openDashboard(doc);
+        if (doc.dockingConfig) return DashboardView.openDashboard(doc);
         switch (locationFields[0]) {
             case 'dashboard':
-                return CurrentUserUtils.openDashboard(doc);
+                return DashboardView.openDashboard(doc);
             case 'close':
                 return CollectionDockingView.CloseSplit(doc, locationParams);
             case 'fullScreen':
@@ -669,7 +668,7 @@ export class MainView extends React.Component {
                         addDocTab={this.addDocTabFunc}
                         pinToPres={emptyFunction}
                         docViewPath={returnEmptyDoclist}
-                        styleProvider={this._sidebarContent.proto === CurrentUserUtils.MyDashboards || this._sidebarContent.proto === CurrentUserUtils.MyFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
+                        styleProvider={this._sidebarContent.proto === Doc.MyDashboards || this._sidebarContent.proto === Doc.MyFilesystem ? DashboardStyleProvider : DefaultStyleProvider}
                         rootSelected={returnTrue}
                         removeDocument={returnFalse}
                         ScreenToLocalTransform={this.mainContainerXf}
@@ -697,7 +696,7 @@ export class MainView extends React.Component {
         return (
             <div key="menu" className="mainView-leftMenuPanel">
                 <DocumentView
-                    Document={CurrentUserUtils.MyLeftSidebarMenu}
+                    Document={Doc.MyLeftSidebarMenu}
                     DataDoc={undefined}
                     addDocument={undefined}
                     addDocTab={this.addDocTabFunc}
@@ -815,9 +814,9 @@ export class MainView extends React.Component {
         this._leftMenuFlyoutWidth = 0;
     });
 
-    remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(CurrentUserUtils.MyDockedBtns, 'data', doc), true);
+    remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.MyDockedBtns, 'data', doc), true);
     moveButtonDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => this.remButtonDoc(doc) && addDocument(doc);
-    addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(CurrentUserUtils.MyDockedBtns, 'data', doc), true);
+    addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(Doc.MyDockedBtns, 'data', doc), true);
 
     buttonBarXf = () => {
         if (!this._docBtnRef.current) return Transform.Identity();
@@ -826,10 +825,10 @@ export class MainView extends React.Component {
     };
 
     @computed get docButtons() {
-        return !CurrentUserUtils.MyDockedBtns ? null : (
-            <div className="mainView-docButtons" ref={this._docBtnRef} style={{ height: !CurrentUserUtils.MyDockedBtns.linearViewIsExpanded ? '42px' : undefined }}>
+        return !Doc.MyDockedBtns ? null : (
+            <div className="mainView-docButtons" ref={this._docBtnRef} style={{ height: !Doc.MyDockedBtns.linearViewIsExpanded ? '42px' : undefined }}>
                 <CollectionLinearView
-                    Document={CurrentUserUtils.MyDockedBtns}
+                    Document={Doc.MyDockedBtns}
                     DataDoc={undefined}
                     fieldKey={'data'}
                     dropAction={'alias'}
@@ -981,7 +980,7 @@ export class MainView extends React.Component {
                         case 'home':
                             return <DashboardView />;
                     }
-                })(CurrentUserUtils.ActivePage)}
+                })(Doc.ActivePage)}
 
                 <PreviewCursor />
                 <TaskCompletionBox />
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index 20b99788c..b01ee5f42 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -1,21 +1,20 @@
-import { action, observable, ObservableMap, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { AclAugment, AclAdmin, AclEdit, DataSym, Doc, Opt, AclSelfEdit } from "../../fields/Doc";
-import { Id } from "../../fields/FieldSymbols";
-import { List } from "../../fields/List";
-import { NumCast } from "../../fields/Types";
-import { GetEffectiveAcl } from "../../fields/util";
-import { unimplementedFunction, Utils } from "../../Utils";
-import { Docs } from "../documents/Documents";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { DragManager } from "../util/DragManager";
-import { undoBatch } from "../util/UndoManager";
-import "./MarqueeAnnotator.scss";
-import { DocumentView } from "./nodes/DocumentView";
-import { FormattedTextBox } from "./nodes/formattedText/FormattedTextBox";
-import { AnchorMenu } from "./pdf/AnchorMenu";
-import React = require("react");
-const _global = (window /* browser */ || global /* node */) as any;
+import { action, observable, ObservableMap, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DataSym, Doc, Opt } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { List } from '../../fields/List';
+import { NumCast } from '../../fields/Types';
+import { GetEffectiveAcl } from '../../fields/util';
+import { unimplementedFunction, Utils } from '../../Utils';
+import { Docs, DocUtils } from '../documents/Documents';
+import { DragManager } from '../util/DragManager';
+import { undoBatch } from '../util/UndoManager';
+import './MarqueeAnnotator.scss';
+import { DocumentView } from './nodes/DocumentView';
+import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
+import { AnchorMenu } from './pdf/AnchorMenu';
+import React = require('react');
+const _global = (window /* browser */ || global) /* node */ as any;
 
 export interface MarqueeAnnotatorProps {
     rootDoc: Doc;
@@ -46,7 +45,7 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
 
     @action
     static clearAnnotations(savedAnnotations: ObservableMap<number, HTMLDivElement[]>) {
-        AnchorMenu.Instance.Status = "marquee";
+        AnchorMenu.Instance.Status = 'marquee';
         AnchorMenu.Instance.fadeOut(true);
         // clear out old marquees and initialize menu for new selection
         Array.from(savedAnnotations.values()).forEach(v => v.forEach(a => a.remove()));
@@ -60,30 +59,30 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         this._startY = this._top = (this.props.down[1] - boundingRect.top) * (this.props.mainCont.offsetHeight / boundingRect.height) + this.props.mainCont.scrollTop;
         this._height = this._width = 0;
 
-        const doc = (this.props.iframe?.()?.contentDocument ?? document);
-        doc.addEventListener("pointermove", this.onSelectMove);
-        doc.addEventListener("pointerup", this.onSelectEnd);
+        const doc = this.props.iframe?.()?.contentDocument ?? document;
+        doc.addEventListener('pointermove', this.onSelectMove);
+        doc.addEventListener('pointerup', this.onSelectEnd);
 
-        AnchorMenu.Instance.OnCrop = (e: PointerEvent) => this.props.anchorMenuCrop?.(this.highlight("rgba(173, 216, 230, 0.75)", true), true);
-        AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight("rgba(173, 216, 230, 0.75)", true));
+        AnchorMenu.Instance.OnCrop = (e: PointerEvent) => this.props.anchorMenuCrop?.(this.highlight('rgba(173, 216, 230, 0.75)', true), true);
+        AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight('rgba(173, 216, 230, 0.75)', true));
         AnchorMenu.Instance.Highlight = this.highlight;
-        AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => this.highlight("rgba(173, 216, 230, 0.75)", true, savedAnnotations);
+        AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations);
         AnchorMenu.Instance.onMakeAnchor = AnchorMenu.Instance.GetAnchor;
 
         /**
-         * This function is used by the AnchorMenu to create an anchor highlight and a new linked text annotation.  
+         * This function is used by the AnchorMenu to create an anchor highlight and a new linked text annotation.
          * It also initiates a Drag/Drop interaction to place the text annotation.
          */
         AnchorMenu.Instance.StartDrag = action((e: PointerEvent, ele: HTMLElement) => {
             e.preventDefault();
             e.stopPropagation();
             const sourceAnchorCreator = () => {
-                const annoDoc = this.highlight("rgba(173, 216, 230, 0.75)", true); // hyperlink color
+                const annoDoc = this.highlight('rgba(173, 216, 230, 0.75)', true); // hyperlink color
                 annoDoc && this.props.addDocument(annoDoc);
                 return annoDoc;
             };
             const targetCreator = (annotationOn: Doc | undefined) => {
-                const target = CurrentUserUtils.GetNewTextDoc("Note linked to " + this.props.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn, undefined, "yellow");
+                const target = DocUtils.GetNewTextDoc('Note linked to ' + this.props.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow');
                 FormattedTextBox.SelectOnLoad = target[Id];
                 return target;
             };
@@ -92,37 +91,39 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
                     if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) {
                         e.annoDragData.linkSourceDoc.isPushpin = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc;
                     }
-                }
+                },
             });
         });
         /**
-         * This function is used by the AnchorMenu to create an anchor highlight and a new linked text annotation.  
+         * This function is used by the AnchorMenu to create an anchor highlight and a new linked text annotation.
          * It also initiates a Drag/Drop interaction to place the text annotation.
          */
-        AnchorMenu.Instance.StartCropDrag = !this.props.anchorMenuCrop ? unimplementedFunction : action((e: PointerEvent, ele: HTMLElement) => {
-            e.preventDefault();
-            e.stopPropagation();
-            var cropRegion: Doc | undefined;
-            const sourceAnchorCreator = () => {
-                cropRegion = this.highlight("rgba(173, 216, 230, 0.75)", true); // hyperlink color
-                cropRegion && this.props.addDocument(cropRegion);
-                return cropRegion;
-            };
-            const targetCreator = (annotationOn: Doc | undefined) => this.props.anchorMenuCrop!(cropRegion, false)!;
-            DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
-                dragComplete: e => {
-                    if (!e.aborted && e.linkDocument) {
-                        Doc.GetProto(e.linkDocument).linkRelationship = "cropped image";
-                        Doc.GetProto(e.linkDocument).title = "crop: " + this.props.docView.rootDoc.title;
-                    }
-                }
-            });
-        });
+        AnchorMenu.Instance.StartCropDrag = !this.props.anchorMenuCrop
+            ? unimplementedFunction
+            : action((e: PointerEvent, ele: HTMLElement) => {
+                  e.preventDefault();
+                  e.stopPropagation();
+                  var cropRegion: Doc | undefined;
+                  const sourceAnchorCreator = () => {
+                      cropRegion = this.highlight('rgba(173, 216, 230, 0.75)', true); // hyperlink color
+                      cropRegion && this.props.addDocument(cropRegion);
+                      return cropRegion;
+                  };
+                  const targetCreator = (annotationOn: Doc | undefined) => this.props.anchorMenuCrop!(cropRegion, false)!;
+                  DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
+                      dragComplete: e => {
+                          if (!e.aborted && e.linkDocument) {
+                              Doc.GetProto(e.linkDocument).linkRelationship = 'cropped image';
+                              Doc.GetProto(e.linkDocument).title = 'crop: ' + this.props.docView.rootDoc.title;
+                          }
+                      },
+                  });
+              });
     }
     componentWillUnmount() {
-        const doc = (this.props.iframe?.()?.contentDocument ?? document);
-        doc.removeEventListener("pointermove", this.onSelectMove);
-        doc.removeEventListener("pointerup", this.onSelectEnd);
+        const doc = this.props.iframe?.()?.contentDocument ?? document;
+        doc.removeEventListener('pointermove', this.onSelectMove);
+        doc.removeEventListener('pointerup', this.onSelectEnd);
     }
 
     @undoBatch
@@ -132,40 +133,42 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         if (savedAnnoMap.size === 0) return undefined;
         const savedAnnos = Array.from(savedAnnoMap.values())[0];
         if (savedAnnos.length && (savedAnnos[0] as any).marqueeing) {
-            const scale = (this.props.scaling?.() || 1);
+            const scale = this.props.scaling?.() || 1;
             const anno = savedAnnos[0];
             const containerOffset = this.props.containerOffset?.() || [0, 0];
-            const marqueeAnno = Docs.Create.FreeformDocument([], { _isLinkButton: isLinkButton, backgroundColor: color, annotationOn: this.props.rootDoc, title: "Annotation on " + this.props.rootDoc.title });
-            marqueeAnno.x = NumCast(this.props.docView.props.Document.panXMin) + (parseInt(anno.style.left || "0") - containerOffset[0]) / scale/ NumCast(this.props.docView.props.Document._viewScale,1);
-            marqueeAnno.y = NumCast(this.props.docView.props.Document.panYMin) + (parseInt(anno.style.top || "0") - containerOffset[1]) / scale/ NumCast(this.props.docView.props.Document._viewScale,1) + NumCast(this.props.scrollTop);
-            marqueeAnno._height = parseInt(anno.style.height || "0") / scale/ NumCast(this.props.docView.props.Document._viewScale,1);
-            marqueeAnno._width = parseInt(anno.style.width || "0") / scale/ NumCast(this.props.docView.props.Document._viewScale,1);
+            const marqueeAnno = Docs.Create.FreeformDocument([], { _isLinkButton: isLinkButton, backgroundColor: color, annotationOn: this.props.rootDoc, title: 'Annotation on ' + this.props.rootDoc.title });
+            marqueeAnno.x = NumCast(this.props.docView.props.Document.panXMin) + (parseInt(anno.style.left || '0') - containerOffset[0]) / scale / NumCast(this.props.docView.props.Document._viewScale, 1);
+            marqueeAnno.y = NumCast(this.props.docView.props.Document.panYMin) + (parseInt(anno.style.top || '0') - containerOffset[1]) / scale / NumCast(this.props.docView.props.Document._viewScale, 1) + NumCast(this.props.scrollTop);
+            marqueeAnno._height = parseInt(anno.style.height || '0') / scale / NumCast(this.props.docView.props.Document._viewScale, 1);
+            marqueeAnno._width = parseInt(anno.style.width || '0') / scale / NumCast(this.props.docView.props.Document._viewScale, 1);
             anno.remove();
             savedAnnoMap.clear();
             return marqueeAnno;
         }
 
-        const textRegionAnno = Docs.Create.HTMLAnchorDocument([], { annotationOn: this.props.rootDoc, backgroundColor: "transparent", title: "Selection on " + this.props.rootDoc.title });
+        const textRegionAnno = Docs.Create.HTMLAnchorDocument([], { annotationOn: this.props.rootDoc, backgroundColor: 'transparent', title: 'Selection on ' + this.props.rootDoc.title });
         let minX = Number.MAX_VALUE;
         let maxX = -Number.MAX_VALUE;
         let minY = Number.MAX_VALUE;
         let maxY = -Number.MIN_VALUE;
         const annoDocs: Doc[] = [];
-        savedAnnoMap.forEach((value: HTMLDivElement[], key: number) => value.map(anno => {
-            const textRegion = new Doc();
-            textRegion.x = parseInt(anno.style.left ?? "0");
-            textRegion.y = parseInt(anno.style.top ?? "0");
-            textRegion._height = parseInt(anno.style.height ?? "0");
-            textRegion._width = parseInt(anno.style.width ?? "0");
-            textRegion.annoTextRegion = textRegionAnno;
-            textRegion.backgroundColor = color;
-            annoDocs.push(textRegion);
-            anno.remove();
-            minY = Math.min(NumCast(textRegion.y), minY);
-            minX = Math.min(NumCast(textRegion.x), minX);
-            maxY = Math.max(NumCast(textRegion.y) + NumCast(textRegion._height), maxY);
-            maxX = Math.max(NumCast(textRegion.x) + NumCast(textRegion._width), maxX);
-        }));
+        savedAnnoMap.forEach((value: HTMLDivElement[], key: number) =>
+            value.map(anno => {
+                const textRegion = new Doc();
+                textRegion.x = parseInt(anno.style.left ?? '0');
+                textRegion.y = parseInt(anno.style.top ?? '0');
+                textRegion._height = parseInt(anno.style.height ?? '0');
+                textRegion._width = parseInt(anno.style.width ?? '0');
+                textRegion.annoTextRegion = textRegionAnno;
+                textRegion.backgroundColor = color;
+                annoDocs.push(textRegion);
+                anno.remove();
+                minY = Math.min(NumCast(textRegion.y), minY);
+                minX = Math.min(NumCast(textRegion.x), minX);
+                maxY = Math.max(NumCast(textRegion.y) + NumCast(textRegion._height), maxY);
+                maxX = Math.max(NumCast(textRegion.x) + NumCast(textRegion._width), maxX);
+            })
+        );
 
         const textRegionAnnoProto = Doc.GetProto(textRegionAnno);
         textRegionAnnoProto.y = Math.max(minY, 0);
@@ -176,29 +179,29 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         textRegionAnnoProto.textInlineAnnotations = new List<Doc>(annoDocs);
         savedAnnoMap.clear();
         return textRegionAnno;
-    }
+    };
     @action
     highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => {
         // creates annotation documents for current highlights
         const effectiveAcl = GetEffectiveAcl(this.props.rootDoc[DataSym]);
         const annotationDoc = [AclAugment, AclSelfEdit, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations);
         !savedAnnotations && annotationDoc && this.props.addDocument(annotationDoc);
-        return annotationDoc as Doc ?? undefined;
-    }
+        return (annotationDoc as Doc) ?? undefined;
+    };
 
     public static previewNewAnnotation = action((savedAnnotations: ObservableMap<number, HTMLDivElement[]>, annotationLayer: HTMLDivElement, div: HTMLDivElement, page: number) => {
         if (div.style.top) {
-            div.style.top = (parseInt(div.style.top)/*+ this.getScrollFromPage(page)*/).toString();
+            div.style.top = parseInt(div.style.top) /*+ this.getScrollFromPage(page)*/
+                .toString();
         }
         annotationLayer.append(div);
-        div.style.backgroundColor = "#ACCEF7";
-        div.style.opacity = "0.5";
+        div.style.backgroundColor = '#ACCEF7';
+        div.style.opacity = '0.5';
         const savedPage = savedAnnotations.get(page);
         if (savedPage) {
             savedPage.push(div);
             savedAnnotations.set(page, savedPage);
-        }
-        else {
+        } else {
             savedAnnotations.set(page, [div]);
         }
     });
@@ -210,58 +213,65 @@ export class MarqueeAnnotator extends React.Component<MarqueeAnnotatorProps> {
         const mainRect = this.props.mainCont.getBoundingClientRect();
         const cliX = e.clientX * (this.props.iframeScaling?.() || 1) - boundingRect.left;
         const cliY = e.clientY * (this.props.iframeScaling?.() || 1) - boundingRect.top;
-        this._width = (cliX * (this.props.mainCont.offsetWidth / mainRect.width)) - this._startX;
-        this._height = (cliY * (this.props.mainCont.offsetHeight / mainRect.height)) - this._startY + this.props.mainCont.scrollTop;
+        this._width = cliX * (this.props.mainCont.offsetWidth / mainRect.width) - this._startX;
+        this._height = cliY * (this.props.mainCont.offsetHeight / mainRect.height) - this._startY + this.props.mainCont.scrollTop;
         this._left = Math.min(this._startX, this._startX + this._width);
         this._top = Math.min(this._startY, this._startY + this._height);
         this._width = Math.abs(this._width);
         this._height = Math.abs(this._height);
         e.stopPropagation();
-    }
+    };
 
     onSelectEnd = (e: PointerEvent) => {
         const mainRect = this.props.mainCont.getBoundingClientRect();
         const cliX = e.clientX * (this.props.iframeScaling?.() || 1) + (this.props.iframe ? mainRect.left : 0);
         const cliY = e.clientY * (this.props.iframeScaling?.() || 1) + (this.props.iframe ? mainRect.top : 0);
-        if (this._width > 10 || this._height > 10) {  // configure and show the annotation/link menu if a the drag region is big enough
-            const marquees = this.props.mainCont.getElementsByClassName("marqueeAnnotator-dragBox");
-            if (marquees?.length) { // copy the temporary marquee to allow for multiple selections (not currently available though).
-                const copy = document.createElement("div");
-                ["border", "opacity"].forEach(prop => copy.style[prop as any] = (marquees[0] as HTMLDivElement).style[prop as any]);
+        if (this._width > 10 || this._height > 10) {
+            // configure and show the annotation/link menu if a the drag region is big enough
+            const marquees = this.props.mainCont.getElementsByClassName('marqueeAnnotator-dragBox');
+            if (marquees?.length) {
+                // copy the temporary marquee to allow for multiple selections (not currently available though).
+                const copy = document.createElement('div');
+                ['border', 'opacity'].forEach(prop => (copy.style[prop as any] = (marquees[0] as HTMLDivElement).style[prop as any]));
                 const bounds = (marquees[0] as HTMLDivElement).getBoundingClientRect();
                 const uitls = Utils.GetScreenTransform(marquees[0] as HTMLDivElement);
-                const rbounds = { top: uitls.translateY, left: uitls.translateX, width: (bounds.right - bounds.left), height: (bounds.bottom - bounds.top) };
+                const rbounds = { top: uitls.translateY, left: uitls.translateX, width: bounds.right - bounds.left, height: bounds.bottom - bounds.top };
                 const otls = Utils.GetScreenTransform(this.props.annotationLayer);
                 const fbounds = { top: (rbounds.top - otls.translateY) / otls.scale, left: (rbounds.left - otls.translateX) / otls.scale, width: rbounds.width / otls.scale, height: rbounds.height / otls.scale };
-                copy.style.top = fbounds.top.toString() + "px";
-                copy.style.left = fbounds.left.toString() + "px";
-                copy.style.width = fbounds.width.toString() + "px";
-                copy.style.height = fbounds.height.toString() + "px";
-                copy.className = "marqueeAnnotator-annotationBox";
+                copy.style.top = fbounds.top.toString() + 'px';
+                copy.style.left = fbounds.left.toString() + 'px';
+                copy.style.width = fbounds.width.toString() + 'px';
+                copy.style.height = fbounds.height.toString() + 'px';
+                copy.className = 'marqueeAnnotator-annotationBox';
                 (copy as any).marqueeing = true;
                 MarqueeAnnotator.previewNewAnnotation(this.props.savedAnnotations(), this.props.annotationLayer, copy, this.props.getPageFromScroll?.(this._top) || 0);
             }
 
             AnchorMenu.Instance.jumpTo(cliX, cliY);
 
-            if (AnchorMenu.Instance.Highlighting) {// when highlighter has been toggled when menu is pinned, we auto-highlight immediately on mouse up
-                this.highlight("rgba(245, 230, 95, 0.75)", false);  // yellowish highlight color for highlighted text (should match AnchorMenu's highlight color)
+            if (AnchorMenu.Instance.Highlighting) {
+                // when highlighter has been toggled when menu is pinned, we auto-highlight immediately on mouse up
+                this.highlight('rgba(245, 230, 95, 0.75)', false); // yellowish highlight color for highlighted text (should match AnchorMenu's highlight color)
             }
             this.props.finishMarquee(undefined, undefined, e);
         } else {
-            runInAction(() => this._width = this._height = 0);
+            runInAction(() => (this._width = this._height = 0));
             this.props.finishMarquee(cliX, cliY, e);
         }
-    }
+    };
 
     render() {
-        return <div className="marqueeAnnotator-dragBox"
-            style={{
-                left: `${this._left}px`, top: `${this._top}px`,
-                width: `${this._width}px`, height: `${this._height}px`,
-                border: `${this._width === 0 ? "" : "2px dashed black"}`,
-                opacity: 0.2
-            }}>
-        </div>;
+        return (
+            <div
+                className="marqueeAnnotator-dragBox"
+                style={{
+                    left: `${this._left}px`,
+                    top: `${this._top}px`,
+                    width: `${this._width}px`,
+                    height: `${this._height}px`,
+                    border: `${this._width === 0 ? '' : '2px dashed black'}`,
+                    opacity: 0.2,
+                }}></div>
+        );
     }
 }
diff --git a/src/client/views/OverlayView.tsx b/src/client/views/OverlayView.tsx
index 598fff29a..5242fabb8 100644
--- a/src/client/views/OverlayView.tsx
+++ b/src/client/views/OverlayView.tsx
@@ -1,23 +1,21 @@
-import { docs } from "googleapis/build/src/apis/docs";
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import { computedFn } from "mobx-utils";
-import * as React from "react";
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import { computedFn } from 'mobx-utils';
+import * as React from 'react';
 import ReactLoading from 'react-loading';
-import { Doc, WidthSym, HeightSym, DocListCast } from "../../fields/Doc";
-import { Id } from "../../fields/FieldSymbols";
-import { Cast, NumCast } from "../../fields/Types";
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents, Utils } from "../../Utils";
-import { DocUtils } from "../documents/Documents";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { DragManager } from "../util/DragManager";
-import { ScriptingGlobals } from "../util/ScriptingGlobals";
-import { Transform } from "../util/Transform";
-import { CollectionFreeFormLinksView } from "./collections/collectionFreeForm/CollectionFreeFormLinksView";
-import { DocumentView } from "./nodes/DocumentView";
+import { Doc, DocListCast, HeightSym, WidthSym } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { NumCast } from '../../fields/Types';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../Utils';
+import { DocUtils } from '../documents/Documents';
+import { DragManager } from '../util/DragManager';
+import { ScriptingGlobals } from '../util/ScriptingGlobals';
+import { Transform } from '../util/Transform';
+import { CollectionFreeFormLinksView } from './collections/collectionFreeForm/CollectionFreeFormLinksView';
+import { DocumentView } from './nodes/DocumentView';
 import './OverlayView.scss';
 import { ScriptingRepl } from './ScriptingRepl';
-import { DefaultStyleProvider } from "./StyleProvider";
+import { DefaultStyleProvider } from './StyleProvider';
 
 export type OverlayDisposer = () => void;
 
@@ -52,18 +50,18 @@ export class OverlayWindow extends React.Component<OverlayWindowProps> {
     }
 
     onPointerDown = (_: React.PointerEvent) => {
-        document.removeEventListener("pointermove", this.onPointerMove);
-        document.removeEventListener("pointerup", this.onPointerUp);
-        document.addEventListener("pointermove", this.onPointerMove);
-        document.addEventListener("pointerup", this.onPointerUp);
-    }
+        document.removeEventListener('pointermove', this.onPointerMove);
+        document.removeEventListener('pointerup', this.onPointerUp);
+        document.addEventListener('pointermove', this.onPointerMove);
+        document.addEventListener('pointerup', this.onPointerUp);
+    };
 
     onResizerPointerDown = (_: React.PointerEvent) => {
-        document.removeEventListener("pointermove", this.onResizerPointerMove);
-        document.removeEventListener("pointerup", this.onResizerPointerUp);
-        document.addEventListener("pointermove", this.onResizerPointerMove);
-        document.addEventListener("pointerup", this.onResizerPointerUp);
-    }
+        document.removeEventListener('pointermove', this.onResizerPointerMove);
+        document.removeEventListener('pointerup', this.onResizerPointerUp);
+        document.addEventListener('pointermove', this.onResizerPointerMove);
+        document.addEventListener('pointerup', this.onResizerPointerUp);
+    };
 
     @action
     onPointerMove = (e: PointerEvent) => {
@@ -71,7 +69,7 @@ export class OverlayWindow extends React.Component<OverlayWindowProps> {
         this.x = Math.max(Math.min(this.x, window.innerWidth - this.width), 0);
         this.y += e.movementY;
         this.y = Math.max(Math.min(this.y, window.innerHeight - this.height), 0);
-    }
+    };
 
     @action
     onResizerPointerMove = (e: PointerEvent) => {
@@ -79,28 +77,28 @@ export class OverlayWindow extends React.Component<OverlayWindowProps> {
         this.width = Math.max(this.width, 30);
         this.height += e.movementY;
         this.height = Math.max(this.height, 30);
-    }
+    };
 
     onPointerUp = (e: PointerEvent) => {
-        document.removeEventListener("pointermove", this.onPointerMove);
-        document.removeEventListener("pointerup", this.onPointerUp);
-    }
+        document.removeEventListener('pointermove', this.onPointerMove);
+        document.removeEventListener('pointerup', this.onPointerUp);
+    };
 
     onResizerPointerUp = (e: PointerEvent) => {
-        document.removeEventListener("pointermove", this.onResizerPointerMove);
-        document.removeEventListener("pointerup", this.onResizerPointerUp);
-    }
+        document.removeEventListener('pointermove', this.onResizerPointerMove);
+        document.removeEventListener('pointerup', this.onResizerPointerUp);
+    };
 
     render() {
         return (
             <div className="overlayWindow-outerDiv" style={{ transform: `translate(${this.x}px, ${this.y}px)`, width: this.width, height: this.height }}>
-                <div className="overlayWindow-titleBar" onPointerDown={this.onPointerDown} >
-                    {this.props.overlayOptions.title || "Untitled"}
-                    <button onClick={this.props.onClick} className="overlayWindow-closeButton">X</button>
-                </div>
-                <div className="overlayWindow-content">
-                    {this.props.children}
+                <div className="overlayWindow-titleBar" onPointerDown={this.onPointerDown}>
+                    {this.props.overlayOptions.title || 'Untitled'}
+                    <button onClick={this.props.onClick} className="overlayWindow-closeButton">
+                        X
+                    </button>
                 </div>
+                <div className="overlayWindow-content">{this.props.children}</div>
                 <div className="overlayWindow-resizeDragger" onPointerDown={this.onResizerPointerDown}></div>
             </div>
         );
@@ -126,13 +124,20 @@ export class OverlayView extends React.Component {
             const index = this._elements.indexOf(ele);
             if (index !== -1) this._elements.splice(index, 1);
         });
-        ele = <div key={Utils.GenerateGuid()} className="overlayView-wrapperDiv" style={{
-            transform: `translate(${options.x}px, ${options.y}px)`,
-            width: options.width,
-            height: options.height,
-            top: 0,
-            left: 0
-        }}>{ele}</div>;
+        ele = (
+            <div
+                key={Utils.GenerateGuid()}
+                className="overlayView-wrapperDiv"
+                style={{
+                    transform: `translate(${options.x}px, ${options.y}px)`,
+                    width: options.width,
+                    height: options.height,
+                    top: 0,
+                    left: 0,
+                }}>
+                {ele}
+            </div>
+        );
         this._elements.push(ele);
         return remove;
     }
@@ -143,23 +148,30 @@ export class OverlayView extends React.Component {
             const index = this._elements.indexOf(contents);
             if (index !== -1) this._elements.splice(index, 1);
         });
-        contents = <OverlayWindow onClick={remove} key={Utils.GenerateGuid()} overlayOptions={options}>{contents}</OverlayWindow>;
+        contents = (
+            <OverlayWindow onClick={remove} key={Utils.GenerateGuid()} overlayOptions={options}>
+                {contents}
+            </OverlayWindow>
+        );
         this._elements.push(contents);
         return remove;
     }
 
     removeOverlayDoc = (doc: Doc | Doc[]) => {
-        (doc instanceof Doc ? [doc] : doc).forEach(doc => Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, doc));
+        (doc instanceof Doc ? [doc] : doc).forEach(doc => Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, doc));
         return true;
-    }
-    
-    docScreenToLocalXf = computedFn(function docScreenToLocalXf(this: any, doc: Doc) {
-        return () => new Transform(-NumCast(doc.x), -NumCast(doc.y), 1);
-    }.bind(this));
+    };
+
+    docScreenToLocalXf = computedFn(
+        function docScreenToLocalXf(this: any, doc: Doc) {
+            return () => new Transform(-NumCast(doc.x), -NumCast(doc.y), 1);
+        }.bind(this)
+    );
 
     @computed get overlayDocs() {
-        return DocListCast(CurrentUserUtils.MyOverlayDocs?.data).map(d => {
-            let offsetx = 0, offsety = 0;
+        return DocListCast(Doc.MyOverlayDocs?.data).map(d => {
+            let offsetx = 0,
+                offsety = 0;
             const dref = React.createRef<HTMLDivElement>();
             const onPointerMove = action((e: PointerEvent, down: number[]) => {
                 if (e.buttons === 1) {
@@ -169,10 +181,10 @@ export class OverlayView extends React.Component {
                 if (e.metaKey) {
                     const dragData = new DragManager.DocumentDragData([d]);
                     dragData.offset = [-offsetx, -offsety];
-                    dragData.dropAction = "move";
+                    dragData.dropAction = 'move';
                     dragData.removeDocument = (doc: Doc | Doc[]) => {
-                        const docs = (doc instanceof Doc) ? [doc] : doc;
-                        docs.forEach(d => Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, d));
+                        const docs = doc instanceof Doc ? [doc] : doc;
+                        docs.forEach(d => Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, d));
                         return true;
                     };
                     dragData.moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean): boolean => {
@@ -189,31 +201,39 @@ export class OverlayView extends React.Component {
                 offsetx = NumCast(d.x) - e.clientX;
                 offsety = NumCast(d.y) - e.clientY;
             };
-            return <div className="overlayView-doc" ref={dref} key={d[Id]} onPointerDown={onPointerDown} style={{ top: d.type === 'presentation' ? 0 : undefined, width: NumCast(d._width), height: NumCast(d._height), transform: `translate(${d.x}px, ${d.y}px)` }}>
-                <DocumentView
-                    Document={d}
-                    rootSelected={returnTrue}
-                    bringToFront={emptyFunction}
-                    addDocument={undefined}
-                    removeDocument={this.removeOverlayDoc}
-                    PanelWidth={d[WidthSym]}
-                    PanelHeight={d[HeightSym]}
-                    ScreenToLocalTransform={this.docScreenToLocalXf(d)}
-                    renderDepth={1}
-                    isDocumentActive={returnTrue}
-                    isContentActive={returnTrue}
-                    whenChildContentsActiveChanged={emptyFunction}
-                    focus={DocUtils.DefaultFocus}
-                    styleProvider={DefaultStyleProvider}
-                    docViewPath={returnEmptyDoclist}
-                    addDocTab={returnFalse}
-                    pinToPres={emptyFunction}
-                    docFilters={returnEmptyFilter}
-                    docRangeFilters={returnEmptyFilter}
-                    searchFilterDocs={returnEmptyDoclist}
-                    ContainingCollectionView={undefined}
-                    ContainingCollectionDoc={undefined} />
-            </div>;
+            return (
+                <div
+                    className="overlayView-doc"
+                    ref={dref}
+                    key={d[Id]}
+                    onPointerDown={onPointerDown}
+                    style={{ top: d.type === 'presentation' ? 0 : undefined, width: NumCast(d._width), height: NumCast(d._height), transform: `translate(${d.x}px, ${d.y}px)` }}>
+                    <DocumentView
+                        Document={d}
+                        rootSelected={returnTrue}
+                        bringToFront={emptyFunction}
+                        addDocument={undefined}
+                        removeDocument={this.removeOverlayDoc}
+                        PanelWidth={d[WidthSym]}
+                        PanelHeight={d[HeightSym]}
+                        ScreenToLocalTransform={this.docScreenToLocalXf(d)}
+                        renderDepth={1}
+                        isDocumentActive={returnTrue}
+                        isContentActive={returnTrue}
+                        whenChildContentsActiveChanged={emptyFunction}
+                        focus={DocUtils.DefaultFocus}
+                        styleProvider={DefaultStyleProvider}
+                        docViewPath={returnEmptyDoclist}
+                        addDocTab={returnFalse}
+                        pinToPres={emptyFunction}
+                        docFilters={returnEmptyFilter}
+                        docRangeFilters={returnEmptyFilter}
+                        searchFilterDocs={returnEmptyDoclist}
+                        ContainingCollectionView={undefined}
+                        ContainingCollectionDoc={undefined}
+                    />
+                </div>
+            );
         });
     }
 
@@ -221,13 +241,10 @@ export class OverlayView extends React.Component {
         return OverlayView.Instance.addElement(<ReactLoading type="spinningBubbles" color="green" height={250} width={250} />, { x: 300, y: 200 });
     }
 
-
     render() {
         return (
             <div className="overlayView" id="overlayView">
-                <div>
-                    {this._elements}
-                </div>
+                <div>{this._elements}</div>
                 <CollectionFreeFormLinksView key="freeformLinks" />
                 {this.overlayDocs}
             </div>
@@ -237,4 +254,4 @@ export class OverlayView extends React.Component {
 // bcz: ugh ... want to be able to pass ScriptingRepl as tag argument, but that doesn't seem to work.. runtime error
 ScriptingGlobals.add(function addOverlayWindow(type: string, options: OverlayElementOptions) {
     OverlayView.Instance.addWindow(<ScriptingRepl />, options);
-});
\ No newline at end of file
+});
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index ef1360ef1..68f5f072d 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -1,17 +1,16 @@
 import { action, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
-import "normalize.css";
+import 'normalize.css';
 import * as React from 'react';
 import { Doc } from '../../fields/Doc';
 import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { returnFalse } from '../../Utils';
 import { DocServer } from '../DocServer';
 import { Docs, DocUtils } from '../documents/Documents';
-import { CurrentUserUtils } from '../util/CurrentUserUtils';
-import { Transform } from "../util/Transform";
+import { Transform } from '../util/Transform';
 import { undoBatch, UndoManager } from '../util/UndoManager';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
-import "./PreviewCursor.scss";
-import { returnFalse } from '../../Utils';
+import './PreviewCursor.scss';
 
 @observer
 export class PreviewCursor extends React.Component<{}> {
@@ -24,47 +23,66 @@ export class PreviewCursor extends React.Component<{}> {
     @observable public static Visible = false;
     constructor(props: any) {
         super(props);
-        document.addEventListener("keydown", this.onKeyPress);
-        document.addEventListener("paste", this.paste);
+        document.addEventListener('keydown', this.onKeyPress);
+        document.addEventListener('paste', this.paste);
     }
 
     paste = async (e: ClipboardEvent) => {
         if (PreviewCursor.Visible && e.clipboardData) {
             const newPoint = PreviewCursor._getTransform().transformPoint(PreviewCursor._clickPoint[0], PreviewCursor._clickPoint[1]);
-            runInAction(() => PreviewCursor.Visible = false);
+            runInAction(() => (PreviewCursor.Visible = false));
 
             // tests for URL and makes web document
             const re: any = /^https?:\/\//g;
-            const plain = e.clipboardData.getData("text/plain");
+            const plain = e.clipboardData.getData('text/plain');
             if (plain) {
                 // tests for youtube and makes video document
-                if (plain.indexOf("www.youtube.com/watch") !== -1) {
-                    const url = plain.replace("youtube.com/watch?v=", "youtube.com/embed/");
-                    undoBatch(() => PreviewCursor._addDocument(Docs.Create.VideoDocument(url, {
-                        title: url, _width: 400, _height: 315, _nativeWidth: 600, _nativeHeight: 472.5,
-                        x: newPoint[0], y: newPoint[1]
-                    })))();
-                }
-
-                else if (re.test(plain)) {
+                if (plain.indexOf('www.youtube.com/watch') !== -1) {
+                    const url = plain.replace('youtube.com/watch?v=', 'youtube.com/embed/');
+                    undoBatch(() =>
+                        PreviewCursor._addDocument(
+                            Docs.Create.VideoDocument(url, {
+                                title: url,
+                                _width: 400,
+                                _height: 315,
+                                _nativeWidth: 600,
+                                _nativeHeight: 472.5,
+                                x: newPoint[0],
+                                y: newPoint[1],
+                            })
+                        )
+                    )();
+                } else if (re.test(plain)) {
                     const url = plain;
-                    undoBatch(() => PreviewCursor._addDocument(Docs.Create.WebDocument(url, {
-                        title: url, _width: 500, _height: 300, useCors: true, x: newPoint[0], y: newPoint[1]
-                    })))();
-                }
-                else if (plain.startsWith("__DashDocId(") || plain.startsWith("__DashCloneId(")) {
-                    const clone = plain.startsWith("__DashCloneId(");
-                    const docids = plain.split(":");
-                    const strs = docids[0].split(",");
-                    const ptx = Number(strs[0].substring((clone ? "__DashCloneId(" : "__DashDocId(").length));
+                    undoBatch(() =>
+                        PreviewCursor._addDocument(
+                            Docs.Create.WebDocument(url, {
+                                title: url,
+                                _width: 500,
+                                _height: 300,
+                                useCors: true,
+                                x: newPoint[0],
+                                y: newPoint[1],
+                            })
+                        )
+                    )();
+                } else if (plain.startsWith('__DashDocId(') || plain.startsWith('__DashCloneId(')) {
+                    const clone = plain.startsWith('__DashCloneId(');
+                    const docids = plain.split(':');
+                    const strs = docids[0].split(',');
+                    const ptx = Number(strs[0].substring((clone ? '__DashCloneId(' : '__DashDocId(').length));
                     const pty = Number(strs[1].substring(0, strs[1].length - 1));
 
-                    const batch = UndoManager.StartBatch("cloning");
+                    const batch = UndoManager.StartBatch('cloning');
                     {
-                        const docs = await Promise.all(docids.filter((did, i) => i).map(async (did) => {
-                            const doc = Cast(await DocServer.GetRefField(did), Doc, null);
-                            return clone ? (await Doc.MakeClone(doc)).clone : doc;
-                        }));
+                        const docs = await Promise.all(
+                            docids
+                                .filter((did, i) => i)
+                                .map(async did => {
+                                    const doc = Cast(await DocServer.GetRefField(did), Doc, null);
+                                    return clone ? (await Doc.MakeClone(doc)).clone : doc;
+                                })
+                        );
                         const firstx = docs.length ? NumCast(docs[0].x) + ptx - newPoint[0] : 0;
                         const firsty = docs.length ? NumCast(docs[0].y) + pty - newPoint[1] : 0;
                         docs.map(doc => {
@@ -75,79 +93,99 @@ export class PreviewCursor extends React.Component<{}> {
                     }
                     batch.end();
                     e.stopPropagation();
-                }
-                else {
+                } else {
                     // creates text document
                     FormattedTextBox.PasteOnLoad = e;
-                    UndoManager.RunInBatch(() => PreviewCursor._addLiveTextDoc(CurrentUserUtils.GetNewTextDoc("-pasted text-", newPoint[0], newPoint[1], 500, undefined, undefined, undefined, 750)), "paste");
+                    UndoManager.RunInBatch(() => PreviewCursor._addLiveTextDoc(DocUtils.GetNewTextDoc('-pasted text-', newPoint[0], newPoint[1], 500, undefined, undefined, undefined, 750)), 'paste');
                 }
-            } else
-                //pasting in images
-                if (e.clipboardData.getData("text/html") !== "" && e.clipboardData.getData("text/html").includes("<img src=")) {
-                    const re: any = /<img src="(.*?)"/g;
-                    const arr: any[] = re.exec(e.clipboardData.getData("text/html"));
+            }
+            //pasting in images
+            else if (e.clipboardData.getData('text/html') !== '' && e.clipboardData.getData('text/html').includes('<img src=')) {
+                const re: any = /<img src="(.*?)"/g;
+                const arr: any[] = re.exec(e.clipboardData.getData('text/html'));
 
-                    undoBatch(() => PreviewCursor._addDocument(Docs.Create.ImageDocument(
-                        arr[1], {
-                        _width: 300, title: arr[1],
-                        x: newPoint[0],
-                        y: newPoint[1],
-                    })))();
-                } else if (e.clipboardData.items.length) {
-                    const batch = UndoManager.StartBatch("collection view drop");
-                    const files: File[] = [];
-                    Array.from(e.clipboardData.items).forEach(item => {
-                        const file = item.getAsFile();
-                        file && files.push(file);
-                    });
-                    const generatedDocuments = await DocUtils.uploadFilesToDocs(files, { x: newPoint[0], y: newPoint[1] });
-                    generatedDocuments.forEach(PreviewCursor._addDocument);
-                    batch.end();
-                }
+                undoBatch(() =>
+                    PreviewCursor._addDocument(
+                        Docs.Create.ImageDocument(arr[1], {
+                            _width: 300,
+                            title: arr[1],
+                            x: newPoint[0],
+                            y: newPoint[1],
+                        })
+                    )
+                )();
+            } else if (e.clipboardData.items.length) {
+                const batch = UndoManager.StartBatch('collection view drop');
+                const files: File[] = [];
+                Array.from(e.clipboardData.items).forEach(item => {
+                    const file = item.getAsFile();
+                    file && files.push(file);
+                });
+                const generatedDocuments = await DocUtils.uploadFilesToDocs(files, { x: newPoint[0], y: newPoint[1] });
+                generatedDocuments.forEach(PreviewCursor._addDocument);
+                batch.end();
+            }
         }
-    }
+    };
 
     @action
     onKeyPress = (e: KeyboardEvent) => {
-        // Mixing events between React and Native is finicky. 
+        // Mixing events between React and Native is finicky.
         //if not these keys, make a textbox if preview cursor is active!
-        if (e.key !== "Escape" && e.key !== "Backspace" && e.key !== "Delete" && e.key !== "CapsLock" &&
-            e.key !== "Alt" && e.key !== "Shift" && e.key !== "Meta" && e.key !== "Control" &&
-            e.key !== "Insert" && e.key !== "Home" && e.key !== "End" && e.key !== "PageUp" && e.key !== "PageDown" &&
-            e.key !== "NumLock" && e.key !== " " &&
+        if (
+            e.key !== 'Escape' &&
+            e.key !== 'Backspace' &&
+            e.key !== 'Delete' &&
+            e.key !== 'CapsLock' &&
+            e.key !== 'Alt' &&
+            e.key !== 'Shift' &&
+            e.key !== 'Meta' &&
+            e.key !== 'Control' &&
+            e.key !== 'Insert' &&
+            e.key !== 'Home' &&
+            e.key !== 'End' &&
+            e.key !== 'PageUp' &&
+            e.key !== 'PageDown' &&
+            e.key !== 'NumLock' &&
+            e.key !== ' ' &&
             (e.keyCode < 112 || e.keyCode > 123) && // F1 thru F12 keys
-            (e.keyCode < 173 || e.keyCode > 183 || e.key === "-") && // mute, volume up/down etc, - is there specifically because its keycode is 173 in Firefox so shouldn't be avoided
-            !e.key.startsWith("Arrow") &&
-            !e.defaultPrevented) {
-            if ((!e.metaKey && !e.ctrlKey) || (e.keyCode >= 48 && e.keyCode <= 57) || (e.keyCode >= 65 && e.keyCode <= 90)) {//  /^[a-zA-Z0-9$*^%#@+-=_|}{[]"':;?/><.,}]$/.test(e.key)) {
+            (e.keyCode < 173 || e.keyCode > 183 || e.key === '-') && // mute, volume up/down etc, - is there specifically because its keycode is 173 in Firefox so shouldn't be avoided
+            !e.key.startsWith('Arrow') &&
+            !e.defaultPrevented
+        ) {
+            if ((!e.metaKey && !e.ctrlKey) || (e.keyCode >= 48 && e.keyCode <= 57) || (e.keyCode >= 65 && e.keyCode <= 90)) {
+                //  /^[a-zA-Z0-9$*^%#@+-=_|}{[]"':;?/><.,}]$/.test(e.key)) {
                 PreviewCursor.Visible && PreviewCursor._onKeyPress?.(e);
-                ((!e.ctrlKey && !e.metaKey) || e.key !== "v") && (PreviewCursor.Visible = false);
+                ((!e.ctrlKey && !e.metaKey) || e.key !== 'v') && (PreviewCursor.Visible = false);
             }
         } else if (PreviewCursor.Visible) {
-            if (e.key === "ArrowRight") {
+            if (e.key === 'ArrowRight') {
                 PreviewCursor._nudge?.(1 * (e.shiftKey ? 2 : 1), 0) && e.stopPropagation();
-            } else if (e.key === "ArrowLeft") {
+            } else if (e.key === 'ArrowLeft') {
                 PreviewCursor._nudge?.(-1 * (e.shiftKey ? 2 : 1), 0) && e.stopPropagation();
-            } else if (e.key === "ArrowUp") {
+            } else if (e.key === 'ArrowUp') {
                 PreviewCursor._nudge?.(0, 1 * (e.shiftKey ? 2 : 1)) && e.stopPropagation();
-            } else if (e.key === "ArrowDown") {
+            } else if (e.key === 'ArrowDown') {
                 PreviewCursor._nudge?.(0, -1 * (e.shiftKey ? 2 : 1)) && e.stopPropagation();
             }
         }
-    }
+    };
 
     //when focus is lost, this will remove the preview cursor
     @action onBlur = (): void => {
         PreviewCursor.Visible = false;
-    }
+    };
 
     @action
-    public static Show(x: number, y: number,
+    public static Show(
+        x: number,
+        y: number,
         onKeyPress: (e: KeyboardEvent) => void,
         addLiveText: (doc: Doc) => void,
         getTransform: () => Transform,
         addDocument: undefined | ((doc: Doc | Doc[]) => boolean),
-        nudge: undefined | ((nudgeX: number, nudgeY: number) => boolean)) {
+        nudge: undefined | ((nudgeX: number, nudgeY: number) => boolean)
+    ) {
         this._clickPoint = [x, y];
         this._onKeyPress = onKeyPress;
         this._addLiveTextDoc = addLiveText;
@@ -157,10 +195,10 @@ export class PreviewCursor extends React.Component<{}> {
         this.Visible = true;
     }
     render() {
-        return (!PreviewCursor._clickPoint || !PreviewCursor.Visible) ? (null) :
-            <div className={`previewCursor${StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme)}`} onBlur={this.onBlur} tabIndex={0} ref={e => e?.focus()}
-                style={{ transform: `translate(${PreviewCursor._clickPoint[0]}px, ${PreviewCursor._clickPoint[1]}px)` }}>
+        return !PreviewCursor._clickPoint || !PreviewCursor.Visible ? null : (
+            <div className={`previewCursor${StrCast(Doc.ActiveDashboard?.colorScheme)}`} onBlur={this.onBlur} tabIndex={0} ref={e => e?.focus()} style={{ transform: `translate(${PreviewCursor._clickPoint[0]}px, ${PreviewCursor._clickPoint[1]}px)` }}>
                 I
-        </div >;
+            </div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index 9c6d9a108..8c4c1d00b 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -1,221 +1,345 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, Opt } from "../../fields/Doc";
-import { Id } from "../../fields/FieldSymbols";
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, Opt } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
 import { InkField } from '../../fields/InkField';
 import { RichTextField } from '../../fields/RichTextField';
-import { BoolCast, StrCast } from "../../fields/Types";
-import { ImageField } from "../../fields/URLField";
+import { BoolCast, StrCast } from '../../fields/Types';
+import { ImageField } from '../../fields/URLField';
 import { DocUtils } from '../documents/Documents';
-import { DocumentType } from '../documents/DocumentTypes';
+import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { SelectionManager } from '../util/SelectionManager';
 import { undoBatch } from '../util/UndoManager';
-import { CollectionViewType } from './collections/CollectionView';
-import { Colors } from "./global/globalEnums";
+import { Colors } from './global/globalEnums';
 import { InkingStroke } from './InkingStroke';
 import { DocumentView } from './nodes/DocumentView';
-import { VideoBox } from "./nodes/VideoBox";
-import { pasteImageBitmap } from "./nodes/WebBoxRenderer";
+import { VideoBox } from './nodes/VideoBox';
+import { pasteImageBitmap } from './nodes/WebBoxRenderer';
 import './PropertiesButtons.scss';
-import React = require("react");
-const higflyout = require("@hig/flyout");
+import React = require('react');
+const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
 enum UtilityButtonState {
     Default,
     OpenRight,
-    OpenExternally
+    OpenExternally,
 }
 @observer
 export class PropertiesButtons extends React.Component<{}, {}> {
     @observable public static Instance: PropertiesButtons;
 
-    @computed get selectedDoc() { return SelectionManager.SelectedSchemaDoc() || SelectionManager.Views().lastElement()?.rootDoc; }
-    @computed get selectedTabView() { return !SelectionManager.SelectedSchemaDoc() && SelectionManager.Views().lastElement()?.topMost; }
+    @computed get selectedDoc() {
+        return SelectionManager.SelectedSchemaDoc() || SelectionManager.Views().lastElement()?.rootDoc;
+    }
+    @computed get selectedTabView() {
+        return !SelectionManager.SelectedSchemaDoc() && SelectionManager.Views().lastElement()?.topMost;
+    }
 
     propertyToggleBtn = (label: string, property: string, tooltip: (on?: any) => string, icon: (on: boolean) => string, onClick?: (dv: Opt<DocumentView>, doc: Doc, property: string) => void, useUserDoc?: boolean) => {
         const targetDoc = useUserDoc ? Doc.UserDoc() : this.selectedDoc;
-        const onPropToggle = (dv: Opt<DocumentView>, doc: Doc, prop: string) => (dv?.layoutDoc || doc)[prop] = (dv?.layoutDoc || doc)[prop] ? false : true;
-        return !targetDoc ? (null) :
+        const onPropToggle = (dv: Opt<DocumentView>, doc: Doc, prop: string) => ((dv?.layoutDoc || doc)[prop] = (dv?.layoutDoc || doc)[prop] ? false : true);
+        return !targetDoc ? null : (
             <Tooltip title={<div className={`dash-tooltip`}>{tooltip(targetDoc?.[property])} </div>} placement="top">
                 <div>
-                    <div className={`propertiesButtons-linkButton-empty toggle-${StrCast(targetDoc[property]).includes(":hover") ? "hover" : targetDoc[property] ? "on" : "off"}`}
+                    <div
+                        className={`propertiesButtons-linkButton-empty toggle-${StrCast(targetDoc[property]).includes(':hover') ? 'hover' : targetDoc[property] ? 'on' : 'off'}`}
                         onPointerDown={e => e.stopPropagation()}
                         onClick={undoBatch(() => {
                             if (SelectionManager.Views().length > 1) {
                                 SelectionManager.Views().forEach(dv => (onClick ?? onPropToggle)(dv, dv.rootDoc, property));
                             } else if (targetDoc) (onClick ?? onPropToggle)(undefined, targetDoc, property);
-                        })} >
+                        })}>
                         <FontAwesomeIcon className="documentdecorations-icon" size="lg" icon={icon(BoolCast(targetDoc?.[property])) as any} />
                     </div>
                     <div className="propertiesButtons-title">{label}</div>
                 </div>
-            </Tooltip>;
-    }
+            </Tooltip>
+        );
+    };
     @computed get lockButton() {
-        return this.propertyToggleBtn("No\xA0Drag", "_lockedPosition", on => `${on ? "Unlock" : "Lock"} position to prevent dragging`, on => "thumbtack");
+        return this.propertyToggleBtn(
+            'No\xA0Drag',
+            '_lockedPosition',
+            on => `${on ? 'Unlock' : 'Lock'} position to prevent dragging`,
+            on => 'thumbtack'
+        );
     }
     @computed get dictationButton() {
-        return this.propertyToggleBtn("Dictate", "_showAudio", on => `${on ? "Hide" : "Show"} dictation/recording controls`, on => "microphone");
+        return this.propertyToggleBtn(
+            'Dictate',
+            '_showAudio',
+            on => `${on ? 'Hide' : 'Show'} dictation/recording controls`,
+            on => 'microphone'
+        );
     }
     @computed get maskButton() {
-        return this.propertyToggleBtn("Mask", "isInkMask", on => on ? "Make plain ink" : "Make highlight mask", on => "paint-brush", (dv, doc) => InkingStroke.toggleMask(dv?.layoutDoc || doc));
+        return this.propertyToggleBtn(
+            'Mask',
+            'isInkMask',
+            on => (on ? 'Make plain ink' : 'Make highlight mask'),
+            on => 'paint-brush',
+            (dv, doc) => InkingStroke.toggleMask(dv?.layoutDoc || doc)
+        );
     }
     @computed get clustersButton() {
-        return this.propertyToggleBtn("Clusters", "_useClusters", on => `${on ? "Hide" : "Show"} clusters`, on => "braille");
+        return this.propertyToggleBtn(
+            'Clusters',
+            '_useClusters',
+            on => `${on ? 'Hide' : 'Show'} clusters`,
+            on => 'braille'
+        );
     }
     @computed get panButton() {
-        return this.propertyToggleBtn("Lock\xA0View", "_lockedTransform", on => `${on ? "Unlock" : "Lock"} panning of view`, on => "lock");
+        return this.propertyToggleBtn(
+            'Lock\xA0View',
+            '_lockedTransform',
+            on => `${on ? 'Unlock' : 'Lock'} panning of view`,
+            on => 'lock'
+        );
     }
     @computed get fitContentButton() {
-        return this.propertyToggleBtn("View All", "_fitContentsToBox", on => `${on ? "Don't" : "Do"} fit content to container visible area`, on => "eye");
+        return this.propertyToggleBtn(
+            'View All',
+            '_fitContentsToBox',
+            on => `${on ? "Don't" : 'Do'} fit content to container visible area`,
+            on => 'eye'
+        );
     }
     @computed get fitWidthButton() {
-        return this.propertyToggleBtn("Fit\xA0Width", "_fitWidth", on => `${on ? "Don't" : "Do"} fit content to width of container`, on => "arrows-alt-h");
+        return this.propertyToggleBtn(
+            'Fit\xA0Width',
+            '_fitWidth',
+            on => `${on ? "Don't" : 'Do'} fit content to width of container`,
+            on => 'arrows-alt-h'
+        );
     }
     @computed get captionButton() {
-        return this.propertyToggleBtn("Caption", "_showCaption", on => `${on ? "Hide" : "Show"} caption footer`, on => "closed-captioning", (dv, doc) => (dv?.rootDoc || doc)._showCaption = (dv?.rootDoc || doc)._showCaption === undefined ? "caption" : undefined);
+        return this.propertyToggleBtn(
+            'Caption',
+            '_showCaption',
+            on => `${on ? 'Hide' : 'Show'} caption footer`,
+            on => 'closed-captioning',
+            (dv, doc) => ((dv?.rootDoc || doc)._showCaption = (dv?.rootDoc || doc)._showCaption === undefined ? 'caption' : undefined)
+        );
     }
     @computed get chromeButton() {
-        return this.propertyToggleBtn("Controls", "_chromeHidden", on => `${on ? "Show" : "Hide"} editing UI`, on => "edit", (dv, doc) => (dv?.rootDoc || doc)._chromeHidden = !(dv?.rootDoc || doc)._chromeHidden);
+        return this.propertyToggleBtn(
+            'Controls',
+            '_chromeHidden',
+            on => `${on ? 'Show' : 'Hide'} editing UI`,
+            on => 'edit',
+            (dv, doc) => ((dv?.rootDoc || doc)._chromeHidden = !(dv?.rootDoc || doc)._chromeHidden)
+        );
     }
     @computed get titleButton() {
-        return this.propertyToggleBtn("Title", "_showTitle", on => "Switch between title styles", on => "text-width", (dv, doc) => (dv?.rootDoc || doc)._showTitle = !(dv?.rootDoc || doc)._showTitle ? "title" : (dv?.rootDoc || doc)._showTitle === "title" ? "title:hover" : undefined);
+        return this.propertyToggleBtn(
+            'Title',
+            '_showTitle',
+            on => 'Switch between title styles',
+            on => 'text-width',
+            (dv, doc) => ((dv?.rootDoc || doc)._showTitle = !(dv?.rootDoc || doc)._showTitle ? 'title' : (dv?.rootDoc || doc)._showTitle === 'title' ? 'title:hover' : undefined)
+        );
     }
     @computed get autoHeightButton() {
-        return this.propertyToggleBtn("Auto\xA0Size", "_autoHeight", on => `Automatical vertical sizing to show all content`, on => "arrows-alt-v");
+        return this.propertyToggleBtn(
+            'Auto\xA0Size',
+            '_autoHeight',
+            on => `Automatical vertical sizing to show all content`,
+            on => 'arrows-alt-v'
+        );
     }
     @computed get gridButton() {
-        return this.propertyToggleBtn("Grid", "_backgroundGridShow", on => `Display background grid in collection`, on => "border-all");
+        return this.propertyToggleBtn(
+            'Grid',
+            '_backgroundGridShow',
+            on => `Display background grid in collection`,
+            on => 'border-all'
+        );
     }
     @computed get groupButton() {
-        return this.propertyToggleBtn("Group", "isGroup", on => `Display collection as a Group`, on => "object-group", (dv, doc) => { doc.isGroup = !doc.isGroup; doc.forceActive = doc.isGroup; });
+        return this.propertyToggleBtn(
+            'Group',
+            'isGroup',
+            on => `Display collection as a Group`,
+            on => 'object-group',
+            (dv, doc) => {
+                doc.isGroup = !doc.isGroup;
+                doc.forceActive = doc.isGroup;
+            }
+        );
     }
     @computed get freezeThumb() {
-        return this.propertyToggleBtn("Freeze\Thumb", "_thumb-frozen", on => `${on ? "Freeze" : "Unfreeze"} thumbnail`, on => "arrows-alt-h", (dv, doc) => {
-            if (doc["thumb-frozen"]) doc["thumb-frozen"] = undefined;
-            else {
-                document.body.focus(); // so that we can access the clipboard without an error
-                setTimeout(() =>
-                    pasteImageBitmap((data_url: any, error: any) => {
-                        error && console.log(error);
-                        data_url && VideoBox.convertDataUri(data_url, doc[Id] + "-thumb-frozen", true).then(
-                            returnedfilename => doc["thumb-frozen"] = new ImageField(returnedfilename));
-                    }));
+        return this.propertyToggleBtn(
+            'FreezeThumb',
+            '_thumb-frozen',
+            on => `${on ? 'Freeze' : 'Unfreeze'} thumbnail`,
+            on => 'arrows-alt-h',
+            (dv, doc) => {
+                if (doc['thumb-frozen']) doc['thumb-frozen'] = undefined;
+                else {
+                    document.body.focus(); // so that we can access the clipboard without an error
+                    setTimeout(() =>
+                        pasteImageBitmap((data_url: any, error: any) => {
+                            error && console.log(error);
+                            data_url && VideoBox.convertDataUri(data_url, doc[Id] + '-thumb-frozen', true).then(returnedfilename => (doc['thumb-frozen'] = new ImageField(returnedfilename)));
+                        })
+                    );
+                }
             }
-        });
+        );
     }
     @computed get snapButton() {
-        return this.propertyToggleBtn("Snap\xA0Lines", "showSnapLines", on => `Display snapping lines when objects are dragged`, on => "border-all", undefined, true);
+        return this.propertyToggleBtn(
+            'Snap\xA0Lines',
+            'showSnapLines',
+            on => `Display snapping lines when objects are dragged`,
+            on => 'border-all',
+            undefined,
+            true
+        );
     }
 
     @computed
     get onClickButton() {
-        return !this.selectedDoc ? (null) : <Tooltip title={<div className="dash-tooltip">Choose onClick behavior</div>} placement="top">
-            <div>
-                <div className="propertiesButtons-linkFlyout">
-                    <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.onClickFlyout}>
-                        <div className={"propertiesButtons-linkButton-empty"} onPointerDown={e => e.stopPropagation()} >
-                            <FontAwesomeIcon className="documentdecorations-icon" icon="mouse-pointer" size="lg" />
-                        </div>
-                    </Flyout>
+        return !this.selectedDoc ? null : (
+            <Tooltip title={<div className="dash-tooltip">Choose onClick behavior</div>} placement="top">
+                <div>
+                    <div className="propertiesButtons-linkFlyout">
+                        <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.onClickFlyout}>
+                            <div className={'propertiesButtons-linkButton-empty'} onPointerDown={e => e.stopPropagation()}>
+                                <FontAwesomeIcon className="documentdecorations-icon" icon="mouse-pointer" size="lg" />
+                            </div>
+                        </Flyout>
+                    </div>
+                    <div className="propertiesButtons-title"> onclick </div>
                 </div>
-                <div className="propertiesButtons-title"> onclick </div>
-            </div>
-        </Tooltip>;
+            </Tooltip>
+        );
     }
     @computed
     get perspectiveButton() {
-        return !this.selectedDoc ? (null) : <Tooltip title={<div className="dash-tooltip">Choose view perspective</div>} placement="top">
-            <div>
-                <div className="propertiesButtons-linkFlyout">
-                    <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.onPerspectiveFlyout}>
-                        <div className={"propertiesButtons-linkButton-empty"} onPointerDown={e => e.stopPropagation()} >
-                            <FontAwesomeIcon className="documentdecorations-icon" icon="mouse-pointer" size="lg" />
-                        </div>
-                    </Flyout>
+        return !this.selectedDoc ? null : (
+            <Tooltip title={<div className="dash-tooltip">Choose view perspective</div>} placement="top">
+                <div>
+                    <div className="propertiesButtons-linkFlyout">
+                        <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={this.onPerspectiveFlyout}>
+                            <div className={'propertiesButtons-linkButton-empty'} onPointerDown={e => e.stopPropagation()}>
+                                <FontAwesomeIcon className="documentdecorations-icon" icon="mouse-pointer" size="lg" />
+                            </div>
+                        </Flyout>
+                    </div>
+                    <div className="propertiesButtons-title"> Perspective </div>
                 </div>
-                <div className="propertiesButtons-title"> Perspective </div>
-            </div>
-        </Tooltip>;
+            </Tooltip>
+        );
     }
 
     @undoBatch
     handlePerspectiveChange = (e: any) => {
         this.selectedDoc && (this.selectedDoc._viewType = e.target.value);
-        SelectionManager.Views().filter(dv => dv.docView).map(dv => dv.docView!).forEach(docView => docView.layoutDoc._viewType = e.target.value);
-    }
+        SelectionManager.Views()
+            .filter(dv => dv.docView)
+            .map(dv => dv.docView!)
+            .forEach(docView => (docView.layoutDoc._viewType = e.target.value));
+    };
 
     @undoBatch
     @action
     handleOptionChange = (onClick: string) => {
         this.selectedDoc && (this.selectedDoc.onClickBehavior = onClick);
-        SelectionManager.Views().filter(dv => dv.docView).map(dv => dv.docView!).forEach(docView => {
-            docView.noOnClick();
-            switch (onClick) {
-                case "enterPortal": docView.makeIntoPortal(); break;
-                case "toggleDetail": docView.setToggleDetail(); break;
-                case "linkInPlace": docView.toggleFollowLink("inPlace", true, false); break;
-                case "linkOnRight": docView.toggleFollowLink("add:right", false, false); break;
-            }
-        });
-    }
+        SelectionManager.Views()
+            .filter(dv => dv.docView)
+            .map(dv => dv.docView!)
+            .forEach(docView => {
+                docView.noOnClick();
+                switch (onClick) {
+                    case 'enterPortal':
+                        docView.makeIntoPortal();
+                        break;
+                    case 'toggleDetail':
+                        docView.setToggleDetail();
+                        break;
+                    case 'linkInPlace':
+                        docView.toggleFollowLink('inPlace', true, false);
+                        break;
+                    case 'linkOnRight':
+                        docView.toggleFollowLink('add:right', false, false);
+                        break;
+                }
+            });
+    };
 
     @undoBatch
     editOnClickScript = () => {
-        if (SelectionManager.Views().length) SelectionManager.Views().forEach(dv => DocUtils.makeCustomViewClicked(dv.rootDoc, undefined, "onClick"));
-        else this.selectedDoc && DocUtils.makeCustomViewClicked(this.selectedDoc, undefined, "onClick");
-    }
+        if (SelectionManager.Views().length) SelectionManager.Views().forEach(dv => DocUtils.makeCustomViewClicked(dv.rootDoc, undefined, 'onClick'));
+        else this.selectedDoc && DocUtils.makeCustomViewClicked(this.selectedDoc, undefined, 'onClick');
+    };
 
     @computed
     get onClickFlyout() {
         const buttonList = [
-            ["nothing", "Select Document"],
-            ["enterPortal", "Enter Portal"],
-            ["toggleDetail", "Toggle Detail"],
-            ["linkInPlace", "Follow Link"],
-            ["linkOnRight", "Open Link on Right"]
+            ['nothing', 'Select Document'],
+            ['enterPortal', 'Enter Portal'],
+            ['toggleDetail', 'Toggle Detail'],
+            ['linkInPlace', 'Follow Link'],
+            ['linkOnRight', 'Open Link on Right'],
         ];
         const currentSelection = this.selectedDoc.onClickBehavior;
         // Get items to place into the list
 
-        const list = buttonList.map((value) => {
+        const list = buttonList.map(value => {
             const click = () => {
                 this.handleOptionChange(value[0]);
             };
-            return <div className="list-item" key={`${value}`}
-                style={{
-                    backgroundColor: value[0] === currentSelection ? Colors.LIGHT_BLUE : undefined
-                }}
-                onClick={click}>
-                {value[1]}
-            </div>;
+            return (
+                <div
+                    className="list-item"
+                    key={`${value}`}
+                    style={{
+                        backgroundColor: value[0] === currentSelection ? Colors.LIGHT_BLUE : undefined,
+                    }}
+                    onClick={click}>
+                    {value[1]}
+                </div>
+            );
         });
-        return <div>
+        return (
             <div>
-                <div className="propertiesButton-dropdownList">
-                    {list}
+                <div>
+                    <div className="propertiesButton-dropdownList">{list}</div>
                 </div>
+                {Doc.noviceMode ? null : (
+                    <div onPointerDown={this.editOnClickScript} className="onClickFlyout-editScript">
+                        {' '}
+                        Edit onClick Script
+                    </div>
+                )}
             </div>
-            {Doc.noviceMode ? (null) : <div onPointerDown={this.editOnClickScript} className="onClickFlyout-editScript"> Edit onClick Script</div>}
-        </div>;
+        );
     }
     @computed
     get onPerspectiveFlyout() {
         const excludedViewTypes = [CollectionViewType.Invalid, CollectionViewType.Docking, CollectionViewType.Pile, CollectionViewType.StackedTimeline, CollectionViewType.Linear];
 
-        const makeLabel = (value: string, label: string) => <div className="radio" key={label}>
-            <label>
-                <input type="radio" value={value} checked={(this.selectedDoc?._viewType ?? "invalid") === value} onChange={this.handlePerspectiveChange} />
-                {label}
-            </label>
-        </div>;
-        return <form>
-            {Object.values(CollectionViewType).filter(type => !excludedViewTypes.includes(type)).map(type => makeLabel(type, type))}
-        </form>;
+        const makeLabel = (value: string, label: string) => (
+            <div className="radio" key={label}>
+                <label>
+                    <input type="radio" value={value} checked={(this.selectedDoc?._viewType ?? 'invalid') === value} onChange={this.handlePerspectiveChange} />
+                    {label}
+                </label>
+            </div>
+        );
+        return (
+            <form>
+                {Object.values(CollectionViewType)
+                    .filter(type => !excludedViewTypes.includes(type))
+                    .map(type => makeLabel(type, type))}
+            </form>
+        );
     }
 
     render() {
@@ -228,27 +352,33 @@ export class PropertiesButtons extends React.Component<{}, {}> {
         const isFreeForm = this.selectedDoc?._viewType === CollectionViewType.Freeform;
         const isTree = this.selectedDoc?._viewType === CollectionViewType.Tree;
         const isTabView = this.selectedTabView;
-        const toggle = (ele: JSX.Element | null, style?: React.CSSProperties) => <div className="propertiesButtons-button" style={style}> {ele} </div>;
+        const toggle = (ele: JSX.Element | null, style?: React.CSSProperties) => (
+            <div className="propertiesButtons-button" style={style}>
+                {' '}
+                {ele}{' '}
+            </div>
+        );
         const isNovice = Doc.noviceMode;
-        return !this.selectedDoc ? (null) :
+        return !this.selectedDoc ? null : (
             <div className="propertiesButtons">
                 {toggle(this.titleButton)}
                 {toggle(this.captionButton)}
                 {toggle(this.lockButton)}
-                {toggle(this.dictationButton, { display: isNovice ? "none" : "" })}
+                {toggle(this.dictationButton, { display: isNovice ? 'none' : '' })}
                 {toggle(this.onClickButton)}
                 {toggle(this.fitWidthButton)}
                 {toggle(this.freezeThumb)}
-                {toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? "none" : "" })}
-                {toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? "none" : "" })}
-                {toggle(this.maskButton, { display: !isInk ? "none" : "" })}
-                {toggle(this.chromeButton, { display: !isCollection || isNovice ? "none" : "" })}
-                {toggle(this.gridButton, { display: !isCollection ? "none" : "" })}
-                {toggle(this.groupButton, { display: isTabView || !isCollection ? "none" : "" })}
-                {toggle(this.snapButton, { display: !isCollection ? "none" : "" })}
-                {toggle(this.clustersButton, { display: !isFreeForm ? "none" : "" })}
-                {toggle(this.panButton, { display: !isFreeForm ? "none" : "" })}
-                {toggle(this.perspectiveButton, { display: !isCollection || isNovice ? "none" : "" })}
-            </div>;
-    }
-}
\ No newline at end of file
+                {toggle(this.fitContentButton, { display: !isFreeForm && !isMap ? 'none' : '' })}
+                {toggle(this.autoHeightButton, { display: !isText && !isStacking && !isTree ? 'none' : '' })}
+                {toggle(this.maskButton, { display: !isInk ? 'none' : '' })}
+                {toggle(this.chromeButton, { display: !isCollection || isNovice ? 'none' : '' })}
+                {toggle(this.gridButton, { display: !isCollection ? 'none' : '' })}
+                {toggle(this.groupButton, { display: isTabView || !isCollection ? 'none' : '' })}
+                {toggle(this.snapButton, { display: !isCollection ? 'none' : '' })}
+                {toggle(this.clustersButton, { display: !isFreeForm ? 'none' : '' })}
+                {toggle(this.panButton, { display: !isFreeForm ? 'none' : '' })}
+                {toggle(this.perspectiveButton, { display: !isCollection || isNovice ? 'none' : '' })}
+            </div>
+        );
+    }
+}
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index 1af706bb5..0f63ebc1d 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -1,20 +1,20 @@
-import { computed } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import { Doc, DocListCast } from "../../fields/Doc";
-import { Id } from "../../fields/FieldSymbols";
-import { Cast, NumCast, StrCast } from "../../fields/Types";
-import { DocFocusOrOpen } from "../util/DocumentManager";
-import { CollectionDockingView } from "./collections/CollectionDockingView";
-import { CollectionViewType } from "./collections/CollectionView";
-import { DocumentView } from "./nodes/DocumentView";
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc, DocListCast } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { Cast, NumCast, StrCast } from '../../fields/Types';
+import { CollectionViewType } from '../documents/DocumentTypes';
+import { DocFocusOrOpen } from '../util/DocumentManager';
+import { CollectionDockingView } from './collections/CollectionDockingView';
+import { DocumentView } from './nodes/DocumentView';
 import './PropertiesDocContextSelector.scss';
 
 type PropertiesDocContextSelectorProps = {
-    DocView?: DocumentView,
-    Stack?: any,
-    hideTitle?: boolean,
-    addDocTab(doc: Doc, location: string): void
+    DocView?: DocumentView;
+    Stack?: any;
+    hideTitle?: boolean;
+    addDocTab(doc: Doc, location: string): void;
 };
 
 @observer
@@ -26,10 +26,23 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
         const aliases = DocListCast(target.aliases);
         const containerProtos = aliases.filter(alias => alias.context && alias.context instanceof Doc && Cast(alias.context, Doc, null) !== targetContext).reduce((set, alias) => set.add(Cast(alias.context, Doc, null)), new Set<Doc>());
         const containerSets = Array.from(containerProtos.keys()).map(container => DocListCast(container.aliases));
-        const containers = containerSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set<Doc>());
+        const containers = containerSets.reduce((p, set) => {
+            set.map(s => p.add(s));
+            return p;
+        }, new Set<Doc>());
         const doclayoutSets = Array.from(containers.keys()).map(dp => DocListCast(dp.aliases));
-        const doclayouts = Array.from(doclayoutSets.reduce((p, set) => { set.map(s => p.add(s)); return p; }, new Set<Doc>()).keys());
-        return doclayouts.filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document)).filter(doc => !Doc.IsSystem(doc)).map(doc => ({ col: doc, target }));
+        const doclayouts = Array.from(
+            doclayoutSets
+                .reduce((p, set) => {
+                    set.map(s => p.add(s));
+                    return p;
+                }, new Set<Doc>())
+                .keys()
+        );
+        return doclayouts
+            .filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document))
+            .filter(doc => !Doc.IsSystem(doc))
+            .map(doc => ({ col: doc, target }));
     }
 
     getOnClick = (col: Doc, target: Doc) => {
@@ -40,14 +53,20 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
             col._panY = NumCast(target.y) + NumCast(target._height) / 2;
         }
         col.hidden = false;
-        this.props.addDocTab(col, "toggle:right");
+        this.props.addDocTab(col, 'toggle:right');
         setTimeout(() => DocFocusOrOpen(Doc.GetProto(this.props.DocView!.props.Document), col), 100);
-    }
+    };
 
     render() {
-        return <div>
-            {this.props.hideTitle ? (null) : <p key="contexts">Contexts:</p>}
-            {this._docs.map(doc => <p key={doc.col[Id] + doc.target[Id]}><a onClick={() => this.getOnClick(doc.col, doc.target)}>{StrCast(doc.col.title)}</a></p>)}
-        </div>;
+        return (
+            <div>
+                {this.props.hideTitle ? null : <p key="contexts">Contexts:</p>}
+                {this._docs.map(doc => (
+                    <p key={doc.col[Id] + doc.target[Id]}>
+                        <a onClick={() => this.getOnClick(doc.col, doc.target)}>{StrCast(doc.col.title)}</a>
+                    </p>
+                ))}
+            </div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index faab2ed26..aecbc4255 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -1,44 +1,42 @@
-import React = require("react");
+import React = require('react');
+import { IconLookup } from '@fortawesome/fontawesome-svg-core';
 import { faAnchor, faArrowRight } from '@fortawesome/free-solid-svg-icons';
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Checkbox, Tooltip } from "@material-ui/core";
-import { intersection } from "lodash";
-import { action, autorun, computed, Lambda, observable } from "mobx";
-import { observer } from "mobx-react";
-import { ColorState, SketchPicker } from "react-color";
-import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, AclSelfEdit, AclSym, AclUnset, DataSym, Doc, Field, HeightSym, NumListCast, Opt, StrListCast, WidthSym } from "../../fields/Doc";
-import { Id } from "../../fields/FieldSymbols";
-import { InkField } from "../../fields/InkField";
-import { List } from "../../fields/List";
-import { ComputedField } from "../../fields/ScriptField";
-import { Cast, NumCast, StrCast, DocCast } from "../../fields/Types";
-import { denormalizeEmail, GetEffectiveAcl, SharingPermissions } from "../../fields/util";
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from "../../Utils";
-import { DocumentType } from "../documents/DocumentTypes";
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
-import { DocumentManager } from "../util/DocumentManager";
-import { LinkManager } from "../util/LinkManager";
-import { SelectionManager } from "../util/SelectionManager";
-import { SharingManager } from "../util/SharingManager";
-import { Transform } from "../util/Transform";
-import { undoBatch, UndoManager } from "../util/UndoManager";
-import { CollectionDockingView } from "./collections/CollectionDockingView";
-import { CollectionViewType } from "./collections/CollectionView";
-import { EditableView } from "./EditableView";
-import { InkStrokeProperties } from "./InkStrokeProperties";
-import { DocumentView, StyleProviderFunc } from "./nodes/DocumentView";
-import { KeyValueBox } from "./nodes/KeyValueBox";
-import { PropertiesButtons } from "./PropertiesButtons";
-import { PropertiesDocContextSelector } from "./PropertiesDocContextSelector";
-import "./PropertiesView.scss";
-import { DefaultStyleProvider } from "./StyleProvider";
-import { PresBox } from "./nodes/trails";
-import { IconLookup } from "@fortawesome/fontawesome-svg-core";
-import { PropertiesDocBacklinksSelector } from "./PropertiesDocBacklinksSelector";
-const higflyout = require("@hig/flyout");
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Checkbox, Tooltip } from '@material-ui/core';
+import { intersection } from 'lodash';
+import { action, autorun, computed, Lambda, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import { ColorState, SketchPicker } from 'react-color';
+import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, AclSelfEdit, AclSym, AclUnset, DataSym, Doc, Field, HeightSym, NumListCast, Opt, StrListCast, WidthSym } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
+import { InkField } from '../../fields/InkField';
+import { List } from '../../fields/List';
+import { ComputedField } from '../../fields/ScriptField';
+import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types';
+import { denormalizeEmail, GetEffectiveAcl, SharingPermissions } from '../../fields/util';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils';
+import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
+import { DocumentManager } from '../util/DocumentManager';
+import { LinkManager } from '../util/LinkManager';
+import { SelectionManager } from '../util/SelectionManager';
+import { SharingManager } from '../util/SharingManager';
+import { Transform } from '../util/Transform';
+import { undoBatch, UndoManager } from '../util/UndoManager';
+import { EditableView } from './EditableView';
+import { InkStrokeProperties } from './InkStrokeProperties';
+import { DocumentView, StyleProviderFunc } from './nodes/DocumentView';
+import { FilterBox } from './nodes/FilterBox';
+import { KeyValueBox } from './nodes/KeyValueBox';
+import { PresBox } from './nodes/trails';
+import { PropertiesButtons } from './PropertiesButtons';
+import { PropertiesDocBacklinksSelector } from './PropertiesDocBacklinksSelector';
+import { PropertiesDocContextSelector } from './PropertiesDocContextSelector';
+import './PropertiesView.scss';
+import { DefaultStyleProvider } from './StyleProvider';
+const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
-const _global = (window /* browser */ || global /* node */) as any;
+const _global = (window /* browser */ || global) /* node */ as any;
 
 interface PropertiesViewProps {
     width: number;
@@ -51,9 +49,13 @@ interface PropertiesViewProps {
 export class PropertiesView extends React.Component<PropertiesViewProps> {
     private _widthUndo?: UndoManager.Batch;
 
-    @computed get MAX_EMBED_HEIGHT() { return 200; }
+    @computed get MAX_EMBED_HEIGHT() {
+        return 200;
+    }
 
-    @computed get selectedDoc() { return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc || CurrentUserUtils.ActiveDashboard; }
+    @computed get selectedDoc() {
+        return SelectionManager.SelectedSchemaDoc() || this.selectedDocumentView?.rootDoc || Doc.ActiveDashboard;
+    }
     @computed get selectedDocumentView() {
         if (SelectionManager.Views().length) return SelectionManager.Views()[0];
         if (PresBox.Instance?._selectedArray.size) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc);
@@ -65,7 +67,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     @computed get isLink(): boolean {
         return this.selectedDoc?.type === DocumentType.LINK;
     }
-    @computed get dataDoc() { return this.selectedDoc?.[DataSym]; }
+    @computed get dataDoc() {
+        return this.selectedDoc?.[DataSym];
+    }
 
     @observable layoutFields: boolean = false;
 
@@ -107,14 +111,16 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         this.selectedDocListenerDisposer?.();
     }
 
-    @computed get isInk() { return this.selectedDoc?.type === DocumentType.INK; }
+    @computed get isInk() {
+        return this.selectedDoc?.type === DocumentType.INK;
+    }
 
     rtfWidth = () => {
         return !this.selectedDoc ? 0 : Math.min(this.selectedDoc?.[WidthSym](), this.props.width - 20);
-    }
+    };
     rtfHeight = () => {
         return !this.selectedDoc ? 0 : this.rtfWidth() <= this.selectedDoc?.[WidthSym]() ? Math.min(this.selectedDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
-    }
+    };
 
     @action
     docWidth = () => {
@@ -126,62 +132,74 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         } else {
             return 0;
         }
-    }
+    };
 
     @action
     docHeight = () => {
         if (this.selectedDoc && this.dataDoc) {
             const layoutDoc = this.selectedDoc;
-            return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT,
-                Doc.NativeAspect(layoutDoc, undefined, true) ? this.docWidth() / Doc.NativeAspect(layoutDoc, undefined, true) :
-                    layoutDoc._fitWidth ? (!Doc.NativeHeight(this.dataDoc) ? NumCast(this.props.height) :
-                        Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc)) / Doc.NativeWidth(layoutDoc) || NumCast(this.props.height))) :
-                        NumCast(layoutDoc._height) || 50));
+            return Math.max(
+                70,
+                Math.min(
+                    this.MAX_EMBED_HEIGHT,
+                    Doc.NativeAspect(layoutDoc, undefined, true)
+                        ? this.docWidth() / Doc.NativeAspect(layoutDoc, undefined, true)
+                        : layoutDoc._fitWidth
+                        ? !Doc.NativeHeight(this.dataDoc)
+                            ? NumCast(this.props.height)
+                            : Math.min((this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / Doc.NativeWidth(layoutDoc) || NumCast(this.props.height))
+                        : NumCast(layoutDoc._height) || 50
+                )
+            );
         }
         return 0;
-    }
+    };
 
     @computed get expandedField() {
         if (this.dataDoc && this.selectedDoc) {
             const ids: { [key: string]: string } = {};
-            const docs = SelectionManager.Views().length < 2 ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] :
-                SelectionManager.Views().map(dv => this.layoutFields ? dv.layoutDoc : dv.dataDoc);
+            const docs = SelectionManager.Views().length < 2 ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] : SelectionManager.Views().map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc));
             docs.forEach(doc => Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key)));
             const rows: JSX.Element[] = [];
             for (const key of Object.keys(ids).slice().sort()) {
                 const docvals = new Set<any>();
                 docs.forEach(doc => docvals.add(doc[key]));
-                const contents = Array.from(docvals.keys()).length > 1 ? "-multiple" : docs[0][key];
-                if (key[0] === "#") {
-                    rows.push(<div style={{ display: "flex", overflowY: "visible", marginBottom: "2px" }} key={key}>
-                        <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key}</span>
-                        &nbsp;
-                    </div>);
+                const contents = Array.from(docvals.keys()).length > 1 ? '-multiple' : docs[0][key];
+                if (key[0] === '#') {
+                    rows.push(
+                        <div style={{ display: 'flex', overflowY: 'visible', marginBottom: '2px' }} key={key}>
+                            <span style={{ fontWeight: 'bold', whiteSpace: 'nowrap' }}>{key}</span>
+                            &nbsp;
+                        </div>
+                    );
                 } else {
-                    const contentElement = <EditableView key="editableView"
-                        contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
-                        height={13}
-                        fontSize={10}
-                        GetValue={() => contents !== undefined ? Field.toString(contents as Field) : "null"}
-                        SetValue={(value: string) => { docs.map(doc => KeyValueBox.SetField(doc, key, value, true)); return true; }}
-                    />;
-                    rows.push(<div style={{ display: "flex", overflowY: "visible", marginBottom: "-1px" }} key={key}>
-                        <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ":"}</span>
-                        &nbsp;
-                        {contentElement}
-                    </div>);
+                    const contentElement = (
+                        <EditableView
+                            key="editableView"
+                            contents={contents !== undefined ? Field.toString(contents as Field) : 'null'}
+                            height={13}
+                            fontSize={10}
+                            GetValue={() => (contents !== undefined ? Field.toString(contents as Field) : 'null')}
+                            SetValue={(value: string) => {
+                                docs.map(doc => KeyValueBox.SetField(doc, key, value, true));
+                                return true;
+                            }}
+                        />
+                    );
+                    rows.push(
+                        <div style={{ display: 'flex', overflowY: 'visible', marginBottom: '-1px' }} key={key}>
+                            <span style={{ fontWeight: 'bold', whiteSpace: 'nowrap' }}>{key + ':'}</span>
+                            &nbsp;
+                            {contentElement}
+                        </div>
+                    );
                 }
             }
-            rows.push(<div className="propertiesView-field" key={"newKeyValue"} style={{ marginTop: "3px" }}>
-                <EditableView
-                    key="editableView"
-                    oneLine
-                    contents={"add key:value or #tags"}
-                    height={13}
-                    fontSize={10}
-                    GetValue={() => ""}
-                    SetValue={this.setKeyValue} />
-            </div>);
+            rows.push(
+                <div className="propertiesView-field" key={'newKeyValue'} style={{ marginTop: '3px' }}>
+                    <EditableView key="editableView" oneLine contents={'add key:value or #tags'} height={13} fontSize={10} GetValue={() => ''} SetValue={this.setKeyValue} />
+                </div>
+            );
             return rows;
         }
     }
@@ -192,74 +210,80 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             const docs = SelectionManager.Views().length < 2 ? [this.dataDoc] : SelectionManager.Views().map(dv => dv.dataDoc);
             docs.forEach(doc => Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key)));
             const rows: JSX.Element[] = [];
-            const noviceReqFields = ["author", "creationDate", "tags"];
-            const noviceLayoutFields = ["_curPage"];
-            const noviceKeys = [...Array.from(Object.keys(ids)).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("acl"))),
-            ...noviceReqFields, ...noviceLayoutFields];
+            const noviceReqFields = ['author', 'creationDate', 'tags'];
+            const noviceLayoutFields = ['_curPage'];
+            const noviceKeys = [...Array.from(Object.keys(ids)).filter(key => key[0] === '#' || key.indexOf('lastModified') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('acl'))), ...noviceReqFields, ...noviceLayoutFields];
             for (const key of noviceKeys.sort()) {
                 const docvals = new Set<any>();
                 docs.forEach(doc => docvals.add(doc[key]));
-                const contents = Array.from(docvals.keys()).length > 1 ? "-multiple" : docs[0][key];
-                if (key[0] === "#") {
-                    rows.push(<div className="propertiesView-uneditable-field" key={key}>
-                        <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key}</span>
-                        &nbsp;
-                    </div>);
+                const contents = Array.from(docvals.keys()).length > 1 ? '-multiple' : docs[0][key];
+                if (key[0] === '#') {
+                    rows.push(
+                        <div className="propertiesView-uneditable-field" key={key}>
+                            <span style={{ fontWeight: 'bold', whiteSpace: 'nowrap' }}>{key}</span>
+                            &nbsp;
+                        </div>
+                    );
                 } else if (contents !== undefined) {
                     const value = Field.toString(contents as Field);
-                    if (noviceReqFields.includes(key) || key.indexOf("lastModified") !== -1) {
-                        rows.push(<div className="propertiesView-uneditable-field" key={key}>
-                            <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ": "}</span>
-                            <div style={{ whiteSpace: "nowrap", overflowX: "hidden" }}>{value}</div>
-                        </div>);
+                    if (noviceReqFields.includes(key) || key.indexOf('lastModified') !== -1) {
+                        rows.push(
+                            <div className="propertiesView-uneditable-field" key={key}>
+                                <span style={{ fontWeight: 'bold', whiteSpace: 'nowrap' }}>{key + ': '}</span>
+                                <div style={{ whiteSpace: 'nowrap', overflowX: 'hidden' }}>{value}</div>
+                            </div>
+                        );
                     } else {
-                        const contentElement = <EditableView key="editableView"
-                            contents={value}
-                            height={13}
-                            fontSize={10}
-                            GetValue={() => contents !== undefined ? Field.toString(contents as Field) : "null"}
-                            SetValue={(value: string) => { docs.map(doc => KeyValueBox.SetField(doc, key, value, true)); return true; }}
-                        />;
+                        const contentElement = (
+                            <EditableView
+                                key="editableView"
+                                contents={value}
+                                height={13}
+                                fontSize={10}
+                                GetValue={() => (contents !== undefined ? Field.toString(contents as Field) : 'null')}
+                                SetValue={(value: string) => {
+                                    docs.map(doc => KeyValueBox.SetField(doc, key, value, true));
+                                    return true;
+                                }}
+                            />
+                        );
 
-                        rows.push(<div style={{ display: "flex", overflowY: "visible", marginBottom: "-1px" }} key={key}>
-                            <span style={{ fontWeight: "bold", whiteSpace: "nowrap" }}>{key + ":"}</span>
-                            &nbsp;
-                            {contentElement}
-                        </div>);
+                        rows.push(
+                            <div style={{ display: 'flex', overflowY: 'visible', marginBottom: '-1px' }} key={key}>
+                                <span style={{ fontWeight: 'bold', whiteSpace: 'nowrap' }}>{key + ':'}</span>
+                                &nbsp;
+                                {contentElement}
+                            </div>
+                        );
                     }
                 }
             }
-            rows.push(<div className="propertiesView-field" key={"newKeyValue"} style={{ marginTop: "3px" }}>
-                <EditableView
-                    key="editableView"
-                    oneLine
-                    contents={"add key:value or #tags"}
-                    height={13}
-                    fontSize={10}
-                    GetValue={() => ""}
-                    SetValue={this.setKeyValue} />
-            </div>);
+            rows.push(
+                <div className="propertiesView-field" key={'newKeyValue'} style={{ marginTop: '3px' }}>
+                    <EditableView key="editableView" oneLine contents={'add key:value or #tags'} height={13} fontSize={10} GetValue={() => ''} SetValue={this.setKeyValue} />
+                </div>
+            );
             return rows;
         }
     }
 
     @undoBatch
     setKeyValue = (value: string) => {
-        const docs = SelectionManager.Views().length < 2 && this.selectedDoc ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] : SelectionManager.Views().map(dv => this.layoutFields ? dv.layoutDoc : dv.dataDoc);
+        const docs = SelectionManager.Views().length < 2 && this.selectedDoc ? [this.layoutFields ? Doc.Layout(this.selectedDoc) : this.dataDoc] : SelectionManager.Views().map(dv => (this.layoutFields ? dv.layoutDoc : dv.dataDoc));
         docs.forEach(doc => {
-            if (value.indexOf(":") !== -1) {
+            if (value.indexOf(':') !== -1) {
                 const newVal = value[0].toUpperCase() + value.substring(1, value.length);
-                const splits = newVal.split(":");
+                const splits = newVal.split(':');
                 KeyValueBox.SetField(doc, splits[0], splits[1], true);
-                const tags = StrCast(doc.tags, ":");
-                if (tags.includes(`${splits[0]}:`) && splits[1] === "undefined") {
-                    KeyValueBox.SetField(doc, "tags", `"${tags.replace(splits[0] + ":", "")}"`, true);
+                const tags = StrCast(doc.tags, ':');
+                if (tags.includes(`${splits[0]}:`) && splits[1] === 'undefined') {
+                    KeyValueBox.SetField(doc, 'tags', `"${tags.replace(splits[0] + ':', '')}"`, true);
                 }
                 return true;
-            } else if (value[0] === "#") {
+            } else if (value[0] === '#') {
                 const newVal = value + `:'${value}'`;
                 doc[DataSym][value] = value;
-                const tags = StrCast(doc.tags, ":");
+                const tags = StrCast(doc.tags, ':');
                 if (!tags.includes(`${value}:`)) {
                     doc[DataSym].tags = `${tags + value + ':'}`;
                 }
@@ -267,68 +291,72 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             }
         });
         return false;
-    }
+    };
 
     @observable transform: Transform = Transform.Identity();
     getTransform = () => this.transform;
     propertiesDocViewRef = (ref: HTMLDivElement) => {
-        const observer = new _global.ResizeObserver(action((entries: any) => {
-            const cliRect = ref.getBoundingClientRect();
-            this.transform = new Transform(-cliRect.x, -cliRect.y, 1);
-        }));
+        const observer = new _global.ResizeObserver(
+            action((entries: any) => {
+                const cliRect = ref.getBoundingClientRect();
+                this.transform = new Transform(-cliRect.x, -cliRect.y, 1);
+            })
+        );
         ref && observer.observe(ref);
-    }
+    };
 
     @computed get contexts() {
-        return !this.selectedDoc ? (null) : <PropertiesDocContextSelector DocView={this.selectedDocumentView} hideTitle={true} addDocTab={this.props.addDocTab} />;
+        return !this.selectedDoc ? null : <PropertiesDocContextSelector DocView={this.selectedDocumentView} hideTitle={true} addDocTab={this.props.addDocTab} />;
     }
 
     @computed get links() {
-        return !this.selectedDoc ? (null) : <PropertiesDocBacklinksSelector Document={this.selectedDoc} hideTitle={true} addDocTab={this.props.addDocTab} />;
+        return !this.selectedDoc ? null : <PropertiesDocBacklinksSelector Document={this.selectedDoc} hideTitle={true} addDocTab={this.props.addDocTab} />;
     }
 
     @computed get layoutPreview() {
         if (SelectionManager.Views().length > 1) {
-            return "-- multiple selected --";
+            return '-- multiple selected --';
         }
         if (this.selectedDoc) {
             const layoutDoc = Doc.Layout(this.selectedDoc);
-            const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfHeight : this.docHeight;
-            const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes("FormattedTextBox") ? this.rtfWidth : this.docWidth;
-            return <div ref={this.propertiesDocViewRef} style={{ pointerEvents: "none", display: "inline-block", height: panelHeight() }} key={this.selectedDoc[Id]}>
-                <DocumentView
-                    Document={layoutDoc}
-                    DataDoc={this.dataDoc}
-                    renderDepth={1}
-                    fitContentsToBox={returnTrue}
-                    rootSelected={returnFalse}
-                    styleProvider={DefaultStyleProvider}
-                    docViewPath={returnEmptyDoclist}
-                    dontCenter={"y"}
-                    isDocumentActive={returnFalse}
-                    isContentActive={emptyFunction}
-                    NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined}
-                    NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined}
-                    PanelWidth={panelWidth}
-                    PanelHeight={panelHeight}
-                    focus={returnFalse}
-                    ScreenToLocalTransform={this.getTransform}
-                    docFilters={returnEmptyFilter}
-                    docRangeFilters={returnEmptyFilter}
-                    searchFilterDocs={returnEmptyDoclist}
-                    ContainingCollectionDoc={undefined}
-                    ContainingCollectionView={undefined}
-                    addDocument={returnFalse}
-                    moveDocument={undefined}
-                    removeDocument={returnFalse}
-                    whenChildContentsActiveChanged={emptyFunction}
-                    addDocTab={returnFalse}
-                    pinToPres={emptyFunction}
-                    bringToFront={returnFalse}
-                    dontRegisterView={true}
-                    dropAction={undefined}
-                />
-            </div>;
+            const panelHeight = StrCast(Doc.LayoutField(layoutDoc)).includes('FormattedTextBox') ? this.rtfHeight : this.docHeight;
+            const panelWidth = StrCast(Doc.LayoutField(layoutDoc)).includes('FormattedTextBox') ? this.rtfWidth : this.docWidth;
+            return (
+                <div ref={this.propertiesDocViewRef} style={{ pointerEvents: 'none', display: 'inline-block', height: panelHeight() }} key={this.selectedDoc[Id]}>
+                    <DocumentView
+                        Document={layoutDoc}
+                        DataDoc={this.dataDoc}
+                        renderDepth={1}
+                        fitContentsToBox={returnTrue}
+                        rootSelected={returnFalse}
+                        styleProvider={DefaultStyleProvider}
+                        docViewPath={returnEmptyDoclist}
+                        dontCenter={'y'}
+                        isDocumentActive={returnFalse}
+                        isContentActive={emptyFunction}
+                        NativeWidth={layoutDoc.type === DocumentType.RTF ? this.rtfWidth : undefined}
+                        NativeHeight={layoutDoc.type === DocumentType.RTF ? this.rtfHeight : undefined}
+                        PanelWidth={panelWidth}
+                        PanelHeight={panelHeight}
+                        focus={returnFalse}
+                        ScreenToLocalTransform={this.getTransform}
+                        docFilters={returnEmptyFilter}
+                        docRangeFilters={returnEmptyFilter}
+                        searchFilterDocs={returnEmptyDoclist}
+                        ContainingCollectionDoc={undefined}
+                        ContainingCollectionView={undefined}
+                        addDocument={returnFalse}
+                        moveDocument={undefined}
+                        removeDocument={returnFalse}
+                        whenChildContentsActiveChanged={emptyFunction}
+                        addDocTab={returnFalse}
+                        pinToPres={emptyFunction}
+                        bringToFront={returnFalse}
+                        dontRegisterView={true}
+                        dropAction={undefined}
+                    />
+                </div>
+            );
         } else {
             return null;
         }
@@ -339,67 +367,85 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      */
     @undoBatch
     changePermissions = (e: any, user: string) => {
-        const docs = SelectionManager.Views().length < 2 ? (this.selectedDoc ? [this.selectedDoc]:[]) : SelectionManager.Views().map(docView => docView.props.Document);
+        const docs = SelectionManager.Views().length < 2 ? (this.selectedDoc ? [this.selectedDoc] : []) : SelectionManager.Views().map(docView => docView.props.Document);
         SharingManager.Instance.shareFromPropertiesSidebar(user, e.currentTarget.value as SharingPermissions, docs);
-    }
+    };
 
     /**
      * @returns the options for the permissions dropdown.
      */
     getPermissionsSelect(user: string, permission: string) {
         const dropdownValues: string[] = Object.values(SharingPermissions);
-        if (permission === "-multiple-") dropdownValues.unshift(permission);
-        if (user === "Override") dropdownValues.unshift("None");
-        return <select className="permissions-select"
-            value={permission}
-            onChange={e => this.changePermissions(e, user)}>
-            {dropdownValues.filter(permission =>
-                !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)).map(permission =>
-                    <option key={permission} value={permission}>  {permission}  </option>)}
-        </select>;
+        if (permission === '-multiple-') dropdownValues.unshift(permission);
+        if (user === 'Override') dropdownValues.unshift('None');
+        return (
+            <select className="permissions-select" value={permission} onChange={e => this.changePermissions(e, user)}>
+                {dropdownValues
+                    .filter(permission => !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any))
+                    .map(permission => (
+                        <option key={permission} value={permission}>
+                            {' '}
+                            {permission}{' '}
+                        </option>
+                    ))}
+            </select>
+        );
     }
 
     /**
      * @returns the notification icon. On clicking, it should notify someone of a document been shared with them.
      */
     @computed get notifyIcon() {
-        return <Tooltip title={<div className="dash-tooltip">Notify with message</div>}>
-            <div className="notify-button">
-                <FontAwesomeIcon className="notify-button-icon" icon="bell" color="white" size="sm" />
-            </div>
-        </Tooltip>;
+        return (
+            <Tooltip title={<div className="dash-tooltip">Notify with message</div>}>
+                <div className="notify-button">
+                    <FontAwesomeIcon className="notify-button-icon" icon="bell" color="white" size="sm" />
+                </div>
+            </Tooltip>
+        );
     }
 
     /**
      * ... next to the owner that opens the main SharingManager interface on click.
      */
     @computed get expansionIcon() {
-        return <Tooltip title={<div className="dash-tooltip">{"Show more permissions"}</div>}>
-            <div className="expansion-button" onPointerDown={() => {
-                if (this.selectedDocumentView || this.selectedDoc) {
-                    SharingManager.Instance.open(this.selectedDocumentView?.props.Document === this.selectedDoc ? this.selectedDocumentView : undefined, this.selectedDoc);
-                }
-            }}>
-                <FontAwesomeIcon className="expansion-button-icon" icon="ellipsis-h" color="black" size="sm" />
-            </div>
-        </Tooltip>;
+        return (
+            <Tooltip title={<div className="dash-tooltip">{'Show more permissions'}</div>}>
+                <div
+                    className="expansion-button"
+                    onPointerDown={() => {
+                        if (this.selectedDocumentView || this.selectedDoc) {
+                            SharingManager.Instance.open(this.selectedDocumentView?.props.Document === this.selectedDoc ? this.selectedDocumentView : undefined, this.selectedDoc);
+                        }
+                    }}>
+                    <FontAwesomeIcon className="expansion-button-icon" icon="ellipsis-h" color="black" size="sm" />
+                </div>
+            </Tooltip>
+        );
     }
 
     /**
      * @returns a row of the permissions panel
      */
     sharingItem(name: string, admin: boolean, permission: string, showExpansionIcon?: boolean) {
-        return <div className="propertiesView-sharingTable-item" key={name + permission}
-        // style={{ backgroundColor: this.selectedUser === name ? "#bcecfc" : "" }}
-        // onPointerDown={action(() => this.selectedUser = this.selectedUser === name ? "" : name)}
-        >
-            <div className="propertiesView-sharingTable-item-name" style={{ width: name !== "Me" ? "85px" : "80px" }}> {name} </div>
-            {/* {name !== "Me" ? this.notifyIcon : null} */}
-            <div className="propertiesView-sharingTable-item-permission">
-                {admin && permission !== "Owner" ? this.getPermissionsSelect(name, permission) : permission}
-                {permission === "Owner" || showExpansionIcon ? this.expansionIcon : null}
+        return (
+            <div
+                className="propertiesView-sharingTable-item"
+                key={name + permission}
+                // style={{ backgroundColor: this.selectedUser === name ? "#bcecfc" : "" }}
+                // onPointerDown={action(() => this.selectedUser = this.selectedUser === name ? "" : name)}
+            >
+                <div className="propertiesView-sharingTable-item-name" style={{ width: name !== 'Me' ? '85px' : '80px' }}>
+                    {' '}
+                    {name}{' '}
+                </div>
+                {/* {name !== "Me" ? this.notifyIcon : null} */}
+                <div className="propertiesView-sharingTable-item-permission">
+                    {admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission) : permission}
+                    {permission === 'Owner' || showExpansionIcon ? this.expansionIcon : null}
+                </div>
             </div>
-        </div>;
+        );
     }
 
     /**
@@ -407,19 +453,18 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      */
     @computed get sharingTable() {
         const AclMap = new Map<symbol, string>([
-            [AclUnset, "None"],
+            [AclUnset, 'None'],
             [AclPrivate, SharingPermissions.None],
             [AclReadonly, SharingPermissions.View],
             [AclAugment, SharingPermissions.Augment],
             [AclSelfEdit, SharingPermissions.SelfEdit],
             [AclEdit, SharingPermissions.Edit],
-            [AclAdmin, SharingPermissions.Admin]
+            [AclAdmin, SharingPermissions.Admin],
         ]);
 
         // all selected docs
-        const docs = SelectionManager.Views().length < 2 ?
-            [this.layoutDocAcls ? this.selectedDoc : this.selectedDoc?.[DataSym]]
-            : SelectionManager.Views().map(docView => this.layoutDocAcls ? docView.props.Document : docView.props.Document[DataSym]);
+        const docs =
+            SelectionManager.Views().length < 2 ? [this.layoutDocAcls ? this.selectedDoc : this.selectedDoc?.[DataSym]] : SelectionManager.Views().map(docView => (this.layoutDocAcls ? docView.props.Document : docView.props.Document[DataSym]));
 
         const target = docs[0];
 
@@ -428,7 +473,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const showAdmin = effectiveAcls.every(acl => acl === AclAdmin);
 
         // users in common between all docs
-        const commonKeys: string[] = intersection(...docs.map(doc => this.layoutDocAcls ? doc?.[AclSym] && Object.keys(doc[AclSym]) : doc?.[DataSym][AclSym] && Object.keys(doc[DataSym][AclSym])));
+        const commonKeys: string[] = intersection(...docs.map(doc => (this.layoutDocAcls ? doc?.[AclSym] && Object.keys(doc[AclSym]) : doc?.[DataSym][AclSym] && Object.keys(doc[DataSym][AclSym]))));
 
         const tableEntries = [];
 
@@ -436,9 +481,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         if (commonKeys.length) {
             for (const key of commonKeys) {
                 const name = denormalizeEmail(key.substring(4));
-                const uniform = docs.every(doc => this.layoutDocAcls ? doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key] : doc?.[DataSym]?.[AclSym]?.[key] === docs[0]?.[DataSym]?.[AclSym]?.[key]);
-                if (name !== Doc.CurrentUserEmail && name !== target.author && name !== "Public" && name !== "Override"/* && sidebarUsersDisplayed![name] !== false*/) {
-                    tableEntries.push(this.sharingItem(name, showAdmin, uniform ? AclMap.get(this.layoutDocAcls ? target[AclSym][key] : target[DataSym][AclSym][key])! : "-multiple-"));
+                const uniform = docs.every(doc => (this.layoutDocAcls ? doc?.[AclSym]?.[key] === docs[0]?.[AclSym]?.[key] : doc?.[DataSym]?.[AclSym]?.[key] === docs[0]?.[DataSym]?.[AclSym]?.[key]));
+                if (name !== Doc.CurrentUserEmail && name !== target.author && name !== 'Public' && name !== 'Override' /* && sidebarUsersDisplayed![name] !== false*/) {
+                    tableEntries.push(this.sharingItem(name, showAdmin, uniform ? AclMap.get(this.layoutDocAcls ? target[AclSym][key] : target[DataSym][AclSym][key])! : '-multiple-'));
                 }
             }
         }
@@ -446,62 +491,53 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const ownerSame = Doc.CurrentUserEmail !== target.author && docs.filter(doc => doc).every(doc => doc.author === docs[0].author);
         // shifts the current user, owner, public to the top of the doc.
         // tableEntries.unshift(this.sharingItem("Override", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Override"] === docs[0]["acl-Override"]) ? (AclMap.get(target[AclSym]?.["acl-Override"]) || "None") : "-multiple-"));
-        tableEntries.unshift(this.sharingItem("Public", showAdmin, docs.filter(doc => doc).every(doc => doc["acl-Public"] === docs[0]["acl-Public"]) ? (AclMap.get(target[AclSym]?.["acl-Public"]) || SharingPermissions.None) : "-multiple-"));
-        tableEntries.unshift(this.sharingItem("Me", showAdmin, docs.filter(doc => doc).every(doc => doc.author === Doc.CurrentUserEmail) ? "Owner" : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? AclMap.get(effectiveAcls[0])! : "-multiple-", !ownerSame));
-        if (ownerSame) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, "Owner"));
+        tableEntries.unshift(this.sharingItem('Public', showAdmin, docs.filter(doc => doc).every(doc => doc['acl-Public'] === docs[0]['acl-Public']) ? AclMap.get(target[AclSym]?.['acl-Public']) || SharingPermissions.None : '-multiple-'));
+        tableEntries.unshift(
+            this.sharingItem('Me', showAdmin, docs.filter(doc => doc).every(doc => doc.author === Doc.CurrentUserEmail) ? 'Owner' : effectiveAcls.every(acl => acl === effectiveAcls[0]) ? AclMap.get(effectiveAcls[0])! : '-multiple-', !ownerSame)
+        );
+        if (ownerSame) tableEntries.unshift(this.sharingItem(StrCast(target.author), showAdmin, 'Owner'));
 
-        return <div className="propertiesView-sharingTable">
-            {tableEntries}
-        </div>;
+        return <div className="propertiesView-sharingTable">{tableEntries}</div>;
     }
 
     @computed get fieldsCheckbox() {
-        return <Checkbox
-            color="primary"
-            onChange={this.toggleCheckbox}
-            checked={this.layoutFields}
-        />;
+        return <Checkbox color="primary" onChange={this.toggleCheckbox} checked={this.layoutFields} />;
     }
 
     @action
     toggleCheckbox = () => {
         this.layoutFields = !this.layoutFields;
-    }
+    };
 
     @computed get editableTitle() {
         const titles = new Set<string>();
         SelectionManager.Views().forEach(dv => titles.add(StrCast(dv.rootDoc.title)));
-        const title = Array.from(titles.keys()).length > 1 ? "--multiple selected--" : StrCast(this.selectedDoc?.title);
-        return <div className="editable-title">
-            <EditableView
-                key="editableView"
-                contents={title}
-                height={25}
-                fontSize={14}
-                GetValue={() => title}
-                SetValue={this.setTitle} />
-        </div>;
+        const title = Array.from(titles.keys()).length > 1 ? '--multiple selected--' : StrCast(this.selectedDoc?.title);
+        return (
+            <div className="editable-title">
+                <EditableView key="editableView" contents={title} height={25} fontSize={14} GetValue={() => title} SetValue={this.setTitle} />
+            </div>
+        );
     }
 
     @undoBatch
     @action
     setTitle = (value: string) => {
         if (SelectionManager.Views().length > 1) {
-            SelectionManager.Views().map(dv => Doc.SetInPlace(dv.rootDoc, "title", value, true));
+            SelectionManager.Views().map(dv => Doc.SetInPlace(dv.rootDoc, 'title', value, true));
             return true;
         } else if (this.dataDoc) {
-            if (this.selectedDoc) Doc.SetInPlace(this.selectedDoc, "title", value, true);
-            else KeyValueBox.SetField(this.dataDoc, "title", value, true);
+            if (this.selectedDoc) Doc.SetInPlace(this.selectedDoc, 'title', value, true);
+            else KeyValueBox.SetField(this.dataDoc, 'title', value, true);
             return true;
         }
         return false;
-    }
-
+    };
 
     @undoBatch
     @action
     rotate = (angle: number) => {
-        const _centerPoints: { X: number, Y: number }[] = [];
+        const _centerPoints: { X: number; Y: number }[] = [];
         if (this.selectedDoc) {
             const doc = this.selectedDoc;
             if (doc.type === DocumentType.INK && doc.x && doc.y && doc._width && doc._height && doc.data) {
@@ -522,7 +558,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 doc.rotation = NumCast(doc.rotation) + angle;
                 const inks = Cast(doc.data, InkField)?.inkData;
                 if (inks) {
-                    const newPoints: { X: number, Y: number }[] = [];
+                    const newPoints: { X: number; Y: number }[] = [];
                     inks.forEach(ink => {
                         const newX = Math.cos(angle) * (ink.X - _centerPoints[index].X) - Math.sin(angle) * (ink.Y - _centerPoints[index].Y) + _centerPoints[index].X;
                         const newY = Math.sin(angle) * (ink.X - _centerPoints[index].X) + Math.cos(angle) * (ink.Y - _centerPoints[index].Y) + _centerPoints[index].Y;
@@ -536,117 +572,134 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                     const right = Math.max(...xs);
                     const bottom = Math.max(...ys);
 
-                    doc._height = (bottom - top);
-                    doc._width = (right - left);
+                    doc._height = bottom - top;
+                    doc._width = right - left;
                 }
                 index++;
             }
         }
-    }
+    };
 
     @computed
     get controlPointsButton() {
-        return <div className="inking-button">
-            <Tooltip title={<div className="dash-tooltip">{"Edit points"}</div>}>
-                <div className="inking-button-points"
-                    style={{ backgroundColor: InkStrokeProperties.Instance._controlButton ? "black" : "" }}
-                    onPointerDown={action(() => InkStrokeProperties.Instance._controlButton = !InkStrokeProperties.Instance._controlButton)} >
-                    <FontAwesomeIcon icon="bezier-curve" color="white" size="lg" />
-                </div>
-            </Tooltip>
-            <Tooltip title={<div className="dash-tooltip">{InkStrokeProperties.Instance._lock ? "Unlock ratio" : "Lock ratio"}</div>}>
-                <div className="inking-button-lock" onPointerDown={action(() => InkStrokeProperties.Instance._lock = !InkStrokeProperties.Instance._lock)} >
-                    <FontAwesomeIcon icon={InkStrokeProperties.Instance._lock ? "lock" : "unlock"} color="white" size="lg" />
-                </div>
-            </Tooltip>
-            <Tooltip title={<div className="dash-tooltip">{"Rotate 90˚"}</div>}>
-                <div className="inking-button-rotate" onPointerDown={action(() => this.rotate(Math.PI / 2))}>
-                    <FontAwesomeIcon icon="undo" color="white" size="lg" />
-                </div>
-            </Tooltip>
-        </div>;
+        return (
+            <div className="inking-button">
+                <Tooltip title={<div className="dash-tooltip">{'Edit points'}</div>}>
+                    <div
+                        className="inking-button-points"
+                        style={{ backgroundColor: InkStrokeProperties.Instance._controlButton ? 'black' : '' }}
+                        onPointerDown={action(() => (InkStrokeProperties.Instance._controlButton = !InkStrokeProperties.Instance._controlButton))}>
+                        <FontAwesomeIcon icon="bezier-curve" color="white" size="lg" />
+                    </div>
+                </Tooltip>
+                <Tooltip title={<div className="dash-tooltip">{InkStrokeProperties.Instance._lock ? 'Unlock ratio' : 'Lock ratio'}</div>}>
+                    <div className="inking-button-lock" onPointerDown={action(() => (InkStrokeProperties.Instance._lock = !InkStrokeProperties.Instance._lock))}>
+                        <FontAwesomeIcon icon={InkStrokeProperties.Instance._lock ? 'lock' : 'unlock'} color="white" size="lg" />
+                    </div>
+                </Tooltip>
+                <Tooltip title={<div className="dash-tooltip">{'Rotate 90˚'}</div>}>
+                    <div className="inking-button-rotate" onPointerDown={action(() => this.rotate(Math.PI / 2))}>
+                        <FontAwesomeIcon icon="undo" color="white" size="lg" />
+                    </div>
+                </Tooltip>
+            </div>
+        );
     }
 
     inputBox = (key: string, value: any, setter: (val: string) => {}, title: string) => {
-        return <div className="inputBox"
-            style={{
-                marginRight: title === "X:" ? "19px" : "",
-                marginLeft: title === "∠:" ? "39px" : ""
-            }}>
-            <div className="inputBox-title"> {title} </div>
-            <input className="inputBox-input"
-                type="text" value={value}
-                onChange={e => {
-                    setter(e.target.value);
-                }}
-                onKeyPress={e => {
-                    e.stopPropagation();
-                }} />
-            <div className="inputBox-button">
-                <div className="inputBox-button-up" key="up2"
-                    onPointerDown={undoBatch(action(() => this.upDownButtons("up", key)))} >
-                    <FontAwesomeIcon icon="caret-up" color="white" size="sm" />
-                </div>
-                <div className="inputbox-Button-down" key="down2"
-                    onPointerDown={undoBatch(action(() => this.upDownButtons("down", key)))} >
-                    <FontAwesomeIcon icon="caret-down" color="white" size="sm" />
+        return (
+            <div
+                className="inputBox"
+                style={{
+                    marginRight: title === 'X:' ? '19px' : '',
+                    marginLeft: title === '∠:' ? '39px' : '',
+                }}>
+                <div className="inputBox-title"> {title} </div>
+                <input
+                    className="inputBox-input"
+                    type="text"
+                    value={value}
+                    onChange={e => {
+                        setter(e.target.value);
+                    }}
+                    onKeyPress={e => {
+                        e.stopPropagation();
+                    }}
+                />
+                <div className="inputBox-button">
+                    <div className="inputBox-button-up" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons('up', key)))}>
+                        <FontAwesomeIcon icon="caret-up" color="white" size="sm" />
+                    </div>
+                    <div className="inputbox-Button-down" key="down2" onPointerDown={undoBatch(action(() => this.upDownButtons('down', key)))}>
+                        <FontAwesomeIcon icon="caret-down" color="white" size="sm" />
+                    </div>
                 </div>
             </div>
-        </div>;
-    }
+        );
+    };
 
     inputBoxDuo = (key: string, value: any, setter: (val: string) => {}, title1: string, key2: string, value2: any, setter2: (val: string) => {}, title2: string) => {
-        return <div className="inputBox-duo">
-            {this.inputBox(key, value, setter, title1)}
-            {title2 === "" ? (null) : this.inputBox(key2, value2, setter2, title2)}
-        </div>;
-    }
+        return (
+            <div className="inputBox-duo">
+                {this.inputBox(key, value, setter, title1)}
+                {title2 === '' ? null : this.inputBox(key2, value2, setter2, title2)}
+            </div>
+        );
+    };
 
     @action
     upDownButtons = (dirs: string, field: string) => {
         switch (field) {
-            case "rot": this.rotate((dirs === "up" ? .1 : -.1)); break;
-            case "Xps": this.selectedDoc && (this.selectedDoc.x = NumCast(this.selectedDoc?.x) + (dirs === "up" ? 10 : -10)); break;
-            case "Yps": this.selectedDoc && (this.selectedDoc.y = NumCast(this.selectedDoc?.y) + (dirs === "up" ? 10 : -10)); break;
-            case "stk": this.selectedDoc && (this.selectedDoc.strokeWidth = NumCast(this.selectedDoc?.strokeWidth) + (dirs === "up" ? .1 : -.1)); break;
-            case "wid":
+            case 'rot':
+                this.rotate(dirs === 'up' ? 0.1 : -0.1);
+                break;
+            case 'Xps':
+                this.selectedDoc && (this.selectedDoc.x = NumCast(this.selectedDoc?.x) + (dirs === 'up' ? 10 : -10));
+                break;
+            case 'Yps':
+                this.selectedDoc && (this.selectedDoc.y = NumCast(this.selectedDoc?.y) + (dirs === 'up' ? 10 : -10));
+                break;
+            case 'stk':
+                this.selectedDoc && (this.selectedDoc.strokeWidth = NumCast(this.selectedDoc?.strokeWidth) + (dirs === 'up' ? 0.1 : -0.1));
+                break;
+            case 'wid':
                 const oldWidth = NumCast(this.selectedDoc?._width);
                 const oldHeight = NumCast(this.selectedDoc?._height);
                 const oldX = NumCast(this.selectedDoc?.x);
                 const oldY = NumCast(this.selectedDoc?.y);
-                this.selectedDoc && (this.selectedDoc._width = oldWidth + (dirs === "up" ? 10 : - 10));
-                InkStrokeProperties.Instance._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) / oldWidth * NumCast(this.selectedDoc?._height)));
+                this.selectedDoc && (this.selectedDoc._width = oldWidth + (dirs === 'up' ? 10 : -10));
+                InkStrokeProperties.Instance._lock && this.selectedDoc && (this.selectedDoc._height = (NumCast(this.selectedDoc?._width) / oldWidth) * NumCast(this.selectedDoc?._height));
                 const doc = this.selectedDoc;
                 if (doc?.type === DocumentType.INK && doc.x && doc.y && doc._height && doc._width) {
                     const ink = Cast(doc.data, InkField)?.inkData;
                     if (ink) {
-                        const newPoints: { X: number, Y: number }[] = [];
+                        const newPoints: { X: number; Y: number }[] = [];
                         for (var j = 0; j < ink.length; j++) {
-                            // (new x — oldx) + (oldxpoint * newWidt)/oldWidth 
-                            const newX = (NumCast(doc.x) - oldX) + (ink[j].X * NumCast(doc._width)) / oldWidth;
-                            const newY = (NumCast(doc.y) - oldY) + (ink[j].Y * NumCast(doc._height)) / oldHeight;
+                            // (new x — oldx) + (oldxpoint * newWidt)/oldWidth
+                            const newX = NumCast(doc.x) - oldX + (ink[j].X * NumCast(doc._width)) / oldWidth;
+                            const newY = NumCast(doc.y) - oldY + (ink[j].Y * NumCast(doc._height)) / oldHeight;
                             newPoints.push({ X: newX, Y: newY });
                         }
                         doc.data = new InkField(newPoints);
                     }
                 }
                 break;
-            case "hgt":
+            case 'hgt':
                 const oWidth = NumCast(this.selectedDoc?._width);
                 const oHeight = NumCast(this.selectedDoc?._height);
                 const oX = NumCast(this.selectedDoc?.x);
                 const oY = NumCast(this.selectedDoc?.y);
-                this.selectedDoc && (this.selectedDoc._height = oHeight + (dirs === "up" ? 10 : - 10));
-                InkStrokeProperties.Instance._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) / oHeight * NumCast(this.selectedDoc?._width)));
+                this.selectedDoc && (this.selectedDoc._height = oHeight + (dirs === 'up' ? 10 : -10));
+                InkStrokeProperties.Instance._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) / oHeight) * NumCast(this.selectedDoc?._width));
                 const docu = this.selectedDoc;
                 if (docu?.type === DocumentType.INK && docu.x && docu.y && docu._height && docu._width) {
                     const ink = Cast(docu.data, InkField)?.inkData;
                     if (ink) {
-                        const newPoints: { X: number, Y: number }[] = [];
+                        const newPoints: { X: number; Y: number }[] = [];
                         for (var j = 0; j < ink.length; j++) {
-                            // (new x — oldx) + (oldxpoint * newWidt)/oldWidth 
-                            const newX = (NumCast(docu.x) - oX) + (ink[j].X * NumCast(docu._width)) / oWidth;
-                            const newY = (NumCast(docu.y) - oY) + (ink[j].Y * NumCast(docu._height)) / oHeight;
+                            // (new x — oldx) + (oldxpoint * newWidt)/oldWidth
+                            const newX = NumCast(docu.x) - oX + (ink[j].X * NumCast(docu._width)) / oWidth;
+                            const newY = NumCast(docu.y) - oY + (ink[j].Y * NumCast(docu._height)) / oHeight;
                             newPoints.push({ X: newX, Y: newY });
                         }
                         docu.data = new InkField(newPoints);
@@ -654,7 +707,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 }
                 break;
         }
-    }
+    };
 
     getField(key: string) {
         //if (this.selectedDoc) {
@@ -664,14 +717,30 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         // }
     }
 
-    @computed get shapeXps() { return this.getField("x"); }
-    @computed get shapeYps() { return this.getField("y"); }
-    @computed get shapeRot() { return this.getField("rotation"); }
-    @computed get shapeHgt() { return this.getField("_height"); }
-    @computed get shapeWid() { return this.getField("_width"); }
-    set shapeXps(value) { this.selectedDoc && (this.selectedDoc.x = Number(value)); }
-    set shapeYps(value) { this.selectedDoc && (this.selectedDoc.y = Number(value)); }
-    set shapeRot(value) { this.selectedDoc && (this.selectedDoc.rotation = Number(value)); }
+    @computed get shapeXps() {
+        return this.getField('x');
+    }
+    @computed get shapeYps() {
+        return this.getField('y');
+    }
+    @computed get shapeRot() {
+        return this.getField('rotation');
+    }
+    @computed get shapeHgt() {
+        return this.getField('_height');
+    }
+    @computed get shapeWid() {
+        return this.getField('_width');
+    }
+    set shapeXps(value) {
+        this.selectedDoc && (this.selectedDoc.x = Number(value));
+    }
+    set shapeYps(value) {
+        this.selectedDoc && (this.selectedDoc.y = Number(value));
+    }
+    set shapeRot(value) {
+        this.selectedDoc && (this.selectedDoc.rotation = Number(value));
+    }
     set shapeWid(value) {
         const oldWidth = NumCast(this.selectedDoc?._width);
         this.selectedDoc && (this.selectedDoc._width = Number(value));
@@ -683,38 +752,122 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         InkStrokeProperties.Instance._lock && this.selectedDoc && (this.selectedDoc._width = (NumCast(this.selectedDoc?._height) * NumCast(this.selectedDoc?._width)) / oldHeight);
     }
 
-    @computed get hgtInput() { return this.inputBoxDuo("hgt", this.shapeHgt, (val: string) => { if (!isNaN(Number(val))) { this.shapeHgt = val; } return true; }, "H:", "wid", this.shapeWid, (val: string) => { if (!isNaN(Number(val))) { this.shapeWid = val; } return true; }, "W:"); }
-    @computed get XpsInput() { return this.inputBoxDuo("Xps", this.shapeXps, (val: string) => { if (val !== "0" && !isNaN(Number(val))) { this.shapeXps = val; } return true; }, "X:", "Yps", this.shapeYps, (val: string) => { if (val !== "0" && !isNaN(Number(val))) { this.shapeYps = val; } return true; }, "Y:"); }
-    @computed get rotInput() { return this.inputBoxDuo("rot", this.shapeRot, (val: string) => { if (!isNaN(Number(val))) { this.rotate(Number(val) - Number(this.shapeRot)); this.shapeRot = val; } return true; }, "∠:", "rot", this.shapeRot, (val: string) => { if (!isNaN(Number(val))) { this.rotate(Number(val) - Number(this.shapeRot)); this.shapeRot = val; } return true; }, ""); }
-
+    @computed get hgtInput() {
+        return this.inputBoxDuo(
+            'hgt',
+            this.shapeHgt,
+            (val: string) => {
+                if (!isNaN(Number(val))) {
+                    this.shapeHgt = val;
+                }
+                return true;
+            },
+            'H:',
+            'wid',
+            this.shapeWid,
+            (val: string) => {
+                if (!isNaN(Number(val))) {
+                    this.shapeWid = val;
+                }
+                return true;
+            },
+            'W:'
+        );
+    }
+    @computed get XpsInput() {
+        return this.inputBoxDuo(
+            'Xps',
+            this.shapeXps,
+            (val: string) => {
+                if (val !== '0' && !isNaN(Number(val))) {
+                    this.shapeXps = val;
+                }
+                return true;
+            },
+            'X:',
+            'Yps',
+            this.shapeYps,
+            (val: string) => {
+                if (val !== '0' && !isNaN(Number(val))) {
+                    this.shapeYps = val;
+                }
+                return true;
+            },
+            'Y:'
+        );
+    }
+    @computed get rotInput() {
+        return this.inputBoxDuo(
+            'rot',
+            this.shapeRot,
+            (val: string) => {
+                if (!isNaN(Number(val))) {
+                    this.rotate(Number(val) - Number(this.shapeRot));
+                    this.shapeRot = val;
+                }
+                return true;
+            },
+            '∠:',
+            'rot',
+            this.shapeRot,
+            (val: string) => {
+                if (!isNaN(Number(val))) {
+                    this.rotate(Number(val) - Number(this.shapeRot));
+                    this.shapeRot = val;
+                }
+                return true;
+            },
+            ''
+        );
+    }
 
     @observable private _fillBtn = false;
     @observable private _lineBtn = false;
 
-    private _lastFill = "#D0021B";
-    private _lastLine = "#D0021B";
-    private _lastDash: any = "2";
+    private _lastFill = '#D0021B';
+    private _lastLine = '#D0021B';
+    private _lastDash: any = '2';
 
-    @computed get colorFil() { const ccol = this.getField("fillColor") || ""; ccol && (this._lastFill = ccol); return ccol; }
-    @computed get colorStk() { const ccol = this.getField("color") || ""; ccol && (this._lastLine = ccol); return ccol; }
-    set colorFil(value) { value && (this._lastFill = value); this.selectedDoc && (this.selectedDoc.fillColor = value ? value : undefined); }
-    set colorStk(value) { value && (this._lastLine = value); this.selectedDoc && (this.selectedDoc.color = value ? value : undefined); }
+    @computed get colorFil() {
+        const ccol = this.getField('fillColor') || '';
+        ccol && (this._lastFill = ccol);
+        return ccol;
+    }
+    @computed get colorStk() {
+        const ccol = this.getField('color') || '';
+        ccol && (this._lastLine = ccol);
+        return ccol;
+    }
+    set colorFil(value) {
+        value && (this._lastFill = value);
+        this.selectedDoc && (this.selectedDoc.fillColor = value ? value : undefined);
+    }
+    set colorStk(value) {
+        value && (this._lastLine = value);
+        this.selectedDoc && (this.selectedDoc.color = value ? value : undefined);
+    }
 
     colorButton(value: string, type: string, setter: () => {}) {
         // return <div className="properties-flyout" onPointerEnter={e => this.changeScrolling(false)}
         //     onPointerLeave={e => this.changeScrolling(true)}>
         //     <Flyout anchorPoint={anchorPoints.LEFT_TOP}
         //         content={type === "fill" ? this.fillPicker : this.linePicker}>
-        return <div className="color-button" key="color" onPointerDown={undoBatch(action(e => setter()))}>
-            <div className="color-button-preview" style={{
-                backgroundColor: value ?? "121212", width: 15, height: 15,
-                display: value === "" || value === "transparent" ? "none" : ""
-            }} />
-            {value === "" || value === "transparent" ? <p style={{ fontSize: 25, color: "red", marginTop: -14 }}>☒</p> : ""}
-        </div>;
+        return (
+            <div className="color-button" key="color" onPointerDown={undoBatch(action(e => setter()))}>
+                <div
+                    className="color-button-preview"
+                    style={{
+                        backgroundColor: value ?? '121212',
+                        width: 15,
+                        height: 15,
+                        display: value === '' || value === 'transparent' ? 'none' : '',
+                    }}
+                />
+                {value === '' || value === 'transparent' ? <p style={{ fontSize: 25, color: 'red', marginTop: -14 }}>☒</p> : ''}
+            </div>
+        );
         //     </Flyout>
         // </div>;
-
     }
 
     @undoBatch
@@ -723,206 +876,271 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
         const val = String(color.hex);
         this.colorStk = val;
         return true;
-    }
+    };
     @undoBatch
     @action
     switchFil = (color: ColorState) => {
         const val = String(color.hex);
         this.colorFil = val;
         return true;
-    }
+    };
 
     colorPicker(setter: (color: string) => {}, type: string) {
-        return <SketchPicker onChange={type === "stk" ? this.switchStk : this.switchFil}
-            presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505',
-                '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B',
-                '#FFFFFF', '#f1efeb', 'transparent']}
-            color={type === "stk" ? this.colorStk : this.colorFil} />;
+        return (
+            <SketchPicker
+                onChange={type === 'stk' ? this.switchStk : this.switchFil}
+                presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
+                color={type === 'stk' ? this.colorStk : this.colorFil}
+            />
+        );
+    }
+
+    @computed get fillButton() {
+        return this.colorButton(this.colorFil, 'fill', () => {
+            this._fillBtn = !this._fillBtn;
+            this._lineBtn = false;
+            return true;
+        });
+    }
+    @computed get lineButton() {
+        return this.colorButton(this.colorStk, 'line', () => {
+            this._lineBtn = !this._lineBtn;
+            this._fillBtn = false;
+            return true;
+        });
     }
 
-    @computed get fillButton() { return this.colorButton(this.colorFil, "fill", () => { this._fillBtn = !this._fillBtn; this._lineBtn = false; return true; }); }
-    @computed get lineButton() { return this.colorButton(this.colorStk, "line", () => { this._lineBtn = !this._lineBtn; this._fillBtn = false; return true; }); }
-
-    @computed get fillPicker() { return this.colorPicker((color: string) => this.colorFil = color, "fil"); }
-    @computed get linePicker() { return this.colorPicker((color: string) => this.colorStk = color, "stk"); }
+    @computed get fillPicker() {
+        return this.colorPicker((color: string) => (this.colorFil = color), 'fil');
+    }
+    @computed get linePicker() {
+        return this.colorPicker((color: string) => (this.colorStk = color), 'stk');
+    }
 
     @computed get strokeAndFill() {
-        return <div>
-            <div key="fill" className="strokeAndFill">
-                <div className="fill">
-                    <div className="fill-title">Fill:</div>
-                    <div className="fill-button">{this.fillButton}</div>
-                </div>
-                <div className="stroke">
-                    <div className="stroke-title"> Stroke: </div>
-                    <div className="stroke-button">{this.lineButton}</div>
+        return (
+            <div>
+                <div key="fill" className="strokeAndFill">
+                    <div className="fill">
+                        <div className="fill-title">Fill:</div>
+                        <div className="fill-button">{this.fillButton}</div>
+                    </div>
+                    <div className="stroke">
+                        <div className="stroke-title"> Stroke: </div>
+                        <div className="stroke-button">{this.lineButton}</div>
+                    </div>
                 </div>
+                {this._fillBtn ? this.fillPicker : ''}
+                {this._lineBtn ? this.linePicker : ''}
             </div>
-            {this._fillBtn ? this.fillPicker : ""}
-            {this._lineBtn ? this.linePicker : ""}
-        </div>;
-    }
-
-    @computed get solidStk() { return this.selectedDoc?.color && (!this.selectedDoc?.strokeDash || this.selectedDoc?.strokeDash === "0") ? true : false; }
-    @computed get dashdStk() { return this.selectedDoc?.strokeDash || ""; }
-    @computed get unStrokd() { return this.selectedDoc?.color ? true : false; }
-    @computed get widthStk() { return this.getField("strokeWidth") || "1"; }
-    @computed get markScal() { return Number(this.getField("strokeMakerScale") || "1"); }
-    @computed get markHead() { return this.getField("strokeStartMarker") || ""; }
-    @computed get markTail() { return this.getField("strokeEndMarker") || ""; }
-    set solidStk(value) { this.dashdStk = ""; this.unStrokd = !value; }
+        );
+    }
+
+    @computed get solidStk() {
+        return this.selectedDoc?.color && (!this.selectedDoc?.strokeDash || this.selectedDoc?.strokeDash === '0') ? true : false;
+    }
+    @computed get dashdStk() {
+        return this.selectedDoc?.strokeDash || '';
+    }
+    @computed get unStrokd() {
+        return this.selectedDoc?.color ? true : false;
+    }
+    @computed get widthStk() {
+        return this.getField('strokeWidth') || '1';
+    }
+    @computed get markScal() {
+        return Number(this.getField('strokeMakerScale') || '1');
+    }
+    @computed get markHead() {
+        return this.getField('strokeStartMarker') || '';
+    }
+    @computed get markTail() {
+        return this.getField('strokeEndMarker') || '';
+    }
+    set solidStk(value) {
+        this.dashdStk = '';
+        this.unStrokd = !value;
+    }
     set dashdStk(value) {
         value && (this._lastDash = value) && (this.unStrokd = false);
         this.selectedDoc && (this.selectedDoc.strokeDash = value ? this._lastDash : undefined);
     }
-    set markScal(value) { this.selectedDoc && (this.selectedDoc.strokeMarkerScale = Number(value)); }
-    set widthStk(value) { this.selectedDoc && (this.selectedDoc.strokeWidth = Number(value)); }
-    set unStrokd(value) { this.colorStk = value ? "" : this._lastLine; }
-    set markHead(value) { this.selectedDoc && (this.selectedDoc.strokeStartMarker = value); }
-    set markTail(value) { this.selectedDoc && (this.selectedDoc.strokeEndMarker = value); }
-
-
-    @computed get stkInput() { return this.regInput("stk", this.widthStk, (val: string) => this.widthStk = val); }
-    @computed get markScaleInput() { return this.regInput("scale", this.markScal.toString(), (val: string) => this.markScal = Number(val)); }
+    set markScal(value) {
+        this.selectedDoc && (this.selectedDoc.strokeMarkerScale = Number(value));
+    }
+    set widthStk(value) {
+        this.selectedDoc && (this.selectedDoc.strokeWidth = Number(value));
+    }
+    set unStrokd(value) {
+        this.colorStk = value ? '' : this._lastLine;
+    }
+    set markHead(value) {
+        this.selectedDoc && (this.selectedDoc.strokeStartMarker = value);
+    }
+    set markTail(value) {
+        this.selectedDoc && (this.selectedDoc.strokeEndMarker = value);
+    }
 
+    @computed get stkInput() {
+        return this.regInput('stk', this.widthStk, (val: string) => (this.widthStk = val));
+    }
+    @computed get markScaleInput() {
+        return this.regInput('scale', this.markScal.toString(), (val: string) => (this.markScal = Number(val)));
+    }
 
     regInput = (key: string, value: any, setter: (val: string) => {}) => {
-        return <div className="inputBox">
-            <input className="inputBox-input"
-                type="text" value={value}
-                onChange={e => setter(e.target.value)} />
-            <div className="inputBox-button">
-                <div className="inputBox-button-up" key="up2"
-                    onPointerDown={undoBatch(action(() => this.upDownButtons("up", key)))} >
-                    <FontAwesomeIcon icon="caret-up" color="white" size="sm" />
-                </div>
-                <div className="inputbox-Button-down" key="down2"
-                    onPointerDown={undoBatch(action(() => this.upDownButtons("down", key)))} >
-                    <FontAwesomeIcon icon="caret-down" color="white" size="sm" />
+        return (
+            <div className="inputBox">
+                <input className="inputBox-input" type="text" value={value} onChange={e => setter(e.target.value)} />
+                <div className="inputBox-button">
+                    <div className="inputBox-button-up" key="up2" onPointerDown={undoBatch(action(() => this.upDownButtons('up', key)))}>
+                        <FontAwesomeIcon icon="caret-up" color="white" size="sm" />
+                    </div>
+                    <div className="inputbox-Button-down" key="down2" onPointerDown={undoBatch(action(() => this.upDownButtons('down', key)))}>
+                        <FontAwesomeIcon icon="caret-down" color="white" size="sm" />
+                    </div>
                 </div>
             </div>
-        </div>;
-    }
+        );
+    };
 
     @computed get widthAndDash() {
-        return <div className="widthAndDash">
-            <div className="width">
-                <div className="width-top">
-                    <div className="width-title">Width:</div>
-                    <div className="width-input">{this.stkInput}</div>
-                </div>
-                <input className="width-range" type="range"
-                    defaultValue={Number(this.widthStk)} min={1} max={100}
-                    onChange={(action((e) => this.widthStk = e.target.value))}
-                    onMouseDown={(e) => { this._widthUndo = UndoManager.StartBatch("width undo"); }}
-                    onMouseUp={(e) => { this._widthUndo?.end(); this._widthUndo = undefined; }}
-                />
-            </div>
-
-            <div className="arrows">
-                <div className="arrows-head">
+        return (
+            <div className="widthAndDash">
+                <div className="width">
                     <div className="width-top">
-                        <div className="width-title">Arrow Scale:</div>
-                        {/* <div className="width-input">{this.markScalInput}</div> */}
+                        <div className="width-title">Width:</div>
+                        <div className="width-input">{this.stkInput}</div>
                     </div>
-                    <input className="width-range" type="range"
-                        defaultValue={this.markScal} min={0} max={10}
-                        onChange={(action(e => this.markScal = +e.target.value))}
-                        onMouseDown={(e) => { this._widthUndo = UndoManager.StartBatch("scale undo"); }}
-                        onMouseUp={(e) => { this._widthUndo?.end(); this._widthUndo = undefined; }}
+                    <input
+                        className="width-range"
+                        type="range"
+                        defaultValue={Number(this.widthStk)}
+                        min={1}
+                        max={100}
+                        onChange={action(e => (this.widthStk = e.target.value))}
+                        onMouseDown={e => {
+                            this._widthUndo = UndoManager.StartBatch('width undo');
+                        }}
+                        onMouseUp={e => {
+                            this._widthUndo?.end();
+                            this._widthUndo = undefined;
+                        }}
                     />
                 </div>
-                <div className="arrows-head">
-                    <div className="arrows-head-title" >Arrow Head: </div>
-                    <input key="markHead" className="arrows-head-input" type="checkbox"
-                        checked={this.markHead !== ""}
-                        onChange={undoBatch(action(() => this.markHead = this.markHead ? "" : "arrow"))} />
+
+                <div className="arrows">
+                    <div className="arrows-head">
+                        <div className="width-top">
+                            <div className="width-title">Arrow Scale:</div>
+                            {/* <div className="width-input">{this.markScalInput}</div> */}
+                        </div>
+                        <input
+                            className="width-range"
+                            type="range"
+                            defaultValue={this.markScal}
+                            min={0}
+                            max={10}
+                            onChange={action(e => (this.markScal = +e.target.value))}
+                            onMouseDown={e => {
+                                this._widthUndo = UndoManager.StartBatch('scale undo');
+                            }}
+                            onMouseUp={e => {
+                                this._widthUndo?.end();
+                                this._widthUndo = undefined;
+                            }}
+                        />
+                    </div>
+                    <div className="arrows-head">
+                        <div className="arrows-head-title">Arrow Head: </div>
+                        <input key="markHead" className="arrows-head-input" type="checkbox" checked={this.markHead !== ''} onChange={undoBatch(action(() => (this.markHead = this.markHead ? '' : 'arrow')))} />
+                    </div>
+                    <div className="arrows-tail">
+                        <div className="arrows-tail-title">Arrow End: </div>
+                        <input key="markTail" className="arrows-tail-input" type="checkbox" checked={this.markTail !== ''} onChange={undoBatch(action(() => (this.markTail = this.markTail ? '' : 'arrow')))} />
+                    </div>
                 </div>
-                <div className="arrows-tail">
-                    <div className="arrows-tail-title" >Arrow End: </div>
-                    <input key="markTail" className="arrows-tail-input" type="checkbox"
-                        checked={this.markTail !== ""}
-                        onChange={undoBatch(action(() => this.markTail = this.markTail ? "" : "arrow"))} />
+                <div className="dashed">
+                    <div className="dashed-title">Dashed Line:</div>
+                    <input key="markHead" className="dashed-input" type="checkbox" checked={this.dashdStk === '2'} onChange={this.changeDash} />
                 </div>
             </div>
-            <div className="dashed">
-                <div className="dashed-title">Dashed Line:</div>
-                <input key="markHead" className="dashed-input"
-                    type="checkbox" checked={this.dashdStk === "2"}
-                    onChange={this.changeDash} />
-            </div>
-        </div>;
+        );
     }
 
-    @undoBatch @action
+    @undoBatch
+    @action
     changeDash = () => {
-        this.dashdStk = this.dashdStk === "2" ? "0" : "2";
-    }
+        this.dashdStk = this.dashdStk === '2' ? '0' : '2';
+    };
 
     @computed get appearanceEditor() {
-        return <div className="appearance-editor">
-            {this.widthAndDash}
-            {this.strokeAndFill}
-        </div>;
+        return (
+            <div className="appearance-editor">
+                {this.widthAndDash}
+                {this.strokeAndFill}
+            </div>
+        );
     }
 
     @computed get transformEditor() {
-        return <div className="transform-editor">
-            {this.controlPointsButton}
-            {this.hgtInput}
-            {this.XpsInput}
-            {this.rotInput}
-        </div>;
+        return (
+            <div className="transform-editor">
+                {this.controlPointsButton}
+                {this.hgtInput}
+                {this.XpsInput}
+                {this.rotInput}
+            </div>
+        );
     }
 
     @computed get optionsSubMenu() {
-        return <div className="propertiesView-settings" onPointerEnter={action(() => this.inOptions = true)}
-            onPointerLeave={action(() => this.inOptions = false)}>
-            <div className="propertiesView-settings-title"
-                onPointerDown={action(() => this.openOptions = !this.openOptions)}
-                style={{ backgroundColor: this.openOptions ? "black" : "" }}>
-                Options
-                <div className="propertiesView-settings-title-icon">
-                    <FontAwesomeIcon icon={this.openOptions ? "caret-down" : "caret-right"} size="lg" color="white" />
+        return (
+            <div className="propertiesView-settings" onPointerEnter={action(() => (this.inOptions = true))} onPointerLeave={action(() => (this.inOptions = false))}>
+                <div className="propertiesView-settings-title" onPointerDown={action(() => (this.openOptions = !this.openOptions))} style={{ backgroundColor: this.openOptions ? 'black' : '' }}>
+                    Options
+                    <div className="propertiesView-settings-title-icon">
+                        <FontAwesomeIcon icon={this.openOptions ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                    </div>
                 </div>
+                {!this.openOptions ? null : (
+                    <div className="propertiesView-settings-content">
+                        <PropertiesButtons />
+                    </div>
+                )}
             </div>
-            {!this.openOptions ? (null) :
-                <div className="propertiesView-settings-content">
-                    <PropertiesButtons />
-                </div>}
-        </div>;
+        );
     }
 
     @computed get sharingSubMenu() {
-        return <div className="propertiesView-sharing">
-            <div className="propertiesView-sharing-title"
-                onPointerDown={action(() => this.openSharing = !this.openSharing)}
-                style={{ backgroundColor: this.openSharing ? "black" : "" }}>
-                Sharing {"&"} Permissions
-                <div className="propertiesView-sharing-title-icon">
-                    <FontAwesomeIcon icon={this.openSharing ? "caret-down" : "caret-right"} size="lg" color="white" />
+        return (
+            <div className="propertiesView-sharing">
+                <div className="propertiesView-sharing-title" onPointerDown={action(() => (this.openSharing = !this.openSharing))} style={{ backgroundColor: this.openSharing ? 'black' : '' }}>
+                    Sharing {'&'} Permissions
+                    <div className="propertiesView-sharing-title-icon">
+                        <FontAwesomeIcon icon={this.openSharing ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                    </div>
                 </div>
-            </div>
-            {!this.openSharing ? (null) :
-                <div className="propertiesView-sharing-content">
-                    <div className="propertiesView-buttonContainer">
-                        {!Doc.noviceMode ? (<div className="propertiesView-acls-checkbox">
-                            <Checkbox
-                                color="primary"
-                                onChange={action(() => this.layoutDocAcls = !this.layoutDocAcls)}
-                                checked={this.layoutDocAcls}
-                            />
-                            <div className="propertiesView-acls-checkbox-text">Layout</div>
-                        </div>) : (null)}
-                        {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
+                {!this.openSharing ? null : (
+                    <div className="propertiesView-sharing-content">
+                        <div className="propertiesView-buttonContainer">
+                            {!Doc.noviceMode ? (
+                                <div className="propertiesView-acls-checkbox">
+                                    <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} />
+                                    <div className="propertiesView-acls-checkbox-text">Layout</div>
+                                </div>
+                            ) : null}
+                            {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}>
                                         <button onPointerDown={() => SharingManager.Instance.distributeOverCollection(this.selectedDoc!)}>
                                             <FontAwesomeIcon icon="redo-alt" color="white" size="1x" />
                                         </button>
                                     </Tooltip> */}
+                        </div>
+                        {this.sharingTable}
                     </div>
-                    {this.sharingTable}
-                </div>}
-        </div>;
+                )}
+            </div>
+        );
     }
 
     /**
@@ -930,11 +1148,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
      * If it doesn't exist, it creates it.
      */
     checkFilterDoc() {
-        if (!this.selectedDoc?.currentFilter) this.selectedDoc!.currentFilter = CurrentUserUtils.createFilterDoc();
+        if (!this.selectedDoc?.currentFilter) this.selectedDoc!.currentFilter = FilterBox.createFilterDoc();
     }
 
     /**
-     * Creates a new currentFilter for this.filterDoc, 
+     * Creates a new currentFilter for this.filterDoc,
      */
     createNewFilterDoc = () => {
         if (this.selectedDoc) {
@@ -944,9 +1162,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             this.selectedDoc._docRangeFilters = new List<string>();
             (this.selectedDoc.currentFilter as Doc)._docFiltersList = currentDocFilters;
             (this.selectedDoc.currentFilter as Doc)._docRangeFiltersList = currentDocRangeFilters;
-            this.selectedDoc.currentFilter = CurrentUserUtils.createFilterDoc();
+            this.selectedDoc.currentFilter = FilterBox.createFilterDoc();
         }
-    }
+    };
 
     /**
      * Updates this.filterDoc's currentFilter and saves the docFilters on the currentFilter
@@ -970,20 +1188,18 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             doc._docRangeFiltersList = new List<string>();
             this.selectedDoc._docRangeFilters = savedDocRangeFilters;
         }
-    }
+    };
 
     @computed get filtersSubMenu() {
-        return !(this.selectedDoc?.currentFilter instanceof Doc) ? (null) : <div className="propertiesView-filters">
-            <div className="propertiesView-filters-title"
-                onPointerDown={action(() => this.openFilters = !this.openFilters)}
-                style={{ backgroundColor: this.openFilters ? "black" : "" }}>
-                Filters
-                <div className="propertiesView-filters-title-icon">
-                    <FontAwesomeIcon icon={this.openFilters ? "caret-down" : "caret-right"} size="lg" color="white" />
+        return !(this.selectedDoc?.currentFilter instanceof Doc) ? null : (
+            <div className="propertiesView-filters">
+                <div className="propertiesView-filters-title" onPointerDown={action(() => (this.openFilters = !this.openFilters))} style={{ backgroundColor: this.openFilters ? 'black' : '' }}>
+                    Filters
+                    <div className="propertiesView-filters-title-icon">
+                        <FontAwesomeIcon icon={this.openFilters ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                    </div>
                 </div>
-            </div>
-            {
-                !this.openFilters ? (null) :
+                {!this.openFilters ? null : (
                     <div className="propertiesView-filters-content" style={{ height: this.selectedDoc.currentFilter[HeightSym]() + 15 }}>
                         <DocumentView
                             Document={this.selectedDoc.currentFilter}
@@ -1014,110 +1230,106 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                             dontCenter="y"
                         />
                     </div>
-            }
-        </div >;
+                )}
+            </div>
+        );
     }
 
     @computed get inkSubMenu() {
-        return <>
-            {!this.isInk ? (null) :
-                <div className="propertiesView-appearance">
-                    <div className="propertiesView-appearance-title"
-                        onPointerDown={action(() => this.openAppearance = !this.openAppearance)}
-                        style={{ backgroundColor: this.openAppearance ? "black" : "" }}>
-                        Appearance
-                        <div className="propertiesView-appearance-title-icon">
-                            <FontAwesomeIcon icon={this.openAppearance ? "caret-down" : "caret-right"} size="lg" color="white" />
+        return (
+            <>
+                {!this.isInk ? null : (
+                    <div className="propertiesView-appearance">
+                        <div className="propertiesView-appearance-title" onPointerDown={action(() => (this.openAppearance = !this.openAppearance))} style={{ backgroundColor: this.openAppearance ? 'black' : '' }}>
+                            Appearance
+                            <div className="propertiesView-appearance-title-icon">
+                                <FontAwesomeIcon icon={this.openAppearance ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                            </div>
                         </div>
+                        {!this.openAppearance ? null : <div className="propertiesView-appearance-content">{this.appearanceEditor}</div>}
                     </div>
-                    {!this.openAppearance ? (null) :
-                        <div className="propertiesView-appearance-content">
-                            {this.appearanceEditor}
-                        </div>}
-                </div>}
-
-            {this.isInk ? <div className="propertiesView-transform">
-                <div className="propertiesView-transform-title"
-                    onPointerDown={action(() => this.openTransform = !this.openTransform)}
-                    style={{ backgroundColor: this.openTransform ? "black" : "" }}>
-                    Transform
-                    <div className="propertiesView-transform-title-icon">
-                        <FontAwesomeIcon icon={this.openTransform ? "caret-down" : "caret-right"} size="lg" color="white" />
+                )}
+
+                {this.isInk ? (
+                    <div className="propertiesView-transform">
+                        <div className="propertiesView-transform-title" onPointerDown={action(() => (this.openTransform = !this.openTransform))} style={{ backgroundColor: this.openTransform ? 'black' : '' }}>
+                            Transform
+                            <div className="propertiesView-transform-title-icon">
+                                <FontAwesomeIcon icon={this.openTransform ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                            </div>
+                        </div>
+                        {this.openTransform ? <div className="propertiesView-transform-content">{this.transformEditor}</div> : null}
                     </div>
-                </div>
-                {this.openTransform ? <div className="propertiesView-transform-content">
-                    {this.transformEditor}
-                </div> : null}
-            </div> : null}
-        </>;
+                ) : null}
+            </>
+        );
     }
 
     @computed get fieldsSubMenu() {
-        return <div className="propertiesView-fields">
-            <div className="propertiesView-fields-title"
-                onPointerDown={action(() => this.openFields = !this.openFields)}
-                style={{ backgroundColor: this.openFields ? "black" : "" }}>
-                Fields {"&"} Tags
-                <div className="propertiesView-fields-title-icon">
-                    <FontAwesomeIcon icon={this.openFields ? "caret-down" : "caret-right"} size="lg" color="white" />
+        return (
+            <div className="propertiesView-fields">
+                <div className="propertiesView-fields-title" onPointerDown={action(() => (this.openFields = !this.openFields))} style={{ backgroundColor: this.openFields ? 'black' : '' }}>
+                    Fields {'&'} Tags
+                    <div className="propertiesView-fields-title-icon">
+                        <FontAwesomeIcon icon={this.openFields ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                    </div>
                 </div>
+                {!Doc.noviceMode && this.openFields ? (
+                    <div className="propertiesView-fields-checkbox">
+                        {this.fieldsCheckbox}
+                        <div className="propertiesView-fields-checkbox-text">Layout</div>
+                    </div>
+                ) : null}
+                {!this.openFields ? null : <div className="propertiesView-fields-content">{Doc.noviceMode ? this.noviceFields : this.expandedField}</div>}
             </div>
-            {!Doc.noviceMode && this.openFields ? <div className="propertiesView-fields-checkbox">
-                {this.fieldsCheckbox}
-                <div className="propertiesView-fields-checkbox-text">Layout</div>
-            </div> : null}
-            {!this.openFields ? (null) :
-                <div className="propertiesView-fields-content">
-                    {Doc.noviceMode ? this.noviceFields : this.expandedField}
-                </div>}
-        </div>;
+        );
     }
 
     @computed get contextsSubMenu() {
-        return <div className="propertiesView-contexts">
-            <div className="propertiesView-contexts-title"
-                onPointerDown={action(() => this.openContexts = !this.openContexts)}
-                style={{ backgroundColor: this.openContexts ? "black" : "" }}>
-                Other Contexts
-                <div className="propertiesView-contexts-title-icon">
-                    <FontAwesomeIcon icon={this.openContexts ? "caret-down" : "caret-right"} size="lg" color="white" />
+        return (
+            <div className="propertiesView-contexts">
+                <div className="propertiesView-contexts-title" onPointerDown={action(() => (this.openContexts = !this.openContexts))} style={{ backgroundColor: this.openContexts ? 'black' : '' }}>
+                    Other Contexts
+                    <div className="propertiesView-contexts-title-icon">
+                        <FontAwesomeIcon icon={this.openContexts ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                    </div>
                 </div>
+                {this.openContexts ? <div className="propertiesView-contexts-content">{this.contexts}</div> : null}
             </div>
-            {this.openContexts ? <div className="propertiesView-contexts-content"  >{this.contexts}</div> : null}
-        </div>;
+        );
     }
 
     @computed get linksSubMenu() {
-        return <div className="propertiesView-contexts">
-            <div className="propertiesView-contexts-title"
-                onPointerDown={action(() => this.openLinks = !this.openLinks)}
-                style={{ backgroundColor: this.openLinks ? "black" : "" }}>
-                Linked To
-                <div className="propertiesView-contexts-title-icon">
-                    <FontAwesomeIcon icon={this.openLinks ? "caret-down" : "caret-right"} size="lg" color="white" />
+        return (
+            <div className="propertiesView-contexts">
+                <div className="propertiesView-contexts-title" onPointerDown={action(() => (this.openLinks = !this.openLinks))} style={{ backgroundColor: this.openLinks ? 'black' : '' }}>
+                    Linked To
+                    <div className="propertiesView-contexts-title-icon">
+                        <FontAwesomeIcon icon={this.openLinks ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                    </div>
                 </div>
+                {this.openLinks ? <div className="propertiesView-contexts-content">{this.links}</div> : null}
             </div>
-            {this.openLinks ? <div className="propertiesView-contexts-content"  >{this.links}</div> : null}
-        </div>;
+        );
     }
 
     @computed get layoutSubMenu() {
-        return <div className="propertiesView-layout">
-            <div className="propertiesView-layout-title"
-                onPointerDown={action(() => this.openLayout = !this.openLayout)}
-                style={{ backgroundColor: this.openLayout ? "black" : "" }}>
-                Layout
-                <div className="propertiesView-layout-title-icon">
-                    <FontAwesomeIcon icon={this.openLayout ? "caret-down" : "caret-right"} size="lg" color="white" />
+        return (
+            <div className="propertiesView-layout">
+                <div className="propertiesView-layout-title" onPointerDown={action(() => (this.openLayout = !this.openLayout))} style={{ backgroundColor: this.openLayout ? 'black' : '' }}>
+                    Layout
+                    <div className="propertiesView-layout-title-icon">
+                        <FontAwesomeIcon icon={this.openLayout ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                    </div>
                 </div>
+                {this.openLayout ? <div className="propertiesView-layout-content">{this.layoutPreview}</div> : null}
             </div>
-            {this.openLayout ? <div className="propertiesView-layout-content"  >{this.layoutPreview}</div> : null}
-        </div>;
+        );
     }
 
     @observable description = Field.toString(LinkManager.currentLink?.description as any as Field);
     @observable relationship = StrCast(LinkManager.currentLink?.linkRelationship);
-    @observable private relationshipButtonColor: string = "";
+    @observable private relationshipButtonColor: string = '';
 
     // @action
     // handleDescriptionChange = (e: React.ChangeEvent<HTMLInputElement>) => { this.description = e.target.value; }
@@ -1159,11 +1371,11 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
             const linkRelationshipSizes = NumListCast(Doc.UserDoc().linkRelationshipSizes);
             const linkColorList = StrListCast(Doc.UserDoc().linkColorList);
 
-            // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color 
+            // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color
             if (!linkRelationshipList?.includes(value)) {
                 linkRelationshipList.push(value);
                 linkRelationshipSizes.push(1);
-                const randColor = "rgb(" + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + "," + Math.floor(Math.random() * 255) + ")";
+                const randColor = 'rgb(' + Math.floor(Math.random() * 255) + ',' + Math.floor(Math.random() * 255) + ',' + Math.floor(Math.random() * 255) + ')';
                 linkColorList.push(randColor);
                 // if the relationship is already in the list AND the new rel is different from the prev rel, update the rel sizes
             } else if (linkRelationshipList && value !== prevRelationship) {
@@ -1171,20 +1383,22 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 //increment size of new relationship size
                 if (index !== -1 && index < linkRelationshipSizes.length) {
                     const pvalue = linkRelationshipSizes[index];
-                    linkRelationshipSizes[index] = (pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue + 1);
+                    linkRelationshipSizes[index] = pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue + 1;
                 }
                 //decrement the size of the previous relationship if it already exists (i.e. not default 'link' relationship upon link creation)
                 if (linkRelationshipList.includes(prevRelationship)) {
                     const pindex = linkRelationshipList.indexOf(prevRelationship);
                     if (pindex !== -1 && pindex < linkRelationshipSizes.length) {
                         const pvalue = linkRelationshipSizes[pindex];
-                        linkRelationshipSizes[pindex] = Math.max(0, (pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue - 1));
+                        linkRelationshipSizes[pindex] = Math.max(0, pvalue === undefined || !Number.isFinite(pvalue) ? 1 : pvalue - 1);
                     }
                 }
-
             }
-            this.relationshipButtonColor = "rgb(62, 133, 55)";
-            setTimeout(action(() => this.relationshipButtonColor = ""), 750);
+            this.relationshipButtonColor = 'rgb(62, 133, 55)';
+            setTimeout(
+                action(() => (this.relationshipButtonColor = '')),
+                750
+            );
             return true;
         }
     });
@@ -1200,68 +1414,72 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     onSelectOutDesc = () => {
         this.setDescripValue(this.description);
         document.getElementById('link_description_input')?.blur();
-    }
+    };
 
     onDescriptionKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
-        if (e.key === "Enter") {
+        if (e.key === 'Enter') {
             this.setDescripValue(this.description);
             document.getElementById('link_description_input')?.blur();
         }
-    }
+    };
 
     onSelectOutRelationship = () => {
         this.setLinkRelationshipValue(this.relationship);
         document.getElementById('link_relationship_input')?.blur();
-    }
+    };
 
     onRelationshipKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
-        if (e.key === "Enter") {
+        if (e.key === 'Enter') {
             this.setLinkRelationshipValue(this.relationship);
             document.getElementById('link_relationship_input')?.blur();
         }
-    }
+    };
 
     toggleAnchor = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (this.selectedDoc.linkAutoMove = !this.selectedDoc.linkAutoMove))));
-    }
+    };
 
     toggleArrow = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (this.selectedDoc.displayArrow = !this.selectedDoc.displayArrow))));
-    }
+    };
 
     toggleZoomToTarget1 = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (DocCast(this.selectedDoc.anchor1).followLinkZoom = !DocCast(this.selectedDoc.anchor1).followLinkZoom))));
-    }
+    };
     toggleZoomToTarget2 = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => this.selectedDoc && (DocCast(this.selectedDoc.anchor2).followLinkZoom = !DocCast(this.selectedDoc.anchor2).followLinkZoom))));
-    }
+    };
 
     @computed
     get editRelationship() {
-        return <input
-            autoComplete={"off"}
-            id="link_relationship_input"
-            value={StrCast(this.selectedDoc?.linkRelationship)}
-            onKeyDown={this.onRelationshipKey}
-            onBlur={this.onSelectOutRelationship}
-            onChange={e => this.handleLinkRelationshipChange(e.currentTarget.value)}
-            className="text"
-            type="text"
-        />;
+        return (
+            <input
+                autoComplete={'off'}
+                id="link_relationship_input"
+                value={StrCast(this.selectedDoc?.linkRelationship)}
+                onKeyDown={this.onRelationshipKey}
+                onBlur={this.onSelectOutRelationship}
+                onChange={e => this.handleLinkRelationshipChange(e.currentTarget.value)}
+                className="text"
+                type="text"
+            />
+        );
     }
 
     @computed
     get editDescription() {
-        return <input
-            autoComplete={"off"}
-            id="link_description_input"
-            value={StrCast(this.selectedDoc?.description)}
-            onKeyDown={this.onDescriptionKey}
-            onBlur={this.onSelectOutDesc}
-            onChange={e => this.handleDescriptionChange(e.currentTarget.value)}
-            className="text"
-            type="text"
-        />;
+        return (
+            <input
+                autoComplete={'off'}
+                id="link_description_input"
+                value={StrCast(this.selectedDoc?.description)}
+                onKeyDown={this.onDescriptionKey}
+                onBlur={this.onSelectOutDesc}
+                onChange={e => this.handleDescriptionChange(e.currentTarget.value)}
+                className="text"
+                type="text"
+            />
+        );
     }
 
     /**
@@ -1278,123 +1496,120 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
     render() {
         const isNovice = Doc.noviceMode;
         if (!this.selectedDoc && !this.isPres) {
-            return <div className="propertiesView" style={{ width: this.props.width }}>
-                <div className="propertiesView-title" style={{ width: this.props.width }}>
-                    No Document Selected
+            return (
+                <div className="propertiesView" style={{ width: this.props.width }}>
+                    <div className="propertiesView-title" style={{ width: this.props.width }}>
+                        No Document Selected
+                    </div>
                 </div>
-            </div>;
-
+            );
         } else {
             if (this.selectedDoc && this.isLink) {
-                return <div className="propertiesView">
-                    <div className="propertiesView-title">
-                        Linking
-                    </div>
-                    <div className="propertiesView-section">
-                        <p className="propertiesView-label">Information</p>
-                        <div className="propertiesView-input first">
-                            <p>Link Relationship</p>
-                            {this.editRelationship}
-                        </div>
-                        <div className="propertiesView-input">
-                            <p>Description</p>
-                            {this.editDescription}
-                        </div>
-                    </div>
-                    <div className="propertiesView-section">
-                        <p className="propertiesView-label">Behavior</p>
-                        <div className="propertiesView-input inline first">
-                            <p>Follow</p>
-                            <select
-                                name="selectList"
-                                id="selectList"
-                                onChange={e => this.changeFollowBehavior(e.currentTarget.value)}
-                                value={StrCast(this.selectedDoc.followLinkLocation, "default")}>
-                                <option value="default">Default</option>
-                                <option value="add:left">Open in new left pane</option>
-                                <option value="add:right">Open in new right pane</option>
-                                <option value="replace:left">Replace left tab</option>
-                                <option value="replace:right">Replace right tab</option>
-                                <option value="fullScreen">Open full screen</option>
-                                <option value="add">Open in new tab</option>
-                                <option value="replace">Replace current tab</option>
-                                {this.selectedDoc.linksToAnnotation
-                                    ? <option value="openExternal">Open in external page</option>
-                                    : null}
-                            </select>
-                        </div>
-                        <div className="propertiesView-input inline">
-                            <p>Auto-move anchor</p>
-                            <button
-                                style={{ background: this.selectedDoc.hidden ? "gray" : !this.selectedDoc.linkAutoMove ? "" : "#4476f7", borderRadius: 3 }}
-                                onPointerDown={this.toggleAnchor} onClick={e => e.stopPropagation()}
-                                className="propertiesButton"
-                            >
-                                <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
-                            </button>
-                        </div>
-                        <div className="propertiesView-input inline">
-                            <p>Display arrow</p>
-                            <button
-                                style={{ background: this.selectedDoc.hidden ? "gray" : !this.selectedDoc.displayArrow ? "" : "#4476f7", borderRadius: 3 }}
-                                onPointerDown={this.toggleArrow} onClick={e => e.stopPropagation()}
-                                className="propertiesButton"
-                            >
-                                <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
-                            </button>
-                        </div>
-                        <div className="propertiesView-input inline">
-                            <p>Zoom to target</p>
-                            <button
-                                style={{ background: this.selectedDoc.hidden ? "gray" : !Cast(this.selectedDoc.anchor1, Doc, null).followLinkZoom ? "" : "#4476f7", borderRadius: 3 }}
-                                onPointerDown={this.toggleZoomToTarget1} onClick={e => e.stopPropagation()}
-                                className="propertiesButton"
-                            >
-                                <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
-                            </button>
+                return (
+                    <div className="propertiesView">
+                        <div className="propertiesView-title">Linking</div>
+                        <div className="propertiesView-section">
+                            <p className="propertiesView-label">Information</p>
+                            <div className="propertiesView-input first">
+                                <p>Link Relationship</p>
+                                {this.editRelationship}
+                            </div>
+                            <div className="propertiesView-input">
+                                <p>Description</p>
+                                {this.editDescription}
+                            </div>
                         </div>
-                        <div className="propertiesView-input inline">
-                            <p>Zoom to source</p>
-                            <button
-                                style={{ background: this.selectedDoc.hidden ? "gray" : !Cast(this.selectedDoc.anchor2, Doc, null).followLinkZoom ? "" : "#4476f7", borderRadius: 3 }}
-                                onPointerDown={this.toggleZoomToTarget2} onClick={e => e.stopPropagation()}
-                                className="propertiesButton"
-                            >
-                                <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
-                            </button>
+                        <div className="propertiesView-section">
+                            <p className="propertiesView-label">Behavior</p>
+                            <div className="propertiesView-input inline first">
+                                <p>Follow</p>
+                                <select name="selectList" id="selectList" onChange={e => this.changeFollowBehavior(e.currentTarget.value)} value={StrCast(this.selectedDoc.followLinkLocation, 'default')}>
+                                    <option value="default">Default</option>
+                                    <option value="add:left">Open in new left pane</option>
+                                    <option value="add:right">Open in new right pane</option>
+                                    <option value="replace:left">Replace left tab</option>
+                                    <option value="replace:right">Replace right tab</option>
+                                    <option value="fullScreen">Open full screen</option>
+                                    <option value="add">Open in new tab</option>
+                                    <option value="replace">Replace current tab</option>
+                                    {this.selectedDoc.linksToAnnotation ? <option value="openExternal">Open in external page</option> : null}
+                                </select>
+                            </div>
+                            <div className="propertiesView-input inline">
+                                <p>Auto-move anchor</p>
+                                <button
+                                    style={{ background: this.selectedDoc.hidden ? 'gray' : !this.selectedDoc.linkAutoMove ? '' : '#4476f7', borderRadius: 3 }}
+                                    onPointerDown={this.toggleAnchor}
+                                    onClick={e => e.stopPropagation()}
+                                    className="propertiesButton">
+                                    <FontAwesomeIcon className="fa-icon" icon={faAnchor as IconLookup} size="lg" />
+                                </button>
+                            </div>
+                            <div className="propertiesView-input inline">
+                                <p>Display arrow</p>
+                                <button
+                                    style={{ background: this.selectedDoc.hidden ? 'gray' : !this.selectedDoc.displayArrow ? '' : '#4476f7', borderRadius: 3 }}
+                                    onPointerDown={this.toggleArrow}
+                                    onClick={e => e.stopPropagation()}
+                                    className="propertiesButton">
+                                    <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+                                </button>
+                            </div>
+                            <div className="propertiesView-input inline">
+                                <p>Zoom to target</p>
+                                <button
+                                    style={{ background: this.selectedDoc.hidden ? 'gray' : !Cast(this.selectedDoc.anchor1, Doc, null).followLinkZoom ? '' : '#4476f7', borderRadius: 3 }}
+                                    onPointerDown={this.toggleZoomToTarget1}
+                                    onClick={e => e.stopPropagation()}
+                                    className="propertiesButton">
+                                    <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+                                </button>
+                            </div>
+                            <div className="propertiesView-input inline">
+                                <p>Zoom to source</p>
+                                <button
+                                    style={{ background: this.selectedDoc.hidden ? 'gray' : !Cast(this.selectedDoc.anchor2, Doc, null).followLinkZoom ? '' : '#4476f7', borderRadius: 3 }}
+                                    onPointerDown={this.toggleZoomToTarget2}
+                                    onClick={e => e.stopPropagation()}
+                                    className="propertiesButton">
+                                    <FontAwesomeIcon className="fa-icon" icon={faArrowRight as IconLookup} size="lg" />
+                                </button>
+                            </div>
                         </div>
                     </div>
-                </div >;
+                );
             }
             if (this.selectedDoc && !this.isPres) {
-                return <div className="propertiesView" style={{
-                    width: this.props.width,
-                    minWidth: this.props.width,
-                    //overflowY: this.scrolling ? "scroll" : "visible"
-                }} >
-                    <div className="propertiesView-title" style={{ width: this.props.width }}>
-                        Properties
-                    </div>
-                    <div className="propertiesView-name">
-                        {this.editableTitle}
-                    </div>
+                return (
+                    <div
+                        className="propertiesView"
+                        style={{
+                            width: this.props.width,
+                            minWidth: this.props.width,
+                            //overflowY: this.scrolling ? "scroll" : "visible"
+                        }}>
+                        <div className="propertiesView-title" style={{ width: this.props.width }}>
+                            Properties
+                        </div>
+                        <div className="propertiesView-name">{this.editableTitle}</div>
 
-                    {this.contextsSubMenu}
+                        {this.contextsSubMenu}
 
-                    {this.linksSubMenu}
+                        {this.linksSubMenu}
 
-                    {this.inkSubMenu}
+                        {this.inkSubMenu}
 
-                    {this.optionsSubMenu}
+                        {this.optionsSubMenu}
 
-                    {this.fieldsSubMenu}
+                        {this.fieldsSubMenu}
 
-                    {isNovice ? null : this.sharingSubMenu}
+                        {isNovice ? null : this.sharingSubMenu}
 
-                    {isNovice ? null : this.filtersSubMenu}
+                        {isNovice ? null : this.filtersSubMenu}
 
-                    {isNovice ? null : this.layoutSubMenu}
-                </div>;
+                        {isNovice ? null : this.layoutSubMenu}
+                    </div>
+                );
             }
             if (this.isPres) {
                 const selectedItem: boolean = PresBox.Instance?._selectedArray.size > 0;
@@ -1402,35 +1617,35 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                 const viewType = PresBox.Instance.activeItem?._viewType;
                 const pannable: boolean = (type === DocumentType.COL && viewType === CollectionViewType.Freeform) || type === DocumentType.IMG;
                 const scrollable: boolean = type === DocumentType.PDF || type === DocumentType.WEB || type === DocumentType.RTF || viewType === CollectionViewType.Stacking;
-                return <div className="propertiesView" style={{ width: this.props.width }}>
-                    <div className="propertiesView-title" style={{ width: this.props.width }}>
-                        Presentation
-                    </div>
-                    <div className="propertiesView-name" style={{ borderBottom: 0 }}>
-                        {this.editableTitle}
-                        <div className="propertiesView-presSelected">
-                            <div className="propertiesView-selectedCount">
-                                {PresBox.Instance?._selectedArray.size} selected
-                            </div>
-                            <div className="propertiesView-selectedList">
-                                {PresBox.Instance?.listOfSelected}
-                            </div>
+                return (
+                    <div className="propertiesView" style={{ width: this.props.width }}>
+                        <div className="propertiesView-title" style={{ width: this.props.width }}>
+                            Presentation
                         </div>
-                    </div>
-                    {!selectedItem ? (null) : <div className="propertiesView-presTrails">
-                        <div className="propertiesView-presTrails-title"
-                            onPointerDown={action(() => { this.openPresTransitions = !this.openPresTransitions; })}
-                            style={{ backgroundColor: this.openPresTransitions ? "black" : "" }}>
-                            &nbsp; <FontAwesomeIcon style={{ alignSelf: "center" }} icon={"rocket"} /> &nbsp; Transitions
-                            <div className="propertiesView-presTrails-title-icon">
-                                <FontAwesomeIcon icon={this.openPresTransitions ? "caret-down" : "caret-right"} size="lg" color="white" />
+                        <div className="propertiesView-name" style={{ borderBottom: 0 }}>
+                            {this.editableTitle}
+                            <div className="propertiesView-presSelected">
+                                <div className="propertiesView-selectedCount">{PresBox.Instance?._selectedArray.size} selected</div>
+                                <div className="propertiesView-selectedList">{PresBox.Instance?.listOfSelected}</div>
                             </div>
                         </div>
-                        {this.openPresTransitions ? <div className="propertiesView-presTrails-content">
-                            {PresBox.Instance.transitionDropdown}
-                        </div> : null}
-                    </div>}
-                    {/* {!selectedItem || type === DocumentType.VID || type === DocumentType.AUDIO ? (null) : <div className="propertiesView-presTrails">
+                        {!selectedItem ? null : (
+                            <div className="propertiesView-presTrails">
+                                <div
+                                    className="propertiesView-presTrails-title"
+                                    onPointerDown={action(() => {
+                                        this.openPresTransitions = !this.openPresTransitions;
+                                    })}
+                                    style={{ backgroundColor: this.openPresTransitions ? 'black' : '' }}>
+                                    &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={'rocket'} /> &nbsp; Transitions
+                                    <div className="propertiesView-presTrails-title-icon">
+                                        <FontAwesomeIcon icon={this.openPresTransitions ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                                    </div>
+                                </div>
+                                {this.openPresTransitions ? <div className="propertiesView-presTrails-content">{PresBox.Instance.transitionDropdown}</div> : null}
+                            </div>
+                        )}
+                        {/* {!selectedItem || type === DocumentType.VID || type === DocumentType.AUDIO ? (null) : <div className="propertiesView-presTrails">
                         <div className="propertiesView-presTrails-title"
                             onPointerDown={action(() => this.openPresProgressivize = !this.openPresProgressivize)}
                             style={{ backgroundColor: this.openPresProgressivize ? "black" : "" }}>
@@ -1443,20 +1658,23 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                             {PresBox.Instance.progressivizeDropdown}
                         </div> : null}
                     </div>} */}
-                    {!selectedItem || (type !== DocumentType.VID && type !== DocumentType.AUDIO) ? (null) : <div className="propertiesView-presTrails">
-                        <div className="propertiesView-presTrails-title"
-                            onPointerDown={action(() => { this.openSlideOptions = !this.openSlideOptions; })}
-                            style={{ backgroundColor: this.openSlideOptions ? "black" : "" }}>
-                            &nbsp; <FontAwesomeIcon style={{ alignSelf: "center" }} icon={type === DocumentType.AUDIO ? "file-audio" : "file-video"} /> &nbsp; {type === DocumentType.AUDIO ? "Audio Options" : "Video Options"}
-                            <div className="propertiesView-presTrails-title-icon">
-                                <FontAwesomeIcon icon={this.openSlideOptions ? "caret-down" : "caret-right"} size="lg" color="white" />
+                        {!selectedItem || (type !== DocumentType.VID && type !== DocumentType.AUDIO) ? null : (
+                            <div className="propertiesView-presTrails">
+                                <div
+                                    className="propertiesView-presTrails-title"
+                                    onPointerDown={action(() => {
+                                        this.openSlideOptions = !this.openSlideOptions;
+                                    })}
+                                    style={{ backgroundColor: this.openSlideOptions ? 'black' : '' }}>
+                                    &nbsp; <FontAwesomeIcon style={{ alignSelf: 'center' }} icon={type === DocumentType.AUDIO ? 'file-audio' : 'file-video'} /> &nbsp; {type === DocumentType.AUDIO ? 'Audio Options' : 'Video Options'}
+                                    <div className="propertiesView-presTrails-title-icon">
+                                        <FontAwesomeIcon icon={this.openSlideOptions ? 'caret-down' : 'caret-right'} size="lg" color="white" />
+                                    </div>
+                                </div>
+                                {this.openSlideOptions ? <div className="propertiesView-presTrails-content">{PresBox.Instance.mediaOptionsDropdown}</div> : null}
                             </div>
-                        </div>
-                        {this.openSlideOptions ? <div className="propertiesView-presTrails-content">
-                            {PresBox.Instance.mediaOptionsDropdown}
-                        </div> : null}
-                    </div>}
-                    {/* <div className="propertiesView-presTrails">
+                        )}
+                        {/* <div className="propertiesView-presTrails">
                         <div className="propertiesView-presTrails-title"
                             onPointerDown={action(() => { this.openAddSlide = !this.openAddSlide; })}
                             style={{ backgroundColor: this.openAddSlide ? "black" : "" }}>
@@ -1469,8 +1687,9 @@ export class PropertiesView extends React.Component<PropertiesViewProps> {
                             {PresBox.Instance.newDocumentDropdown}
                         </div> : null}
                     </div> */}
-                </div>;
+                    </div>
+                );
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 8f73ac2c3..e81a9c40f 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -1,22 +1,20 @@
 import { computed } from 'mobx';
-import { observer } from "mobx-react";
-import { Doc, DocListCast, StrListCast, Opt } from "../../fields/Doc";
+import { observer } from 'mobx-react';
+import { Doc, DocListCast, StrListCast } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { List } from '../../fields/List';
 import { NumCast, StrCast } from '../../fields/Types';
 import { emptyFunction, OmitKeys, returnAll, returnOne, returnTrue, returnZero } from '../../Utils';
 import { Docs, DocUtils } from '../documents/Documents';
+import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { Transform } from '../util/Transform';
 import { CollectionStackingView } from './collections/CollectionStackingView';
-import { CollectionViewType } from './collections/CollectionView';
 import { FieldViewProps } from './nodes/FieldView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { SearchBox } from './search/SearchBox';
-import "./SidebarAnnos.scss";
+import './SidebarAnnos.scss';
 import { StyleProp } from './StyleProvider';
-import React = require("react");
-import { DocumentViewProps } from './nodes/DocumentView';
-import { DocumentType } from '../documents/DocumentTypes';
+import React = require('react');
 
 interface ExtraProps {
     fieldKey: string;
@@ -28,8 +26,8 @@ interface ExtraProps {
     nativeWidth: number;
     whenChildContentsActiveChanged: (isActive: boolean) => void;
     ScreenToLocalTransform: () => Transform;
-    sidebarAddDocument: (doc: (Doc | Doc[]), suffix: string) => boolean;
-    removeDocument: (doc: (Doc | Doc[]), suffix: string) => boolean;
+    sidebarAddDocument: (doc: Doc | Doc[], suffix: string) => boolean;
+    removeDocument: (doc: Doc | Doc[], suffix: string) => boolean;
     moveDocument: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean, annotationKey?: string) => boolean;
 }
 @observer
@@ -42,29 +40,41 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
     @computed get allMetadata() {
         const keys = new Set<string>();
         DocListCast(this.props.rootDoc[this.sidebarKey]).forEach(doc => SearchBox.documentKeys(doc).forEach(key => keys.add(key)));
-        return Array.from(keys.keys()).filter(key => key[0]).filter(key => key[0] !== "_" && (key[0] === key[0].toUpperCase())).sort();
+        return Array.from(keys.keys())
+            .filter(key => key[0])
+            .filter(key => key[0] !== '_' && key[0] === key[0].toUpperCase())
+            .sort();
     }
     @computed get allUsers() {
         const keys = new Set<string>();
         DocListCast(this.props.rootDoc[this.sidebarKey]).forEach(doc => keys.add(StrCast(doc.author)));
         return Array.from(keys.keys()).sort();
     }
-    get filtersKey() { return "_" + this.sidebarKey + "-docFilters"; }
+    get filtersKey() {
+        return '_' + this.sidebarKey + '-docFilters';
+    }
 
     anchorMenuClick = (anchor: Doc) => {
-        const startup = StrListCast(this.props.rootDoc.docFilters).map(filter => filter.split(":")[0]).join(" ");
+        const startup = StrListCast(this.props.rootDoc.docFilters)
+            .map(filter => filter.split(':')[0])
+            .join(' ');
         const target = Docs.Create.TextDocument(startup, {
-            title: "-note-",
-            annotationOn: this.props.rootDoc, _width: 200, _height: 50, _fitWidth: true, _autoHeight: true, _fontSize: StrCast(Doc.UserDoc().fontSize),
-            _fontFamily: StrCast(Doc.UserDoc().fontFamily)
+            title: '-note-',
+            annotationOn: this.props.rootDoc,
+            _width: 200,
+            _height: 50,
+            _fitWidth: true,
+            _autoHeight: true,
+            _fontSize: StrCast(Doc.UserDoc().fontSize),
+            _fontFamily: StrCast(Doc.UserDoc().fontFamily),
         });
         FormattedTextBox.SelectOnLoad = target[Id];
         FormattedTextBox.DontSelectInitialText = true;
-        this.allMetadata.map(tag => target[tag] = tag);
-        DocUtils.MakeLink({ doc: anchor }, { doc: target }, "inline comment:comment on");
+        this.allMetadata.map(tag => (target[tag] = tag));
+        DocUtils.MakeLink({ doc: anchor }, { doc: target }, 'inline comment:comment on');
         this.addDocument(target);
         this._stackRef.current?.focusDocument(target);
-    }
+    };
     makeDocUnfiltered = (doc: Doc) => {
         if (DocListCast(this.props.rootDoc[this.sidebarKey]).includes(doc)) {
             if (this.props.layoutDoc[this.filtersKey]) {
@@ -73,65 +83,84 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
             return true;
         }
         return false;
-    }
+    };
 
-    get sidebarKey() { return this.props.fieldKey + "-sidebar"; }
+    get sidebarKey() {
+        return this.props.fieldKey + '-sidebar';
+    }
     filtersHeight = () => 38;
-    screenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(Doc.NativeWidth(this.props.dataDoc), 0).scale(this.props.scaling?.() || 1);
+    screenToLocalTransform = () =>
+        this.props
+            .ScreenToLocalTransform()
+            .translate(Doc.NativeWidth(this.props.dataDoc), 0)
+            .scale(this.props.scaling?.() || 1);
     // panelWidth = () => !this.props.layoutDoc._showSidebar ? 0 :
     //     this.props.usePanelWidth ? this.props.PanelWidth() :
     //         (NumCast(this.props.layoutDoc.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth() / NumCast(this.props.layoutDoc.nativeWidth);
-    panelWidth = () => !this.props.showSidebar ? 0 : this.props.layoutDoc.type === DocumentType.RTF || this.props.layoutDoc.type === DocumentType.MAP ? this.props.PanelWidth() : (NumCast(this.props.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth() / NumCast(this.props.nativeWidth);
+    panelWidth = () =>
+        !this.props.showSidebar
+            ? 0
+            : this.props.layoutDoc.type === DocumentType.RTF || this.props.layoutDoc.type === DocumentType.MAP
+            ? this.props.PanelWidth()
+            : ((NumCast(this.props.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth()) / NumCast(this.props.nativeWidth);
     panelHeight = () => this.props.PanelHeight() - this.filtersHeight();
     addDocument = (doc: Doc | Doc[]) => this.props.sidebarAddDocument(doc, this.sidebarKey);
     moveDocument = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(doc, targetCollection, addDocument, this.sidebarKey);
     removeDocument = (doc: Doc | Doc[]) => this.props.removeDocument(doc, this.sidebarKey);
     docFilters = () => [...StrListCast(this.props.layoutDoc._docFilters), ...StrListCast(this.props.layoutDoc[this.filtersKey])];
-    showTitle = () => "title";
+    showTitle = () => 'title';
     setHeightCallback = (height: number) => this.props.setHeight?.(height + this.filtersHeight());
     render() {
         const renderTag = (tag: string) => {
             const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`${tag}:${tag}:check`);
-            return <div key={tag} className={`sidebarAnnos-filterTag${active ? "-active" : ""}`}
-                onClick={e => Doc.setDocFilter(this.props.rootDoc, tag, tag, "check", true, this.sidebarKey, e.shiftKey)}>
-                {tag}
-            </div>;
+            return (
+                <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.rootDoc, tag, tag, 'check', true, this.sidebarKey, e.shiftKey)}>
+                    {tag}
+                </div>
+            );
         };
         const renderMeta = (tag: string) => {
             const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`${tag}:${tag}:exists`);
-            return <div key={tag} className={`sidebarAnnos-filterTag${active ? "-active" : ""}`}
-                onClick={e => Doc.setDocFilter(this.props.rootDoc, tag, tag, "exists", true, this.sidebarKey, e.shiftKey)}>
-                {tag}
-            </div>;
+            return (
+                <div key={tag} className={`sidebarAnnos-filterTag${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.rootDoc, tag, tag, 'exists', true, this.sidebarKey, e.shiftKey)}>
+                    {tag}
+                </div>
+            );
         };
         const renderUsers = (user: string) => {
             const active = StrListCast(this.props.rootDoc[this.filtersKey]).includes(`author:${user}:check`);
-            return <div key={user} className={`sidebarAnnos-filterUser${active ? "-active" : ""}`}
-                onClick={e => Doc.setDocFilter(this.props.rootDoc, "author", user, "check", true, this.sidebarKey, e.shiftKey)}>
-                {user}
-            </div>;
+            return (
+                <div key={user} className={`sidebarAnnos-filterUser${active ? '-active' : ''}`} onClick={e => Doc.setDocFilter(this.props.rootDoc, 'author', user, 'check', true, this.sidebarKey, e.shiftKey)}>
+                    {user}
+                </div>
+            );
         };
         // TODO: Calculation of the topbar is hardcoded and different for text nodes - it should all be the same and all be part of SidebarAnnos
-        return !this.props.showSidebar ? (null) :
-            <div style={{
-                position: "absolute", pointerEvents: this.props.isContentActive() ? "all" : undefined, top: this.props.rootDoc.type !== DocumentType.RTF && StrCast(this.props.rootDoc._showTitle) === "title" ? 15 : 0, right: 0,
-                background: this.props.styleProvider?.(this.props.rootDoc, this.props, StyleProp.WidgetColor),
-                width: `${this.panelWidth()}px`,
-                height: "100%"
-            }}>
-                <div className="sidebarAnnos-tagList" style={{ height: this.filtersHeight(), width: this.panelWidth() }}
-                    onWheel={e => e.stopPropagation()}>
+        return !this.props.showSidebar ? null : (
+            <div
+                style={{
+                    position: 'absolute',
+                    pointerEvents: this.props.isContentActive() ? 'all' : undefined,
+                    top: this.props.rootDoc.type !== DocumentType.RTF && StrCast(this.props.rootDoc._showTitle) === 'title' ? 15 : 0,
+                    right: 0,
+                    background: this.props.styleProvider?.(this.props.rootDoc, this.props, StyleProp.WidgetColor),
+                    width: `${this.panelWidth()}px`,
+                    height: '100%',
+                }}>
+                <div className="sidebarAnnos-tagList" style={{ height: this.filtersHeight(), width: this.panelWidth() }} onWheel={e => e.stopPropagation()}>
                     {this.allUsers.map(renderUsers)}
                     {this.allMetadata.map(renderMeta)}
                 </div>
-                <div style={{ width: "100%", height: this.panelHeight(), position: "relative" }}>
-                    <CollectionStackingView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit} ref={this._stackRef}
+                <div style={{ width: '100%', height: this.panelHeight(), position: 'relative' }}>
+                    <CollectionStackingView
+                        {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                        ref={this._stackRef}
                         NativeWidth={returnZero}
                         NativeHeight={returnZero}
                         PanelHeight={this.panelHeight}
                         PanelWidth={this.panelWidth}
                         docFilters={this.docFilters}
-                        scaleField={this.sidebarKey + "-scale"}
+                        scaleField={this.sidebarKey + '-scale'}
                         setHeight={this.setHeightCallback}
                         isAnnotationOverlay={false}
                         select={emptyFunction}
@@ -151,6 +180,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                         pointerEvents={returnAll}
                     />
                 </div>
-            </div>;
+            </div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index a9770d253..340a5df45 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -2,18 +2,13 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, runInAction } from 'mobx';
 import { extname } from 'path';
-import { Doc, Opt, StrListCast } from '../../fields/Doc';
-import { List } from '../../fields/List';
-import { listSpec } from '../../fields/Schema';
+import { Doc, Opt } from '../../fields/Doc';
 import { BoolCast, Cast, ImageCast, NumCast, StrCast } from '../../fields/Types';
 import { DashColor, lightOrDark } from '../../Utils';
-import { DocumentType } from '../documents/DocumentTypes';
-import { CurrentUserUtils } from '../util/CurrentUserUtils';
+import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { DocFocusOrOpen } from '../util/DocumentManager';
 import { ColorScheme } from '../util/SettingsManager';
-import { SnappingManager } from '../util/SnappingManager';
 import { undoBatch, UndoManager } from '../util/UndoManager';
-import { CollectionViewType } from './collections/CollectionView';
 import { TreeSort } from './collections/TreeView';
 import { Colors } from './global/globalEnums';
 import { InkingStroke } from './InkingStroke';
@@ -49,7 +44,7 @@ export enum StyleProp {
 }
 
 function darkScheme() {
-    return CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark;
+    return Doc.ActiveDashboard?.colorScheme === ColorScheme.Dark;
 }
 
 function toggleLockedPosition(doc: Doc) {
diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx
index 689ee4fc1..156513f47 100644
--- a/src/client/views/TemplateMenu.tsx
+++ b/src/client/views/TemplateMenu.tsx
@@ -1,43 +1,42 @@
-import { action, computed, observable, ObservableSet, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, DocListCast } from "../../fields/Doc";
-import { List } from "../../fields/List";
-import { ScriptField } from "../../fields/ScriptField";
-import { Cast, StrCast } from "../../fields/Types";
-import { TraceMobx } from "../../fields/util";
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../Utils";
-import { Docs, DocUtils } from "../documents/Documents";
-import { ScriptingGlobals } from "../util/ScriptingGlobals";
-import { Transform } from "../util/Transform";
-import { undoBatch } from "../util/UndoManager";
-import { CollectionTreeView } from "./collections/CollectionTreeView";
-import { DocumentView } from "./nodes/DocumentView";
-import { DefaultStyleProvider } from "./StyleProvider";
+import { action, computed, observable, ObservableSet, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, DocListCast } from '../../fields/Doc';
+import { List } from '../../fields/List';
+import { ScriptField } from '../../fields/ScriptField';
+import { Cast, StrCast } from '../../fields/Types';
+import { TraceMobx } from '../../fields/util';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../Utils';
+import { Docs, DocUtils } from '../documents/Documents';
+import { ScriptingGlobals } from '../util/ScriptingGlobals';
+import { Transform } from '../util/Transform';
+import { undoBatch } from '../util/UndoManager';
+import { CollectionTreeView } from './collections/CollectionTreeView';
+import { DocumentView } from './nodes/DocumentView';
+import { DefaultStyleProvider } from './StyleProvider';
 import './TemplateMenu.scss';
-import React = require("react");
-import { CurrentUserUtils } from "../util/CurrentUserUtils";
+import React = require('react');
 
 @observer
-class TemplateToggle extends React.Component<{ template: string, checked: boolean, toggle: (event: React.ChangeEvent<HTMLInputElement>, template: string) => void }> {
+class TemplateToggle extends React.Component<{ template: string; checked: boolean; toggle: (event: React.ChangeEvent<HTMLInputElement>, template: string) => void }> {
     render() {
         if (this.props.template) {
             return (
                 <li className="templateToggle">
-                    <input type="checkbox" checked={this.props.checked} onChange={(event) => this.props.toggle(event, this.props.template)} />
+                    <input type="checkbox" checked={this.props.checked} onChange={event => this.props.toggle(event, this.props.template)} />
                     {this.props.template}
                 </li>
             );
         } else {
-            return (null);
+            return null;
         }
     }
 }
 @observer
-class OtherToggle extends React.Component<{ checked: boolean, name: string, toggle: (event: React.ChangeEvent<HTMLInputElement>) => void }> {
+class OtherToggle extends React.Component<{ checked: boolean; name: string; toggle: (event: React.ChangeEvent<HTMLInputElement>) => void }> {
     render() {
         return (
             <li className="chromeToggle">
-                <input type="checkbox" checked={this.props.checked} onChange={(event) => this.props.toggle(event)} />
+                <input type="checkbox" checked={this.props.checked} onChange={event => this.props.toggle(event)} />
                 {this.props.name}
             </li>
         );
@@ -49,7 +48,6 @@ export interface TemplateMenuProps {
     templates?: Map<string, boolean>;
 }
 
-
 @observer
 export class TemplateMenu extends React.Component<TemplateMenuProps> {
     _addedKeys = new ObservableSet();
@@ -58,109 +56,118 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> {
 
     toggleLayout = (e: React.ChangeEvent<HTMLInputElement>, layout: string): void => {
         this.props.docViews.map(dv => dv.switchViews(e.target.checked, layout));
-    }
+    };
     toggleDefault = (e: React.ChangeEvent<HTMLInputElement>): void => {
-        this.props.docViews.map(dv => dv.switchViews(false, "layout"));
-    }
+        this.props.docViews.map(dv => dv.switchViews(false, 'layout'));
+    };
 
     toggleAudio = (e: React.ChangeEvent<HTMLInputElement>): void => {
-        this.props.docViews.map(dv => dv.props.Document._showAudio = e.target.checked);
-    }
+        this.props.docViews.map(dv => (dv.props.Document._showAudio = e.target.checked));
+    };
 
     @undoBatch
     @action
     toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: string): void => {
-        this.props.docViews.forEach(d => Doc.Layout(d.layoutDoc)["_show" + template] = event.target.checked ? template.toLowerCase() : "");
-    }
+        this.props.docViews.forEach(d => (Doc.Layout(d.layoutDoc)['_show' + template] = event.target.checked ? template.toLowerCase() : ''));
+    };
 
     @action
     toggleTemplateActivity = (): void => {
         this._hidden = !this._hidden;
-    }
+    };
 
     @undoBatch
     @action
     toggleChrome = (): void => {
-        this.props.docViews.map(dv => Doc.Layout(dv.layoutDoc)).forEach(layout => layout._chromeHidden = !layout._chromeHidden);
-    }
+        this.props.docViews.map(dv => Doc.Layout(dv.layoutDoc)).forEach(layout => (layout._chromeHidden = !layout._chromeHidden));
+    };
 
     // todo: add brushes to brushMap to save with a style name
     onCustomKeypress = (e: React.KeyboardEvent) => {
-        if (e.key === "Enter") {
+        if (e.key === 'Enter') {
             runInAction(() => this._addedKeys.add(this._customRef.current!.value));
         }
-    }
+    };
     componentDidMount() {
         !this._addedKeys && (this._addedKeys = new ObservableSet());
-        Array.from(Object.keys(Doc.GetProto(this.props.docViews[0].props.Document))).
-            filter(key => key.startsWith("layout_")).
-            map(key => runInAction(() => this._addedKeys.add(key.replace("layout_", ""))));
+        Array.from(Object.keys(Doc.GetProto(this.props.docViews[0].props.Document)))
+            .filter(key => key.startsWith('layout_'))
+            .map(key => runInAction(() => this._addedKeys.add(key.replace('layout_', ''))));
     }
 
     return100 = () => 100;
     @computed get scriptField() {
-        const script = ScriptField.MakeScript("docs.map(d => switchView(d, this))", { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name, firstDoc: Doc.name },
-            { docs: new List<Doc>(this.props.docViews.map(dv => dv.props.Document)) });
+        const script = ScriptField.MakeScript(
+            'docs.map(d => switchView(d, this))',
+            { this: Doc.name, heading: 'string', checked: 'string', containingTreeView: Doc.name, firstDoc: Doc.name },
+            { docs: new List<Doc>(this.props.docViews.map(dv => dv.props.Document)) }
+        );
         return script ? () => script : undefined;
     }
     templateIsUsed = (selDoc: Doc, templateDoc: Doc) => {
         const template = StrCast(templateDoc.dragFactory ? Cast(templateDoc.dragFactory, Doc, null)?.title : templateDoc.title);
-        return StrCast(selDoc.layoutKey) === "layout_" + template ? 'check' : 'unchecked';
-    }
+        return StrCast(selDoc.layoutKey) === 'layout_' + template ? 'check' : 'unchecked';
+    };
     render() {
         TraceMobx();
         const firstDoc = this.props.docViews[0].props.Document;
-        const templateName = StrCast(firstDoc.layoutKey, "layout").replace("layout_", "");
-        const noteTypes = DocListCast(Cast(Doc.UserDoc()["template-notes"], Doc, null)?.data);
-        const addedTypes = Doc.noviceMode ? [] : DocListCast(Cast(Doc.UserDoc()["template-buttons"], Doc, null)?.data);
+        const templateName = StrCast(firstDoc.layoutKey, 'layout').replace('layout_', '');
+        const noteTypes = DocListCast(Cast(Doc.UserDoc()['template-notes'], Doc, null)?.data);
+        const addedTypes = Doc.noviceMode ? [] : DocListCast(Cast(Doc.UserDoc()['template-buttons'], Doc, null)?.data);
         const layout = Doc.Layout(firstDoc);
         const templateMenu: Array<JSX.Element> = [];
-        this.props.templates?.forEach((checked, template) =>
-            templateMenu.push(<TemplateToggle key={template} template={template} checked={checked} toggle={this.toggleTemplate} />));
-        templateMenu.push(<OtherToggle key={"audio"} name={"Audio"} checked={firstDoc._showAudio ? true : false} toggle={this.toggleAudio} />);
-        templateMenu.push(<OtherToggle key={"default"} name={"Default"} checked={templateName === "layout"} toggle={this.toggleDefault} />);
-        !Doc.noviceMode && templateMenu.push(<OtherToggle key={"chrome"} name={"Chrome"} checked={!layout._chromeHidden} toggle={this.toggleChrome} />);
-        addedTypes.concat(noteTypes).map(template => template.treeViewChecked = this.templateIsUsed(firstDoc, template));
-        this._addedKeys && Array.from(this._addedKeys).filter(key => !noteTypes.some(nt => nt.title === key)).forEach(template => templateMenu.push(
-            <OtherToggle key={template} name={template} checked={templateName === template} toggle={e => this.toggleLayout(e, template)} />));
-        return <ul className="template-list" style={{ display: "block" }}>
-            {Doc.noviceMode ? (null) : <input placeholder="+ layout" ref={this._customRef} onKeyPress={this.onCustomKeypress} />}
-            {templateMenu}
-            {Doc.noviceMode ? (null) : <CollectionTreeView
-                Document={CurrentUserUtils.MyTemplates}
-                CollectionView={undefined}
-                ContainingCollectionDoc={undefined}
-                ContainingCollectionView={undefined}
-                styleProvider={DefaultStyleProvider}
-                setHeight={returnFalse}
-                docViewPath={returnEmptyDoclist}
-                docFilters={returnEmptyFilter}
-                docRangeFilters={returnEmptyFilter}
-                searchFilterDocs={returnEmptyDoclist}
-                rootSelected={returnFalse}
-                onCheckedClick={this.scriptField}
-                onChildClick={this.scriptField}
-                dropAction={undefined}
-                isAnyChildContentActive={returnFalse}
-                isContentActive={returnTrue}
-                bringToFront={emptyFunction}
-                focus={emptyFunction}
-                whenChildContentsActiveChanged={emptyFunction}
-                ScreenToLocalTransform={Transform.Identity}
-                isSelected={returnFalse}
-                pinToPres={emptyFunction}
-                select={emptyFunction}
-                renderDepth={1}
-                addDocTab={returnFalse}
-                PanelWidth={this.return100}
-                PanelHeight={this.return100}
-                treeViewHideHeaderFields={true}
-                dontRegisterView={true}
-                fieldKey={"data"}
-                moveDocument={returnFalse}
-                removeDocument={returnFalse}
-                addDocument={returnFalse} />}
-        </ul>;
+        this.props.templates?.forEach((checked, template) => templateMenu.push(<TemplateToggle key={template} template={template} checked={checked} toggle={this.toggleTemplate} />));
+        templateMenu.push(<OtherToggle key={'audio'} name={'Audio'} checked={firstDoc._showAudio ? true : false} toggle={this.toggleAudio} />);
+        templateMenu.push(<OtherToggle key={'default'} name={'Default'} checked={templateName === 'layout'} toggle={this.toggleDefault} />);
+        !Doc.noviceMode && templateMenu.push(<OtherToggle key={'chrome'} name={'Chrome'} checked={!layout._chromeHidden} toggle={this.toggleChrome} />);
+        addedTypes.concat(noteTypes).map(template => (template.treeViewChecked = this.templateIsUsed(firstDoc, template)));
+        this._addedKeys &&
+            Array.from(this._addedKeys)
+                .filter(key => !noteTypes.some(nt => nt.title === key))
+                .forEach(template => templateMenu.push(<OtherToggle key={template} name={template} checked={templateName === template} toggle={e => this.toggleLayout(e, template)} />));
+        return (
+            <ul className="template-list" style={{ display: 'block' }}>
+                {Doc.noviceMode ? null : <input placeholder="+ layout" ref={this._customRef} onKeyPress={this.onCustomKeypress} />}
+                {templateMenu}
+                {Doc.noviceMode ? null : (
+                    <CollectionTreeView
+                        Document={Doc.MyTemplates}
+                        CollectionView={undefined}
+                        ContainingCollectionDoc={undefined}
+                        ContainingCollectionView={undefined}
+                        styleProvider={DefaultStyleProvider}
+                        setHeight={returnFalse}
+                        docViewPath={returnEmptyDoclist}
+                        docFilters={returnEmptyFilter}
+                        docRangeFilters={returnEmptyFilter}
+                        searchFilterDocs={returnEmptyDoclist}
+                        rootSelected={returnFalse}
+                        onCheckedClick={this.scriptField}
+                        onChildClick={this.scriptField}
+                        dropAction={undefined}
+                        isAnyChildContentActive={returnFalse}
+                        isContentActive={returnTrue}
+                        bringToFront={emptyFunction}
+                        focus={emptyFunction}
+                        whenChildContentsActiveChanged={emptyFunction}
+                        ScreenToLocalTransform={Transform.Identity}
+                        isSelected={returnFalse}
+                        pinToPres={emptyFunction}
+                        select={emptyFunction}
+                        renderDepth={1}
+                        addDocTab={returnFalse}
+                        PanelWidth={this.return100}
+                        PanelHeight={this.return100}
+                        treeViewHideHeaderFields={true}
+                        dontRegisterView={true}
+                        fieldKey={'data'}
+                        moveDocument={returnFalse}
+                        removeDocument={returnFalse}
+                        addDocument={returnFalse}
+                    />
+                )}
+            </ul>
+        );
     }
 }
 
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 27478e59b..d47dfbea0 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -12,18 +12,17 @@ import { inheritParentAcls } from '../../../fields/util';
 import { emptyFunction, incrementTitleCopy } from '../../../Utils';
 import { DocServer } from '../../DocServer';
 import { Docs } from '../../documents/Documents';
-import { DocumentType } from '../../documents/DocumentTypes';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 import { SelectionManager } from '../../util/SelectionManager';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { DashboardView } from '../DashboardView';
 import { LightboxView } from '../LightboxView';
 import './CollectionDockingView.scss';
 import { CollectionFreeFormView } from './collectionFreeForm';
 import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-import { CollectionViewType } from './CollectionView';
 import { TabDocView } from './TabDocView';
 import React = require('react');
 const _global = (window /* browser */ || global) /* node */ as any;
@@ -121,7 +120,7 @@ export class CollectionDockingView extends CollectionSubView() {
         SelectionManager.DeselectAll();
         const instance = CollectionDockingView.Instance;
         if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === 'layout') {
-            return CurrentUserUtils.openDashboard(doc);
+            return DashboardView.openDashboard(doc);
         }
         const newItemStackConfig = {
             type: 'stack',
@@ -170,7 +169,7 @@ export class CollectionDockingView extends CollectionSubView() {
     @undoBatch
     @action
     public static AddSplit(document: Doc, pullSide: string, stack?: any, panelName?: string) {
-        if (document._viewType === CollectionViewType.Docking) return CurrentUserUtils.openDashboard(document);
+        if (document._viewType === CollectionViewType.Docking) return DashboardView.openDashboard(document);
 
         const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document);
         if (tab) {
@@ -378,7 +377,7 @@ export class CollectionDockingView extends CollectionSubView() {
                 }
             }
         }
-        if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
+        if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
             e.stopPropagation();
         }
     };
@@ -404,7 +403,7 @@ export class CollectionDockingView extends CollectionSubView() {
             const cloned = await Doc.MakeClone(doc);
             Array.from(cloned.map.entries()).map(entry => (json = json.replace(entry[0], entry[1][Id])));
             Doc.GetProto(cloned.clone).dockingConfig = json;
-            return CurrentUserUtils.openDashboard(cloned.clone);
+            return DashboardView.openDashboard(cloned.clone);
         }
         const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g);
         const origtabids = matches?.map(m => m.replace('"documentId":"', '').replace('"', '')) || [];
@@ -424,7 +423,7 @@ export class CollectionDockingView extends CollectionSubView() {
             return newtab;
         });
         const copy = Docs.Create.DockDocument(newtabs, json, { title: incrementTitleCopy(StrCast(doc.title)) });
-        return CurrentUserUtils.openDashboard(await copy);
+        return DashboardView.openDashboard(await copy);
     }
 
     @action
@@ -451,8 +450,8 @@ export class CollectionDockingView extends CollectionSubView() {
 
     tabDestroyed = (tab: any) => {
         if (tab.DashDoc?.type !== DocumentType.KVP) {
-            Doc.AddDocToList(CurrentUserUtils.MyHeaderBar, 'data', tab.DashDoc);
-            Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
+            Doc.AddDocToList(Doc.MyHeaderBar, 'data', tab.DashDoc);
+            Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
         }
         const dview = CollectionDockingView.Instance.props.Document;
         const fieldKey = CollectionDockingView.Instance.props.fieldKey;
@@ -469,7 +468,7 @@ export class CollectionDockingView extends CollectionSubView() {
 
     stackCreated = (stack: any) => {
         stack.header?.element.on('mousedown', (e: any) => {
-            const dashboard = CurrentUserUtils.ActiveDashboard;
+            const dashboard = Doc.ActiveDashboard;
             if (dashboard && e.target === stack.header?.element[0] && e.button === 2) {
                 dashboard['pane-count'] = NumCast(dashboard['pane-count']) + 1;
                 const docToAdd = Docs.Create.FreeformDocument([], {
@@ -507,7 +506,7 @@ export class CollectionDockingView extends CollectionSubView() {
             .click(
                 action(() => {
                     // stack.config.fixed = !stack.config.fixed;  // force the stack to have a fixed size
-                    const dashboard = CurrentUserUtils.ActiveDashboard;
+                    const dashboard = Doc.ActiveDashboard;
                     if (dashboard) {
                         dashboard['pane-count'] = NumCast(dashboard['pane-count']) + 1;
                         const docToAdd = Docs.Create.FreeformDocument([], {
@@ -545,7 +544,7 @@ ScriptingGlobals.add(
 );
 ScriptingGlobals.add(
     function openInOverlay(doc: any) {
-        return Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, doc);
+        return Doc.AddDocToList(Doc.MyOverlayDocs, undefined, doc);
     },
     'opens up document in screen overlay layer',
     '(doc: any)'
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 668d82387..2c0e44bc3 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -1,46 +1,46 @@
-import React = require("react");
+import React = require('react');
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
-import { FontAwesomeIcon, FontAwesomeIconProps } from "@fortawesome/react-fontawesome";
-import { Tooltip } from "@material-ui/core";
-import { action, computed, Lambda, observable, reaction, runInAction, trace } from "mobx";
-import { observer } from "mobx-react";
-import { ColorState } from "react-color";
-import { Doc, DocListCast, Opt } from "../../../fields/Doc";
-import { Document } from "../../../fields/documentSchemas";
-import { Id } from "../../../fields/FieldSymbols";
-import { InkTool } from "../../../fields/InkField";
-import { List } from "../../../fields/List";
-import { ObjectField } from "../../../fields/ObjectField";
-import { RichTextField } from "../../../fields/RichTextField";
-import { listSpec } from "../../../fields/Schema";
-import { ScriptField } from "../../../fields/ScriptField";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
-import { Docs } from "../../documents/Documents";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { DragManager } from "../../util/DragManager";
-import { ScriptingGlobals } from "../../util/ScriptingGlobals";
-import { SelectionManager } from "../../util/SelectionManager";
-import { Transform } from "../../util/Transform";
-import { undoBatch } from "../../util/UndoManager";
-import { AntimodeMenu, AntimodeMenuProps } from "../AntimodeMenu";
-import { EditableView } from "../EditableView";
-import { GestureOverlay } from "../GestureOverlay";
-import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from "../InkingStroke";
-import { LightboxView } from "../LightboxView";
-import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
-import { DocumentView } from "../nodes/DocumentView";
-import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
-import { RichTextMenu } from "../nodes/formattedText/RichTextMenu";
-import { PresBox } from "../nodes/trails/PresBox";
-import { DefaultStyleProvider } from "../StyleProvider";
-import { CollectionDockingView } from "./CollectionDockingView";
-import { CollectionLinearView } from "./collectionLinear";
-import "./CollectionMenu.scss";
-import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView";
-import { TabDocView } from "./TabDocView";
-import { Colors } from "../global/globalEnums";
+import { FontAwesomeIcon, FontAwesomeIconProps } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
+import { action, computed, Lambda, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { ColorState } from 'react-color';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { Document } from '../../../fields/documentSchemas';
+import { Id } from '../../../fields/FieldSymbols';
+import { InkTool } from '../../../fields/InkField';
+import { List } from '../../../fields/List';
+import { ObjectField } from '../../../fields/ObjectField';
+import { RichTextField } from '../../../fields/RichTextField';
+import { listSpec } from '../../../fields/Schema';
+import { ScriptField } from '../../../fields/ScriptField';
+import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from '../../../Utils';
+import { Docs } from '../../documents/Documents';
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { DragManager } from '../../util/DragManager';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
+import { SelectionManager } from '../../util/SelectionManager';
+import { SettingsManager } from '../../util/SettingsManager';
+import { Transform } from '../../util/Transform';
+import { undoBatch } from '../../util/UndoManager';
+import { AntimodeMenu } from '../AntimodeMenu';
+import { EditableView } from '../EditableView';
+import { GestureOverlay } from '../GestureOverlay';
+import { Colors } from '../global/globalEnums';
+import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from '../InkingStroke';
+import { LightboxView } from '../LightboxView';
+import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
+import { DocumentView } from '../nodes/DocumentView';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
+import { PresBox } from '../nodes/trails/PresBox';
+import { DefaultStyleProvider } from '../StyleProvider';
+import { CollectionDockingView } from './CollectionDockingView';
+import { CollectionLinearView } from './collectionLinear';
+import './CollectionMenu.scss';
+import { COLLECTION_BORDER_WIDTH } from './CollectionView';
+import { TabDocView } from './TabDocView';
 
 interface CollectionMenuProps {
     panelHeight: () => number;
@@ -48,7 +48,7 @@ interface CollectionMenuProps {
 }
 
 @observer
-export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
+export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
     @observable static Instance: CollectionMenu;
 
     @observable SelectedCollection: DocumentView | undefined;
@@ -58,16 +58,18 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
 
     constructor(props: any) {
         super(props);
-        this.FieldKey = "";
-        runInAction(() => CollectionMenu.Instance = this);
+        this.FieldKey = '';
+        runInAction(() => (CollectionMenu.Instance = this));
         this._canFade = false; // don't let the inking menu fade away
-        runInAction(() => this.Pinned = Cast(Doc.UserDoc()["menuCollections-pinned"], "boolean", true));
+        runInAction(() => (this.Pinned = Cast(Doc.UserDoc()['menuCollections-pinned'], 'boolean', true)));
         this.jumpTo(300, 300);
     }
 
     componentDidMount() {
-        reaction(() => SelectionManager.Views().length && SelectionManager.Views()[0],
-            view => view && this.SetSelection(view));
+        reaction(
+            () => SelectionManager.Views().length && SelectionManager.Views()[0],
+            view => view && this.SetSelection(view)
+        );
     }
 
     @action
@@ -77,84 +79,87 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
 
     @action
     toggleMenuPin = (e: React.MouseEvent) => {
-        Doc.UserDoc()["menuCollections-pinned"] = this.Pinned = !this.Pinned;
+        Doc.UserDoc()['menuCollections-pinned'] = this.Pinned = !this.Pinned;
         if (!this.Pinned && this._left < 0) {
             this.jumpTo(300, 300);
         }
-    }
+    };
 
     @action
     toggleTopBar = () => {
-        if (CurrentUserUtils.headerBarHeight > 0) {
-            CurrentUserUtils.headerBarHeight = 0;
+        if (SettingsManager.headerBarHeight > 0) {
+            SettingsManager.headerBarHeight = 0;
         } else {
-            CurrentUserUtils.headerBarHeight = 60;
+            SettingsManager.headerBarHeight = 60;
         }
-    }
+    };
 
     buttonBarXf = () => {
         if (!this._docBtnRef.current) return Transform.Identity();
         const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current);
         return new Transform(-translateX, -translateY, 1 / scale);
-    }
+    };
 
     @computed get contMenuButtons() {
-        const selDoc = CurrentUserUtils.MyContextMenuBtns;
-        return !(selDoc instanceof Doc) ? (null) : <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: this.props.panelHeight() }} >
-            <CollectionLinearView
-                Document={selDoc}
-                DataDoc={undefined}
-                fieldKey={"data"}
-                dropAction={"alias"}
-                setHeight={returnFalse}
-                styleProvider={DefaultStyleProvider}
-                rootSelected={returnTrue}
-                bringToFront={emptyFunction}
-                select={emptyFunction}
-                isContentActive={returnTrue}
-                isAnyChildContentActive={returnFalse}
-                isSelected={returnFalse}
-                docViewPath={returnEmptyDoclist}
-                moveDocument={returnFalse}
-                CollectionView={undefined}
-                addDocument={returnFalse}
-                addDocTab={returnFalse}
-                pinToPres={emptyFunction}
-                removeDocument={returnFalse}
-                ScreenToLocalTransform={this.buttonBarXf}
-                PanelWidth={this.props.panelWidth}
-                PanelHeight={this.props.panelHeight}
-                renderDepth={0}
-                focus={emptyFunction}
-                whenChildContentsActiveChanged={emptyFunction}
-                docFilters={returnEmptyFilter}
-                docRangeFilters={returnEmptyFilter}
-                searchFilterDocs={returnEmptyDoclist}
-                ContainingCollectionView={undefined}
-                ContainingCollectionDoc={undefined} />
-        </div>;
+        const selDoc = Doc.MyContextMenuBtns;
+        return !(selDoc instanceof Doc) ? null : (
+            <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: this.props.panelHeight() }}>
+                <CollectionLinearView
+                    Document={selDoc}
+                    DataDoc={undefined}
+                    fieldKey={'data'}
+                    dropAction={'alias'}
+                    setHeight={returnFalse}
+                    styleProvider={DefaultStyleProvider}
+                    rootSelected={returnTrue}
+                    bringToFront={emptyFunction}
+                    select={emptyFunction}
+                    isContentActive={returnTrue}
+                    isAnyChildContentActive={returnFalse}
+                    isSelected={returnFalse}
+                    docViewPath={returnEmptyDoclist}
+                    moveDocument={returnFalse}
+                    CollectionView={undefined}
+                    addDocument={returnFalse}
+                    addDocTab={returnFalse}
+                    pinToPres={emptyFunction}
+                    removeDocument={returnFalse}
+                    ScreenToLocalTransform={this.buttonBarXf}
+                    PanelWidth={this.props.panelWidth}
+                    PanelHeight={this.props.panelHeight}
+                    renderDepth={0}
+                    focus={emptyFunction}
+                    whenChildContentsActiveChanged={emptyFunction}
+                    docFilters={returnEmptyFilter}
+                    docRangeFilters={returnEmptyFilter}
+                    searchFilterDocs={returnEmptyDoclist}
+                    ContainingCollectionView={undefined}
+                    ContainingCollectionDoc={undefined}
+                />
+            </div>
+        );
     }
 
     render() {
+        const propIcon = SettingsManager.headerBarHeight > 0 ? 'angle-double-up' : 'angle-double-down';
+        const propTitle = SettingsManager.headerBarHeight > 0 ? 'Close Header Bar' : 'Open Header Bar';
 
-        const propIcon = CurrentUserUtils.headerBarHeight > 0 ? "angle-double-up" : "angle-double-down";
-        const propTitle = CurrentUserUtils.headerBarHeight > 0 ? "Close Header Bar" : "Open Header Bar";
-
-        const prop = <Tooltip title={<div className="dash-tooltip">{propTitle}</div>} key="topar" placement="bottom">
-            <div className="collectionMenu-hardCodedButton"
-                style={{ backgroundColor: CurrentUserUtils.propertiesWidth > 0 ? Colors.MEDIUM_BLUE : undefined }}
-                onPointerDown={this.toggleTopBar}>
-                <FontAwesomeIcon icon={propIcon} size="lg" />
-            </div>
-        </Tooltip>;
+        const prop = (
+            <Tooltip title={<div className="dash-tooltip">{propTitle}</div>} key="topar" placement="bottom">
+                <div className="collectionMenu-hardCodedButton" style={{ backgroundColor: SettingsManager.propertiesWidth > 0 ? Colors.MEDIUM_BLUE : undefined }} onPointerDown={this.toggleTopBar}>
+                    <FontAwesomeIcon icon={propIcon} size="lg" />
+                </div>
+            </Tooltip>
+        );
 
         // NEW BUTTONS
         //dash col linear view buttons
-        const contMenuButtons =
+        const contMenuButtons = (
             <div className="collectionMenu-container">
                 {this.contMenuButtons}
                 {prop}
-            </div>;
+            </div>
+        );
 
         return contMenuButtons;
 
@@ -187,49 +192,62 @@ const stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation();
 export class CollectionViewBaseChrome extends React.Component<CollectionViewMenuProps> {
     //(!)?\(\(\(doc.(\w+) && \(doc.\w+ as \w+\).includes\(\"(\w+)\"\)
 
-    get document() { return this.props.docView?.props.Document; }
-    get target() { return this.document; }
+    get document() {
+        return this.props.docView?.props.Document;
+    }
+    get target() {
+        return this.document;
+    }
     _templateCommand = {
-        params: ["target", "source"], title: "item view",
-        script: "self.target.childLayoutTemplate = getDocTemplate(self.source?.[0])",
+        params: ['target', 'source'],
+        title: 'item view',
+        script: 'self.target.childLayoutTemplate = getDocTemplate(self.source?.[0])',
         immediate: undoBatch((source: Doc[]) => {
             let formatStr = source.length && Cast(source[0].text, RichTextField, null)?.Text;
-            try { formatStr && JSON.parse(formatStr); } catch (e) { formatStr = ""; }
+            try {
+                formatStr && JSON.parse(formatStr);
+            } catch (e) {
+                formatStr = '';
+            }
             if (source.length === 1 && formatStr) {
-                Doc.SetInPlace(this.target, "childLayoutString", formatStr, false);
+                Doc.SetInPlace(this.target, 'childLayoutString', formatStr, false);
             } else if (source.length) {
                 this.target.childLayoutTemplate = Doc.getDocTemplate(source?.[0]);
             } else {
-                Doc.SetInPlace(this.target, "childLayoutString", undefined, true);
-                Doc.SetInPlace(this.target, "childLayoutTemplate", undefined, true);
+                Doc.SetInPlace(this.target, 'childLayoutString', undefined, true);
+                Doc.SetInPlace(this.target, 'childLayoutTemplate', undefined, true);
             }
         }),
         initialize: emptyFunction,
     };
     _narrativeCommand = {
-        params: ["target", "source"], title: "child click view",
-        script: "self.target.childClickedOpenTemplateView = getDocTemplate(self.source?.[0])",
+        params: ['target', 'source'],
+        title: 'child click view',
+        script: 'self.target.childClickedOpenTemplateView = getDocTemplate(self.source?.[0])',
         immediate: undoBatch((source: Doc[]) => source.length && (this.target.childClickedOpenTemplateView = Doc.getDocTemplate(source?.[0]))),
         initialize: emptyFunction,
     };
     _contentCommand = {
-        params: ["target", "source"], title: "set content",
-        script: "getProto(self.target).data = copyField(self.source);",
-        immediate: undoBatch((source: Doc[]) => Doc.GetProto(this.target).data = new List<Doc>(source)),
+        params: ['target', 'source'],
+        title: 'set content',
+        script: 'getProto(self.target).data = copyField(self.source);',
+        immediate: undoBatch((source: Doc[]) => (Doc.GetProto(this.target).data = new List<Doc>(source))),
         initialize: emptyFunction,
     };
     _onClickCommand = {
-        params: ["target", "proxy"], title: "copy onClick",
+        params: ['target', 'proxy'],
+        title: 'copy onClick',
         script: `{ if (self.proxy?.[0]) {
              getProto(self.proxy[0]).onClick = copyField(self.target.onClick); 
              getProto(self.proxy[0]).target = self.target.target;
              getProto(self.proxy[0]).source = copyField(self.target.source); 
             }}`,
-        immediate: undoBatch((source: Doc[]) => { }),
+        immediate: undoBatch((source: Doc[]) => {}),
         initialize: emptyFunction,
     };
     _openLinkInCommand = {
-        params: ["target", "container"], title: "link follow target",
+        params: ['target', 'container'],
+        title: 'link follow target',
         script: `{ if (self.container?.length) {
             getProto(self.target).linkContainer = self.container[0];
             getProto(self.target).isLinkButton = true;
@@ -239,126 +257,180 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
             if (container.length) {
                 Doc.GetProto(this.target).linkContainer = container[0];
                 Doc.GetProto(this.target).isLinkButton = true;
-                Doc.GetProto(this.target).onClick = ScriptField.MakeScript("getProto(self.linkContainer).data = new List([self.links[0]?.anchor2])");
+                Doc.GetProto(this.target).onClick = ScriptField.MakeScript('getProto(self.linkContainer).data = new List([self.links[0]?.anchor2])');
             }
         }),
         initialize: emptyFunction,
     };
     _viewCommand = {
-        params: ["target"], title: "bookmark view",
+        params: ['target'],
+        title: 'bookmark view',
         script: "self.target._panX = self['target-panX']; self.target._panY = self['target-panY']; self.target._viewScale = self['target-viewScale']; gotoFrame(self.target, self['target-currentFrame']);",
-        immediate: undoBatch((source: Doc[]) => { this.target._panX = 0; this.target._panY = 0; this.target._viewScale = 1; this.target._currentFrame = (this.target._currentFrame === undefined ? undefined : 0); }),
-        initialize: (button: Doc) => { button['target-panX'] = this.target._panX; button['target-panY'] = this.target._panY; button['target-viewScale'] = this.target._viewScale; button['target-currentFrame'] = this.target._currentFrame; },
+        immediate: undoBatch((source: Doc[]) => {
+            this.target._panX = 0;
+            this.target._panY = 0;
+            this.target._viewScale = 1;
+            this.target._currentFrame = this.target._currentFrame === undefined ? undefined : 0;
+        }),
+        initialize: (button: Doc) => {
+            button['target-panX'] = this.target._panX;
+            button['target-panY'] = this.target._panY;
+            button['target-viewScale'] = this.target._viewScale;
+            button['target-currentFrame'] = this.target._currentFrame;
+        },
     };
     _clusterCommand = {
-        params: ["target"], title: "fit content",
-        script: "self.target._fitContentsToBox = !self.target._fitContentsToBox;",
-        immediate: undoBatch((source: Doc[]) => this.target._fitContentsToBox = !this.target._fitContentsToBox),
-        initialize: emptyFunction
+        params: ['target'],
+        title: 'fit content',
+        script: 'self.target._fitContentsToBox = !self.target._fitContentsToBox;',
+        immediate: undoBatch((source: Doc[]) => (this.target._fitContentsToBox = !this.target._fitContentsToBox)),
+        initialize: emptyFunction,
     };
     _fitContentCommand = {
-        params: ["target"], title: "toggle clusters",
-        script: "self.target._useClusters = !self.target._useClusters;",
-        immediate: undoBatch((source: Doc[]) => this.target._useClusters = !this.target._useClusters),
-        initialize: emptyFunction
+        params: ['target'],
+        title: 'toggle clusters',
+        script: 'self.target._useClusters = !self.target._useClusters;',
+        immediate: undoBatch((source: Doc[]) => (this.target._useClusters = !this.target._useClusters)),
+        initialize: emptyFunction,
     };
     _saveFilterCommand = {
-        params: ["target"], title: "save filter",
+        params: ['target'],
+        title: 'save filter',
         script: `self.target._docFilters = compareLists(self['target-docFilters'],self.target._docFilters) ? undefined : copyField(self['target-docFilters']); 
                  self.target._searchFilterDocs = compareLists(self['target-searchFilterDocs'],self.target._searchFilterDocs) ? undefined: copyField(self['target-searchFilterDocs']);`,
-        immediate: undoBatch((source: Doc[]) => { this.target._docFilters = undefined; this.target._searchFilterDocs = undefined; }),
+        immediate: undoBatch((source: Doc[]) => {
+            this.target._docFilters = undefined;
+            this.target._searchFilterDocs = undefined;
+        }),
         initialize: (button: Doc) => {
-            const activeDash = CurrentUserUtils.ActiveDashboard;
+            const activeDash = Doc.ActiveDashboard;
             if (activeDash) {
-                button['target-docFilters'] = (CurrentUserUtils.MySearcher._docFilters || activeDash._docFilters) instanceof ObjectField ?
-                    ObjectField.MakeCopy((CurrentUserUtils.MySearcher._docFilters || activeDash._docFilters) as any as ObjectField) : undefined;
+                button['target-docFilters'] = (Doc.MySearcher._docFilters || activeDash._docFilters) instanceof ObjectField ? ObjectField.MakeCopy((Doc.MySearcher._docFilters || activeDash._docFilters) as any as ObjectField) : undefined;
                 button['target-searchFilterDocs'] = activeDash._searchFilterDocs instanceof ObjectField ? ObjectField.MakeCopy(activeDash._searchFilterDocs as any as ObjectField) : undefined;
             }
         },
     };
 
-    @computed get _freeform_commands() { return Doc.noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand]; }
-    @computed get _stacking_commands() { return Doc.noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
-    @computed get _masonry_commands() { return Doc.noviceMode ? undefined : [this._contentCommand, this._templateCommand]; }
-    @computed get _schema_commands() { return Doc.noviceMode ? undefined : [this._templateCommand, this._narrativeCommand]; }
-    @computed get _doc_commands() { return Doc.noviceMode ? undefined : [this._openLinkInCommand, this._onClickCommand]; }
-    @computed get _tree_commands() { return undefined; }
+    @computed get _freeform_commands() {
+        return Doc.noviceMode ? [this._viewCommand, this._saveFilterCommand] : [this._viewCommand, this._saveFilterCommand, this._contentCommand, this._templateCommand, this._narrativeCommand];
+    }
+    @computed get _stacking_commands() {
+        return Doc.noviceMode ? undefined : [this._contentCommand, this._templateCommand];
+    }
+    @computed get _masonry_commands() {
+        return Doc.noviceMode ? undefined : [this._contentCommand, this._templateCommand];
+    }
+    @computed get _schema_commands() {
+        return Doc.noviceMode ? undefined : [this._templateCommand, this._narrativeCommand];
+    }
+    @computed get _doc_commands() {
+        return Doc.noviceMode ? undefined : [this._openLinkInCommand, this._onClickCommand];
+    }
+    @computed get _tree_commands() {
+        return undefined;
+    }
     private get _buttonizableCommands() {
         switch (this.props.type) {
-            default: return this._doc_commands;
-            case CollectionViewType.Freeform: return this._freeform_commands;
-            case CollectionViewType.Tree: return this._tree_commands;
-            case CollectionViewType.Schema: return this._schema_commands;
-            case CollectionViewType.Stacking: return this._stacking_commands;
-            case CollectionViewType.Masonry: return this._stacking_commands;
-            case CollectionViewType.Time: return this._freeform_commands;
-            case CollectionViewType.Carousel: return this._freeform_commands;
-            case CollectionViewType.Carousel3D: return this._freeform_commands;
+            default:
+                return this._doc_commands;
+            case CollectionViewType.Freeform:
+                return this._freeform_commands;
+            case CollectionViewType.Tree:
+                return this._tree_commands;
+            case CollectionViewType.Schema:
+                return this._schema_commands;
+            case CollectionViewType.Stacking:
+                return this._stacking_commands;
+            case CollectionViewType.Masonry:
+                return this._stacking_commands;
+            case CollectionViewType.Time:
+                return this._freeform_commands;
+            case CollectionViewType.Carousel:
+                return this._freeform_commands;
+            case CollectionViewType.Carousel3D:
+                return this._freeform_commands;
         }
     }
     private _commandRef = React.createRef<HTMLInputElement>();
     private _viewRef = React.createRef<HTMLInputElement>();
-    @observable private _currentKey: string = "";
+    @observable private _currentKey: string = '';
 
     componentDidMount = action(() => {
-        this._currentKey = this._currentKey || (this._buttonizableCommands?.length ? this._buttonizableCommands[0]?.title : "");
+        this._currentKey = this._currentKey || (this._buttonizableCommands?.length ? this._buttonizableCommands[0]?.title : '');
     });
 
     @undoBatch
     viewChanged = (e: React.ChangeEvent) => {
-        const target = this.document !== CurrentUserUtils.MyLeftSidebarPanel ? this.document : this.document.proto as Doc;
+        const target = this.document !== Doc.MyLeftSidebarPanel ? this.document : (this.document.proto as Doc);
         //@ts-ignore
         target._viewType = e.target.selectedOptions[0].value;
-    }
+    };
 
     commandChanged = (e: React.ChangeEvent) => {
         //@ts-ignore
-        runInAction(() => this._currentKey = e.target.selectedOptions[0].value);
-    }
-
+        runInAction(() => (this._currentKey = e.target.selectedOptions[0].value));
+    };
 
     @action closeViewSpecs = () => {
         this.document._facetWidth = 0;
-    }
+    };
 
     @computed get subChrome() {
-        switch (this.props.docView.props.LayoutTemplateString ? CollectionViewType.Freeform : this.props.type) { // bcz: ARgh!  hack to get menu for tree view outline items
-            default: return this.otherSubChrome;
+        switch (
+            this.props.docView.props.LayoutTemplateString ? CollectionViewType.Freeform : this.props.type // bcz: ARgh!  hack to get menu for tree view outline items
+        ) {
+            default:
+                return this.otherSubChrome;
             case CollectionViewType.Invalid:
-            case CollectionViewType.Freeform: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={this.props.type === CollectionViewType.Invalid} />);
-            case CollectionViewType.Stacking: return (<CollectionStackingViewChrome key="collchrome" {...this.props} />);
-            case CollectionViewType.Schema: return (<CollectionSchemaViewChrome key="collchrome" {...this.props} />);
-            case CollectionViewType.Tree: return (<CollectionTreeViewChrome key="collchrome" {...this.props} />);
-            case CollectionViewType.Masonry: return (<CollectionStackingViewChrome key="collchrome" {...this.props} />);
+            case CollectionViewType.Freeform:
+                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={this.props.type === CollectionViewType.Invalid} />;
+            case CollectionViewType.Stacking:
+                return <CollectionStackingViewChrome key="collchrome" {...this.props} />;
+            case CollectionViewType.Schema:
+                return <CollectionSchemaViewChrome key="collchrome" {...this.props} />;
+            case CollectionViewType.Tree:
+                return <CollectionTreeViewChrome key="collchrome" {...this.props} />;
+            case CollectionViewType.Masonry:
+                return <CollectionStackingViewChrome key="collchrome" {...this.props} />;
             case CollectionViewType.Carousel:
-            case CollectionViewType.Carousel3D: return (<Collection3DCarouselViewChrome key="collchrome" {...this.props} />);
-            case CollectionViewType.Grid: return (<CollectionGridViewChrome key="collchrome" {...this.props} />);
-            case CollectionViewType.Docking: return (<CollectionDockingChrome key="collchrome" {...this.props} />);
+            case CollectionViewType.Carousel3D:
+                return <Collection3DCarouselViewChrome key="collchrome" {...this.props} />;
+            case CollectionViewType.Grid:
+                return <CollectionGridViewChrome key="collchrome" {...this.props} />;
+            case CollectionViewType.Docking:
+                return <CollectionDockingChrome key="collchrome" {...this.props} />;
         }
     }
 
     @computed get otherSubChrome() {
         const docType = this.props.docView.Document.type;
         switch (docType) {
-            default: return (null);
-            case DocumentType.IMG: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
-            case DocumentType.PDF: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
-            case DocumentType.INK: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
-            case DocumentType.WEB: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
-            case DocumentType.VID: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
-            case DocumentType.RTF: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={this.props.type === CollectionViewType.Invalid} isDoc={true} />);
-            case DocumentType.MAP: return (<CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />);
+            default:
+                return null;
+            case DocumentType.IMG:
+                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
+            case DocumentType.PDF:
+                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
+            case DocumentType.INK:
+                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
+            case DocumentType.WEB:
+                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
+            case DocumentType.VID:
+                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
+            case DocumentType.RTF:
+                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={this.props.type === CollectionViewType.Invalid} isDoc={true} />;
+            case DocumentType.MAP:
+                return <CollectionFreeFormViewChrome key="collchrome" {...this.props} isOverlay={false} isDoc={true} />;
         }
     }
 
-
     private dropDisposer?: DragManager.DragDropDisposer;
     protected createDropTarget = (ele: HTMLDivElement) => {
         this.dropDisposer?.();
         if (ele) {
             this.dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.document);
         }
-    }
+    };
 
     @undoBatch
     @action
@@ -372,120 +444,139 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
     }
 
     dragViewDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, (e, down, delta) => {
-            const vtype = this.props.type;
-            const c = {
-                params: ["target"], title: vtype,
-                script: `this.target._viewType = '${StrCast(this.props.type)}'`,
-                immediate: (source: Doc[]) => this.document._viewType = Doc.getDocTemplate(source?.[0]),
-                initialize: emptyFunction,
-            };
-            DragManager.StartButtonDrag([this._viewRef.current!], c.script, StrCast(c.title),
-                { target: this.document }, c.params, c.initialize, e.clientX, e.clientY);
-            return true;
-        }, emptyFunction, emptyFunction);
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            (e, down, delta) => {
+                const vtype = this.props.type;
+                const c = {
+                    params: ['target'],
+                    title: vtype,
+                    script: `this.target._viewType = '${StrCast(this.props.type)}'`,
+                    immediate: (source: Doc[]) => (this.document._viewType = Doc.getDocTemplate(source?.[0])),
+                    initialize: emptyFunction,
+                };
+                DragManager.StartButtonDrag([this._viewRef.current!], c.script, StrCast(c.title), { target: this.document }, c.params, c.initialize, e.clientX, e.clientY);
+                return true;
+            },
+            emptyFunction,
+            emptyFunction
+        );
+    };
     dragCommandDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, (e, down, delta) => {
-            this._buttonizableCommands?.filter(c => c.title === this._currentKey).map(c =>
-                DragManager.StartButtonDrag([this._commandRef.current!], c.script, c.title,
-                    { target: this.document }, c.params, c.initialize, e.clientX, e.clientY));
-            return true;
-        }, emptyFunction, () => {
-            this._buttonizableCommands?.filter(c => c.title === this._currentKey).map(c => c.immediate([]));
-        });
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            (e, down, delta) => {
+                this._buttonizableCommands?.filter(c => c.title === this._currentKey).map(c => DragManager.StartButtonDrag([this._commandRef.current!], c.script, c.title, { target: this.document }, c.params, c.initialize, e.clientX, e.clientY));
+                return true;
+            },
+            emptyFunction,
+            () => {
+                this._buttonizableCommands?.filter(c => c.title === this._currentKey).map(c => c.immediate([]));
+            }
+        );
+    };
 
     @computed get templateChrome() {
-        return <div className="collectionViewBaseChrome-template" ref={this.createDropTarget} >
-            <Tooltip title={<div className="dash-tooltip">drop document to apply or drag to create button</div>} placement="bottom">
-                <div className="commandEntry-outerDiv" ref={this._commandRef} onPointerDown={this.dragCommandDown}>
-                    <button className={"antimodeMenu-button"} >
-                        <FontAwesomeIcon icon="bullseye" size="lg" />
-                    </button>
-                    <select
-                        className="collectionViewBaseChrome-cmdPicker" onPointerDown={stopPropagation} onChange={this.commandChanged} value={this._currentKey}>
-                        <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={"empty"} value={""} />
-                        {this._buttonizableCommands?.map(cmd =>
-                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={cmd.title} value={cmd.title}>{cmd.title}</option>
-                        )}
-                    </select>
-                </div>
-            </Tooltip>
-        </div>;
+        return (
+            <div className="collectionViewBaseChrome-template" ref={this.createDropTarget}>
+                <Tooltip title={<div className="dash-tooltip">drop document to apply or drag to create button</div>} placement="bottom">
+                    <div className="commandEntry-outerDiv" ref={this._commandRef} onPointerDown={this.dragCommandDown}>
+                        <button className={'antimodeMenu-button'}>
+                            <FontAwesomeIcon icon="bullseye" size="lg" />
+                        </button>
+                        <select className="collectionViewBaseChrome-cmdPicker" onPointerDown={stopPropagation} onChange={this.commandChanged} value={this._currentKey}>
+                            <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={'empty'} value={''} />
+                            {this._buttonizableCommands?.map(cmd => (
+                                <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} key={cmd.title} value={cmd.title}>
+                                    {cmd.title}
+                                </option>
+                            ))}
+                        </select>
+                    </div>
+                </Tooltip>
+            </div>
+        );
     }
 
     @computed get viewModes() {
         const excludedViewTypes = [CollectionViewType.Invalid, CollectionViewType.Docking, CollectionViewType.Pile, CollectionViewType.StackedTimeline, CollectionViewType.Linear];
-        const isPres: boolean = (this.document && this.document.type === DocumentType.PRES);
-        return isPres ? (null) : (<div className="collectionViewBaseChrome-viewModes" >
-            <Tooltip title={<div className="dash-tooltip">drop document to apply or drag to create button</div>} placement="bottom">
-                <div className="commandEntry-outerDiv" ref={this._viewRef} onPointerDown={this.dragViewDown}>
-                    <button className={"antimodeMenu-button"}>
-                        <FontAwesomeIcon icon="bullseye" size="lg" />
-                    </button>
-                    <select
-                        className="collectionViewBaseChrome-viewPicker"
-                        onPointerDown={stopPropagation}
-                        onChange={this.viewChanged}
-                        value={StrCast(this.props.type)}>
-                        {Object.values(CollectionViewType).filter(type => !excludedViewTypes.includes(type)).map(type => (
-                            <option
-                                key={Utils.GenerateGuid()}
-                                className="collectionViewBaseChrome-viewOption"
-                                onPointerDown={stopPropagation}
-                                value={type}>
-                                {type[0].toUpperCase() + type.substring(1)}
-                            </option>
-                        ))}
-                    </select>
-                </div>
-            </Tooltip>
-        </div>);
+        const isPres: boolean = this.document && this.document.type === DocumentType.PRES;
+        return isPres ? null : (
+            <div className="collectionViewBaseChrome-viewModes">
+                <Tooltip title={<div className="dash-tooltip">drop document to apply or drag to create button</div>} placement="bottom">
+                    <div className="commandEntry-outerDiv" ref={this._viewRef} onPointerDown={this.dragViewDown}>
+                        <button className={'antimodeMenu-button'}>
+                            <FontAwesomeIcon icon="bullseye" size="lg" />
+                        </button>
+                        <select className="collectionViewBaseChrome-viewPicker" onPointerDown={stopPropagation} onChange={this.viewChanged} value={StrCast(this.props.type)}>
+                            {Object.values(CollectionViewType)
+                                .filter(type => !excludedViewTypes.includes(type))
+                                .map(type => (
+                                    <option key={Utils.GenerateGuid()} className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value={type}>
+                                        {type[0].toUpperCase() + type.substring(1)}
+                                    </option>
+                                ))}
+                        </select>
+                    </div>
+                </Tooltip>
+            </div>
+        );
     }
 
-    @computed get selectedDocumentView() { return SelectionManager.Views().lastElement(); }
-    @computed get selectedDoc() { return SelectionManager.Docs().lastElement(); }
+    @computed get selectedDocumentView() {
+        return SelectionManager.Views().lastElement();
+    }
+    @computed get selectedDoc() {
+        return SelectionManager.Docs().lastElement();
+    }
     @computed get notACollection() {
         if (this.selectedDoc) {
             const layoutField = Doc.LayoutField(this.selectedDoc);
-            return this.props.type === CollectionViewType.Docking ||
-                typeof (layoutField) === "string" && !layoutField?.includes("CollectionView");
-        }
-        else return false;
+            return this.props.type === CollectionViewType.Docking || (typeof layoutField === 'string' && !layoutField?.includes('CollectionView'));
+        } else return false;
     }
     @computed
     get pinButton() {
         const targetDoc = this.selectedDoc;
         const isPinned = targetDoc && Doc.isDocPinned(targetDoc);
-        return !targetDoc ? (null) : <Tooltip key="pin" title={<div className="dash-tooltip">{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}</div>} placement="top">
-            <button className="antimodeMenu-button" style={{ backgroundColor: isPinned ? "121212" : undefined, borderLeft: "1px solid gray" }}
-                onClick={e => TabDocView.PinDoc(targetDoc, { /* unpin: isPinned*/ })}>
-                <FontAwesomeIcon className="colMenu-icon" size="lg" icon="map-pin" />
-            </button>
-        </Tooltip>;
+        return !targetDoc ? null : (
+            <Tooltip key="pin" title={<div className="dash-tooltip">{Doc.isDocPinned(targetDoc) ? 'Unpin from presentation' : 'Pin to presentation'}</div>} placement="top">
+                <button
+                    className="antimodeMenu-button"
+                    style={{ backgroundColor: isPinned ? '121212' : undefined, borderLeft: '1px solid gray' }}
+                    onClick={e =>
+                        TabDocView.PinDoc(targetDoc, {
+                            /* unpin: isPinned*/
+                        })
+                    }>
+                    <FontAwesomeIcon className="colMenu-icon" size="lg" icon="map-pin" />
+                </button>
+            </Tooltip>
+        );
     }
 
     @undoBatch
     @action
     startRecording = () => {
-        const doc = Docs.Create.ScreenshotDocument({ title: "screen recording", _fitWidth: true, _width: 400, _height: 200, mediaState: "pendingRecording" });
-        //Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, doc);
-        CollectionDockingView.AddSplit(doc, "right");
-    }
+        const doc = Docs.Create.ScreenshotDocument({ title: 'screen recording', _fitWidth: true, _width: 400, _height: 200, mediaState: 'pendingRecording' });
+        //Doc.AddDocToList(Doc.MyOverlayDocs, undefined, doc);
+        CollectionDockingView.AddSplit(doc, 'right');
+    };
 
     @computed
     get recordButton() {
         const targetDoc = this.selectedDoc;
-        return <Tooltip key="record" title={<div className="dash-tooltip">{"Capture screen"}</div>} placement="top">
-            <button className="antimodeMenu-button"
-                onClick={e => this.startRecording()}>
-                <div className="recordButtonOutline" style={{}}>
-                    <div className="recordButtonInner" style={{}}>
+        return (
+            <Tooltip key="record" title={<div className="dash-tooltip">{'Capture screen'}</div>} placement="top">
+                <button className="antimodeMenu-button" onClick={e => this.startRecording()}>
+                    <div className="recordButtonOutline" style={{}}>
+                        <div className="recordButtonInner" style={{}}></div>
                     </div>
-                </div>
-            </button>
-        </Tooltip>;
+                </button>
+            </Tooltip>
+        );
     }
 
     @undoBatch
@@ -517,21 +608,20 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
                 activeDoc.presPinView = true;
             }
         }
-    }
+    };
 
     @computed
     get pinWithViewButton() {
-        const presPinWithViewIcon = <img src={`/assets/pinWithView.png`} style={{ margin: "auto", width: 19 }} />;
-        return !this.selectedDoc ? (null) :
-            <Tooltip title={<div className="dash-tooltip">{"Pin with current view"}</div>} placement="top">
-                <button className="antimodeMenu-button" style={{ justifyContent: 'center' }}
-                    onClick={() => this.pinWithView(this.selectedDoc)}>
+        const presPinWithViewIcon = <img src={`/assets/pinWithView.png`} style={{ margin: 'auto', width: 19 }} />;
+        return !this.selectedDoc ? null : (
+            <Tooltip title={<div className="dash-tooltip">{'Pin with current view'}</div>} placement="top">
+                <button className="antimodeMenu-button" style={{ justifyContent: 'center' }} onClick={() => this.pinWithView(this.selectedDoc)}>
                     {presPinWithViewIcon}
                 </button>
-            </Tooltip>;
+            </Tooltip>
+        );
     }
 
-
     @undoBatch
     onAlias = () => {
         if (this.selectedDoc && this.selectedDocumentView) {
@@ -544,10 +634,10 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
             alias.y = NumCast(this.selectedDoc.y) + 30;
             this.selectedDocumentView.props.addDocument?.(alias);
         }
-    }
+    };
     onAliasButtonDown = (e: React.PointerEvent): void => {
         setupMoveUpEvents(this, e, this.onAliasButtonMoved, emptyFunction, emptyFunction);
-    }
+    };
 
     @undoBatch
     onAliasButtonMoved = (e: PointerEvent) => {
@@ -555,62 +645,72 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
         if (contentDiv && this.selectedDoc) {
             const dragData = new DragManager.DocumentDragData([this.selectedDoc]);
             const offset = [e.clientX - contentDiv.getBoundingClientRect().x, e.clientY - contentDiv.getBoundingClientRect().y];
-            dragData.defaultDropAction = "alias";
+            dragData.defaultDropAction = 'alias';
             dragData.canEmbed = true;
             DragManager.StartDocumentDrag([contentDiv], dragData, e.clientX, e.clientY, {
                 offsetX: offset[0],
                 offsetY: offset[1],
-                hideSource: false
+                hideSource: false,
             });
             return true;
         }
         return false;
-    }
+    };
 
     @computed
     get aliasButton() {
         const targetDoc = this.selectedDoc;
-        return !targetDoc || targetDoc.type === DocumentType.PRES ? (null) : <Tooltip title={<div className="dash-tooltip">{"Tap or Drag to create an alias"}</div>} placement="top">
-            <button className="antimodeMenu-button" onPointerDown={this.onAliasButtonDown} onClick={this.onAlias} style={{ cursor: "drag" }}>
-                <FontAwesomeIcon className="colMenu-icon" icon="copy" size="lg" />
-            </button>
-        </Tooltip>;
+        return !targetDoc || targetDoc.type === DocumentType.PRES ? null : (
+            <Tooltip title={<div className="dash-tooltip">{'Tap or Drag to create an alias'}</div>} placement="top">
+                <button className="antimodeMenu-button" onPointerDown={this.onAliasButtonDown} onClick={this.onAlias} style={{ cursor: 'drag' }}>
+                    <FontAwesomeIcon className="colMenu-icon" icon="copy" size="lg" />
+                </button>
+            </Tooltip>
+        );
     }
 
     @computed get lightboxButton() {
         const targetDoc = this.selectedDoc;
-        return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"View in Lightbox"}</div>} placement="top">
-            <button className="antimodeMenu-button" onPointerDown={() => {
-                const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
-                LightboxView.SetLightboxDoc(targetDoc, undefined, docs);
-            }}>
-                <FontAwesomeIcon className="colMenu-icon" icon="desktop" size="lg" />
-            </button>
-        </Tooltip>;
+        return !targetDoc ? null : (
+            <Tooltip title={<div className="dash-tooltip">{'View in Lightbox'}</div>} placement="top">
+                <button
+                    className="antimodeMenu-button"
+                    onPointerDown={() => {
+                        const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]);
+                        LightboxView.SetLightboxDoc(targetDoc, undefined, docs);
+                    }}>
+                    <FontAwesomeIcon className="colMenu-icon" icon="desktop" size="lg" />
+                </button>
+            </Tooltip>
+        );
     }
 
     @computed get toggleOverlayButton() {
-        return <>
-            <Tooltip title={<div className="dash-tooltip">Toggle Overlay Layer</div>} placement="bottom">
-                <button className={"antimodeMenu-button"} key="float"
-                    style={{
-                        backgroundColor: this.props.docView.layoutDoc.z ? "121212" : undefined,
-                        pointerEvents: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? "none" : undefined,
-                        color: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? "dimgrey" : undefined
-                    }}
-                    onClick={undoBatch(() => this.props.docView.props.CollectionFreeFormDocumentView?.().float())}>
-                    <FontAwesomeIcon icon={["fab", "buffer"]} size={"lg"} />
-                </button>
-            </Tooltip>
-        </>;
+        return (
+            <>
+                <Tooltip title={<div className="dash-tooltip">Toggle Overlay Layer</div>} placement="bottom">
+                    <button
+                        className={'antimodeMenu-button'}
+                        key="float"
+                        style={{
+                            backgroundColor: this.props.docView.layoutDoc.z ? '121212' : undefined,
+                            pointerEvents: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? 'none' : undefined,
+                            color: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? 'dimgrey' : undefined,
+                        }}
+                        onClick={undoBatch(() => this.props.docView.props.CollectionFreeFormDocumentView?.().float())}>
+                        <FontAwesomeIcon icon={['fab', 'buffer']} size={'lg'} />
+                    </button>
+                </Tooltip>
+            </>
+        );
     }
 
     render() {
         return (
-            <div className="collectionMenu-cont" >
+            <div className="collectionMenu-cont">
                 <div className="collectionMenu">
                     <div className="collectionViewBaseChrome">
-                        {this.notACollection || this.props.type === CollectionViewType.Invalid ? (null) : this.viewModes}
+                        {this.notACollection || this.props.type === CollectionViewType.Invalid ? null : this.viewModes}
                         <div className="collectionMenu-divider" key="divider1"></div>
                         {this.aliasButton}
                         {/* {this.pinButton} */}
@@ -621,7 +721,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
                         <div className="collectionMenu-divider" key="divider3"></div>
                         {this.lightboxButton}
                         {this.recordButton}
-                        {!this._buttonizableCommands ? (null) : this.templateChrome}
+                        {!this._buttonizableCommands ? null : this.templateChrome}
                     </div>
                 </div>
             </div>
@@ -632,30 +732,40 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
 @observer
 export class CollectionDockingChrome extends React.Component<CollectionViewMenuProps> {
     render() {
-        return (null);
+        return null;
     }
 }
 
 @observer
-export class CollectionFreeFormViewChrome extends React.Component<CollectionViewMenuProps & { isOverlay: boolean, isDoc?: boolean }> {
+export class CollectionFreeFormViewChrome extends React.Component<CollectionViewMenuProps & { isOverlay: boolean; isDoc?: boolean }> {
     public static Instance: CollectionFreeFormViewChrome;
     constructor(props: any) {
         super(props);
         CollectionFreeFormViewChrome.Instance = this;
     }
-    get document() { return this.props.docView.props.Document; }
+    get document() {
+        return this.props.docView.props.Document;
+    }
     @computed get dataField() {
-        return this.document[this.props.docView.LayoutFieldKey + (this.props.isOverlay ? "-annotations" : "")];
+        return this.document[this.props.docView.LayoutFieldKey + (this.props.isOverlay ? '-annotations' : '')];
+    }
+    @computed get childDocs() {
+        return DocListCast(this.dataField);
+    }
+    @computed get selectedDocumentView() {
+        return SelectionManager.Views().lastElement();
+    }
+    @computed get selectedDoc() {
+        return SelectionManager.Docs().lastElement();
+    }
+    @computed get isText() {
+        return this.selectedDoc?.type === DocumentType.RTF || (RichTextMenu.Instance?.view as any) ? true : false;
     }
-    @computed get childDocs() { return DocListCast(this.dataField); }
-    @computed get selectedDocumentView() { return SelectionManager.Views().lastElement(); }
-    @computed get selectedDoc() { return SelectionManager.Docs().lastElement(); }
-    @computed get isText() { return this.selectedDoc?.type === DocumentType.RTF || (RichTextMenu.Instance?.view as any) ? true : false; }
 
     @undoBatch
     @action
     nextKeyframe = (): void => {
-        const currentFrame = Cast(this.document._currentFrame, "number", null);
+        const currentFrame = Cast(this.document._currentFrame, 'number', null);
         if (currentFrame === undefined) {
             this.document._currentFrame = 0;
             CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
@@ -663,72 +773,88 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
         CollectionFreeFormDocumentView.updateKeyframe(this.childDocs, currentFrame || 0);
         this.document._currentFrame = Math.max(0, (currentFrame || 0) + 1);
         this.document.lastFrame = Math.max(NumCast(this.document._currentFrame), NumCast(this.document.lastFrame));
-    }
+    };
     @undoBatch
     @action
     prevKeyframe = (): void => {
-        const currentFrame = Cast(this.document._currentFrame, "number", null);
+        const currentFrame = Cast(this.document._currentFrame, 'number', null);
         if (currentFrame === undefined) {
             this.document._currentFrame = 0;
             CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0);
         }
         CollectionFreeFormDocumentView.gotoKeyframe(this.childDocs.slice());
         this.document._currentFrame = Math.max(0, (currentFrame || 0) - 1);
-    }
+    };
 
-    private _palette = ["#D0021B", "#F5A623", "#F8E71C", "#8B572A", "#7ED321", "#417505", "#9013FE", "#4A90E2", "#50E3C2", "#B8E986", "#000000", "#4A4A4A", "#9B9B9B", "#FFFFFF", ""];
-    private _width = ["1", "5", "10", "100"];
+    private _palette = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', ''];
+    private _width = ['1', '5', '10', '100'];
     private _dotsize = [10, 20, 30, 40];
-    private _draw = ["∿", "=", "⎯", "→", "↔︎", "ロ", "O"];
-    private _head = ["", "", "", "", "arrow", "", ""];
-    private _end = ["", "", "", "arrow", "arrow", "", ""];
-    private _shapePrims = ["", "", "line", "line", "line", "rectangle", "circle"];
-    private _title = ["pen", "highlighter", "line", "line with arrow", "line with double arrows", "square", "circle"];
-    private _faName = ["pen-fancy", "highlighter", "minus", "long-arrow-alt-right", "arrows-alt-h", "square", "circle"];
+    private _draw = ['∿', '=', '⎯', '→', '↔︎', 'ロ', 'O'];
+    private _head = ['', '', '', '', 'arrow', '', ''];
+    private _end = ['', '', '', 'arrow', 'arrow', '', ''];
+    private _shapePrims = ['', '', 'line', 'line', 'line', 'rectangle', 'circle'];
+    private _title = ['pen', 'highlighter', 'line', 'line with arrow', 'line with double arrows', 'square', 'circle'];
+    private _faName = ['pen-fancy', 'highlighter', 'minus', 'long-arrow-alt-right', 'arrows-alt-h', 'square', 'circle'];
     @observable _selectedPrimitive = this._shapePrims.length;
     @observable _keepPrimitiveMode = false; // for whether primitive selection enters a one-shot or persistent mode
     @observable _colorBtn = false;
     @observable _widthBtn = false;
     @observable _fillBtn = false;
 
-    @action clearKeepPrimitiveMode() { this._selectedPrimitive = this._shapePrims.length; }
+    @action clearKeepPrimitiveMode() {
+        this._selectedPrimitive = this._shapePrims.length;
+    }
     @action primCreated() {
-        if (!this._keepPrimitiveMode) { //get out of ink mode after each stroke=
-            if (CurrentUserUtils.ActiveTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor);
-            CurrentUserUtils.ActiveTool = InkTool.None;
+        if (!this._keepPrimitiveMode) {
+            //get out of ink mode after each stroke=
+            if (Doc.ActiveTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor);
+            Doc.ActiveTool = InkTool.None;
             this._selectedPrimitive = this._shapePrims.length;
-            SetActiveArrowStart("none");
-            SetActiveArrowEnd("none");
+            SetActiveArrowStart('none');
+            SetActiveArrowEnd('none');
         }
     }
 
     @action
     changeColor = (color: string, type: string) => {
         const col: ColorState = {
-            hex: color, hsl: { a: 0, h: 0, s: 0, l: 0, source: "" }, hsv: { a: 0, h: 0, s: 0, v: 0, source: "" },
-            rgb: { a: 0, r: 0, b: 0, g: 0, source: "" }, oldHue: 0, source: "",
+            hex: color,
+            hsl: { a: 0, h: 0, s: 0, l: 0, source: '' },
+            hsv: { a: 0, h: 0, s: 0, v: 0, source: '' },
+            rgb: { a: 0, r: 0, b: 0, g: 0, source: '' },
+            oldHue: 0,
+            source: '',
         };
-        if (type === "color") {
+        if (type === 'color') {
             SetActiveInkColor(Utils.colorString(col));
-        } else if (type === "fill") {
+        } else if (type === 'fill') {
             SetActiveFillColor(Utils.colorString(col));
         }
-    }
+    };
 
     @action
     editProperties = (value: any, field: string) => {
-        SelectionManager.Views().forEach(action((element: DocumentView) => {
-            const doc = Document(element.rootDoc);
-            if (doc.type === DocumentType.INK) {
-                switch (field) {
-                    case "width": doc.strokeWidth = Number(value); break;
-                    case "color": doc.color = String(value); break;
-                    case "fill": doc.fillColor = String(value); break;
-                    case "dash": doc.strokeDash = value;
+        SelectionManager.Views().forEach(
+            action((element: DocumentView) => {
+                const doc = Document(element.rootDoc);
+                if (doc.type === DocumentType.INK) {
+                    switch (field) {
+                        case 'width':
+                            doc.strokeWidth = Number(value);
+                            break;
+                        case 'color':
+                            doc.color = String(value);
+                            break;
+                        case 'fill':
+                            doc.fillColor = String(value);
+                            break;
+                        case 'dash':
+                            doc.strokeDash = value;
+                    }
                 }
-            }
-        }));
-    }
+            })
+        );
+    };
 
     @computed get drawButtons() {
         const func = action((e: React.MouseEvent | React.PointerEvent, i: number, keep: boolean) => {
@@ -736,147 +862,184 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView
             // these are for shapes
             if (this._selectedPrimitive !== i) {
                 this._selectedPrimitive = i;
-                if (this._title[i] === "highlighter") {
-                    CurrentUserUtils.ActiveTool = InkTool.Highlighter;
+                if (this._title[i] === 'highlighter') {
+                    Doc.ActiveTool = InkTool.Highlighter;
                     GestureOverlay.Instance.SavedColor = ActiveInkColor();
-                    SetActiveInkColor("rgba(245, 230, 95, 0.75)");
+                    SetActiveInkColor('rgba(245, 230, 95, 0.75)');
                 } else {
-                    CurrentUserUtils.ActiveTool = InkTool.Pen;
+                    Doc.ActiveTool = InkTool.Pen;
                 }
                 SetActiveArrowStart(this._head[i]);
                 SetActiveArrowEnd(this._end[i]);
-                SetActiveBezierApprox("300");
+                SetActiveBezierApprox('300');
 
                 GestureOverlay.Instance.InkShape = this._shapePrims[i];
             } else {
                 this._selectedPrimitive = this._shapePrims.length;
-                CurrentUserUtils.ActiveTool = InkTool.None;
-                SetActiveArrowStart("");
-                SetActiveArrowEnd("");
-                GestureOverlay.Instance.InkShape = "";
-                SetActiveBezierApprox("0");
+                Doc.ActiveTool = InkTool.None;
+                SetActiveArrowStart('');
+                SetActiveArrowEnd('');
+                GestureOverlay.Instance.InkShape = '';
+                SetActiveBezierApprox('0');
             }
             e.stopPropagation();
         });
-        return <div className="btn-draw" key="draw">
-            {this._draw.map((icon, i) =>
-                <Tooltip key={icon} title={<div className="dash-tooltip">{this._title[i]}</div>} placement="bottom">
-                    <button className="antimodeMenu-button"
-                        onPointerDown={e => func(e, i, false)}
-                        onDoubleClick={e => func(e, i, true)}
-                        style={{ backgroundColor: i === this._selectedPrimitive ? "525252" : "", fontSize: "20" }}>
-                        <FontAwesomeIcon icon={this._faName[i] as IconProp} size="sm" />
-                    </button>
-                </Tooltip>)}
-        </div>;
+        return (
+            <div className="btn-draw" key="draw">
+                {this._draw.map((icon, i) => (
+                    <Tooltip key={icon} title={<div className="dash-tooltip">{this._title[i]}</div>} placement="bottom">
+                        <button className="antimodeMenu-button" onPointerDown={e => func(e, i, false)} onDoubleClick={e => func(e, i, true)} style={{ backgroundColor: i === this._selectedPrimitive ? '525252' : '', fontSize: '20' }}>
+                            <FontAwesomeIcon icon={this._faName[i] as IconProp} size="sm" />
+                        </button>
+                    </Tooltip>
+                ))}
+            </div>
+        );
     }
 
-    toggleButton = (key: string, value: boolean, setter: () => {}, icon: FontAwesomeIconProps["icon"], ele: JSX.Element | null) => {
-        return <Tooltip title={<div className="dash-tooltip">{key}</div>} placement="bottom">
-            <button className="antimodeMenu-button" key={key}
-                onPointerDown={action(e => setter())}
-                style={{ backgroundColor: value ? "121212" : "" }}>
-                <FontAwesomeIcon icon={icon} size="lg" />
-                {ele}
-            </button>
-        </Tooltip>;
-    }
+    toggleButton = (key: string, value: boolean, setter: () => {}, icon: FontAwesomeIconProps['icon'], ele: JSX.Element | null) => {
+        return (
+            <Tooltip title={<div className="dash-tooltip">{key}</div>} placement="bottom">
+                <button className="antimodeMenu-button" key={key} onPointerDown={action(e => setter())} style={{ backgroundColor: value ? '121212' : '' }}>
+                    <FontAwesomeIcon icon={icon} size="lg" />
+                    {ele}
+                </button>
+            </Tooltip>
+        );
+    };
 
     @computed get widthPicker() {
-        const widthPicker = this.toggleButton("stroke width", this._widthBtn, () => this._widthBtn = !this._widthBtn, "bars", null);
-        return !this._widthBtn ? widthPicker :
+        const widthPicker = this.toggleButton('stroke width', this._widthBtn, () => (this._widthBtn = !this._widthBtn), 'bars', null);
+        return !this._widthBtn ? (
+            widthPicker
+        ) : (
             <div className="btn2-group" key="width">
                 {widthPicker}
-                {this._width.map((wid, i) =>
+                {this._width.map((wid, i) => (
                     <Tooltip title={<div className="dash-tooltip">change width</div>} placement="bottom">
-                        <button className="antimodeMenu-button" key={wid}
-                            onPointerDown={action(() => { SetActiveInkWidth(wid); this._widthBtn = false; this.editProperties(wid, "width"); })}
-                            style={{ backgroundColor: this._widthBtn ? "121212" : "", zIndex: 1001, fontSize: this._dotsize[i], padding: 0, textAlign: "center" }}>
+                        <button
+                            className="antimodeMenu-button"
+                            key={wid}
+                            onPointerDown={action(() => {
+                                SetActiveInkWidth(wid);
+                                this._widthBtn = false;
+                                this.editProperties(wid, 'width');
+                            })}
+                            style={{ backgroundColor: this._widthBtn ? '121212' : '', zIndex: 1001, fontSize: this._dotsize[i], padding: 0, textAlign: 'center' }}>
                             •
                         </button>
-                    </Tooltip>)}
-            </div>;
+                    </Tooltip>
+                ))}
+            </div>
+        );
     }
 
     @computed get colorPicker() {
-        const colorPicker = this.toggleButton("stroke color", this._colorBtn, () => this._colorBtn = !this._colorBtn, "pen-nib",
-            <div className="color-previewI" style={{ backgroundColor: ActiveInkColor() ?? "121212" }} />);
-        return !this._colorBtn ? colorPicker :
+        const colorPicker = this.toggleButton('stroke color', this._colorBtn, () => (this._colorBtn = !this._colorBtn), 'pen-nib', <div className="color-previewI" style={{ backgroundColor: ActiveInkColor() ?? '121212' }} />);
+        return !this._colorBtn ? (
+            colorPicker
+        ) : (
             <div className="btn-group" key="color">
                 {colorPicker}
-                {this._palette.map(color =>
-                    <button className="antimodeMenu-button" key={color}
-                        onPointerDown={action(() => { this.changeColor(color, "color"); this._colorBtn = false; this.editProperties(color, "color"); })}
-                        style={{ backgroundColor: this._colorBtn ? "121212" : "", zIndex: 1001 }}>
+                {this._palette.map(color => (
+                    <button
+                        className="antimodeMenu-button"
+                        key={color}
+                        onPointerDown={action(() => {
+                            this.changeColor(color, 'color');
+                            this._colorBtn = false;
+                            this.editProperties(color, 'color');
+                        })}
+                        style={{ backgroundColor: this._colorBtn ? '121212' : '', zIndex: 1001 }}>
                         {/* <FontAwesomeIcon icon="pen-nib" size="lg" /> */}
                         <div className="color-previewII" style={{ backgroundColor: color }}>
-                            {color === "" ? <p style={{ fontSize: 40, color: "red", marginTop: -10, marginLeft: -5, position: "fixed" }}>☒</p> : ""}
+                            {color === '' ? <p style={{ fontSize: 40, color: 'red', marginTop: -10, marginLeft: -5, position: 'fixed' }}>☒</p> : ''}
                         </div>
-                    </button >)}
-            </div >;
+                    </button>
+                ))}
+            </div>
+        );
     }
     @computed get fillPicker() {
-        const fillPicker = this.toggleButton("shape fill color", this._fillBtn, () => this._fillBtn = !this._fillBtn, "fill-drip",
-            <div className="color-previewI" style={{ backgroundColor: ActiveFillColor() ?? "121212" }} />);
-        return !this._fillBtn ? fillPicker :
-            <div className="btn-group" key="fill" >
+        const fillPicker = this.toggleButton('shape fill color', this._fillBtn, () => (this._fillBtn = !this._fillBtn), 'fill-drip', <div className="color-previewI" style={{ backgroundColor: ActiveFillColor() ?? '121212' }} />);
+        return !this._fillBtn ? (
+            fillPicker
+        ) : (
+            <div className="btn-group" key="fill">
                 {fillPicker}
-                {this._palette.map(color =>
-                    <button className="antimodeMenu-button" key={color}
-                        onPointerDown={action(() => { this.changeColor(color, "fill"); this._fillBtn = false; this.editProperties(color, "fill"); })}
-                        style={{ backgroundColor: this._fillBtn ? "121212" : "", zIndex: 1001 }}>
+                {this._palette.map(color => (
+                    <button
+                        className="antimodeMenu-button"
+                        key={color}
+                        onPointerDown={action(() => {
+                            this.changeColor(color, 'fill');
+                            this._fillBtn = false;
+                            this.editProperties(color, 'fill');
+                        })}
+                        style={{ backgroundColor: this._fillBtn ? '121212' : '', zIndex: 1001 }}>
                         <div className="color-previewII" style={{ backgroundColor: color }}>
-                            {color === "" ? <p style={{ fontSize: 40, color: "red", marginTop: -10, marginLeft: -5, position: "fixed" }}>☒</p> : ""}
+                            {color === '' ? <p style={{ fontSize: 40, color: 'red', marginTop: -10, marginLeft: -5, position: 'fixed' }}>☒</p> : ''}
                         </div>
-                    </button>)}
-
-            </div>;
+                    </button>
+                ))}
+            </div>
+        );
     }
 
     render() {
-        return !this.props.docView.layoutDoc ? (null) :
+        return !this.props.docView.layoutDoc ? null : (
             <div className="collectionFreeFormMenu-cont">
                 <RichTextMenu key="rich" />
-                {!this.isText ?
+                {!this.isText ? (
                     <>
                         {this.drawButtons}
                         {this.widthPicker}
                         {this.colorPicker}
                         {this.fillPicker}
-                        {Doc.noviceMode || this.props.isDoc ? (null) :
+                        {Doc.noviceMode || this.props.isDoc ? null : (
                             <>
                                 <Tooltip key="back" title={<div className="dash-tooltip">Back Frame</div>} placement="bottom">
                                     <div className="backKeyframe" onClick={this.prevKeyframe}>
-                                        <FontAwesomeIcon icon={"caret-left"} size={"lg"} />
+                                        <FontAwesomeIcon icon={'caret-left'} size={'lg'} />
                                     </div>
                                 </Tooltip>
                                 <Tooltip key="num" title={<div className="dash-tooltip">Toggle View All</div>} placement="bottom">
-                                    <div className="numKeyframe" style={{ color: this.props.docView.ComponentView?.getKeyFrameEditing?.() ? "white" : "black", backgroundColor: this.props.docView.ComponentView?.getKeyFrameEditing?.() ? "#5B9FDD" : "#AEDDF8" }}
-                                        onClick={action(() => this.props.docView.ComponentView?.setKeyFrameEditing?.(!this.props.docView.ComponentView?.getKeyFrameEditing?.()))} >
+                                    <div
+                                        className="numKeyframe"
+                                        style={{ color: this.props.docView.ComponentView?.getKeyFrameEditing?.() ? 'white' : 'black', backgroundColor: this.props.docView.ComponentView?.getKeyFrameEditing?.() ? '#5B9FDD' : '#AEDDF8' }}
+                                        onClick={action(() => this.props.docView.ComponentView?.setKeyFrameEditing?.(!this.props.docView.ComponentView?.getKeyFrameEditing?.()))}>
                                         {NumCast(this.document._currentFrame)}
                                     </div>
                                 </Tooltip>
                                 <Tooltip key="fwd" title={<div className="dash-tooltip">Forward Frame</div>} placement="bottom">
                                     <div className="fwdKeyframe" onClick={this.nextKeyframe}>
-                                        <FontAwesomeIcon icon={"caret-right"} size={"lg"} />
+                                        <FontAwesomeIcon icon={'caret-right'} size={'lg'} />
                                     </div>
                                 </Tooltip>
-                            </>}
-                    </> : (null)
-                }
-                {!this.selectedDocumentView?.ComponentView?.menuControls ? (null) : this.selectedDocumentView?.ComponentView?.menuControls?.()}
-            </div>;
+                            </>
+                        )}
+                    </>
+                ) : null}
+                {!this.selectedDocumentView?.ComponentView?.menuControls ? null : this.selectedDocumentView?.ComponentView?.menuControls?.()}
+            </div>
+        );
     }
 }
 @observer
 export class CollectionStackingViewChrome extends React.Component<CollectionViewMenuProps> {
-    @observable private _currentKey: string = "";
+    @observable private _currentKey: string = '';
     @observable private suggestions: string[] = [];
 
-    get document() { return this.props.docView.props.Document; }
+    get document() {
+        return this.props.docView.props.Document;
+    }
 
-    @computed private get descending() { return StrCast(this.document._columnsSort) === "descending"; }
-    @computed get pivotField() { return StrCast(this.document._pivotField); }
+    @computed private get descending() {
+        return StrCast(this.document._columnsSort) === 'descending';
+    }
+    @computed get pivotField() {
+        return StrCast(this.document._pivotField);
+    }
 
     getKeySuggestions = async (value: string): Promise<string[]> => {
         const val = value.toLowerCase();
@@ -884,16 +1047,14 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
 
         if (Doc.noviceMode) {
             if (docs instanceof Doc) {
-                const keys = Object.keys(docs).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
-                    key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
-                    (key[0].toUpperCase() === key[0] && key[0] !== "_"));
+                const keys = Object.keys(docs).filter(key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('lastModified') >= 0 || (key[0].toUpperCase() === key[0] && key[0] !== '_'));
                 return keys.filter(key => key.toLowerCase().indexOf(val) > -1);
             } else {
                 const keys = new Set<string>();
                 docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
-                const noviceKeys = Array.from(keys).filter(key => key.indexOf("title") >= 0 || key.indexOf("author") >= 0 ||
-                    key.indexOf("creationDate") >= 0 || key.indexOf("lastModified") >= 0 ||
-                    (key[0]?.toUpperCase() === key[0] && key[0] !== "_"));
+                const noviceKeys = Array.from(keys).filter(
+                    key => key.indexOf('title') >= 0 || key.indexOf('author') >= 0 || key.indexOf('creationDate') >= 0 || key.indexOf('lastModified') >= 0 || (key[0]?.toUpperCase() === key[0] && key[0] !== '_')
+                );
                 return noviceKeys.filter(key => key.toLowerCase().indexOf(val) > -1);
             }
         }
@@ -905,81 +1066,77 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
             docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key)));
             return Array.from(keys).filter(key => key.toLowerCase().indexOf(val) > -1);
         }
-    }
+    };
 
     @action
     onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => {
         this._currentKey = newValue;
-    }
+    };
 
     getSuggestionValue = (suggestion: string) => suggestion;
 
     renderSuggestion = (suggestion: string) => {
         return <p>{suggestion}</p>;
-    }
+    };
 
     onSuggestionFetch = async ({ value }: { value: string }) => {
         const sugg = await this.getKeySuggestions(value);
         runInAction(() => {
             this.suggestions = sugg;
         });
-    }
+    };
 
     @action
     onSuggestionClear = () => {
         this.suggestions = [];
-    }
+    };
 
     @action
     setValue = (value: string) => {
         this.document._pivotField = value;
         return true;
-    }
+    };
 
     @action toggleSort = () => {
-        this.document._columnsSort =
-            this.document._columnsSort === "descending" ? "ascending" :
-                this.document._columnsSort === "ascending" ? undefined : "descending";
-    }
-    @action resetValue = () => { this._currentKey = this.pivotField; };
+        this.document._columnsSort = this.document._columnsSort === 'descending' ? 'ascending' : this.document._columnsSort === 'ascending' ? undefined : 'descending';
+    };
+    @action resetValue = () => {
+        this._currentKey = this.pivotField;
+    };
 
     render() {
         const doctype = this.props.docView.Document.type;
-        const isPres: boolean = (doctype === DocumentType.PRES);
-        return (
-            isPres ? (null) : <div className="collectionStackingViewChrome-cont">
+        const isPres: boolean = doctype === DocumentType.PRES;
+        return isPres ? null : (
+            <div className="collectionStackingViewChrome-cont">
                 <div className="collectionStackingViewChrome-pivotField-cont">
-                    <div className="collectionStackingViewChrome-pivotField-label">
-                        GROUP BY:
-                    </div>
-                    <div className="collectionStackingViewChrome-sortIcon" onClick={this.toggleSort} style={{ transform: `rotate(${this.descending ? "180" : "0"}deg)` }}>
+                    <div className="collectionStackingViewChrome-pivotField-label">GROUP BY:</div>
+                    <div className="collectionStackingViewChrome-sortIcon" onClick={this.toggleSort} style={{ transform: `rotate(${this.descending ? '180' : '0'}deg)` }}>
                         <FontAwesomeIcon icon="caret-up" size="2x" color="white" />
                     </div>
                     <div className="collectionStackingViewChrome-pivotField">
                         <EditableView
                             GetValue={() => this.pivotField}
-                            autosuggestProps={
-                                {
-                                    resetValue: this.resetValue,
-                                    value: this._currentKey,
-                                    onChange: this.onKeyChange,
-                                    autosuggestProps: {
-                                        inputProps:
-                                        {
-                                            value: this._currentKey,
-                                            onChange: this.onKeyChange
-                                        },
-                                        getSuggestionValue: this.getSuggestionValue,
-                                        suggestions: this.suggestions,
-                                        alwaysRenderSuggestions: true,
-                                        renderSuggestion: this.renderSuggestion,
-                                        onSuggestionsFetchRequested: this.onSuggestionFetch,
-                                        onSuggestionsClearRequested: this.onSuggestionClear
-                                    }
-                                }}
+                            autosuggestProps={{
+                                resetValue: this.resetValue,
+                                value: this._currentKey,
+                                onChange: this.onKeyChange,
+                                autosuggestProps: {
+                                    inputProps: {
+                                        value: this._currentKey,
+                                        onChange: this.onKeyChange,
+                                    },
+                                    getSuggestionValue: this.getSuggestionValue,
+                                    suggestions: this.suggestions,
+                                    alwaysRenderSuggestions: true,
+                                    renderSuggestion: this.renderSuggestion,
+                                    onSuggestionsFetchRequested: this.onSuggestionFetch,
+                                    onSuggestionsClearRequested: this.onSuggestionClear,
+                                },
+                            }}
                             oneLine
                             SetValue={this.setValue}
-                            contents={this.pivotField ? this.pivotField : "N/A"}
+                            contents={this.pivotField ? this.pivotField : 'N/A'}
                         />
                     </div>
                 </div>
@@ -988,11 +1145,12 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
     }
 }
 
-
 @observer
 export class CollectionSchemaViewChrome extends React.Component<CollectionViewMenuProps> {
     // private _textwrapAllRows: boolean = Cast(this.document.textwrappedSchemaRows, listSpec("string"), []).length > 0;
-    get document() { return this.props.docView.props.Document; }
+    get document() {
+        return this.props.docView.props.Document;
+    }
 
     @undoBatch
     togglePreview = () => {
@@ -1002,12 +1160,12 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewMe
         const previewWidth = NumCast(this.document.schemaPreviewWidth);
         const tableWidth = panelWidth - 2 * borderWidth - dividerWidth - previewWidth;
         this.document.schemaPreviewWidth = previewWidth === 0 ? Math.min(tableWidth / 3, 200) : 0;
-    }
+    };
 
     @undoBatch
     @action
     toggleTextwrap = async () => {
-        const textwrappedRows = Cast(this.document.textwrappedSchemaRows, listSpec("string"), []);
+        const textwrappedRows = Cast(this.document.textwrappedSchemaRows, listSpec('string'), []);
         if (textwrappedRows.length) {
             this.document.textwrappedSchemaRows = new List<string>([]);
         } else {
@@ -1015,56 +1173,52 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewMe
             const allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]);
             this.document.textwrappedSchemaRows = new List<string>(allRows);
         }
-    }
-
+    };
 
     render() {
         const previewWidth = NumCast(this.document.schemaPreviewWidth);
-        const textWrapped = Cast(this.document.textwrappedSchemaRows, listSpec("string"), []).length > 0;
+        const textWrapped = Cast(this.document.textwrappedSchemaRows, listSpec('string'), []).length > 0;
 
         return (
             <div className="collectionSchemaViewChrome-cont">
                 <div className="collectionSchemaViewChrome-toggle">
                     <div className="collectionSchemaViewChrome-label">Show Preview: </div>
                     <div className="collectionSchemaViewChrome-toggler" onClick={this.togglePreview}>
-                        <div className={"collectionSchemaViewChrome-togglerButton" + (previewWidth !== 0 ? " on" : " off")}>
-                            {previewWidth !== 0 ? "on" : "off"}
-                        </div>
+                        <div className={'collectionSchemaViewChrome-togglerButton' + (previewWidth !== 0 ? ' on' : ' off')}>{previewWidth !== 0 ? 'on' : 'off'}</div>
                     </div>
                 </div>
-            </div >
+            </div>
         );
     }
 }
 
 @observer
 export class CollectionTreeViewChrome extends React.Component<CollectionViewMenuProps> {
-
-    get document() { return this.props.docView.props.Document; }
+    get document() {
+        return this.props.docView.props.Document;
+    }
     get sortAscending() {
-        return this.document[this.props.fieldKey + "-sortAscending"];
+        return this.document[this.props.fieldKey + '-sortAscending'];
     }
     set sortAscending(value) {
-        this.document[this.props.fieldKey + "-sortAscending"] = value;
+        this.document[this.props.fieldKey + '-sortAscending'] = value;
     }
     @computed private get ascending() {
-        return Cast(this.sortAscending, "boolean", null);
+        return Cast(this.sortAscending, 'boolean', null);
     }
 
     @action toggleSort = () => {
         if (this.sortAscending) this.sortAscending = undefined;
         else if (this.sortAscending === undefined) this.sortAscending = false;
         else this.sortAscending = true;
-    }
+    };
 
     render() {
         return (
             <div className="collectionTreeViewChrome-cont">
                 <button className="collectionTreeViewChrome-sort" onClick={this.toggleSort}>
-                    <div className="collectionTreeViewChrome-sortLabel">
-                        Sort
-                    </div>
-                    <div className="collectionTreeViewChrome-sortIcon" style={{ transform: `rotate(${this.ascending === undefined ? "90" : this.ascending ? "180" : "0"}deg)` }}>
+                    <div className="collectionTreeViewChrome-sortLabel">Sort</div>
+                    <div className="collectionTreeViewChrome-sortIcon" style={{ transform: `rotate(${this.ascending === undefined ? '90' : this.ascending ? '180' : '0'}deg)` }}>
                         <FontAwesomeIcon icon="caret-up" size="2x" color="white" />
                     </div>
                 </button>
@@ -1073,10 +1227,12 @@ export class CollectionTreeViewChrome extends React.Component<CollectionViewMenu
     }
 }
 
-// Enter scroll speed for 3D Carousel 
+// Enter scroll speed for 3D Carousel
 @observer
 export class Collection3DCarouselViewChrome extends React.Component<CollectionViewMenuProps> {
-    get document() { return this.props.docView.props.Document; }
+    get document() {
+        return this.props.docView.props.Document;
+    }
     @computed get scrollSpeed() {
         return this.document._autoScrollSpeed;
     }
@@ -1089,22 +1245,16 @@ export class Collection3DCarouselViewChrome extends React.Component<CollectionVi
             return true;
         }
         return false;
-    }
+    };
 
     render() {
         return (
             <div className="collection3DCarouselViewChrome-cont">
                 <div className="collection3DCarouselViewChrome-scrollSpeed-cont">
-                    {FormattedTextBox.Focused ? <RichTextMenu /> : (null)}
-                    <div className="collectionStackingViewChrome-scrollSpeed-label">
-                        AUTOSCROLL SPEED:
-                    </div>
+                    {FormattedTextBox.Focused ? <RichTextMenu /> : null}
+                    <div className="collectionStackingViewChrome-scrollSpeed-label">AUTOSCROLL SPEED:</div>
                     <div className="collection3DCarouselViewChrome-scrollSpeed">
-                        <EditableView
-                            GetValue={() => StrCast(this.scrollSpeed)}
-                            oneLine
-                            SetValue={this.setValue}
-                            contents={this.scrollSpeed ? this.scrollSpeed : 1000} />
+                        <EditableView GetValue={() => StrCast(this.scrollSpeed)} oneLine SetValue={this.setValue} contents={this.scrollSpeed ? this.scrollSpeed : 1000} />
                     </div>
                 </div>
             </div>
@@ -1117,21 +1267,21 @@ export class Collection3DCarouselViewChrome extends React.Component<CollectionVi
  */
 @observer
 export class CollectionGridViewChrome extends React.Component<CollectionViewMenuProps> {
-
     private clicked: boolean = false;
     private entered: boolean = false;
     private decrementLimitReached: boolean = false;
     @observable private resize = false;
     private resizeListenerDisposer: Opt<Lambda>;
-    get document() { return this.props.docView.props.Document; }
+    get document() {
+        return this.props.docView.props.Document;
+    }
 
     componentDidMount() {
-
-        runInAction(() => this.resize = this.props.docView.props.PanelWidth() < 700);
+        runInAction(() => (this.resize = this.props.docView.props.PanelWidth() < 700));
 
         // listener to reduce text on chrome resize (panel resize)
         this.resizeListenerDisposer = computed(() => this.props.docView.props.PanelWidth()).observe(({ newValue }) => {
-            runInAction(() => this.resize = newValue < 700);
+            runInAction(() => (this.resize = newValue < 700));
         });
     }
 
@@ -1139,14 +1289,16 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
         this.resizeListenerDisposer?.();
     }
 
-    get numCols() { return NumCast(this.document.gridNumCols, 10); }
+    get numCols() {
+        return NumCast(this.document.gridNumCols, 10);
+    }
 
     /**
-    * Sets the value of `numCols` on the grid's Document to the value entered.
-    */
+     * Sets the value of `numCols` on the grid's Document to the value entered.
+     */
     onNumColsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
-        if (e.currentTarget.valueAsNumber > 0) undoBatch(() => this.document.gridNumCols = e.currentTarget.valueAsNumber)();
-    }
+        if (e.currentTarget.valueAsNumber > 0) undoBatch(() => (this.document.gridNumCols = e.currentTarget.valueAsNumber))();
+    };
 
     /**
      * Sets the value of `rowHeight` on the grid's Document to the value entered.
@@ -1166,7 +1318,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
     @undoBatch
     toggleFlex = () => {
         this.document.gridFlex = !BoolCast(this.document.gridFlex, true);
-    }
+    };
 
     /**
      * Increments the value of numCols on button click
@@ -1174,9 +1326,9 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
     onIncrementButtonClick = () => {
         this.clicked = true;
         this.entered && (this.document.gridNumCols as number)--;
-        undoBatch(() => this.document.gridNumCols = this.numCols + 1)();
+        undoBatch(() => (this.document.gridNumCols = this.numCols + 1))();
         this.entered = false;
-    }
+    };
 
     /**
      * Decrements the value of numCols on button click
@@ -1185,11 +1337,11 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
         this.clicked = true;
         if (this.numCols > 1 && !this.decrementLimitReached) {
             this.entered && (this.document.gridNumCols as number)++;
-            undoBatch(() => this.document.gridNumCols = this.numCols - 1)();
+            undoBatch(() => (this.document.gridNumCols = this.numCols - 1))();
             if (this.numCols === 1) this.decrementLimitReached = true;
         }
         this.entered = false;
-    }
+    };
 
     /**
      * Increments the value of numCols on button hover
@@ -1201,7 +1353,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
         }
         this.decrementLimitReached = false;
         this.clicked = false;
-    }
+    };
 
     /**
      * Decrements the value of numCols on button hover
@@ -1211,21 +1363,20 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
         if (!this.clicked) {
             if (this.numCols > 1) {
                 this.document.gridNumCols = this.numCols - 1;
-            }
-            else {
+            } else {
                 this.decrementLimitReached = true;
             }
         }
 
         this.clicked = false;
-    }
+    };
 
     /**
      * Toggles the value of preventCollision
      */
     toggleCollisions = () => {
         this.document.gridPreventCollision = !this.document.gridPreventCollision;
-    }
+    };
 
     /**
      * Changes the value of the compactType
@@ -1233,16 +1384,26 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
     changeCompactType = (e: React.ChangeEvent<HTMLSelectElement>) => {
         // need to change startCompaction so that this operation will be undoable.
         this.document.gridStartCompaction = e.target.selectedOptions[0].value;
-    }
+    };
 
     render() {
         return (
-            <div className="collectionGridViewChrome-cont" >
-                <span className="grid-control" style={{ width: this.resize ? "25%" : "30%" }}>
+            <div className="collectionGridViewChrome-cont">
+                <span className="grid-control" style={{ width: this.resize ? '25%' : '30%' }}>
                     <span className="grid-icon">
                         <FontAwesomeIcon icon="columns" size="1x" />
                     </span>
-                    <input className="collectionGridViewChrome-entryBox" type="number" value={this.numCols} onChange={this.onNumColsChange} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} />
+                    <input
+                        className="collectionGridViewChrome-entryBox"
+                        type="number"
+                        value={this.numCols}
+                        onChange={this.onNumColsChange}
+                        onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => {
+                            e.stopPropagation();
+                            e.preventDefault();
+                            e.currentTarget.focus();
+                        }}
+                    />
                     <input className="collectionGridViewChrome-columnButton" onClick={this.onIncrementButtonClick} onMouseEnter={this.incrementValue} onMouseLeave={this.decrementValue} type="button" value="↑" />
                     <input className="collectionGridViewChrome-columnButton" style={{ marginRight: 5 }} onClick={this.onDecrementButtonClick} onMouseEnter={this.decrementValue} onMouseLeave={this.incrementValue} type="button" value="↓" />
                 </span>
@@ -1252,36 +1413,30 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
                     </span>
                     <input className="collectionGridViewChrome-entryBox" type="number" placeholder={this.document.rowHeight as string} onKeyDown={this.onRowHeightEnter} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} />
                 </span> */}
-                <span className="grid-control" style={{ width: this.resize ? "12%" : "20%" }}>
+                <span className="grid-control" style={{ width: this.resize ? '12%' : '20%' }}>
                     <input type="checkbox" style={{ marginRight: 5 }} onChange={this.toggleCollisions} checked={!this.document.gridPreventCollision} />
-                    <label className="flexLabel">{this.resize ? "Coll" : "Collisions"}</label>
+                    <label className="flexLabel">{this.resize ? 'Coll' : 'Collisions'}</label>
                 </span>
 
-                <select className="collectionGridViewChrome-viewPicker"
+                <select
+                    className="collectionGridViewChrome-viewPicker"
                     style={{ marginRight: 5 }}
                     onPointerDown={stopPropagation}
                     onChange={this.changeCompactType}
                     value={StrCast(this.document.gridStartCompaction, StrCast(this.document.gridCompaction))}>
-                    {["vertical", "horizontal", "none"].map(type =>
-                        <option className="collectionGridViewChrome-viewOption"
-                            onPointerDown={stopPropagation}
-                            value={type}>
-                            {this.resize ? type[0].toUpperCase() + type.substring(1) : "Compact: " + type}
+                    {['vertical', 'horizontal', 'none'].map(type => (
+                        <option className="collectionGridViewChrome-viewOption" onPointerDown={stopPropagation} value={type}>
+                            {this.resize ? type[0].toUpperCase() + type.substring(1) : 'Compact: ' + type}
                         </option>
-                    )}
+                    ))}
                 </select>
 
-                <span className="grid-control" style={{ width: this.resize ? "12%" : "20%" }}>
-                    <input style={{ marginRight: 5 }} type="checkbox" onChange={this.toggleFlex}
-                        checked={BoolCast(this.document.gridFlex, true)} />
-                    <label className="flexLabel">{this.resize ? "Flex" : "Flexible"}</label>
+                <span className="grid-control" style={{ width: this.resize ? '12%' : '20%' }}>
+                    <input style={{ marginRight: 5 }} type="checkbox" onChange={this.toggleFlex} checked={BoolCast(this.document.gridFlex, true)} />
+                    <label className="flexLabel">{this.resize ? 'Flex' : 'Flexible'}</label>
                 </span>
 
-                <button onClick={() => this.document.gridResetLayout = true}>
-                    {!this.resize ? "Reset" :
-                        <FontAwesomeIcon icon="redo-alt" size="1x" />}
-                </button>
-
+                <button onClick={() => (this.document.gridResetLayout = true)}>{!this.resize ? 'Reset' : <FontAwesomeIcon icon="redo-alt" size="1x" />}</button>
             </div>
         );
     }
@@ -1289,7 +1444,7 @@ export class CollectionGridViewChrome extends React.Component<CollectionViewMenu
 ScriptingGlobals.add(function gotoFrame(doc: any, newFrame: any) {
     const dataField = doc[Doc.LayoutFieldKey(doc)];
     const childDocs = DocListCast(dataField);
-    const currentFrame = Cast(doc._currentFrame, "number", null);
+    const currentFrame = Cast(doc._currentFrame, 'number', null);
     if (currentFrame === undefined) {
         doc._currentFrame = 0;
         CollectionFreeFormDocumentView.setupKeyframes(childDocs, 0);
@@ -1297,4 +1452,3 @@ ScriptingGlobals.add(function gotoFrame(doc: any, newFrame: any) {
     CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0);
     doc._currentFrame = newFrame === undefined ? 0 : Math.max(0, newFrame);
 });
-
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index b00017453..dcf3f7c51 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -1,50 +1,33 @@
-import React = require("react");
-import {
-    action,
-    computed,
-    IReactionDisposer,
-    observable,
-    reaction
-} from "mobx";
-import { observer } from "mobx-react";
-import { computedFn } from "mobx-utils";
-import { Doc, DocListCast } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
-import { List } from "../../../fields/List";
-import { listSpec } from "../../../fields/Schema";
-import { ComputedField, ScriptField } from "../../../fields/ScriptField";
-import { Cast, NumCast } from "../../../fields/Types";
-import {
-    emptyFunction,
-    formatTime,
-    OmitKeys,
-    returnFalse,
-    returnOne, returnTrue, setupMoveUpEvents, smoothScrollHorizontal, StopEvent
-} from "../../../Utils";
-import { Docs } from "../../documents/Documents";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { DocumentManager } from "../../util/DocumentManager";
-import { DragManager } from "../../util/DragManager";
-import { LinkManager } from "../../util/LinkManager";
-import { ScriptingGlobals } from "../../util/ScriptingGlobals";
-import { SelectionManager } from "../../util/SelectionManager";
-import { SnappingManager } from "../../util/SnappingManager";
-import { Transform } from "../../util/Transform";
-import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { AudioWaveform } from "../AudioWaveform";
-import { CollectionSubView } from "../collections/CollectionSubView";
-import { Colors } from "../global/globalEnums";
-import { LightboxView } from "../LightboxView";
-import {
-    DocAfterFocusFunc,
-    DocFocusFunc,
-    DocumentView,
-    DocumentViewProps
-} from "../nodes/DocumentView";
-import { LabelBox } from "../nodes/LabelBox";
-import "./CollectionStackedTimeline.scss";
-import { VideoBox } from "../nodes/VideoBox";
-import { ImageField } from "../../../fields/URLField";
+import React = require('react');
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import { computedFn } from 'mobx-utils';
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { listSpec } from '../../../fields/Schema';
+import { ComputedField, ScriptField } from '../../../fields/ScriptField';
+import { Cast, NumCast } from '../../../fields/Types';
+import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, returnTrue, setupMoveUpEvents, smoothScrollHorizontal, StopEvent } from '../../../Utils';
+import { Docs } from '../../documents/Documents';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { DocumentManager } from '../../util/DocumentManager';
+import { DragManager } from '../../util/DragManager';
+import { LinkFollower } from '../../util/LinkFollower';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
+import { SelectionManager } from '../../util/SelectionManager';
+import { SnappingManager } from '../../util/SnappingManager';
+import { Transform } from '../../util/Transform';
+import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { AudioWaveform } from '../AudioWaveform';
+import { CollectionSubView } from '../collections/CollectionSubView';
+import { Colors } from '../global/globalEnums';
+import { LightboxView } from '../LightboxView';
+import { DocAfterFocusFunc, DocFocusFunc, DocumentView, DocumentViewProps } from '../nodes/DocumentView';
+import { LabelBox } from '../nodes/LabelBox';
+import './CollectionStackedTimeline.scss';
+import { VideoBox } from '../nodes/VideoBox';
+import { ImageField } from '../../../fields/URLField';
 
 export type CollectionStackedTimelineProps = {
     Play: () => void;
@@ -68,7 +51,6 @@ export enum TrimScope {
     None = 0,
 }
 
-
 @observer
 export class CollectionStackedTimeline extends CollectionSubView<CollectionStackedTimelineProps>() {
     @observable static SelectingRegion: CollectionStackedTimeline | undefined = undefined;
@@ -94,21 +76,38 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
 
     @observable _thumbnail: string | undefined;
 
-    // ensures that clip doesn't get trimmed so small that controls cannot be adjusted anymore 
-    get minTrimLength() { return Math.max(this._timeline?.getBoundingClientRect() ? 0.05 * this.clipDuration : 0, 0.5); }
-
-    @computed get trimStart() { return this.IsTrimming !== TrimScope.None ? this._trimStart : this.clipStart; }
-    @computed get trimDuration() { return this.trimEnd - this.trimStart; }
-    @computed get trimEnd() { return this.IsTrimming !== TrimScope.None ? this._trimEnd : this.clipEnd; }
+    // ensures that clip doesn't get trimmed so small that controls cannot be adjusted anymore
+    get minTrimLength() {
+        return Math.max(this._timeline?.getBoundingClientRect() ? 0.05 * this.clipDuration : 0, 0.5);
+    }
 
-    @computed get clipStart() { return this.IsTrimming === TrimScope.All ? 0 : NumCast(this.layoutDoc.clipStart); }
-    @computed get clipDuration() { return this.clipEnd - this.clipStart; }
-    @computed get clipEnd() { return this.IsTrimming === TrimScope.All ? this.props.rawDuration : NumCast(this.layoutDoc.clipEnd, this.props.rawDuration); }
+    @computed get trimStart() {
+        return this.IsTrimming !== TrimScope.None ? this._trimStart : this.clipStart;
+    }
+    @computed get trimDuration() {
+        return this.trimEnd - this.trimStart;
+    }
+    @computed get trimEnd() {
+        return this.IsTrimming !== TrimScope.None ? this._trimEnd : this.clipEnd;
+    }
 
-    @computed get currentTime() { return NumCast(this.layoutDoc._currentTimecode); }
+    @computed get clipStart() {
+        return this.IsTrimming === TrimScope.All ? 0 : NumCast(this.layoutDoc.clipStart);
+    }
+    @computed get clipDuration() {
+        return this.clipEnd - this.clipStart;
+    }
+    @computed get clipEnd() {
+        return this.IsTrimming === TrimScope.All ? this.props.rawDuration : NumCast(this.layoutDoc.clipEnd, this.props.rawDuration);
+    }
 
-    @computed get zoomFactor() { return this._zoomFactor; }
+    @computed get currentTime() {
+        return NumCast(this.layoutDoc._currentTimecode);
+    }
 
+    @computed get zoomFactor() {
+        return this._zoomFactor;
+    }
 
     constructor(props: any) {
         super(props);
@@ -117,32 +116,33 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             CollectionStackedTimeline.RangeScript ||
             ScriptField.MakeFunction(`scriptContext.clickAnchor(this, clientX)`, {
                 self: Doc.name,
-                scriptContext: "any",
-                clientX: "number",
+                scriptContext: 'any',
+                clientX: 'number',
             })!;
         CollectionStackedTimeline.RangePlayScript =
             CollectionStackedTimeline.RangePlayScript ||
             ScriptField.MakeFunction(`scriptContext.playOnClick(this, clientX)`, {
                 self: Doc.name,
-                scriptContext: "any",
-                clientX: "number",
+                scriptContext: 'any',
+                clientX: 'number',
             })!;
     }
 
     componentDidMount() {
-        document.addEventListener("keydown", this.keyEvents, true);
+        document.addEventListener('keydown', this.keyEvents, true);
     }
 
     @action
     componentWillUnmount() {
-        document.removeEventListener("keydown", this.keyEvents, true);
+        document.removeEventListener('keydown', this.keyEvents, true);
         if (CollectionStackedTimeline.SelectingRegion === this) {
             CollectionStackedTimeline.SelectingRegion = undefined;
         }
     }
 
-
-    public get IsTrimming() { return this._trimming; }
+    public get IsTrimming() {
+        return this._trimming;
+    }
 
     @action
     public StartTrimming(scope: TrimScope) {
@@ -162,81 +162,63 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         this._zoomFactor = zoom;
     }
 
-
     anchorStart = (anchor: Doc) => NumCast(anchor._timecodeToShow, NumCast(anchor[this.props.startTag]));
     anchorEnd = (anchor: Doc, val: any = null) => NumCast(anchor._timecodeToHide, NumCast(anchor[this.props.endTag], val) ?? null);
 
-
     // converts screen pixel offset to time
     toTimeline = (screen_delta: number, width: number) => {
-        return Math.max(
-            this.clipStart,
-            Math.min(this.clipEnd, (screen_delta / width) * this.clipDuration + this.clipStart));
-    }
-
+        return Math.max(this.clipStart, Math.min(this.clipEnd, (screen_delta / width) * this.clipDuration + this.clipStart));
+    };
 
     rangeClickScript = () => CollectionStackedTimeline.RangeScript;
     rangePlayScript = () => CollectionStackedTimeline.RangePlayScript;
 
-
     // handles key events for for creating key anchors, scrubbing, exiting trim
     @action
     keyEvents = (e: KeyboardEvent) => {
         if (
             // need to include range inputs because after dragging video time slider it becomes target element
-            !(e.target instanceof HTMLInputElement && !(e.target.type === "range")) &&
+            !(e.target instanceof HTMLInputElement && !(e.target.type === 'range')) &&
             this.props.isSelected(true)
         ) {
             // if shift pressed scrub 1 second otherwise 1/10th
             const jump = e.shiftKey ? 1 : 0.1;
             switch (e.key) {
-                case " ":
+                case ' ':
                     if (!CollectionStackedTimeline.SelectingRegion) {
                         this._markerStart = this._markerEnd = this.currentTime;
                         CollectionStackedTimeline.SelectingRegion = this;
                     } else {
                         this._markerEnd = this.currentTime;
-                        CollectionStackedTimeline.createAnchor(
-                            this.rootDoc,
-                            this.dataDoc,
-                            this.props.fieldKey,
-                            this.props.startTag,
-                            this.props.endTag,
-                            this._markerStart,
-                            this._markerEnd
-                        );
+                        CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this._markerStart, this._markerEnd);
                         this._markerEnd = undefined;
                         CollectionStackedTimeline.SelectingRegion = undefined;
                     }
                     e.stopPropagation();
                     break;
-                case "Escape":
+                case 'Escape':
                     // abandons current trim
                     this._trimStart = this.clipStart;
                     this._trimStart = this.clipEnd;
                     this._trimming = TrimScope.None;
                     e.stopPropagation();
                     break;
-                case "ArrowLeft":
+                case 'ArrowLeft':
                     this.props.setTime(Math.min(Math.max(this.clipStart, this.currentTime - jump), this.clipEnd));
                     e.stopPropagation();
                     break;
-                case "ArrowRight":
+                case 'ArrowRight':
                     this.props.setTime(Math.min(Math.max(this.clipStart, this.currentTime + jump), this.clipEnd));
                     e.stopPropagation();
                     break;
             }
         }
-    }
-
+    };
 
     getLinkData(l: Doc) {
         let la1 = l.anchor1 as Doc;
         let la2 = l.anchor2 as Doc;
-        const linkTime = NumCast(
-            la2[this.props.startTag],
-            NumCast(la1[this.props.startTag])
-        );
+        const linkTime = NumCast(la2[this.props.startTag], NumCast(la1[this.props.startTag]));
         if (Doc.AreProtosEqual(la1, this.dataDoc)) {
             la1 = l.anchor2 as Doc;
             la2 = l.anchor1 as Doc;
@@ -244,7 +226,6 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         return { la1, la2, linkTime };
     }
 
-
     // handles dragging selection to create markers
     @action
     onPointerDownTimeline = (e: React.PointerEvent): void => {
@@ -259,7 +240,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             setupMoveUpEvents(
                 this,
                 e,
-                action((e) => {
+                action(e => {
                     if (!wasSelecting) {
                         this._markerStart = this._markerEnd = this.toTimeline(clientX - rect.x, rect.width);
                         wasSelecting = true;
@@ -274,24 +255,11 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                         this._markerStart = this._markerEnd;
                         this._markerEnd = tmp;
                     }
-                    if (
-                        !isClick &&
-                        Math.abs(movement[0]) > 15 &&
-                        !this.IsTrimming
-                    ) {
-                        const anchor = CollectionStackedTimeline.createAnchor(
-                            this.rootDoc,
-                            this.dataDoc,
-                            this.props.fieldKey,
-                            this.props.startTag,
-                            this.props.endTag,
-                            this._markerStart,
-                            this._markerEnd
-                        );
+                    if (!isClick && Math.abs(movement[0]) > 15 && !this.IsTrimming) {
+                        const anchor = CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this._markerStart, this._markerEnd);
                         setTimeout(() => DocumentManager.Instance.getDocumentView(anchor)?.select(false));
                     }
-                    (!isClick || !wasSelecting) &&
-                        (this._markerEnd = undefined);
+                    (!isClick || !wasSelecting) && (this._markerEnd = undefined);
                 }),
                 (e, doubleTap) => {
                     if (e.button !== 2) {
@@ -303,23 +271,14 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 undefined,
                 () => {
                     if (shiftKey) {
-                        CollectionStackedTimeline.createAnchor(
-                            this.rootDoc,
-                            this.dataDoc,
-                            this.props.fieldKey,
-                            this.props.startTag,
-                            this.props.endTag,
-                            this.currentTime
-                        );
+                        CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this.currentTime);
                     } else {
                         !wasPlaying && this.props.setTime(this.toTimeline(clientX - rect.x, rect.width));
                     }
                 }
             );
         }
-
-    }
-
+    };
 
     @action
     onHover = (e: React.MouseEvent): void => {
@@ -329,15 +288,14 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         if (rect) {
             this._hoverTime = this.toTimeline(clientX - rect.x, rect.width);
             if (this.dataDoc.thumbnails) {
-                const nearest = Math.floor(this._hoverTime / this.props.rawDuration * VideoBox.numThumbnails);
-                const thumbnails = Cast(this.dataDoc.thumbnails, listSpec("string"), []);
-                const imgField = thumbnails && thumbnails.length > 0 ? new ImageField(thumbnails[nearest]) : new ImageField("");
-                const src = imgField && imgField.url.href ? imgField.url.href.replace(".png", "_s.png") : "";
+                const nearest = Math.floor((this._hoverTime / this.props.rawDuration) * VideoBox.numThumbnails);
+                const thumbnails = Cast(this.dataDoc.thumbnails, listSpec('string'), []);
+                const imgField = thumbnails && thumbnails.length > 0 ? new ImageField(thumbnails[nearest]) : new ImageField('');
+                const src = imgField && imgField.url.href ? imgField.url.href.replace('.png', '_s.png') : '';
                 this._thumbnail = src ? src : undefined;
             }
         }
-    }
-
+    };
 
     // for dragging trim start handle
     @action
@@ -348,13 +306,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             e,
             action((e, [], []) => {
                 if (rect && this.props.isContentActive()) {
-                    this._trimStart = Math.min(
-                        Math.max(
-                            this.trimStart + (e.movementX / rect.width) * this.clipDuration,
-                            this.clipStart
-                        ),
-                        this.trimEnd - this.minTrimLength
-                    );
+                    this._trimStart = Math.min(Math.max(this.trimStart + (e.movementX / rect.width) * this.clipDuration, this.clipStart), this.trimEnd - this.minTrimLength);
                 }
                 return false;
             }),
@@ -363,7 +315,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 doubleTap && (this._trimStart = this.clipStart);
             })
         );
-    }
+    };
 
     // for dragging trim end handle
     @action
@@ -374,13 +326,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             e,
             action((e, [], []) => {
                 if (rect && this.props.isContentActive()) {
-                    this._trimEnd = Math.max(
-                        Math.min(
-                            this.trimEnd + (e.movementX / rect.width) * this.clipDuration,
-                            this.clipEnd
-                        ),
-                        this.trimStart + this.minTrimLength
-                    );
+                    this._trimEnd = Math.max(Math.min(this.trimEnd + (e.movementX / rect.width) * this.clipDuration, this.clipEnd), this.trimStart + this.minTrimLength);
                 }
                 return false;
             }),
@@ -389,15 +335,14 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 doubleTap && (this._trimEnd = this.clipEnd);
             })
         );
-    }
-
+    };
 
     // for rendering scrolling when timeline zoomed
     @action
     setScroll = (e: React.UIEvent) => {
         e.stopPropagation();
         this._scroll = this._timelineWrapper!.scrollLeft;
-    }
+    };
 
     // smooth scrolls to time like when following links overflowed due to zoom
     @action
@@ -406,14 +351,12 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             if (time > this.toTimeline(this._scroll + this.props.PanelWidth(), this.timelineContentWidth)) {
                 this._scroll = Math.min(this._scroll + this.props.PanelWidth(), this.timelineContentWidth - this.props.PanelWidth());
                 smoothScrollHorizontal(200, this._timelineWrapper, this._scroll);
-            }
-            else if (time < this.toTimeline(this._scroll, this.timelineContentWidth)) {
-                this._scroll = time / this.timelineContentWidth * this.clipDuration;
+            } else if (time < this.toTimeline(this._scroll, this.timelineContentWidth)) {
+                this._scroll = (time / this.timelineContentWidth) * this.clipDuration;
                 smoothScrollHorizontal(200, this._timelineWrapper, this._scroll);
             }
         }
-    }
-
+    };
 
     // handles dragging and dropping markers in timeline
     @action
@@ -428,9 +371,9 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         docDragData.droppedDocuments.forEach(drop => {
             const anchorEnd = this.anchorEnd(drop);
             if (anchorEnd !== undefined) {
-                Doc.SetInPlace(drop, drop._timecodeToHide === undefined ? this.props.endTag : "timecodeToHide", timelinePt + anchorEnd - this.anchorStart(drop), false);
+                Doc.SetInPlace(drop, drop._timecodeToHide === undefined ? this.props.endTag : 'timecodeToHide', timelinePt + anchorEnd - this.anchorStart(drop), false);
             }
-            Doc.SetInPlace(drop, drop._timecodeToShow === undefined ? this.props.startTag : "timecodeToShow", timelinePt, false);
+            Doc.SetInPlace(drop, drop._timecodeToShow === undefined ? this.props.startTag : 'timecodeToShow', timelinePt, false);
         });
 
         return true;
@@ -439,38 +382,28 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
     onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
         if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData, 0);
         return false;
-    }
-
+    };
 
     // creates marker on timeline
     @undoBatch
     @action
-    static createAnchor(
-        rootDoc: Doc,
-        dataDoc: Doc,
-        fieldKey: string,
-        startTag: string,
-        endTag: string,
-        anchorStartTime?: number,
-        anchorEndTime?: number,
-        docAnchor?: Doc
-    ) {
+    static createAnchor(rootDoc: Doc, dataDoc: Doc, fieldKey: string, startTag: string, endTag: string, anchorStartTime?: number, anchorEndTime?: number, docAnchor?: Doc) {
         if (anchorStartTime === undefined) return rootDoc;
-        const anchor = docAnchor ?? Docs.Create.LabelDocument({
-            title: ComputedField.MakeFunction(
-                `self["${endTag}"] ? "#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"]) : "#" + formatToTime(self["${startTag}"])`
-            ) as any,
-            _minFontSize: 12,
-            _maxFontSize: 24,
-            _singleLine: false,
-            _stayInCollection: true,
-            useLinkSmallAnchor: true,
-            hideLinkButton: true,
-            _isLinkButton: true,
-            annotationOn: rootDoc,
-            _timelineLabel: true,
-            borderRounding: anchorEndTime === undefined ? "100%" : undefined
-        });
+        const anchor =
+            docAnchor ??
+            Docs.Create.LabelDocument({
+                title: ComputedField.MakeFunction(`self["${endTag}"] ? "#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"]) : "#" + formatToTime(self["${startTag}"])`) as any,
+                _minFontSize: 12,
+                _maxFontSize: 24,
+                _singleLine: false,
+                _stayInCollection: true,
+                useLinkSmallAnchor: true,
+                hideLinkButton: true,
+                _isLinkButton: true,
+                annotationOn: rootDoc,
+                _timelineLabel: true,
+                borderRounding: anchorEndTime === undefined ? '100%' : undefined,
+            });
         Doc.GetProto(anchor)[startTag] = anchorStartTime;
         Doc.GetProto(anchor)[endTag] = anchorEndTime;
         if (Cast(dataDoc[fieldKey], listSpec(Doc), null)) {
@@ -481,7 +414,6 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
         return anchor;
     }
 
-
     @action
     playOnClick = (anchorDoc: Doc, clientX: number) => {
         const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.25;
@@ -493,10 +425,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                 this.scrollToTime(seekTimeInSeconds);
             }
         } else {
-            if (
-                seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) &&
-                endTime > NumCast(this.layoutDoc._currentTimecode)
-            ) {
+            if (seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) && endTime > NumCast(this.layoutDoc._currentTimecode)) {
                 if (!this.layoutDoc.autoPlayAnchors && this.props.playing()) {
                     this.props.Pause();
                 } else {
@@ -508,59 +437,43 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             }
         }
         return { select: true };
-    }
+    };
 
     @action
     clickAnchor = (anchorDoc: Doc, clientX: number) => {
         if (anchorDoc.isLinkButton) {
-            LinkManager.FollowLink(undefined, anchorDoc, this.props, false);
+            LinkFollower.FollowLink(undefined, anchorDoc, this.props, false);
         }
         const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.25;
         const endTime = this.anchorEnd(anchorDoc);
-        if (
-            seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) + 1e-4 &&
-            endTime > NumCast(this.layoutDoc._currentTimecode) - 1e-4
-        ) {
+        if (seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) + 1e-4 && endTime > NumCast(this.layoutDoc._currentTimecode) - 1e-4) {
             if (this.props.playing()) this.props.Pause();
             else if (this.layoutDoc.autoPlayAnchors) this.props.Play();
             else if (!this.layoutDoc.autoPlayAnchors) {
                 const rect = this._timeline?.getBoundingClientRect();
-                rect &&
-                    this.props.setTime(this.toTimeline(clientX - rect.x, rect.width));
+                rect && this.props.setTime(this.toTimeline(clientX - rect.x, rect.width));
             }
         } else {
             if (this.layoutDoc.autoPlayAnchors) {
                 this.props.playFrom(seekTimeInSeconds, endTime);
-            }
-            else {
+            } else {
                 this.props.setTime(seekTimeInSeconds);
             }
         }
         return { select: true };
-    }
+    };
 
     // makes sure no anchors overlaps each other by setting the correct position and width
-    getLevel = (
-        m: Doc,
-        placed: { anchorStartTime: number; anchorEndTime: number; level: number }[]
-    ) => {
+    getLevel = (m: Doc, placed: { anchorStartTime: number; anchorEndTime: number; level: number }[]) => {
         const timelineContentWidth = this.timelineContentWidth;
         const x1 = this.anchorStart(m);
-        const x2 = this.anchorEnd(
-            m,
-            x1 + (10 / timelineContentWidth) * this.clipDuration
-        );
+        const x2 = this.anchorEnd(m, x1 + (10 / timelineContentWidth) * this.clipDuration);
         let max = 0;
         const overlappedLevels = new Set(
-            placed.map((p) => {
+            placed.map(p => {
                 const y1 = p.anchorStartTime;
                 const y2 = p.anchorEndTime;
-                if (
-                    (x1 >= y1 && x1 <= y2) ||
-                    (x2 >= y1 && x2 <= y2) ||
-                    (y1 >= x1 && y1 <= x2) ||
-                    (y2 >= x1 && y2 <= x2)
-                ) {
+                if ((x1 >= y1 && x1 <= y2) || (x2 >= y1 && x2 <= y2) || (y1 >= x1 && y1 <= x2) || (y2 >= x1 && y2 <= x2)) {
                     max = Math.max(max, p.level);
                     return p.level;
                 }
@@ -571,14 +484,17 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
 
         placed.push({ anchorStartTime: x1, anchorEndTime: x2, level });
         return level;
-    }
-
+    };
 
     dictationHeightPercent = 50;
     dictationHeight = () => (this.props.PanelHeight() * (100 - this.dictationHeightPercent)) / 100;
 
-    @computed get timelineContentHeight() { return this.props.PanelHeight() * this.dictationHeightPercent / 100; }
-    @computed get timelineContentWidth() { return this.props.PanelWidth() * this.zoomFactor; }  // subtract size of container border
+    @computed get timelineContentHeight() {
+        return (this.props.PanelHeight() * this.dictationHeightPercent) / 100;
+    }
+    @computed get timelineContentWidth() {
+        return this.props.PanelWidth() * this.zoomFactor;
+    } // subtract size of container border
 
     dictationScreenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this.timelineContentHeight);
 
@@ -586,24 +502,18 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
 
     currentTimecode = () => this.currentTime;
 
-
     @computed get renderDictation() {
         const dictation = Cast(this.dataDoc[this.props.dictationKey], Doc, null);
         return !dictation ? null : (
             <div
                 style={{
-                    position: "absolute",
-                    height: "100%",
+                    position: 'absolute',
+                    height: '100%',
                     top: this.timelineContentHeight,
                     background: Colors.LIGHT_BLUE,
-                }}
-            >
+                }}>
                 <DocumentView
-                    {...OmitKeys(this.props, [
-                        "NativeWidth",
-                        "NativeHeight",
-                        "setContentView",
-                    ]).omit}
+                    {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
                     Document={dictation}
                     PanelHeight={this.dictationHeight}
                     isAnnotationOverlay={true}
@@ -618,8 +528,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
                     moveDocument={returnFalse}
                     addDocument={returnFalse}
                     CollectionView={undefined}
-                    renderDepth={this.props.renderDepth + 1}
-                ></DocumentView>
+                    renderDepth={this.props.renderDepth + 1}></DocumentView>
             </div>
         );
     }
@@ -644,145 +553,131 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
             anchorEndTime: number;
             level: number;
         }[] = [];
-        const drawAnchors = this.childDocs.map((anchor) => ({
+        const drawAnchors = this.childDocs.map(anchor => ({
             level: this.getLevel(anchor, overlaps),
             anchor,
         }));
         const maxLevel = overlaps.reduce((m, o) => Math.max(m, o.level), 0) + 2;
-        return (<div ref={this.createDashEventsTarget} style={{ pointerEvents: SnappingManager.GetIsDragging() ? "all" : undefined }}>
-            <div className="timeline-container"
-                style={{ width: this.props.PanelWidth() }}
-                onWheel={e => e.stopPropagation()}
-                onScroll={this.setScroll}
-                onMouseMove={(e) => this.isContentActive() && this.onHover(e)}
-                ref={wrapper => this._timelineWrapper = wrapper}>
+        return (
+            <div ref={this.createDashEventsTarget} style={{ pointerEvents: SnappingManager.GetIsDragging() ? 'all' : undefined }}>
                 <div
-                    className="collectionStackedTimeline"
-                    ref={(timeline: HTMLDivElement | null) => (this._timeline = timeline)}
-                    onClick={(e) => this.isContentActive() && StopEvent(e)}
-                    onPointerDown={(e) => this.isContentActive() && this.onPointerDownTimeline(e)}
-                    style={{ width: this.timelineContentWidth }}>
-
-                    {drawAnchors.map((d) => {
-                        const start = this.anchorStart(d.anchor);
-                        const end = this.anchorEnd(
-                            d.anchor,
-                            start + (10 / this.timelineContentWidth) * this.clipDuration
-                        );
-                        if (end < this.clipStart || start > this.clipEnd) return (null);
-                        const left = Math.max((start - this.clipStart) / this.clipDuration * this.timelineContentWidth, 0);
-                        const top = (d.level / maxLevel) * this.props.PanelHeight();
-                        const timespan = Math.max(0, Math.min(end - this.clipStart, this.clipEnd)) - Math.max(0, start - this.clipStart);
-                        const width = (timespan / this.clipDuration) * this.timelineContentWidth;
-                        const height = this.props.PanelHeight() / maxLevel;
-                        return this.props.Document.hideAnchors ? null : (
-                            <div
-                                className={"collectionStackedTimeline-marker-timeline"}
-                                key={d.anchor[Id]}
-                                style={{
-                                    left,
-                                    top,
-                                    width: `${width}px`,
-                                    height: `${height}px`,
-                                }}
-                                onClick={(e) => {
-                                    this.props.playFrom(start, this.anchorEnd(d.anchor));
-                                    e.stopPropagation();
-                                }}
-                            >
-                                <StackedTimelineAnchor
-                                    {...this.props}
-                                    mark={d.anchor}
-                                    rangeClickScript={this.rangeClickScript}
-                                    rangePlayScript={this.rangePlayScript}
-                                    left={left - this._scroll}
-                                    top={top}
-                                    width={width}
-                                    height={height}
-                                    toTimeline={this.toTimeline}
-                                    layoutDoc={this.layoutDoc}
-                                    // isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
-                                    currentTimecode={this.currentTimecode}
-                                    _timeline={this._timeline}
-                                    stackedTimeline={this}
-                                    trimStart={this.trimStart}
-                                    trimEnd={this.trimEnd}
-                                />
-                            </div>
-                        );
-                    })}
-                    {!this.IsTrimming && this.selectionContainer}
-                    <AudioWaveform
-                        rawDuration={this.props.rawDuration}
-                        duration={this.clipDuration}
-                        mediaPath={this.props.mediaPath}
-                        layoutDoc={this.layoutDoc}
-                        clipStart={this.clipStart}
-                        clipEnd={this.clipEnd}
-                        zoomFactor={this.zoomFactor}
-                        PanelHeight={this.timelineContentHeight}
-                        PanelWidth={this.timelineContentWidth}
-                    />
-                    {/* {this.renderDictation} */}
-
+                    className="timeline-container"
+                    style={{ width: this.props.PanelWidth() }}
+                    onWheel={e => e.stopPropagation()}
+                    onScroll={this.setScroll}
+                    onMouseMove={e => this.isContentActive() && this.onHover(e)}
+                    ref={wrapper => (this._timelineWrapper = wrapper)}>
                     <div
-                        className="collectionStackedTimeline-hover"
-                        style={{
-                            left: `${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%`,
-                        }}
-                    />
+                        className="collectionStackedTimeline"
+                        ref={(timeline: HTMLDivElement | null) => (this._timeline = timeline)}
+                        onClick={e => this.isContentActive() && StopEvent(e)}
+                        onPointerDown={e => this.isContentActive() && this.onPointerDownTimeline(e)}
+                        style={{ width: this.timelineContentWidth }}>
+                        {drawAnchors.map(d => {
+                            const start = this.anchorStart(d.anchor);
+                            const end = this.anchorEnd(d.anchor, start + (10 / this.timelineContentWidth) * this.clipDuration);
+                            if (end < this.clipStart || start > this.clipEnd) return null;
+                            const left = Math.max(((start - this.clipStart) / this.clipDuration) * this.timelineContentWidth, 0);
+                            const top = (d.level / maxLevel) * this.props.PanelHeight();
+                            const timespan = Math.max(0, Math.min(end - this.clipStart, this.clipEnd)) - Math.max(0, start - this.clipStart);
+                            const width = (timespan / this.clipDuration) * this.timelineContentWidth;
+                            const height = this.props.PanelHeight() / maxLevel;
+                            return this.props.Document.hideAnchors ? null : (
+                                <div
+                                    className={'collectionStackedTimeline-marker-timeline'}
+                                    key={d.anchor[Id]}
+                                    style={{
+                                        left,
+                                        top,
+                                        width: `${width}px`,
+                                        height: `${height}px`,
+                                    }}
+                                    onClick={e => {
+                                        this.props.playFrom(start, this.anchorEnd(d.anchor));
+                                        e.stopPropagation();
+                                    }}>
+                                    <StackedTimelineAnchor
+                                        {...this.props}
+                                        mark={d.anchor}
+                                        rangeClickScript={this.rangeClickScript}
+                                        rangePlayScript={this.rangePlayScript}
+                                        left={left - this._scroll}
+                                        top={top}
+                                        width={width}
+                                        height={height}
+                                        toTimeline={this.toTimeline}
+                                        layoutDoc={this.layoutDoc}
+                                        // isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
+                                        currentTimecode={this.currentTimecode}
+                                        _timeline={this._timeline}
+                                        stackedTimeline={this}
+                                        trimStart={this.trimStart}
+                                        trimEnd={this.trimEnd}
+                                    />
+                                </div>
+                            );
+                        })}
+                        {!this.IsTrimming && this.selectionContainer}
+                        <AudioWaveform
+                            rawDuration={this.props.rawDuration}
+                            duration={this.clipDuration}
+                            mediaPath={this.props.mediaPath}
+                            layoutDoc={this.layoutDoc}
+                            clipStart={this.clipStart}
+                            clipEnd={this.clipEnd}
+                            zoomFactor={this.zoomFactor}
+                            PanelHeight={this.timelineContentHeight}
+                            PanelWidth={this.timelineContentWidth}
+                        />
+                        {/* {this.renderDictation} */}
+
+                        <div
+                            className="collectionStackedTimeline-hover"
+                            style={{
+                                left: `${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%`,
+                            }}
+                        />
+
+                        <div
+                            className="collectionStackedTimeline-current"
+                            style={{
+                                left: `${((this.currentTime - this.clipStart) / this.clipDuration) * 100}%`,
+                            }}
+                        />
+
+                        {this.IsTrimming !== TrimScope.None && (
+                            <>
+                                <div className="collectionStackedTimeline-trim-shade" style={{ width: `${((this.trimStart - this.clipStart) / this.clipDuration) * 100}%` }}></div>
 
-                    <div
-                        className="collectionStackedTimeline-current"
-                        style={{
-                            left: `${((this.currentTime - this.clipStart) / this.clipDuration) * 100}%`,
-                        }}
-                    />
-
-                    {this.IsTrimming !== TrimScope.None && (
-                        <>
-                            <div
-                                className="collectionStackedTimeline-trim-shade"
-                                style={{ width: `${((this.trimStart - this.clipStart) / this.clipDuration) * 100}%` }}
-                            ></div>
-
-                            <div
-                                className="collectionStackedTimeline-trim-controls"
-                                style={{
-                                    left: `${((this.trimStart - this.clipStart) / this.clipDuration) * 100}%`,
-                                    width: `${((this.trimEnd - this.trimStart) / this.clipDuration) * 100}%`,
-                                }}
-                            >
                                 <div
-                                    className="collectionStackedTimeline-trim-handle"
-                                    onPointerDown={this.trimLeft}
-                                ></div>
+                                    className="collectionStackedTimeline-trim-controls"
+                                    style={{
+                                        left: `${((this.trimStart - this.clipStart) / this.clipDuration) * 100}%`,
+                                        width: `${((this.trimEnd - this.trimStart) / this.clipDuration) * 100}%`,
+                                    }}>
+                                    <div className="collectionStackedTimeline-trim-handle" onPointerDown={this.trimLeft}></div>
+                                    <div className="collectionStackedTimeline-trim-handle" onPointerDown={this.trimRight}></div>
+                                </div>
+
                                 <div
-                                    className="collectionStackedTimeline-trim-handle"
-                                    onPointerDown={this.trimRight}
-                                ></div>
-                            </div>
-
-                            <div
-                                className="collectionStackedTimeline-trim-shade"
-                                style={{
-                                    left: `${((this.trimEnd - this.clipStart) / this.clipDuration) * 100}%`,
-                                    width: `${((this.clipEnd - this.trimEnd) / this.clipDuration) * 100}%`,
-                                }}
-                            ></div>
-                        </>
-                    )}
+                                    className="collectionStackedTimeline-trim-shade"
+                                    style={{
+                                        left: `${((this.trimEnd - this.clipStart) / this.clipDuration) * 100}%`,
+                                        width: `${((this.clipEnd - this.trimEnd) / this.clipDuration) * 100}%`,
+                                    }}></div>
+                            </>
+                        )}
+                    </div>
+                </div>
+                <div className="timeline-hoverUI" style={{ left: `calc(${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%` }}>
+                    <div className="hoverTime">{formatTime(this._hoverTime - this.clipStart)}</div>
+                    {this._thumbnail && <img className="videoBox-thumbnail" src={this._thumbnail} />}
                 </div>
             </div>
-            <div className="timeline-hoverUI" style={{ left: `calc(${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%` }}>
-                <div className="hoverTime">{formatTime(this._hoverTime - this.clipStart)}</div>
-                {this._thumbnail && <img className="videoBox-thumbnail" src={this._thumbnail} />}
-            </div>
-        </div >);
+        );
     }
 }
 
-
 /**
  * StackedTimelineAnchor
  * creates the anchors to display markers, links, and embedded documents on timeline
@@ -814,7 +709,6 @@ interface StackedTimelineAnchorProps {
     trimEnd: number;
 }
 
-
 @observer
 class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps> {
     _lastTimecode: number;
@@ -831,23 +725,14 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
         const start = Math.max(NumCast(this.props.mark[this.props.startTag]), this.props.trimStart) - this.props.trimStart;
         const end = Math.min(NumCast(this.props.mark[this.props.endTag]), this.props.trimEnd) - this.props.trimStart;
         return `#${formatTime(start)}-${formatTime(end)}`;
-    }
+    };
 
     componentDidMount() {
         this._disposer = reaction(
             () => this.props.currentTimecode(),
-            (time) => {
-                const dictationDoc = Cast(
-                    this.props.layoutDoc["data-dictation"],
-                    Doc,
-                    null
-                );
-                const isDictation =
-                    dictationDoc &&
-                    DocListCast(this.props.mark.links).some(
-                        (link) =>
-                            Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc
-                    );
+            time => {
+                const dictationDoc = Cast(this.props.layoutDoc['data-dictation'], Doc, null);
+                const isDictation = dictationDoc && DocListCast(this.props.mark.links).some(link => Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc);
                 if (
                     !LightboxView.LightboxDoc &&
                     // bcz: when should links be followed?  we don't want to move away from the video to follow a link but we can open it in a sidebar/etc.  But we don't know that upfront.
@@ -859,13 +744,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
                     time < NumCast(this.props.mark[this.props.endTag]) &&
                     this._lastTimecode < NumCast(this.props.mark[this.props.startTag]) - 1e-5
                 ) {
-                    LinkManager.FollowLink(
-                        undefined,
-                        this.props.mark,
-                        this.props as any as DocumentViewProps,
-                        false,
-                        true
-                    );
+                    LinkFollower.FollowLink(undefined, this.props.mark, this.props as any as DocumentViewProps, false, true);
                 }
                 this._lastTimecode = time;
             }
@@ -876,7 +755,6 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
         this._disposer?.();
     }
 
-
     // starting the drag event for anchor resizing
     onAnchorDown = (e: React.PointerEvent, anchor: Doc, left: boolean): void => {
         this.props._timeline?.setPointerCapture(e.pointerId);
@@ -885,19 +763,13 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
             return this.props.toTimeline(e.clientX - rect.x, rect.width);
         };
         const changeAnchor = (anchor: Doc, left: boolean, time: number | undefined) => {
-            const timelineOnly = Cast(anchor[this.props.startTag], "number", null) !== undefined;
+            const timelineOnly = Cast(anchor[this.props.startTag], 'number', null) !== undefined;
             if (timelineOnly) {
                 if (!left && time !== undefined && time <= NumCast(anchor[this.props.startTag])) time = undefined;
-                Doc.SetInPlace(
-                    anchor,
-                    left ? this.props.startTag : this.props.endTag,
-                    time,
-                    true
-                );
-                if (!left) Doc.SetInPlace(anchor, "borderRounding", time !== undefined ? undefined : "100%", true);
-            }
-            else {
-                anchor[left ? "_timecodeToShow" : "_timecodeToHide"] = time;
+                Doc.SetInPlace(anchor, left ? this.props.startTag : this.props.endTag, time, true);
+                if (!left) Doc.SetInPlace(anchor, 'borderRounding', time !== undefined ? undefined : '100%', true);
+            } else {
+                anchor[left ? '_timecodeToShow' : '_timecodeToHide'] = time;
             }
             return false;
         };
@@ -906,46 +778,34 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
         setupMoveUpEvents(
             this,
             e,
-            (e) => {
-                if (!undo) undo = UndoManager.StartBatch("drag anchor");
+            e => {
+                if (!undo) undo = UndoManager.StartBatch('drag anchor');
                 this.props.setTime(newTime(e));
                 return changeAnchor(anchor, left, newTime(e));
             },
-            (e) => {
+            e => {
                 this.props.setTime(newTime(e));
                 this.props._timeline?.releasePointerCapture(e.pointerId);
                 undo?.end();
             },
             emptyFunction
         );
-    }
-
+    };
 
     // context menu
     contextMenuItems = () => {
-        const resetTitle = { script: ScriptField.MakeFunction(`self.title = self["${this.props.endTag}"] ? "#" + formatToTime(self["${this.props.startTag}"]) + "-" + formatToTime(self["${this.props.endTag}"]) : "#" + formatToTime(self["${this.props.startTag}"])`)!, icon: "folder-plus", label: "Reset Title" };
+        const resetTitle = {
+            script: ScriptField.MakeFunction(`self.title = self["${this.props.endTag}"] ? "#" + formatToTime(self["${this.props.startTag}"]) + "-" + formatToTime(self["${this.props.endTag}"]) : "#" + formatToTime(self["${this.props.startTag}"])`)!,
+            icon: 'folder-plus',
+            label: 'Reset Title',
+        };
         return [resetTitle];
-    }
-
+    };
 
     // renders anchor LabelBox
-    renderInner = computedFn(function (
-        this: StackedTimelineAnchor,
-        mark: Doc,
-        script: undefined | (() => ScriptField),
-        doublescript: undefined | (() => ScriptField),
-        screenXf: () => Transform,
-        width: () => number,
-        height: () => number
-    ) {
+    renderInner = computedFn(function (this: StackedTimelineAnchor, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), screenXf: () => Transform, width: () => number, height: () => number) {
         const anchor = observable({ view: undefined as any });
-        const focusFunc = (
-            doc: Doc,
-            willZoom?: boolean,
-            scale?: number,
-            afterFocus?: DocAfterFocusFunc,
-            docTransform?: Transform
-        ) => {
+        const focusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, docTransform?: Transform) => {
             this.props.playLink(mark);
             this.props.focus(doc, { willZoom, scale, afterFocus, docTransform });
         };
@@ -954,13 +814,13 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
             view: (
                 <DocumentView
                     key="view"
-                    {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
-                    ref={action((r: DocumentView | null) => anchor.view = r)}
+                    {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight']).omit}
+                    ref={action((r: DocumentView | null) => (anchor.view = r))}
                     Document={mark}
                     DataDoc={undefined}
                     renderDepth={this.props.renderDepth + 1}
                     LayoutTemplate={undefined}
-                    LayoutTemplateString={LabelBox.LayoutStringWithTitle("data", this.computeTitle())}
+                    LayoutTemplateString={LabelBox.LayoutStringWithTitle('data', this.computeTitle())}
                     isDocumentActive={this.props.isDocumentActive}
                     PanelWidth={width}
                     PanelHeight={height}
@@ -985,32 +845,14 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
     height = () => this.props.height;
 
     render() {
-        const inner = this.renderInner(
-            this.props.mark,
-            this.props.rangeClickScript,
-            this.props.rangePlayScript,
-            this.anchorScreenToLocalXf,
-            this.width,
-            this.height
-        );
+        const inner = this.renderInner(this.props.mark, this.props.rangeClickScript, this.props.rangePlayScript, this.anchorScreenToLocalXf, this.width, this.height);
         return (
             <>
                 {inner.view}
-                {!inner.anchor.view ||
-                    !SelectionManager.IsSelected(inner.anchor.view) ? null : (
+                {!inner.anchor.view || !SelectionManager.IsSelected(inner.anchor.view) ? null : (
                     <>
-                        <div
-                            key="left"
-                            className="collectionStackedTimeline-left-resizer"
-                            onPointerDown={(e) => this.onAnchorDown(e, this.props.mark, true)}
-                        />
-                        <div
-                            key="right"
-                            className="collectionStackedTimeline-resizer"
-                            onPointerDown={(e) =>
-                                this.onAnchorDown(e, this.props.mark, false)
-                            }
-                        />
+                        <div key="left" className="collectionStackedTimeline-left-resizer" onPointerDown={e => this.onAnchorDown(e, this.props.mark, true)} />
+                        <div key="right" className="collectionStackedTimeline-resizer" onPointerDown={e => this.onAnchorDown(e, this.props.mark, false)} />
                     </>
                 )}
             </>
@@ -1025,4 +867,4 @@ ScriptingGlobals.add(function min(num1: number, num2: number): number {
 });
 ScriptingGlobals.add(function max(num1: number, num2: number): number {
     return Math.max(num1, num2);
-});
\ No newline at end of file
+});
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 7d40cab8c..6850fb23a 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -12,6 +12,7 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Ty
 import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
+import { CollectionViewType } from '../../documents/DocumentTypes';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
@@ -22,14 +23,13 @@ import { EditableView } from '../EditableView';
 import { LightboxView } from '../LightboxView';
 import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView';
 import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from '../nodes/DocumentView';
+import { FieldViewProps } from '../nodes/FieldView';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { StyleProp } from '../StyleProvider';
 import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow';
 import './CollectionStackingView.scss';
 import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn';
 import { CollectionSubView } from './CollectionSubView';
-import { CollectionViewType } from './CollectionView';
-import { FieldViewProps } from '../nodes/FieldView';
-import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 export type collectionStackingViewProps = {
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 03450b798..5479929bd 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,24 +1,23 @@
-import { action, computed, IReactionDisposer, reaction, observable, runInAction } from "mobx";
-import CursorField from "../../../fields/CursorField";
-import { Doc, Opt, Field, DocListCast, AclPrivate, StrListCast } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
-import { List } from "../../../fields/List";
-import { listSpec } from "../../../fields/Schema";
-import { ScriptField } from "../../../fields/ScriptField";
-import { WebField } from "../../../fields/URLField";
-import { Cast, ScriptCast, NumCast, StrCast } from "../../../fields/Types";
-import { GestureUtils } from "../../../pen-gestures/GestureUtils";
-import { Utils, returnFalse, returnEmptyFilter } from "../../../Utils";
-import { DocServer } from "../../DocServer";
-import { ImageUtils } from "../../util/Import & Export/ImageUtils";
-import { InteractionUtils } from "../../util/InteractionUtils";
-import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { DocComponent } from "../DocComponent";
-import React = require("react");
+import { action, computed, observable } from 'mobx';
 import ReactLoading from 'react-loading';
 import * as rp from 'request-promise';
-import { Networking } from "../../Network";
-
+import CursorField from '../../../fields/CursorField';
+import { AclPrivate, Doc, DocListCast, Field, Opt, StrListCast } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { listSpec } from '../../../fields/Schema';
+import { ScriptField } from '../../../fields/ScriptField';
+import { Cast, ScriptCast, StrCast } from '../../../fields/Types';
+import { WebField } from '../../../fields/URLField';
+import { GestureUtils } from '../../../pen-gestures/GestureUtils';
+import { returnFalse, Utils } from '../../../Utils';
+import { DocServer } from '../../DocServer';
+import { Networking } from '../../Network';
+import { ImageUtils } from '../../util/Import & Export/ImageUtils';
+import { InteractionUtils } from '../../util/InteractionUtils';
+import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { DocComponent } from '../DocComponent';
+import React = require('react');
 
 export interface SubCollectionViewProps extends CollectionViewProps {
     CollectionView: Opt<CollectionView>;
@@ -33,7 +32,8 @@ export function CollectionSubView<X>(moreProps?: X) {
         protected _mainCont?: HTMLDivElement;
         @observable _focusFilters: Opt<string[]>; // docFilters that are overridden when previewing a link to an anchor which has docFilters set on it
         @observable _focusRangeFilters: Opt<string[]>; // docRangeFilters that are overridden when previewing a link to an anchor which has docRangeFilters set on it
-        protected createDashEventsTarget = (ele: HTMLDivElement | null) => { //used for stacking and masonry view
+        protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
+            //used for stacking and masonry view
             this.dropDisposer?.();
             this.gestureDisposer?.();
             this._multiTouchDisposer?.();
@@ -43,8 +43,9 @@ export function CollectionSubView<X>(moreProps?: X) {
                 this.gestureDisposer = GestureUtils.MakeGestureTarget(ele, this.onGesture.bind(this));
                 this._multiTouchDisposer = InteractionUtils.MakeMultiTouchTarget(ele, this.onTouchStart.bind(this));
             }
-        }
-        protected CreateDropTarget(ele: HTMLDivElement) { //used in schema view
+        };
+        protected CreateDropTarget(ele: HTMLDivElement) {
+            //used in schema view
             this.createDashEventsTarget(ele);
         }
 
@@ -54,13 +55,12 @@ export function CollectionSubView<X>(moreProps?: X) {
         }
 
         @computed get dataDoc() {
-            return (this.props.DataDoc instanceof Doc && this.props.Document.isTemplateForField ? Doc.GetProto(this.props.DataDoc) :
-                this.props.Document.resolvedDataDoc ? this.props.Document : Doc.GetProto(this.props.Document)); // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template
+            return this.props.DataDoc instanceof Doc && this.props.Document.isTemplateForField ? Doc.GetProto(this.props.DataDoc) : this.props.Document.resolvedDataDoc ? this.props.Document : Doc.GetProto(this.props.Document); // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template
         }
 
         rootSelected = (outsideReaction?: boolean) => {
             return this.props.isSelected(outsideReaction) || (this.rootDoc && this.props.rootSelected(outsideReaction));
-        }
+        };
 
         // The data field for rendering this collection will be on the this.props.Document unless we're rendering a template in which case we try to use props.DataDoc.
         // When a document has a DataDoc but it's not a template, then it contains its own rendering data, but needs to pass the DataDoc through
@@ -73,10 +73,12 @@ export function CollectionSubView<X>(moreProps?: X) {
             return this.dataDoc[this.props.fieldKey];
         }
 
-        get childLayoutPairs(): { layout: Doc; data: Doc; }[] {
+        get childLayoutPairs(): { layout: Doc; data: Doc }[] {
             const { Document, DataDoc } = this.props;
-            const validPairs = this.childDocs.map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.isAnnotationOverlay ? DataDoc : undefined, doc)).
-                filter(pair => {  // filter out any documents that have a proto that we don't have permissions to
+            const validPairs = this.childDocs
+                .map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.isAnnotationOverlay ? DataDoc : undefined, doc))
+                .filter(pair => {
+                    // filter out any documents that have a proto that we don't have permissions to
                     return pair.layout && (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate));
                 });
             return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types
@@ -85,21 +87,24 @@ export function CollectionSubView<X>(moreProps?: X) {
             return Cast(this.dataField, listSpec(Doc));
         }
         collectionFilters = () => this._focusFilters ?? StrListCast(this.props.Document._docFilters);
-        collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
+        collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.props.Document._docRangeFilters, listSpec('string'), []);
         childDocFilters = () => [...(this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)) || []), ...this.collectionFilters()];
         unrecursiveDocFilters = () => [...(this.props.docFilters?.().filter(f => !Utils.IsRecursiveFilter(f)) || [])];
         childDocRangeFilters = () => [...(this.props.docRangeFilters?.() || []), ...this.collectionRangeDocFilters()];
-        IsFiltered = () => this.collectionFilters().length || this.collectionRangeDocFilters().length ? "hasFilter" :
-            this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined
+        IsFiltered = () =>
+            this.collectionFilters().length || this.collectionRangeDocFilters().length ? 'hasFilter' : this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? 'inheritsFilter' : undefined;
         searchFilterDocs = () => this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs);
         @computed.struct get childDocs() {
             TraceMobx();
             let rawdocs: (Doc | Promise<Doc>)[] = [];
-            if (this.dataField instanceof Doc) { // if collection data is just a document, then promote it to a singleton list;
+            if (this.dataField instanceof Doc) {
+                // if collection data is just a document, then promote it to a singleton list;
                 rawdocs = [this.dataField];
-            } else if (Cast(this.dataField, listSpec(Doc), null)) { // otherwise, if the collection data is a list, then use it.  
+            } else if (Cast(this.dataField, listSpec(Doc), null)) {
+                // otherwise, if the collection data is a list, then use it.
                 rawdocs = Cast(this.dataField, listSpec(Doc), null);
-            } else {   // Finally, if it's not a doc or a list and the document is a template, we try to render the root doc.
+            } else {
+                // Finally, if it's not a doc or a list and the document is a template, we try to render the root doc.
                 // For example, if an image doc is rendered with a slide template, the template will try to render the data field as a collection.
                 // Since the data field is actually an image, we set the list of documents to the singleton of root document's proto which will be an image.
                 const rootDoc = Cast(this.props.Document.rootDocument, Doc, null);
@@ -117,19 +122,19 @@ export function CollectionSubView<X>(moreProps?: X) {
                 return childDocs.filter(cd => !cd.cookies); // remove any documents that require a cookie if there are no filters to provide one
             }
 
-            // console.log(CurrentUserUtils.ActiveDashboard._docFilters);
+            // console.log(Doc.ActiveDashboard._docFilters);
             // if (!this.props.Document._docFilters && this.props.Document.currentFilter) {
             //     (this.props.Document.currentFilter as Doc).filterBoolean = (this.props.ContainingCollectionDoc?.currentFilter as Doc)?.filterBoolean;
             // }
             const docsforFilter: Doc[] = [];
-            childDocs.forEach((d) => {
+            childDocs.forEach(d => {
                 // if (DocUtils.Excluded(d, docFilters)) return;
-                let notFiltered = d.z || Doc.IsSystem(d) || (DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), docRangeFilters, viewSpecScript, this.props.Document).length > 0);
+                let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), docRangeFilters, viewSpecScript, this.props.Document).length > 0;
                 if (notFiltered) {
-                    notFiltered = ((!searchDocs.length || searchDocs.includes(d)) && (DocUtils.FilterDocs([d], childDocFilters, docRangeFilters, viewSpecScript, this.props.Document).length > 0));
+                    notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, docRangeFilters, viewSpecScript, this.props.Document).length > 0;
                     const fieldKey = Doc.LayoutFieldKey(d);
-                    const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
-                    const data = d[annos ? fieldKey + "-annotations" : fieldKey];
+                    const annos = !Field.toString(Doc.LayoutField(d) as Field).includes('CollectionView');
+                    const data = d[annos ? fieldKey + '-annotations' : fieldKey];
                     if (data !== undefined) {
                         let subDocs = DocListCast(data);
                         if (subDocs.length > 0) {
@@ -137,11 +142,12 @@ export function CollectionSubView<X>(moreProps?: X) {
                             notFiltered = notFiltered || (!searchDocs.length && DocUtils.FilterDocs(subDocs, childDocFilters, docRangeFilters, viewSpecScript, d).length);
                             while (subDocs.length > 0 && !notFiltered) {
                                 newarray = [];
-                                subDocs.forEach((t) => {
+                                subDocs.forEach(t => {
                                     const fieldKey = Doc.LayoutFieldKey(t);
-                                    const annos = !Field.toString(Doc.LayoutField(t) as Field).includes("CollectionView");
-                                    notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], childDocFilters, docRangeFilters, viewSpecScript, d).length));
-                                    DocListCast(t[annos ? fieldKey + "-annotations" : fieldKey]).forEach((newdoc) => newarray.push(newdoc));
+                                    const annos = !Field.toString(Doc.LayoutField(t) as Field).includes('CollectionView');
+                                    notFiltered =
+                                        notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], childDocFilters, docRangeFilters, viewSpecScript, d).length));
+                                    DocListCast(t[annos ? fieldKey + '-annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
                                 });
                                 subDocs = newarray;
                             }
@@ -157,7 +163,7 @@ export function CollectionSubView<X>(moreProps?: X) {
         protected async setCursorPosition(position: [number, number]) {
             let ind;
             const doc = this.props.Document;
-            const id = CurrentUserUtils.id;
+            const id = Doc.UserDoc()[Id];
             const email = Doc.CurrentUserEmail;
             const pos = { x: position[0], y: position[1] };
             if (id && email) {
@@ -167,7 +173,7 @@ export function CollectionSubView<X>(moreProps?: X) {
                 }
                 // The following conditional detects a recurring bug we've seen on the server
                 if (proto[Id] === Docs.Prototypes.get(DocumentType.COL)[Id]) {
-                    alert("COLLECTION PROTO CURSOR ISSUE DETECTED! Check console for more info...");
+                    alert('COLLECTION PROTO CURSOR ISSUE DETECTED! Check console for more info...');
                     console.log(doc);
                     console.log(proto);
                     throw new Error(`AHA! You were trying to set a cursor on a collection's proto, which is the original collection proto! Look at the two previously printed lines for document values!`);
@@ -186,8 +192,7 @@ export function CollectionSubView<X>(moreProps?: X) {
         }
 
         @undoBatch
-        protected onGesture(e: Event, ge: GestureUtils.GestureEvent) {
-        }
+        protected onGesture(e: Event, ge: GestureUtils.GestureEvent) {}
 
         protected onInternalPreDrop(e: Event, de: DragManager.DropEvent, targetAction: dropActionType) {
             if (de.complete.docDragData) {
@@ -210,12 +215,16 @@ export function CollectionSubView<X>(moreProps?: X) {
                 const dropAction = docDragData.dropAction || docDragData.userDropAction;
                 const targetDocments = DocListCast(this.dataDoc[this.props.fieldKey]);
                 const someMoved = !docDragData.userDropAction && docDragData.draggedDocuments.some(drag => targetDocments.includes(drag));
-                if (someMoved) docDragData.droppedDocuments = docDragData.droppedDocuments.map((drop, i) => targetDocments.includes(docDragData.draggedDocuments[i]) ? docDragData.draggedDocuments[i] : drop);
-                if ((!dropAction || dropAction === "same" || dropAction === "move" || someMoved) && docDragData.moveDocument) {
+                if (someMoved) docDragData.droppedDocuments = docDragData.droppedDocuments.map((drop, i) => (targetDocments.includes(docDragData.draggedDocuments[i]) ? docDragData.draggedDocuments[i] : drop));
+                if ((!dropAction || dropAction === 'same' || dropAction === 'move' || someMoved) && docDragData.moveDocument) {
                     const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d);
                     const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
                     if (movedDocs.length) {
-                        const canAdd = this.props.Document._viewType === CollectionViewType.Pile || de.embedKey || (!this.props.isAnnotationOverlay || this.props.Document.allowOverlayDrop) ||
+                        const canAdd =
+                            this.props.Document._viewType === CollectionViewType.Pile ||
+                            de.embedKey ||
+                            !this.props.isAnnotationOverlay ||
+                            this.props.Document.allowOverlayDrop ||
                             Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.props.Document);
                         added = docDragData.moveDocument(movedDocs, this.props.Document, canAdd ? this.addDocument : returnFalse);
                     } else {
@@ -228,11 +237,10 @@ export function CollectionSubView<X>(moreProps?: X) {
                     ScriptCast(this.props.Document.dropConverter)?.script.run({ dragData: docDragData });
                     added = this.addDocument(docDragData.droppedDocuments);
                 }
-                !added && alert("You cannot perform this move");
+                !added && alert('You cannot perform this move');
                 e.stopPropagation();
                 return added;
-            }
-            else if (de.complete.annoDragData) {
+            } else if (de.complete.annoDragData) {
                 const dropCreator = de.complete.annoDragData.dropDocCreator;
                 de.complete.annoDragData.dropDocCreator = () => {
                     const dropped = dropCreator(this.props.isAnnotationOverlay ? this.rootDoc : undefined);
@@ -253,11 +261,11 @@ export function CollectionSubView<X>(moreProps?: X) {
             }
 
             const { dataTransfer } = e;
-            const html = dataTransfer.getData("text/html");
-            const text = dataTransfer.getData("text/plain");
-            const uriList = dataTransfer.getData("text/uri-list");
+            const html = dataTransfer.getData('text/html');
+            const text = dataTransfer.getData('text/plain');
+            const uriList = dataTransfer.getData('text/uri-list');
 
-            if (text && text.startsWith("<div")) {
+            if (text && text.startsWith('<div')) {
                 return;
             }
 
@@ -271,11 +279,15 @@ export function CollectionSubView<X>(moreProps?: X) {
                     const href = FormattedTextBox.GetHref(html);
                     if (href) {
                         const docid = FormattedTextBox.GetDocFromUrl(href);
-                        if (docid) { // prosemirror text containing link to dash document
+                        if (docid) {
+                            // prosemirror text containing link to dash document
                             DocServer.GetRefField(docid).then(f => {
                                 if (f instanceof Doc) {
-                                    if (options.x || options.y) { f.x = options.x as number; f.y = options.y as number; } // should be in CollectionFreeFormView
-                                    (f instanceof Doc) && addDocument(f);
+                                    if (options.x || options.y) {
+                                        f.x = options.x as number;
+                                        f.y = options.y as number;
+                                    } // should be in CollectionFreeFormView
+                                    f instanceof Doc && addDocument(f);
                                 }
                             });
                         } else {
@@ -286,47 +298,50 @@ export function CollectionSubView<X>(moreProps?: X) {
                     }
                     return;
                 }
-                if (!html.startsWith("<a")) {
-                    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 cors = img.includes("corsProxy") ? img.match(/http.*corsProxy\//)![0] : "";
-                    img = cors ? img.replace(cors, "") : img;
+                if (!html.startsWith('<a')) {
+                    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 cors = img.includes('corsProxy') ? img.match(/http.*corsProxy\//)![0] : '';
+                    img = cors ? img.replace(cors, '') : img;
                     if (img) {
-                        const split = img.split("src=\"")[1].split("\"")[0];
+                        const split = img.split('src="')[1].split('"')[0];
                         let source = split;
-                        if (split.startsWith("data:image") && split.includes("base64")) {
-                            const [{ accessPaths }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [split] });
+                        if (split.startsWith('data:image') && split.includes('base64')) {
+                            const [{ accessPaths }] = await Networking.PostToServer('/uploadRemoteImage', { sources: [split] });
                             source = Utils.prepend(accessPaths.agnostic.client);
                         }
-                        if (source.startsWith("http")) {
+                        if (source.startsWith('http')) {
                             const doc = Docs.Create.ImageDocument(source, { ...options, _width: 300 });
                             ImageUtils.ExtractExif(doc);
                             addDocument(doc);
                         }
                         return;
                     } else {
-                        const path = window.location.origin + "/doc/";
+                        const path = window.location.origin + '/doc/';
                         if (text.startsWith(path)) {
-                            const docid = text.replace(Doc.globalServerPath(), "").split("?")[0];
+                            const docid = text.replace(Doc.globalServerPath(), '').split('?')[0];
                             DocServer.GetRefField(docid).then(f => {
                                 if (f instanceof Doc) {
-                                    if (options.x || options.y) { f.x = options.x as number; f.y = options.y as number;  } // should be in CollectionFreeFormView
-                                    (f instanceof Doc) && addDocument(f);
+                                    if (options.x || options.y) {
+                                        f.x = options.x as number;
+                                        f.y = options.y as number;
+                                    } // should be in CollectionFreeFormView
+                                    f instanceof Doc && addDocument(f);
                                 }
                             });
                         } else {
                             const srcWeb = SelectionManager.Views().lastElement();
                             const srcUrl = (srcWeb?.Document.data as WebField)?.url?.href?.match(/https?:\/\/[^/]*/)?.[0];
-                            const reg = new RegExp(Utils.prepend(""), "g");
+                            const reg = new RegExp(Utils.prepend(''), 'g');
                             const modHtml = srcUrl ? html.replace(reg, srcUrl) : html;
-                            const backgroundColor = tags.map(tag => tag.match(/.*(background-color: ?[^;]*)/)?.[1]?.replace(/background-color: ?(.*)/, "$1")).filter(t => t)?.[0];
-                            const htmlDoc = Docs.Create.HtmlDocument(modHtml, { ...options, title: srcUrl ? "from:" + srcUrl : "-web clip-", _width: 300, _height: 300, backgroundColor });
-                            Doc.GetProto(htmlDoc)["data-text"] = Doc.GetProto(htmlDoc).text = text;
+                            const backgroundColor = tags.map(tag => tag.match(/.*(background-color: ?[^;]*)/)?.[1]?.replace(/background-color: ?(.*)/, '$1')).filter(t => t)?.[0];
+                            const htmlDoc = Docs.Create.HtmlDocument(modHtml, { ...options, title: srcUrl ? 'from:' + srcUrl : '-web clip-', _width: 300, _height: 300, backgroundColor });
+                            Doc.GetProto(htmlDoc)['data-text'] = Doc.GetProto(htmlDoc).text = text;
                             addDocument(htmlDoc);
                             if (srcWeb) {
-                                const iframe = SelectionManager.Views()[0].ContentDiv?.getElementsByTagName("iframe")?.[0];
-                                const focusNode = (iframe?.contentDocument?.getSelection()?.focusNode as any);
+                                const iframe = SelectionManager.Views()[0].ContentDiv?.getElementsByTagName('iframe')?.[0];
+                                const focusNode = iframe?.contentDocument?.getSelection()?.focusNode as any;
                                 if (focusNode) {
                                     const anchor = srcWeb?.ComponentView?.getAnchor?.();
                                     anchor && DocUtils.MakeLink({ doc: htmlDoc }, { doc: anchor });
@@ -339,11 +354,10 @@ export function CollectionSubView<X>(moreProps?: X) {
             }
 
             if (uriList || text) {
-                if ((uriList || text).includes("www.youtube.com/watch") || text.includes("www.youtube.com/embed")) {
-
-                    const batch = UndoManager.StartBatch("youtube upload");
+                if ((uriList || text).includes('www.youtube.com/watch') || text.includes('www.youtube.com/embed')) {
+                    const batch = UndoManager.StartBatch('youtube upload');
                     const generatedDocuments: Doc[] = [];
-                    this.slowLoadDocuments((uriList || text).split("v=")[1].split("&")[0], options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument).then(batch.end);
+                    this.slowLoadDocuments((uriList || text).split('v=')[1].split('&')[0], options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument).then(batch.end);
 
                     return;
                 }
@@ -374,15 +388,16 @@ export function CollectionSubView<X>(moreProps?: X) {
                 //     alias._height = 512;
                 //     alias._width = 400;
                 //     addDocument(alias);
-                // } else 
+                // } else
                 {
-                    const newDoc = Docs.Create.WebDocument(uriList.split("#annotations:")[0], {// clean hypothes.is URLs that reference a specific annotation (eg. https://en.wikipedia.org/wiki/Cartoon#annotations:t7qAeNbCEeqfG5972KR2Ig)
+                    const newDoc = Docs.Create.WebDocument(uriList.split('#annotations:')[0], {
+                        // clean hypothes.is URLs that reference a specific annotation (eg. https://en.wikipedia.org/wiki/Cartoon#annotations:t7qAeNbCEeqfG5972KR2Ig)
                         ...options,
-                        title: uriList.split("#annotations:")[0],
+                        title: uriList.split('#annotations:')[0],
                         _width: 400,
                         _height: 512,
                         _nativeWidth: 850,
-                        useCors: true
+                        useCors: true,
                     });
                     addDocument(newDoc);
                 }
@@ -394,82 +409,99 @@ export function CollectionSubView<X>(moreProps?: X) {
             const files: File[] = [];
             const generatedDocuments: Doc[] = [];
             if (!length) {
-                alert("No uploadable content found.");
+                alert('No uploadable content found.');
                 return;
             }
 
-            const batch = UndoManager.StartBatch("collection view drop");
+            const batch = UndoManager.StartBatch('collection view drop');
             for (let i = 0; i < length; i++) {
                 const item = e.dataTransfer.items[i];
-                if (item.kind === "string" && item.type.includes("uri")) {
+                if (item.kind === 'string' && item.type.includes('uri')) {
                     const stringContents = await new Promise<string>(resolve => item.getAsString(resolve));
-                    const type = (await rp.head(Utils.CorsProxy(stringContents)))["content-type"];
+                    const type = (await rp.head(Utils.CorsProxy(stringContents)))['content-type'];
                     if (type) {
                         const doc = await DocUtils.DocumentFromType(type, Utils.CorsProxy(stringContents), options);
                         doc && generatedDocuments.push(doc);
                     }
                 }
-                if (item.kind === "file") {
+                if (item.kind === 'file') {
                     const file = item.getAsFile();
                     file?.type && files.push(file);
 
-                    file?.type === "application/json" && Utils.readUploadedFileAsText(file).then(result => {
-                        const json = JSON.parse(result as string);
-                        addDocument(Docs.Create.TreeDocument(
-                            json["rectangular-puzzle"].crossword.clues[0].clue.map((c: any) => {
-                                const label = Docs.Create.LabelDocument({ title: c["#text"], _width: 120, _height: 20 });
-                                const proto = Doc.GetProto(label);
-                                proto._width = 120;
-                                proto._height = 20;
-                                return proto;
-                            }
-                            ), { _width: 150, _height: 600, title: "across", backgroundColor: "white", _singleLine: true }));
-                    });
+                    file?.type === 'application/json' &&
+                        Utils.readUploadedFileAsText(file).then(result => {
+                            const json = JSON.parse(result as string);
+                            addDocument(
+                                Docs.Create.TreeDocument(
+                                    json['rectangular-puzzle'].crossword.clues[0].clue.map((c: any) => {
+                                        const label = Docs.Create.LabelDocument({ title: c['#text'], _width: 120, _height: 20 });
+                                        const proto = Doc.GetProto(label);
+                                        proto._width = 120;
+                                        proto._height = 20;
+                                        return proto;
+                                    }),
+                                    { _width: 150, _height: 600, title: 'across', backgroundColor: 'white', _singleLine: true }
+                                )
+                            );
+                        });
                 }
             }
             this.slowLoadDocuments(files, options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument).then(batch.end);
         }
-        slowLoadDocuments = async (files: (File[] | string), options: DocumentOptions, generatedDocuments: Doc[], text: string, completed: ((doc: Doc[]) => void) | undefined, clientX: number, clientY: number, addDocument: (doc: Doc | Doc[]) => boolean) => {
-            const disposer = OverlayView.Instance.addElement(
-                <ReactLoading type={"spinningBubbles"} color={"green"} height={250} width={250} />, { x: clientX - 125, y: clientY - 125 });
-            if (typeof files === "string") {
-                generatedDocuments.push(...await DocUtils.uploadYoutubeVideo(files, options));
+        slowLoadDocuments = async (
+            files: File[] | string,
+            options: DocumentOptions,
+            generatedDocuments: Doc[],
+            text: string,
+            completed: ((doc: Doc[]) => void) | undefined,
+            clientX: number,
+            clientY: number,
+            addDocument: (doc: Doc | Doc[]) => boolean
+        ) => {
+            const disposer = OverlayView.Instance.addElement(<ReactLoading type={'spinningBubbles'} color={'green'} height={250} width={250} />, { x: clientX - 125, y: clientY - 125 });
+            if (typeof files === 'string') {
+                generatedDocuments.push(...(await DocUtils.uploadYoutubeVideo(files, options)));
             } else {
-                generatedDocuments.push(...await DocUtils.uploadFilesToDocs(files, options));
+                generatedDocuments.push(...(await DocUtils.uploadFilesToDocs(files, options)));
             }
             if (generatedDocuments.length) {
                 // Creating a dash document
                 const isFreeformView = this.props.Document._viewType === CollectionViewType.Freeform;
-                const set = !isFreeformView ? generatedDocuments :
-                    generatedDocuments.length > 1 ? generatedDocuments.map(d => { DocUtils.iconify(d); return d; }) : [];
+                const set = !isFreeformView
+                    ? generatedDocuments
+                    : generatedDocuments.length > 1
+                    ? generatedDocuments.map(d => {
+                          DocUtils.iconify(d);
+                          return d;
+                      })
+                    : [];
                 if (completed) completed(set);
                 else {
                     if (isFreeformView && generatedDocuments.length > 1) {
-                        addDocument(DocUtils.pileup(generatedDocuments, options.x as number, options.y as number)!,);
+                        addDocument(DocUtils.pileup(generatedDocuments, options.x as number, options.y as number)!);
                     } else {
                         generatedDocuments.forEach(addDocument);
                     }
                 }
             } else {
-                if (text && !text.includes("https://")) {
+                if (text && !text.includes('https://')) {
                     addDocument(Docs.Create.TextDocument(text, { ...options, title: text.substring(0, 20), _width: 400, _height: 315 }));
                 } else {
-                    alert("Document upload failed - possibly an unsupported file type.");
+                    alert('Document upload failed - possibly an unsupported file type.');
                 }
             }
             disposer();
-        }
+        };
     }
 
     return CollectionSubView;
 }
 
-import { DragManager, dropActionType } from "../../util/DragManager";
-import { Docs, DocumentOptions, DocUtils } from "../../documents/Documents";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { FormattedTextBox, GoogleRef } from "../nodes/formattedText/FormattedTextBox";
-import { CollectionView, CollectionViewType, CollectionViewProps } from "./CollectionView";
-import { SelectionManager } from "../../util/SelectionManager";
-import { OverlayView } from "../OverlayView";
-import { GetEffectiveAcl, TraceMobx } from "../../../fields/util";
\ No newline at end of file
+import { GetEffectiveAcl, TraceMobx } from '../../../fields/util';
+import { Docs, DocumentOptions, DocUtils } from '../../documents/Documents';
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { DragManager, dropActionType } from '../../util/DragManager';
+import { SelectionManager } from '../../util/SelectionManager';
+import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import { OverlayView } from '../OverlayView';
+import { CollectionView, CollectionViewProps } from './CollectionView';
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index f5b9162d3..809a73a77 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -9,7 +9,6 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Ty
 import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, OmitKeys, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
@@ -20,6 +19,7 @@ import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { EditableView } from '../EditableView';
 import { DocumentView } from '../nodes/DocumentView';
+import { FieldViewProps } from '../nodes/FieldView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { StyleProp } from '../StyleProvider';
 import { CollectionFreeFormView } from './collectionFreeForm';
@@ -27,7 +27,6 @@ import { CollectionSubView } from './CollectionSubView';
 import './CollectionTreeView.scss';
 import { TreeView } from './TreeView';
 import React = require('react');
-import { FieldViewProps } from '../nodes/FieldView';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 export type collectionTreeViewProps = {
@@ -81,7 +80,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
         return this.doc.treeViewType === TreeViewType.fileSystem;
     }
     @computed get dashboardMode() {
-        return this.doc === CurrentUserUtils.MyDashboards;
+        return this.doc === Doc.MyDashboards;
     }
 
     @observable _explainerHeight = 0; // height of the description of the tree view
@@ -92,9 +91,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
     @observable _isAnyChildContentActive = false;
     whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive)));
     isContentActive = (outsideReaction?: boolean) =>
-        CurrentUserUtils.ActiveTool !== InkTool.None || this.props.isContentActive?.() || this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isAnyChildContentActive || this.props.rootSelected(outsideReaction)
-            ? true
-            : false;
+        Doc.ActiveTool !== InkTool.None || this.props.isContentActive?.() || this.props.Document.forceActive || this.props.isSelected(outsideReaction) || this._isAnyChildContentActive || this.props.rootSelected(outsideReaction) ? true : false;
 
     componentWillUnmount() {
         this._isDisposing = true;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 1576ec40f..f38efe578 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -9,12 +9,13 @@ import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types';
 import { TraceMobx } from '../../../fields/util';
 import { returnEmptyString } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
+import { CollectionViewType } from '../../documents/DocumentTypes';
 import { BranchCreate, BranchTask } from '../../documents/Gitlike';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { ImageUtils } from '../../util/Import & Export/ImageUtils';
 import { InteractionUtils } from '../../util/InteractionUtils';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
+import { DashboardView } from '../DashboardView';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
 import { FieldView, FieldViewProps } from '../nodes/FieldView';
 import { CollectionCarousel3DView } from './CollectionCarousel3DView';
@@ -35,26 +36,6 @@ import './CollectionView.scss';
 export const COLLECTION_BORDER_WIDTH = 2;
 const path = require('path');
 
-export enum CollectionViewType {
-    Invalid = 'invalid',
-    Freeform = 'freeform',
-    Schema = 'schema',
-    Docking = 'docking',
-    Tree = 'tree',
-    Stacking = 'stacking',
-    Masonry = 'masonry',
-    Multicolumn = 'multicolumn',
-    Multirow = 'multirow',
-    Time = 'time',
-    Carousel = 'carousel',
-    Carousel3D = '3D Carousel',
-    Linear = 'linear',
-    //Staff = "staff",
-    Map = 'map',
-    Grid = 'grid',
-    Pile = 'pileup',
-    StackedTimeline = 'stacked timeline',
-}
 interface CollectionViewProps_ extends FieldViewProps {
     isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc)
     isAnnotationOverlayScrollable?: boolean; // whether the annotation overlay can be vertically scrolled (just for tree views, currently)
@@ -127,39 +108,26 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
     };
 
     screenToLocalTransform = () => (this.props.renderDepth ? this.props.ScreenToLocalTransform() : this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth()));
+    // prettier-ignore
     private renderSubView = (type: CollectionViewType | undefined, props: SubCollectionViewProps) => {
         TraceMobx();
         if (type === undefined) return null;
         switch (type) {
             default:
-            case CollectionViewType.Freeform:
-                return <CollectionFreeFormView key="collview" {...props} />;
-            case CollectionViewType.Schema:
-                return <CollectionSchemaView key="collview" {...props} />;
-            case CollectionViewType.Docking:
-                return <CollectionDockingView key="collview" {...props} />;
-            case CollectionViewType.Tree:
-                return <CollectionTreeView key="collview" {...props} />;
-            case CollectionViewType.Multicolumn:
-                return <CollectionMulticolumnView key="collview" {...props} />;
-            case CollectionViewType.Multirow:
-                return <CollectionMultirowView key="collview" {...props} />;
-            case CollectionViewType.Linear:
-                return <CollectionLinearView key="collview" {...props} />;
-            case CollectionViewType.Pile:
-                return <CollectionPileView key="collview" {...props} />;
-            case CollectionViewType.Carousel:
-                return <CollectionCarouselView key="collview" {...props} />;
-            case CollectionViewType.Carousel3D:
-                return <CollectionCarousel3DView key="collview" {...props} />;
-            case CollectionViewType.Stacking:
-                return <CollectionStackingView key="collview" {...props} />;
-            case CollectionViewType.Masonry:
-                return <CollectionStackingView key="collview" {...props} />;
-            case CollectionViewType.Time:
-                return <CollectionTimeView key="collview" {...props} />;
-            case CollectionViewType.Grid:
-                return <CollectionGridView key="collview" {...props} />;
+            case CollectionViewType.Freeform: return <CollectionFreeFormView key="collview" {...props} />;
+            case CollectionViewType.Schema: return <CollectionSchemaView key="collview" {...props} />;
+            case CollectionViewType.Docking: return <CollectionDockingView key="collview" {...props} />;
+            case CollectionViewType.Tree: return <CollectionTreeView key="collview" {...props} />;
+            case CollectionViewType.Multicolumn: return <CollectionMulticolumnView key="collview" {...props} />;
+            case CollectionViewType.Multirow: return <CollectionMultirowView key="collview" {...props} />;
+            case CollectionViewType.Linear: return <CollectionLinearView key="collview" {...props} />;
+            case CollectionViewType.Pile: return <CollectionPileView key="collview" {...props} />;
+            case CollectionViewType.Carousel: return <CollectionCarouselView key="collview" {...props} />;
+            case CollectionViewType.Carousel3D: return <CollectionCarousel3DView key="collview" {...props} />;
+            case CollectionViewType.Stacking: return <CollectionStackingView key="collview" {...props} />;
+            case CollectionViewType.Masonry: return <CollectionStackingView key="collview" {...props} />;
+            case CollectionViewType.Time: return <CollectionTimeView key="collview" {...props} />;
+            case CollectionViewType.Grid: return <CollectionGridView key="collview" {...props} />;
             //case CollectionViewType.Staff: return <CollectionStaffView key="collview" {...props} />;
         }
     };
@@ -193,7 +161,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
     onContextMenu = (e: React.MouseEvent): void => {
         const cm = ContextMenu.Instance;
         if (e.nativeEvent.cancelBubble) return; // nested calls to React to render can cause the same event to trigger in the outer view even if the inner view has handled it.  This avoid CollectionDockingView menu options from being added when the event has been handled by a sub-document.
-        if (cm && !e.isPropagationStopped() && this.rootDoc[Id] !== CurrentUserUtils.MainDocId) {
+        if (cm && !e.isPropagationStopped() && this.rootDoc[Id] !== Doc.MainDocId) {
             // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
             this.setupViewTypes(
                 'UI Controls...',
@@ -235,7 +203,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
                 });
             }
             if (this.Document._viewType === CollectionViewType.Docking) {
-                optionItems.push({ description: 'Create Dashboard', event: () => CurrentUserUtils.createNewDashboard(), icon: 'project-diagram' });
+                optionItems.push({ description: 'Create Dashboard', event: () => DashboardView.createNewDashboard(), icon: 'project-diagram' });
             }
 
             !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'hand-point-right' });
@@ -286,9 +254,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
         return StrCast(this.rootDoc.childLayoutString);
     }
 
-    isContentActive = (outsideReaction?: boolean) => {
-        return this.props.isContentActive();
-    };
+    isContentActive = (outsideReaction?: boolean) => this.props.isContentActive();
+
     render() {
         TraceMobx();
         const props: SubCollectionViewProps = {
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index c0a61c90f..b8aaea622 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -9,18 +9,20 @@ import { DataSym, Doc, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
 import { FieldId } from '../../../fields/RefField';
+import { listSpec } from '../../../fields/Schema';
+import { ScriptField } from '../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
 import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../../Utils';
 import { DocServer } from '../../DocServer';
 import { DocUtils } from '../../documents/Documents';
-import { DocumentType } from '../../documents/DocumentTypes';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager, dropActionType } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { DashboardView } from '../DashboardView';
 import { Colors, Shadows } from '../global/globalEnums';
 import { LightboxView } from '../LightboxView';
 import { MainView } from '../MainView';
@@ -30,11 +32,9 @@ import { PinProps, PresBox, PresMovement } from '../nodes/trails';
 import { DefaultStyleProvider, StyleProp } from '../StyleProvider';
 import { CollectionDockingView } from './CollectionDockingView';
 import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
-import { CollectionView, CollectionViewType } from './CollectionView';
+import { CollectionView } from './CollectionView';
 import './TabDocView.scss';
 import React = require('react');
-import { listSpec } from '../../../fields/Schema';
-import { ScriptField } from '../../../fields/ScriptField';
 const _global = (window /* browser */ || global) /* node */ as any;
 
 interface TabDocViewProps {
@@ -215,7 +215,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         const batch = UndoManager.StartBatch('pinning doc');
 
         // all docs will be added to the ActivePresentation as stored on CurrentUserUtils
-        const curPres = CurrentUserUtils.ActivePresentation;
+        const curPres = Doc.ActivePresentation;
         curPres &&
             docList.forEach(doc => {
                 // Edge Case 1: Cannot pin document to itself
@@ -306,7 +306,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 .map(d => d.DashDoc)
                 .includes(curPres)
         ) {
-            const docs = Cast(CurrentUserUtils.MyOverlayDocs.data, listSpec(Doc), []);
+            const docs = Cast(Doc.MyOverlayDocs.data, listSpec(Doc), []);
             if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1);
             CollectionDockingView.AddSplit(curPres, 'right');
             setTimeout(() => DocumentManager.Instance.jumpToDocument(docList.lastElement(), false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things
@@ -361,7 +361,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
         const locationParams = locationFields.length > 1 ? locationFields[1] : '';
         switch (locationFields[0]) {
             case 'dashboard':
-                return CurrentUserUtils.openDashboard(doc);
+                return DashboardView.openDashboard(doc);
             case 'close':
                 return CollectionDockingView.CloseSplit(doc, locationParams);
             case 'fullScreen':
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 704b8989a..eb5faf4e1 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -1,7 +1,7 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction } from 'mobx';
+import { observer } from 'mobx-react';
 import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
 import { List } from '../../../fields/List';
@@ -12,26 +12,25 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Ty
 import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, returnTrue, simulateMouseClick, Utils } from '../../../Utils';
 import { Docs, DocUtils } from '../../documents/Documents';
-import { DocumentType } from "../../documents/DocumentTypes";
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
-import { DocumentManager, DocFocusOrOpen } from '../../util/DocumentManager';
-import { DragManager, dropActionType } from "../../util/DragManager";
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { DocumentManager } from '../../util/DocumentManager';
+import { DragManager, dropActionType } from '../../util/DragManager';
 import { SelectionManager } from '../../util/SelectionManager';
 import { SnappingManager } from '../../util/SnappingManager';
 import { Transform } from '../../util/Transform';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
-import { EditableView } from "../EditableView";
+import { EditableView } from '../EditableView';
 import { TREE_BULLET_WIDTH } from '../global/globalCssVariables.scss';
 import { DocumentView, DocumentViewInternal, DocumentViewProps, StyleProviderFunc } from '../nodes/DocumentView';
+import { FieldViewProps } from '../nodes/FieldView';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { RichTextMenu } from '../nodes/formattedText/RichTextMenu';
+import { KeyValueBox } from '../nodes/KeyValueBox';
 import { StyleProp } from '../StyleProvider';
 import { CollectionTreeView, TreeViewType } from './CollectionTreeView';
-import { CollectionView, CollectionViewType } from './CollectionView';
-import "./TreeView.scss";
-import React = require("react");
-import { KeyValueBox } from '../nodes/KeyValueBox';
-import { FieldViewProps } from '../nodes/FieldView';
+import { CollectionView } from './CollectionView';
+import './TreeView.scss';
+import React = require('react');
 
 export interface TreeViewProps {
     treeView: CollectionTreeView;
@@ -55,7 +54,7 @@ export interface TreeViewProps {
     indentDocument?: (editTitle: boolean) => void;
     outdentDocument?: (editTitle: boolean) => void;
     ScreenToLocalTransform: () => Transform;
-    contextMenuItems: { script: ScriptField, filter: ScriptField, icon: string, label: string }[];
+    contextMenuItems: { script: ScriptField; filter: ScriptField; icon: string; label: string }[];
     dontRegisterView?: boolean;
     styleProvider?: StyleProviderFunc | undefined;
     treeViewHideHeaderFields: () => boolean;
@@ -70,24 +69,26 @@ export interface TreeViewProps {
     hierarchyIndex?: number[];
 }
 
-const treeBulletWidth = function () { return Number(TREE_BULLET_WIDTH.replace("px", "")); };
+const treeBulletWidth = function () {
+    return Number(TREE_BULLET_WIDTH.replace('px', ''));
+};
 
 export enum TreeSort {
-    Up = "up",
-    Down = "down",
-    Zindex = "z",
-    None = "none"
+    Up = 'up',
+    Down = 'down',
+    Zindex = 'z',
+    None = 'none',
 }
 /**
  * Renders a treeView of a collection of documents
- * 
+ *
  * special fields:
  * treeViewOpen : flag denoting whether the documents sub-tree (contents) is visible or hidden
  * treeViewExpandedView : name of field whose contents are being displayed as the document's subtree
  */
 @observer
 export class TreeView extends React.Component<TreeViewProps> {
-    static _editTitleOnLoad: Opt<{ id: string, parent: TreeView | CollectionTreeView | undefined }>;
+    static _editTitleOnLoad: Opt<{ id: string; parent: TreeView | CollectionTreeView | undefined }>;
     static _openTitleScript: Opt<ScriptField | undefined>;
     static _openLevelScript: Opt<ScriptField | undefined>;
     private _header: React.RefObject<HTMLDivElement> = React.createRef();
@@ -98,7 +99,9 @@ export class TreeView extends React.Component<TreeViewProps> {
     private _openScript: (() => ScriptField) | undefined;
     private _treedropDisposer?: DragManager.DragDropDisposer;
 
-    get treeViewOpenIsTransient() { return this.props.treeView.doc.treeViewOpenIsTransient || Doc.IsPrototype(this.doc); }
+    get treeViewOpenIsTransient() {
+        return this.props.treeView.doc.treeViewOpenIsTransient || Doc.IsPrototype(this.doc);
+    }
     set treeViewOpen(c: boolean) {
         if (this.treeViewOpenIsTransient) this._transientOpenState = c;
         else {
@@ -109,26 +112,61 @@ export class TreeView extends React.Component<TreeViewProps> {
     @observable _transientOpenState = false; // override of the treeViewOpen field allowing the display state to be independent of the document's state
     @observable _editTitle: boolean = false;
     @observable _dref: DocumentView | undefined | null;
-    get displayName() { return "TreeView(" + this.props.document.title + ")"; }  // this makes mobx trace() statements more descriptive
+    get displayName() {
+        return 'TreeView(' + this.props.document.title + ')';
+    } // this makes mobx trace() statements more descriptive
     get defaultExpandedView() {
-        return this.doc.viewType === CollectionViewType.Docking ? this.fieldKey :
-            this.props.treeView.dashboardMode ? this.fieldKey :
-                this.props.treeView.fileSysMode ? (this.doc.isFolder ? this.fieldKey : "aliases") : // for displaying 
-                    this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields");
-    }
-
-    @computed get doc() { return this.props.document; }
-    @computed get treeViewOpen() { return (!this.treeViewOpenIsTransient && Doc.GetT(this.doc, "treeViewOpen", "boolean", true)) || this._transientOpenState; }
-    @computed get treeViewExpandedView() { return this.validExpandViewTypes.includes(StrCast(this.doc.treeViewExpandedView)) ? StrCast(this.doc.treeViewExpandedView) : this.defaultExpandedView; }
-    @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containerCollection.maxEmbedHeight, 200); }
-    @computed get dataDoc() { return this.props.document.treeViewChildrenOnRoot ? this.doc : this.doc[DataSym]; }
-    @computed get layoutDoc() { return Doc.Layout(this.doc); }
-    @computed get fieldKey() { return StrCast(this.doc._treeViewFieldKey, Doc.LayoutFieldKey(this.doc)); }
-    @computed get childDocs() { return this.childDocList(this.fieldKey); }
-    @computed get childLinks() { return this.childDocList("links"); }
-    @computed get childAliases() { return this.childDocList("aliases"); }
-    @computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); }
-    @computed get selected() { return SelectionManager.IsSelected(this._docRef); }
+        return this.doc.viewType === CollectionViewType.Docking
+            ? this.fieldKey
+            : this.props.treeView.dashboardMode
+            ? this.fieldKey
+            : this.props.treeView.fileSysMode
+            ? this.doc.isFolder
+                ? this.fieldKey
+                : 'aliases' // for displaying
+            : this.props.treeView.outlineMode || this.childDocs
+            ? this.fieldKey
+            : Doc.noviceMode
+            ? 'layout'
+            : StrCast(this.props.treeView.doc.treeViewExpandedView, 'fields');
+    }
+
+    @computed get doc() {
+        return this.props.document;
+    }
+    @computed get treeViewOpen() {
+        return (!this.treeViewOpenIsTransient && Doc.GetT(this.doc, 'treeViewOpen', 'boolean', true)) || this._transientOpenState;
+    }
+    @computed get treeViewExpandedView() {
+        return this.validExpandViewTypes.includes(StrCast(this.doc.treeViewExpandedView)) ? StrCast(this.doc.treeViewExpandedView) : this.defaultExpandedView;
+    }
+    @computed get MAX_EMBED_HEIGHT() {
+        return NumCast(this.props.containerCollection.maxEmbedHeight, 200);
+    }
+    @computed get dataDoc() {
+        return this.props.document.treeViewChildrenOnRoot ? this.doc : this.doc[DataSym];
+    }
+    @computed get layoutDoc() {
+        return Doc.Layout(this.doc);
+    }
+    @computed get fieldKey() {
+        return StrCast(this.doc._treeViewFieldKey, Doc.LayoutFieldKey(this.doc));
+    }
+    @computed get childDocs() {
+        return this.childDocList(this.fieldKey);
+    }
+    @computed get childLinks() {
+        return this.childDocList('links');
+    }
+    @computed get childAliases() {
+        return this.childDocList('aliases');
+    }
+    @computed get childAnnos() {
+        return this.childDocList(this.fieldKey + '-annotations');
+    }
+    @computed get selected() {
+        return SelectionManager.IsSelected(this._docRef);
+    }
     //      SelectionManager.Views().lastElement()?.props.Document === this.props.document; }
 
     @observable _presTimer!: NodeJS.Timeout;
@@ -136,64 +174,76 @@ export class TreeView extends React.Component<TreeViewProps> {
 
     @observable _selectedArray: ObservableMap = new ObservableMap<Doc, any>();
     // the selected item's index
-    @computed get itemIndex() { return NumCast(this.doc._itemIndex); }
-    // the item that's active 
-    @computed get activeItem() { return this.childDocs ? Cast(this.childDocs[NumCast(this.doc._itemIndex)], Doc, null) : undefined; }
-    @computed get targetDoc() { return Cast(this.activeItem?.presentationTargetDoc, Doc, null); }
+    @computed get itemIndex() {
+        return NumCast(this.doc._itemIndex);
+    }
+    // the item that's active
+    @computed get activeItem() {
+        return this.childDocs ? Cast(this.childDocs[NumCast(this.doc._itemIndex)], Doc, null) : undefined;
+    }
+    @computed get targetDoc() {
+        return Cast(this.activeItem?.presentationTargetDoc, Doc, null);
+    }
 
     childDocList(field: string) {
         const layout = Cast(Doc.LayoutField(this.doc), Doc, null);
-        return (this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
+        return (
+            (this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field
             (layout ? DocListCastOrNull(layout[field]) : undefined) || // else if there's a layout doc, display it's fields
-            DocListCastOrNull(this.doc[field]); // otherwise use the document's data field
+            DocListCastOrNull(this.doc[field])
+        ); // otherwise use the document's data field
     }
     @undoBatch move = (doc: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => {
-        if (this.doc !== target && addDoc !== returnFalse) { // bcz: this should all be running in a Temp undo batch instead of hackily testing for returnFalse
+        if (this.doc !== target && addDoc !== returnFalse) {
+            // bcz: this should all be running in a Temp undo batch instead of hackily testing for returnFalse
             if (this.props.removeDoc?.(doc) === true) {
                 return addDoc(doc);
             }
         }
         return false;
-    }
+    };
     @undoBatch @action remove = (doc: Doc | Doc[], key: string) => {
         this.props.treeView.props.select(false);
         const ind = this.dataDoc[key].indexOf(doc);
         const res = (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && Doc.RemoveDocFromList(this.dataDoc, key, doc), true);
         res && ind > 0 && DocumentManager.Instance.getDocumentView(this.dataDoc[key][ind - 1], this.props.treeView.props.CollectionView)?.select(false);
         return res;
-    }
+    };
 
     @action setEditTitle = (docView?: DocumentView) => {
         this._selDisposer?.();
         if (!docView) {
             this._editTitle = false;
-        }
-        else if (docView.isSelected()) {
+        } else if (docView.isSelected()) {
             const doc = docView.Document;
             SelectionManager.SelectSchemaViewDoc(doc);
             this._editTitle = true;
-            this._selDisposer = reaction(() => SelectionManager.SelectedSchemaDoc(), seldoc => seldoc !== doc && this.setEditTitle(undefined));
+            this._selDisposer = reaction(
+                () => SelectionManager.SelectedSchemaDoc(),
+                seldoc => seldoc !== doc && this.setEditTitle(undefined)
+            );
         } else {
             docView.select(false);
         }
-    }
+    };
     @action
     openLevel = (docView: DocumentView) => {
         if (this.props.document.isFolder || Doc.IsSystem(this.props.document)) {
             this.treeViewOpen = !this.treeViewOpen;
         } else {
             // choose an appropriate alias or make one. --- choose the first alias that (1) user owns,  (2) has no context field ... otherwise make a new alias
-            const bestAlias = docView.props.Document.author === Doc.CurrentUserEmail && !Doc.IsPrototype(docView.props.Document) ? docView.props.Document : DocListCast(this.props.document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
+            const bestAlias =
+                docView.props.Document.author === Doc.CurrentUserEmail && !Doc.IsPrototype(docView.props.Document) ? docView.props.Document : DocListCast(this.props.document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
             const nextBestAlias = DocListCast(this.props.document.aliases).find(doc => doc.author === Doc.CurrentUserEmail);
-            this.props.addDocTab(bestAlias ?? nextBestAlias ?? Doc.MakeAlias(this.props.document), "lightbox");
+            this.props.addDocTab(bestAlias ?? nextBestAlias ?? Doc.MakeAlias(this.props.document), 'lightbox');
         }
-    }
+    };
 
     constructor(props: any) {
         super(props);
         if (!TreeView._openLevelScript) {
-            TreeView._openTitleScript = ScriptField.MakeScript("scriptContext.setEditTitle(documentView)", { scriptContext: "any", documentView: "any" });
-            TreeView._openLevelScript = ScriptField.MakeScript(`scriptContext.openLevel(documentView)`, { scriptContext: "any", documentView: "any" });
+            TreeView._openTitleScript = ScriptField.MakeScript('scriptContext.setEditTitle(documentView)', { scriptContext: 'any', documentView: 'any' });
+            TreeView._openLevelScript = ScriptField.MakeScript(`scriptContext.openLevel(documentView)`, { scriptContext: 'any', documentView: 'any' });
         }
         this._openScript = Doc.IsSystem(this.props.document) ? undefined : () => TreeView._openLevelScript!;
         this._editTitleScript = Doc.IsSystem(this.props.document) ? () => TreeView._openLevelScript! : () => TreeView._openTitleScript!;
@@ -202,16 +252,16 @@ export class TreeView extends React.Component<TreeViewProps> {
     _treeEle: any;
     protected createTreeDropTarget = (ele: HTMLDivElement) => {
         this._treedropDisposer?.();
-        ele && (this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), undefined, this.preTreeDrop.bind(this)), this.doc);
+        ele && ((this._treedropDisposer = DragManager.MakeDropTarget(ele, this.treeDrop.bind(this), undefined, this.preTreeDrop.bind(this))), this.doc);
         if (this._treeEle) this.props.unobserveHeight(this._treeEle);
-        this.props.observeHeight(this._treeEle = ele);
-    }
+        this.props.observeHeight((this._treeEle = ele));
+    };
 
     componentWillUnmount() {
         this._selDisposer?.();
         this._treeEle && this.props.unobserveHeight(this._treeEle);
-        document.removeEventListener("pointermove", this.onDragMove, true);
-        document.removeEventListener("pointermove", this.onDragUp, true);
+        document.removeEventListener('pointermove', this.onDragMove, true);
+        document.removeEventListener('pointermove', this.onDragUp, true);
         // TODO: [AL] add these
         this.props.hierarchyIndex !== undefined && this.props.RemFromMap?.(this.doc, this.props.hierarchyIndex);
     }
@@ -225,49 +275,60 @@ export class TreeView extends React.Component<TreeViewProps> {
     }
 
     onDragUp = (e: PointerEvent) => {
-        document.removeEventListener("pointerup", this.onDragUp, true);
-        document.removeEventListener("pointermove", this.onDragMove, true);
-    }
+        document.removeEventListener('pointerup', this.onDragUp, true);
+        document.removeEventListener('pointermove', this.onDragMove, true);
+    };
     onPointerEnter = (e: React.PointerEvent): void => {
         this.props.isContentActive(true) && Doc.BrushDoc(this.dataDoc);
         if (e.buttons === 1 && SnappingManager.GetIsDragging()) {
-            this._header.current!.className = "treeView-header";
-            document.removeEventListener("pointermove", this.onDragMove, true);
-            document.removeEventListener("pointerup", this.onDragUp, true);
-            document.addEventListener("pointermove", this.onDragMove, true);
-            document.addEventListener("pointerup", this.onDragUp, true);
+            this._header.current!.className = 'treeView-header';
+            document.removeEventListener('pointermove', this.onDragMove, true);
+            document.removeEventListener('pointerup', this.onDragUp, true);
+            document.addEventListener('pointermove', this.onDragMove, true);
+            document.addEventListener('pointerup', this.onDragUp, true);
         }
-    }
+    };
     onPointerLeave = (e: React.PointerEvent): void => {
         Doc.UnBrushDoc(this.dataDoc);
-        if (this._header.current?.className !== "treeView-header-editing") {
-            this._header.current!.className = "treeView-header";
+        if (this._header.current?.className !== 'treeView-header-editing') {
+            this._header.current!.className = 'treeView-header';
         }
-        document.removeEventListener("pointerup", this.onDragUp, true);
-        document.removeEventListener("pointermove", this.onDragMove, true);
-    }
+        document.removeEventListener('pointerup', this.onDragUp, true);
+        document.removeEventListener('pointermove', this.onDragMove, true);
+    };
     onDragMove = (e: PointerEvent): void => {
         Doc.UnBrushDoc(this.dataDoc);
         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 && this.childDocList.length);
-        this._header.current!.className = "treeView-header";
+        const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * 0.75) || (!before && this.treeViewOpen && this.childDocList.length);
+        this._header.current!.className = 'treeView-header';
         if (!this.props.treeView.outlineMode || DragManager.DocDragData?.treeViewDoc === this.props.treeView.rootDoc) {
-            if (inside) this._header.current!.className += " treeView-header-inside";
-            else if (before) this._header.current!.className += " treeView-header-above";
-            else if (!before) this._header.current!.className += " treeView-header-below";
+            if (inside) this._header.current!.className += ' treeView-header-inside';
+            else if (before) this._header.current!.className += ' treeView-header-above';
+            else if (!before) this._header.current!.className += ' treeView-header-below';
         }
         e.stopPropagation();
-    }
+    };
 
     public static makeTextBullet() {
-        const bullet = Docs.Create.TextDocument("-text-", {
-            layout: CollectionView.LayoutString("data"),
-            title: "-title-",
-            treeViewExpandedViewLock: true, treeViewExpandedView: "data",
-            _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewType: TreeViewType.outline,
-            x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _width: 1000, _height: 10
+        const bullet = Docs.Create.TextDocument('-text-', {
+            layout: CollectionView.LayoutString('data'),
+            title: '-title-',
+            treeViewExpandedViewLock: true,
+            treeViewExpandedView: 'data',
+            _viewType: CollectionViewType.Tree,
+            hideLinkButton: true,
+            _showSidebar: true,
+            treeViewType: TreeViewType.outline,
+            x: 0,
+            y: 0,
+            _xMargin: 0,
+            _yMargin: 0,
+            _autoHeight: true,
+            _singleLine: true,
+            _width: 1000,
+            _height: 10,
         });
         Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text');
         Doc.GetProto(bullet).data = new List<Doc>([]);
@@ -279,19 +340,19 @@ export class TreeView extends React.Component<TreeViewProps> {
         const bullet = TreeView.makeTextBullet();
         TreeView._editTitleOnLoad = { id: bullet[Id], parent: this };
         return this.props.addDocument(bullet);
-    }
+    };
 
     makeFolder = () => {
-        const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true });
+        const folder = Docs.Create.TreeDocument([], { title: 'Untitled folder', _stayInCollection: true, isFolder: true });
         TreeView._editTitleOnLoad = { id: folder[Id], parent: this.props.parentTreeView };
         return this.props.addDocument(folder);
-    }
+    };
     deleteItem = () => this.props.removeDoc?.(this.doc);
 
     preTreeDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
         const dragData = de.complete.docDragData;
-        dragData && (dragData.dropAction = this.props.treeView.props.Document === dragData.treeViewDoc ? "same" : dragData.dropAction);
-    }
+        dragData && (dragData.dropAction = this.props.treeView.props.Document === dragData.treeViewDoc ? 'same' : dragData.dropAction);
+    };
 
     @undoBatch
     treeDrop = (e: Event, de: DragManager.DropEvent) => {
@@ -299,11 +360,11 @@ export class TreeView extends React.Component<TreeViewProps> {
         if (!this._header.current) return;
         const rect = this._header.current.getBoundingClientRect();
         const before = pt[1] < rect.top + rect.height / 2;
-        const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
+        const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * 0.75) || (!before && this.treeViewOpen && this.childDocList.length);
         if (de.complete.linkDragData) {
             const sourceDoc = de.complete.linkDragData.linkSourceGetAnchor();
             const destDoc = this.doc;
-            DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, "tree link", "");
+            DocUtils.MakeLink({ doc: sourceDoc }, { doc: destDoc }, 'tree link', '');
             e.stopPropagation();
         }
         const docDragData = de.complete.docDragData;
@@ -313,22 +374,16 @@ export class TreeView extends React.Component<TreeViewProps> {
                 e.stopPropagation();
             }
         }
-    }
+    };
 
     dropDocuments(droppedDocuments: Doc[], before: boolean, inside: number | boolean, dropAction: dropActionType, moveDocument: DragManager.MoveFunction | undefined, forceAdd: boolean) {
         const parentAddDoc = (doc: Doc | Doc[]) => this.props.addDocument(doc, undefined, before);
-        const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.containerCollection)?.freezeChildren).includes("add")) || forceAdd;
-        const localAdd = (doc: Doc) => Doc.AddDocToList(this.dataDoc, this.fieldKey, doc) && ((doc.context = this.doc.context) || true) ? true : false;
-        const addDoc = !inside ? parentAddDoc :
-            (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc), true as boolean);
-        const move = (!dropAction || dropAction === "proto" || dropAction === "move" || dropAction === "same") && moveDocument;
+        const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.containerCollection)?.freezeChildren).includes('add')) || forceAdd;
+        const localAdd = (doc: Doc) => (Doc.AddDocToList(this.dataDoc, this.fieldKey, doc) && ((doc.context = this.doc.context) || true) ? true : false);
+        const addDoc = !inside ? parentAddDoc : (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc), true as boolean);
+        const move = (!dropAction || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same') && moveDocument;
         if (canAdd) {
-            return UndoManager.RunInTempBatch(() => droppedDocuments.reduce((added, d) =>
-                (move ?
-                    move(d, undefined, addDoc) || (dropAction === "proto" ? addDoc(d) : false)
-                    :
-                    addDoc(d)) || added,
-                false));
+            return UndoManager.RunInTempBatch(() => droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false));
         }
         return false;
     }
@@ -339,7 +394,7 @@ export class TreeView extends React.Component<TreeViewProps> {
         const outerXf = Utils.GetScreenTransform(this.props.treeView.MainEle());
         const offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
         return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]);
-    }
+    };
     docTransform = () => this.refTransform(this._dref?.ContentRef?.current);
     getTransform = () => this.refTransform(this._tref.current);
     docWidth = () => {
@@ -348,22 +403,25 @@ export class TreeView extends React.Component<TreeViewProps> {
         if (layoutDoc._fitWidth) return Math.min(this.props.panelWidth() - treeBulletWidth(), layoutDoc[WidthSym]());
         if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT * aspect, this.props.panelWidth() - treeBulletWidth()));
         return Math.min((this.props.panelWidth() - treeBulletWidth()) / (this.props.treeView.props.scaling?.() || 1), Doc.NativeWidth(layoutDoc) ? layoutDoc[WidthSym]() : this.layoutDoc[WidthSym]());
-    }
+    };
     docHeight = () => {
         const layoutDoc = this.layoutDoc;
-        return Math.max(70, Math.min(this.MAX_EMBED_HEIGHT, (() => {
-            const aspect = Doc.NativeAspect(layoutDoc);
-            if (aspect) return this.docWidth() / (aspect || 1);
-            return layoutDoc._fitWidth ?
-                (!Doc.NativeHeight(this.doc) ?
-                    NumCast(this.props.containerCollection._height)
-                    :
-                    Math.min(this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc)) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.containerCollection._height))
-                    ))
-                :
-                (layoutDoc[HeightSym]() || 50);
-        })()));
-    }
+        return Math.max(
+            70,
+            Math.min(
+                this.MAX_EMBED_HEIGHT,
+                (() => {
+                    const aspect = Doc.NativeAspect(layoutDoc);
+                    if (aspect) return this.docWidth() / (aspect || 1);
+                    return layoutDoc._fitWidth
+                        ? !Doc.NativeHeight(this.doc)
+                            ? NumCast(this.props.containerCollection._height)
+                            : Math.min((this.docWidth() * NumCast(layoutDoc.scrollHeight, Doc.NativeHeight(layoutDoc))) / (Doc.NativeWidth(layoutDoc) || NumCast(this.props.containerCollection._height)))
+                        : layoutDoc[HeightSym]() || 50;
+                })()
+            )
+        );
+    };
 
     @computed get expandedField() {
         const ids: { [key: string]: string } = {};
@@ -372,11 +430,11 @@ export class TreeView extends React.Component<TreeViewProps> {
         doc && Object.keys(doc).forEach(key => !(key in ids) && doc[key] !== ComputedField.undefined && (ids[key] = key));
 
         for (const key of Object.keys(ids).slice().sort()) {
-            if (this.props.skipFields?.includes(key) || key === "title" || key === "treeViewOpen") continue;
+            if (this.props.skipFields?.includes(key) || key === 'title' || key === 'treeViewOpen') continue;
             const contents = doc[key];
             let contentElement: (JSX.Element | null)[] | JSX.Element = [];
 
-            if (contents instanceof Doc || (Cast(contents, listSpec(Doc)) && (Cast(contents, listSpec(Doc))!.length && Cast(contents, listSpec(Doc))![0] instanceof 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 | Doc[]) => this.remove(doc, key);
                 const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
                     const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
@@ -384,74 +442,109 @@ export class TreeView extends React.Component<TreeViewProps> {
                     return added;
                 };
                 const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
-                contentElement = TreeView.GetChildElements(contents instanceof Doc ? [contents] : DocListCast(contents),
-                    this.props.treeView, this, doc, undefined, this.props.containerCollection, this.props.prevSibling, addDoc, remDoc, this.move,
-                    this.props.dropAction, this.props.addDocTab, this.titleStyleProvider, this.props.ScreenToLocalTransform, this.props.isContentActive,
-                    this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields,
-                    [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenChildContentsActiveChanged,
-                    this.props.dontRegisterView, emptyFunction, emptyFunction, this.childContextMenuItems(),
+                contentElement = TreeView.GetChildElements(
+                    contents instanceof Doc ? [contents] : DocListCast(contents),
+                    this.props.treeView,
+                    this,
+                    doc,
+                    undefined,
+                    this.props.containerCollection,
+                    this.props.prevSibling,
+                    addDoc,
+                    remDoc,
+                    this.move,
+                    this.props.dropAction,
+                    this.props.addDocTab,
+                    this.titleStyleProvider,
+                    this.props.ScreenToLocalTransform,
+                    this.props.isContentActive,
+                    this.props.panelWidth,
+                    this.props.renderDepth,
+                    this.props.treeViewHideHeaderFields,
+                    [...this.props.renderedIds, doc[Id]],
+                    this.props.onCheckedClick,
+                    this.props.onChildClick,
+                    this.props.skipFields,
+                    false,
+                    this.props.whenChildContentsActiveChanged,
+                    this.props.dontRegisterView,
+                    emptyFunction,
+                    emptyFunction,
+                    this.childContextMenuItems(),
                     // TODO: [AL] Add these
                     this.props.AddToMap,
                     this.props.RemFromMap,
                     this.props.hierarchyIndex
                 );
             } else {
-                contentElement = <EditableView key="editableView"
-                    contents={contents !== undefined ? Field.toString(contents as Field) : "null"}
-                    height={13}
-                    fontSize={12}
-                    GetValue={() => Field.toKeyValueString(doc, key)}
-                    SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)} />;
+                contentElement = (
+                    <EditableView
+                        key="editableView"
+                        contents={contents !== undefined ? Field.toString(contents as Field) : 'null'}
+                        height={13}
+                        fontSize={12}
+                        GetValue={() => Field.toKeyValueString(doc, key)}
+                        SetValue={(value: string) => KeyValueBox.SetField(doc, key, value, true)}
+                    />
+                );
             }
-            rows.push(<div style={{ display: "flex" }} key={key}>
-                <span style={{ fontWeight: "bold" }}>{key + ":"}</span>
-                &nbsp;
-                {contentElement}
-            </div>);
+            rows.push(
+                <div style={{ display: 'flex' }} key={key}>
+                    <span style={{ fontWeight: 'bold' }}>{key + ':'}</span>
+                    &nbsp;
+                    {contentElement}
+                </div>
+            );
         }
-        rows.push(<div style={{ display: "flex" }} key={"newKeyValue"}>
-            <EditableView
-                key="editableView"
-                contents={"+key:value"}
-                height={13}
-                fontSize={12}
-                GetValue={returnEmptyString}
-                SetValue={value => value.indexOf(":") !== -1 && KeyValueBox.SetField(doc, value.substring(0, value.indexOf(":")), value.substring(value.indexOf(":") + 1, value.length), true)} />
-        </div>);
+        rows.push(
+            <div style={{ display: 'flex' }} key={'newKeyValue'}>
+                <EditableView
+                    key="editableView"
+                    contents={'+key:value'}
+                    height={13}
+                    fontSize={12}
+                    GetValue={returnEmptyString}
+                    SetValue={value => value.indexOf(':') !== -1 && KeyValueBox.SetField(doc, value.substring(0, value.indexOf(':')), value.substring(value.indexOf(':') + 1, value.length), true)}
+                />
+            </div>
+        );
         return rows;
     }
 
     rtfWidth = () => {
-        const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ""))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
-        return Math.min(layout[WidthSym](), (this.props.panelWidth() - treeBulletWidth())) / (this.props.treeView.props.scaling?.() || 1);
-    }
+        const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ''))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
+        return Math.min(layout[WidthSym](), this.props.panelWidth() - treeBulletWidth()) / (this.props.treeView.props.scaling?.() || 1);
+    };
     rtfHeight = () => {
-        const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ""))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
+        const layout = (temp => temp && Doc.expandTemplateLayout(temp, this.props.document, ''))(this.props.treeView.props.childLayoutTemplate?.()) || this.layoutDoc;
         return this.rtfWidth() <= layout[WidthSym]() ? Math.min(layout[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
-    }
+    };
     rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), treeBulletWidth());
     expandPanelHeight = () => {
         if (this.layoutDoc._fitWidth) return this.docHeight();
         const aspect = this.layoutDoc[WidthSym]() / this.layoutDoc[HeightSym]();
         const docAspect = this.docWidth() / this.docHeight();
-        return (docAspect < aspect) ? this.docWidth() / aspect : this.docHeight();
-    }
+        return docAspect < aspect ? this.docWidth() / aspect : this.docHeight();
+    };
     expandPanelWidth = () => {
         if (this.layoutDoc._fitWidth) return this.docWidth();
         const aspect = this.layoutDoc[WidthSym]() / this.layoutDoc[HeightSym]();
         const docAspect = this.docWidth() / this.docHeight();
-        return (docAspect > aspect) ? this.docHeight() * aspect : this.docWidth();
-    }
+        return docAspect > aspect ? this.docHeight() * aspect : this.docWidth();
+    };
 
     @computed get renderContent() {
         TraceMobx();
         const expandKey = this.treeViewExpandedView;
-        const sortings = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string, label: string } };
-        if (["links", "annotations", "aliases", this.fieldKey].includes(expandKey)) {
+        const sortings = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string; label: string } };
+        if (['links', 'annotations', 'aliases', this.fieldKey].includes(expandKey)) {
             const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None);
             const sortKeys = Object.keys(sortings);
-            const curSortIndex = Math.max(0, sortKeys.findIndex(val => val === sorting));
-            const key = (expandKey === "annotations" ? `${this.fieldKey}-` : "") + expandKey;
+            const curSortIndex = Math.max(
+                0,
+                sortKeys.findIndex(val => val === sorting)
+            );
+            const key = (expandKey === 'annotations' ? `${this.fieldKey}-` : '') + expandKey;
             const remDoc = (doc: Doc | Doc[]) => this.remove(doc, key);
             const localAdd = (doc: Doc, addBefore?: Doc, before?: boolean) => {
                 // if there's a sort ordering specified that can be modified on drop (eg, zorder can be modified, alphabetical can't),
@@ -461,107 +554,161 @@ export class TreeView extends React.Component<TreeViewProps> {
                     const docs = TreeView.sortDocs(this.childDocs || ([] as Doc[]), ordering);
                     doc.zIndex = addBefore ? NumCast(addBefore.zIndex) + (before ? -0.5 : 0.5) : 1000;
                     docs.push(doc);
-                    docs.sort((a, b) => NumCast(a.zIndex) > NumCast(b.zIndex) ? 1 : -1).forEach((d, i) => d.zIndex = i);
+                    docs.sort((a, b) => (NumCast(a.zIndex) > NumCast(b.zIndex) ? 1 : -1)).forEach((d, i) => (d.zIndex = i));
                 }
                 const added = Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
                 added && (doc.context = this.doc.context);
                 return added;
             };
             const addDoc = (doc: Doc | Doc[], addBefore?: Doc, before?: boolean) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && localAdd(doc, addBefore, before), true);
-            const docs = expandKey === "aliases" ? this.childAliases : expandKey === "links" ? this.childLinks : expandKey === "annotations" ? this.childAnnos : this.childDocs;
-            let downX = 0, downY = 0;
-            return <>
-                {!docs?.length || this.props.AddToMap /* hack to identify pres box trees */ ? (null) : <div className={'treeView-sorting'} style={{ background: sortings[sorting]?.color }} >
-                    {sortings[sorting]?.label}
-                </div>}
-                <ul key={expandKey + "more"} title="click to change sort order" className={this.doc.treeViewHideTitle ? "no-indent" : ""}
-                    onPointerDown={e => { downX = e.clientX; downY = e.clientY; e.stopPropagation(); }}
-                    onClick={(e) => {
-                        if (this.props.isContentActive() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) {
-                            !this.props.treeView.outlineMode && (this.doc.treeViewSortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]);
+            const docs = expandKey === 'aliases' ? this.childAliases : expandKey === 'links' ? this.childLinks : expandKey === 'annotations' ? this.childAnnos : this.childDocs;
+            let downX = 0,
+                downY = 0;
+            return (
+                <>
+                    {!docs?.length || this.props.AddToMap /* hack to identify pres box trees */ ? null : (
+                        <div className={'treeView-sorting'} style={{ background: sortings[sorting]?.color }}>
+                            {sortings[sorting]?.label}
+                        </div>
+                    )}
+                    <ul
+                        key={expandKey + 'more'}
+                        title="click to change sort order"
+                        className={this.doc.treeViewHideTitle ? 'no-indent' : ''}
+                        onPointerDown={e => {
+                            downX = e.clientX;
+                            downY = e.clientY;
                             e.stopPropagation();
-                        }
-                    }}>
-                    {!docs ? (null) :
-                        TreeView.GetChildElements(docs, this.props.treeView, this, this.layoutDoc,
-                            this.dataDoc, this.props.containerCollection, this.props.prevSibling, addDoc, remDoc, this.move,
-                            StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.titleStyleProvider, this.props.ScreenToLocalTransform,
-                            this.props.isContentActive, this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields,
-                            [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenChildContentsActiveChanged,
-                            this.props.dontRegisterView, emptyFunction, emptyFunction, this.childContextMenuItems(),
-                            // TODO: [AL] add these
-                            this.props.AddToMap,
-                            this.props.RemFromMap,
-                            this.props.hierarchyIndex)}
-                </ul >
-            </>;
-        } else if (this.treeViewExpandedView === "fields") {
-            return <ul key={this.doc[Id] + this.doc.title}>
-                <div style={{ display: "inline-block" }} >
-                    {this.expandedField}
-                </div>
-            </ul>;
+                        }}
+                        onClick={e => {
+                            if (this.props.isContentActive() && Math.abs(e.clientX - downX) < 3 && Math.abs(e.clientY - downY) < 3) {
+                                !this.props.treeView.outlineMode && (this.doc.treeViewSortCriterion = sortKeys[(curSortIndex + 1) % sortKeys.length]);
+                                e.stopPropagation();
+                            }
+                        }}>
+                        {!docs
+                            ? null
+                            : TreeView.GetChildElements(
+                                  docs,
+                                  this.props.treeView,
+                                  this,
+                                  this.layoutDoc,
+                                  this.dataDoc,
+                                  this.props.containerCollection,
+                                  this.props.prevSibling,
+                                  addDoc,
+                                  remDoc,
+                                  this.move,
+                                  StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType,
+                                  this.props.addDocTab,
+                                  this.titleStyleProvider,
+                                  this.props.ScreenToLocalTransform,
+                                  this.props.isContentActive,
+                                  this.props.panelWidth,
+                                  this.props.renderDepth,
+                                  this.props.treeViewHideHeaderFields,
+                                  [...this.props.renderedIds, this.doc[Id]],
+                                  this.props.onCheckedClick,
+                                  this.props.onChildClick,
+                                  this.props.skipFields,
+                                  false,
+                                  this.props.whenChildContentsActiveChanged,
+                                  this.props.dontRegisterView,
+                                  emptyFunction,
+                                  emptyFunction,
+                                  this.childContextMenuItems(),
+                                  // TODO: [AL] add these
+                                  this.props.AddToMap,
+                                  this.props.RemFromMap,
+                                  this.props.hierarchyIndex
+                              )}
+                    </ul>
+                </>
+            );
+        } else if (this.treeViewExpandedView === 'fields') {
+            return (
+                <ul key={this.doc[Id] + this.doc.title}>
+                    <div style={{ display: 'inline-block' }}>{this.expandedField}</div>
+                </ul>
+            );
         }
-        return <ul onPointerDown={e => { e.preventDefault(); e.stopPropagation(); }}>{this.renderEmbeddedDocument(false, this.props.treeView.props.childDocumentsActive ?? returnFalse)}</ul>; // "layout"
+        return (
+            <ul
+                onPointerDown={e => {
+                    e.preventDefault();
+                    e.stopPropagation();
+                }}>
+                {this.renderEmbeddedDocument(false, this.props.treeView.props.childDocumentsActive ?? returnFalse)}
+            </ul>
+        ); // "layout"
     }
 
-    get onCheckedClick() { return this.doc.type === DocumentType.COL ? undefined : this.props.onCheckedClick?.() ?? ScriptCast(this.doc.onCheckedClick); }
+    get onCheckedClick() {
+        return this.doc.type === DocumentType.COL ? undefined : this.props.onCheckedClick?.() ?? ScriptCast(this.doc.onCheckedClick);
+    }
 
     @action
     bulletClick = (e: React.MouseEvent) => {
         if (this.onCheckedClick) {
-            this.onCheckedClick?.script.run({
-                this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc,
-                heading: this.props.containerCollection.title,
-                checked: this.doc.treeViewChecked === "check" ? "x" : this.doc.treeViewChecked === "x" ? "remove" : "check",
-                containingTreeView: this.props.treeView.props.Document,
-            }, console.log);
+            this.onCheckedClick?.script.run(
+                {
+                    this: this.doc.isTemplateForField && this.props.dataDoc ? this.props.dataDoc : this.doc,
+                    heading: this.props.containerCollection.title,
+                    checked: this.doc.treeViewChecked === 'check' ? 'x' : this.doc.treeViewChecked === 'x' ? 'remove' : 'check',
+                    containingTreeView: this.props.treeView.props.Document,
+                },
+                console.log
+            );
         } else {
             this.treeViewOpen = !this.treeViewOpen;
         }
         e.stopPropagation();
-    }
+    };
 
     @computed get renderBullet() {
         TraceMobx();
-        const iconType = this.props.treeView.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewIcon + (this.treeViewOpen ? ":open" : "")) || "question";
-        const checked = this.onCheckedClick ? (this.doc.treeViewChecked ?? "unchecked") : undefined;
-        return <div className={`bullet${this.props.treeView.outlineMode ? "-outline" : ""}`} key={"bullet"}
-            title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"}
-            onClick={this.bulletClick}
-            style={this.props.treeView.outlineMode ?
-                {
-                    opacity: this.titleStyleProvider?.(this.doc, this.props.treeView.props, StyleProp.Opacity)
-                } :
-                {
-                    pointerEvents: this.props.isContentActive() ? "all" : undefined,
-                    opacity: checked === "unchecked" || typeof iconType !== "string" ? undefined : 0.4,
-                    color: StrCast(this.doc.color, checked === "unchecked" ? "white" : "inherit"),
-                }}>
-            {this.props.treeView.outlineMode ?
-                !(this.doc.text as RichTextField)?.Text ? (null) :
-                    <FontAwesomeIcon size="sm" icon={[this.childDocs?.length && !this.treeViewOpen ? "fas" : "far", "circle"]} /> :
-                <div className="treeView-bulletIcons" >
-                    <div className={`treeView-${this.onCheckedClick ? "checkIcon" : "expandIcon"}`}>
-                        <FontAwesomeIcon size="sm" icon={
-                            checked === "check" ? "check" :
-                                checked === "x" ? "times" :
-                                    checked === "unchecked" ? "square" :
-                                        !this.treeViewOpen ? "caret-right" : "caret-down"} />
+        const iconType = this.props.treeView.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewIcon + (this.treeViewOpen ? ':open' : '')) || 'question';
+        const checked = this.onCheckedClick ? this.doc.treeViewChecked ?? 'unchecked' : undefined;
+        return (
+            <div
+                className={`bullet${this.props.treeView.outlineMode ? '-outline' : ''}`}
+                key={'bullet'}
+                title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : 'view fields'}
+                onClick={this.bulletClick}
+                style={
+                    this.props.treeView.outlineMode
+                        ? {
+                              opacity: this.titleStyleProvider?.(this.doc, this.props.treeView.props, StyleProp.Opacity),
+                          }
+                        : {
+                              pointerEvents: this.props.isContentActive() ? 'all' : undefined,
+                              opacity: checked === 'unchecked' || typeof iconType !== 'string' ? undefined : 0.4,
+                              color: StrCast(this.doc.color, checked === 'unchecked' ? 'white' : 'inherit'),
+                          }
+                }>
+                {this.props.treeView.outlineMode ? (
+                    !(this.doc.text as RichTextField)?.Text ? null : (
+                        <FontAwesomeIcon size="sm" icon={[this.childDocs?.length && !this.treeViewOpen ? 'fas' : 'far', 'circle']} />
+                    )
+                ) : (
+                    <div className="treeView-bulletIcons">
+                        <div className={`treeView-${this.onCheckedClick ? 'checkIcon' : 'expandIcon'}`}>
+                            <FontAwesomeIcon size="sm" icon={checked === 'check' ? 'check' : checked === 'x' ? 'times' : checked === 'unchecked' ? 'square' : !this.treeViewOpen ? 'caret-right' : 'caret-down'} />
+                        </div>
+                        {this.onCheckedClick ? null : typeof iconType === 'string' ? <FontAwesomeIcon icon={iconType as IconProp} /> : iconType}
                     </div>
-                    {this.onCheckedClick ? (null) : typeof iconType === "string" ? <FontAwesomeIcon icon={iconType as IconProp} /> : iconType}
-                </div>
-            }
-        </div>;
+                )}
+            </div>
+        );
     }
 
     @computed get validExpandViewTypes() {
-        const annos = () => DocListCast(this.doc[this.fieldKey + "-annotations"]).length && !this.props.treeView.dashboardMode ? "annotations" : "";
-        const links = () => DocListCast(this.doc.links).length && !this.props.treeView.dashboardMode ? "links" : "";
-        const data = () => this.childDocs || this.props.treeView.dashboardMode ? this.fieldKey : "";
-        const aliases = () => this.props.treeView.dashboardMode ? "" : "aliases";
-        const fields = () => Doc.noviceMode ? "" : "fields";
-        const layout = (Doc.noviceMode) || this.doc.viewType === CollectionViewType.Docking ? [] : ["layout"];
+        const annos = () => (DocListCast(this.doc[this.fieldKey + '-annotations']).length && !this.props.treeView.dashboardMode ? 'annotations' : '');
+        const links = () => (DocListCast(this.doc.links).length && !this.props.treeView.dashboardMode ? 'links' : '');
+        const data = () => (this.childDocs || this.props.treeView.dashboardMode ? this.fieldKey : '');
+        const aliases = () => (this.props.treeView.dashboardMode ? '' : 'aliases');
+        const fields = () => (Doc.noviceMode ? '' : 'fields');
+        const layout = Doc.noviceMode || this.doc.viewType === CollectionViewType.Docking ? [] : ['layout'];
         return [data(), ...layout, ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()].filter(m => m);
     }
     @action
@@ -571,49 +718,68 @@ export class TreeView extends React.Component<TreeViewProps> {
             this.doc.treeViewExpandedView = next(this.validExpandViewTypes);
         }
         this.treeViewOpen = true;
-    }
+    };
 
     @computed get headerElements() {
-        return this.props.treeViewHideHeaderFields() || this.doc.treeViewHideHeaderFields ? (null)
-            : <>
-                {this.doc.hideContextMenu ? (null) : <FontAwesomeIcon title="context menu" key="bars" icon="bars" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} />}
-                {this.doc.treeViewExpandedViewLock || Doc.IsSystem(this.doc) ? (null) :
+        return this.props.treeViewHideHeaderFields() || this.doc.treeViewHideHeaderFields ? null : (
+            <>
+                {this.doc.hideContextMenu ? null : (
+                    <FontAwesomeIcon
+                        title="context menu"
+                        key="bars"
+                        icon="bars"
+                        size="sm"
+                        onClick={e => {
+                            this.showContextMenu(e);
+                            e.stopPropagation();
+                        }}
+                    />
+                )}
+                {this.doc.treeViewExpandedViewLock || Doc.IsSystem(this.doc) ? null : (
                     <span className="collectionTreeView-keyHeader" title="type of expanded data" key={this.treeViewExpandedView} onPointerDown={this.expandNextviewType}>
                         {this.treeViewExpandedView}
-                    </span>}
-            </>;
+                    </span>
+                )}
+            </>
+        );
     }
 
     showContextMenu = (e: React.MouseEvent) => {
         DocumentViewInternal.SelectAfterContextMenu = false;
         simulateMouseClick(this._docRef?.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30);
         DocumentViewInternal.SelectAfterContextMenu = true;
-    }
+    };
     contextMenuItems = () => {
-        const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: "any" })!, icon: "folder-plus", label: "New Folder" };
-        const deleteItem = { script: ScriptField.MakeFunction(`scriptContext.deleteItem()`, { scriptContext: "any" })!, icon: "folder-plus", label: "Delete" };
+        const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'New Folder' };
+        const deleteItem = { script: ScriptField.MakeFunction(`scriptContext.deleteItem()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'Delete' };
         const folderOp = this.childDocs?.length ? [makeFolder] : [];
-        const openAlias = { script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, icon: "copy", label: "Open Alias" };
-        const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: "eye", label: "Focus or Open" };
-        return [...this.props.contextMenuItems.filter(mi => !mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result), ... (this.doc.isFolder ? folderOp :
-            Doc.IsSystem(this.doc) ? [] :
-                this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc) ?
-                    [openAlias, makeFolder] :
-                    this.doc.viewType === CollectionViewType.Docking ? [] :
-                        [deleteItem, openAlias, focusDoc])];
-    }
+        const openAlias = { script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, icon: 'copy', label: 'Open Alias' };
+        const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: 'eye', label: 'Focus or Open' };
+        return [
+            ...this.props.contextMenuItems.filter(mi => (!mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result)),
+            ...(this.doc.isFolder
+                ? folderOp
+                : Doc.IsSystem(this.doc)
+                ? []
+                : this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc)
+                ? [openAlias, makeFolder]
+                : this.doc.viewType === CollectionViewType.Docking
+                ? []
+                : [deleteItem, openAlias, focusDoc]),
+        ];
+    };
     childContextMenuItems = () => {
         const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []);
         const customFilters = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []);
         const icons = StrListCast(this.doc.childContextMenuIcons);
         return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
-    }
+    };
 
     onChildClick = () => {
         return this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!);
-    }
+    };
 
-    onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeViewChildDoubleClick,(!this.props.treeView.outlineMode ? this._openScript?.():null));
+    onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeViewChildDoubleClick, !this.props.treeView.outlineMode ? this._openScript?.() : null);
 
     refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document);
     ignoreEvent = (e: any) => {
@@ -621,51 +787,59 @@ export class TreeView extends React.Component<TreeViewProps> {
             e.stopPropagation();
             e.preventDefault();
         }
-    }
-    titleStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => {
+    };
+    titleStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
         if (!doc || doc !== this.doc) return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
 
-        switch (property.split(":")[0]) {
-            case StyleProp.Opacity: return this.props.treeView.outlineMode ? undefined : 1;
-            case StyleProp.BackgroundColor: return this.selected ? "#7089bb" : StrCast(doc._backgroundColor, StrCast(doc.backgroundColor));
-            case StyleProp.DocContents: return this.props.treeView.outlineMode ? (null) :
-                <div className="treeView-label" style={{    // just render a title for a tree view label (identified by treeViewDoc being set in 'props')
-                    maxWidth: props?.PanelWidth() || undefined,
-                    background: props?.styleProvider?.(doc, props, StyleProp.BackgroundColor),
-                }}>
-                    {StrCast(doc?.title)}
-                </div>;
-            default: return this.props?.treeView?.props.styleProvider?.(doc, props, property);
+        switch (property.split(':')[0]) {
+            case StyleProp.Opacity:
+                return this.props.treeView.outlineMode ? undefined : 1;
+            case StyleProp.BackgroundColor:
+                return this.selected ? '#7089bb' : StrCast(doc._backgroundColor, StrCast(doc.backgroundColor));
+            case StyleProp.DocContents:
+                return this.props.treeView.outlineMode ? null : (
+                    <div
+                        className="treeView-label"
+                        style={{
+                            // just render a title for a tree view label (identified by treeViewDoc being set in 'props')
+                            maxWidth: props?.PanelWidth() || undefined,
+                            background: props?.styleProvider?.(doc, props, StyleProp.BackgroundColor),
+                        }}>
+                        {StrCast(doc?.title)}
+                    </div>
+                );
+            default:
+                return this.props?.treeView?.props.styleProvider?.(doc, props, property);
         }
-    }
-    embeddedStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => {
-        if (property.startsWith(StyleProp.Decorations)) return (null);
+    };
+    embeddedStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
+        if (property.startsWith(StyleProp.Decorations)) return null;
         return this.props?.treeView?.props.styleProvider?.(doc, props, property); // properties are inherited from the CollectionTreeView, not the hierarchical parent in the treeView
-    }
+    };
     onKeyDown = (e: React.KeyboardEvent, fieldProps: FieldViewProps) => {
         if (this.doc.treeViewHideHeader || (this.doc.treeViewHideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode) {
             switch (e.key) {
-                case "Tab":
+                case 'Tab':
                     e.stopPropagation?.();
                     e.preventDefault?.();
                     setTimeout(() => RichTextMenu.Instance.TextView?.EditorView?.focus(), 150);
-                    UndoManager.RunInBatch(() => e.shiftKey ? this.props.outdentDocument?.(true) : this.props.indentDocument?.(true), "tab");
+                    UndoManager.RunInBatch(() => (e.shiftKey ? this.props.outdentDocument?.(true) : this.props.indentDocument?.(true)), 'tab');
                     return true;
-                case "Backspace":
+                case 'Backspace':
                     if (!(this.doc.text as RichTextField)?.Text && this.props.removeDoc?.(this.doc)) {
                         e.stopPropagation?.();
                         e.preventDefault?.();
                         return true;
                     }
                     break;
-                case "Enter":
+                case 'Enter':
                     e.stopPropagation?.();
                     e.preventDefault?.();
-                    return UndoManager.RunInBatch(this.makeTextCollection, "bullet");
+                    return UndoManager.RunInBatch(this.makeTextCollection, 'bullet');
             }
         }
         return false;
-    }
+    };
     titleWidth = () => Math.max(20, Math.min(this.props.treeView.truncateTitleWidth(), this.props.panelWidth() - 2 * treeBulletWidth()));
 
     return18 = () => 18;
@@ -675,28 +849,32 @@ export class TreeView extends React.Component<TreeViewProps> {
     @computed
     get renderTitle() {
         TraceMobx();
-        const view = this._editTitle ? <EditableView key="_editTitle"
-            oneLine={true}
-            display={"inline-block"}
-            editing={this._editTitle}
-            background={"#7089bb"}
-            contents={StrCast(this.doc.title)}
-            height={12}
-            sizeToContent={true}
-            fontSize={12}
-            GetValue={() => StrCast(this.doc.title)}
-            OnTab={undoBatch((shift?: boolean) => {
-                if (!shift) this.props.indentDocument?.(true);
-                else this.props.outdentDocument?.(true);
-            })}
-            OnEmpty={undoBatch(() => this.props.treeView.outlineMode && this.props.removeDoc?.(this.doc))}
-            OnFillDown={val => this.props.treeView.fileSysMode && this.makeFolder()}
-            SetValue={undoBatch((value: string, shiftKey: boolean, enterKey: boolean) => {
-                Doc.SetInPlace(this.doc, "title", value, false);
-                this.props.treeView.outlineMode && enterKey && this.makeTextCollection();
-            })}
-        />
-            : <DocumentView key="title"
+        const view = this._editTitle ? (
+            <EditableView
+                key="_editTitle"
+                oneLine={true}
+                display={'inline-block'}
+                editing={this._editTitle}
+                background={'#7089bb'}
+                contents={StrCast(this.doc.title)}
+                height={12}
+                sizeToContent={true}
+                fontSize={12}
+                GetValue={() => StrCast(this.doc.title)}
+                OnTab={undoBatch((shift?: boolean) => {
+                    if (!shift) this.props.indentDocument?.(true);
+                    else this.props.outdentDocument?.(true);
+                })}
+                OnEmpty={undoBatch(() => this.props.treeView.outlineMode && this.props.removeDoc?.(this.doc))}
+                OnFillDown={val => this.props.treeView.fileSysMode && this.makeFolder()}
+                SetValue={undoBatch((value: string, shiftKey: boolean, enterKey: boolean) => {
+                    Doc.SetInPlace(this.doc, 'title', value, false);
+                    this.props.treeView.outlineMode && enterKey && this.makeTextCollection();
+                })}
+            />
+        ) : (
+            <DocumentView
+                key="title"
                 ref={action((r: any) => {
                     this._docRef = r ? r : undefined;
                     if (this._docRef && TreeView._editTitleOnLoad?.id === this.props.document[Id] && TreeView._editTitleOnLoad.parent === this.props.parentTreeView) {
@@ -743,139 +921,161 @@ export class TreeView extends React.Component<TreeViewProps> {
                 ContainingCollectionView={undefined}
                 ContainingCollectionDoc={this.props.treeView.props.Document}
                 ContentScaling={returnOne}
-            />;
-
-        const buttons = this.props.styleProvider?.(this.doc, { ...this.props.treeView.props, ContainingCollectionDoc: this.props.parentTreeView?.doc }, StyleProp.Decorations + (Doc.IsSystem(this.props.containerCollection) ? ":afterHeader" : ""));
-        return <>
-            <div className={`docContainer${Doc.IsSystem(this.props.document) || this.props.document.isFolder ? "-system" : ""}`} ref={this._tref} title="click to edit title. Double Click or Drag to Open"
-                style={{
-                    fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? "bold" : undefined,
-                    textDecoration: Doc.GetT(this.doc, "title", "string", true) ? "underline" : undefined,
-                    outline: this.doc === CurrentUserUtils.ActiveDashboard ? "dashed 1px #06123232" : undefined,
-                    pointerEvents: !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined
-                }} >
-                {view}
-            </div >
-            <div className="treeView-rightButtons">
-                {buttons} {/* hide and lock buttons */}
-                {this.headerElements}
-            </div>
-        </>;
-    }
+            />
+        );
 
-    renderBulletHeader = (contents: JSX.Element, editing: boolean) => {
-        return <>
-            <div className={`treeView-header` + (editing ? "-editing" : "")} key="titleheader"
-                style={{ width: StrCast(this.doc.treeViewHeaderWidth, "max-content") }}
-                ref={this._header}
-                onClick={this.ignoreEvent}
-                onPointerDown={this.ignoreEvent}
-                onPointerEnter={this.onPointerEnter}
-                onPointerLeave={this.onPointerLeave}>
-                {contents}
-            </div>
-            {this.renderBorder}
-        </>;
+        const buttons = this.props.styleProvider?.(this.doc, { ...this.props.treeView.props, ContainingCollectionDoc: this.props.parentTreeView?.doc }, StyleProp.Decorations + (Doc.IsSystem(this.props.containerCollection) ? ':afterHeader' : ''));
+        return (
+            <>
+                <div
+                    className={`docContainer${Doc.IsSystem(this.props.document) || this.props.document.isFolder ? '-system' : ''}`}
+                    ref={this._tref}
+                    title="click to edit title. Double Click or Drag to Open"
+                    style={{
+                        fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? 'bold' : undefined,
+                        textDecoration: Doc.GetT(this.doc, 'title', 'string', true) ? 'underline' : undefined,
+                        outline: this.doc === Doc.ActiveDashboard ? 'dashed 1px #06123232' : undefined,
+                        pointerEvents: !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined,
+                    }}>
+                    {view}
+                </div>
+                <div className="treeView-rightButtons">
+                    {buttons} {/* hide and lock buttons */}
+                    {this.headerElements}
+                </div>
+            </>
+        );
     }
 
+    renderBulletHeader = (contents: JSX.Element, editing: boolean) => {
+        return (
+            <>
+                <div
+                    className={`treeView-header` + (editing ? '-editing' : '')}
+                    key="titleheader"
+                    style={{ width: StrCast(this.doc.treeViewHeaderWidth, 'max-content') }}
+                    ref={this._header}
+                    onClick={this.ignoreEvent}
+                    onPointerDown={this.ignoreEvent}
+                    onPointerEnter={this.onPointerEnter}
+                    onPointerLeave={this.onPointerLeave}>
+                    {contents}
+                </div>
+                {this.renderBorder}
+            </>
+        );
+    };
 
     renderEmbeddedDocument = (asText: boolean, isActive: () => boolean | undefined) => {
         const isExpandable = this.doc._treeViewGrowsHorizontally;
         const panelWidth = asText || isExpandable ? this.rtfWidth : this.expandPanelWidth;
         const panelHeight = asText ? this.rtfOutlineHeight : isExpandable ? this.rtfHeight : this.expandPanelHeight;
-        return <DocumentView key={this.doc[Id]} ref={action((r: DocumentView | null) => this._dref = r)}
-            Document={this.doc}
-            DataDoc={undefined}
-            PanelWidth={panelWidth}
-            PanelHeight={panelHeight}
-            NativeWidth={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfWidth : undefined}
-            NativeHeight={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfHeight : undefined}
-            LayoutTemplateString={asText ? FormattedTextBox.LayoutString("text") : undefined}
-            LayoutTemplate={this.props.treeView.props.childLayoutTemplate}
-            isContentActive={isActive}
-            isDocumentActive={isActive}
-            styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
-            hideTitle={asText}
-            fitContentsToBox={returnTrue}
-            hideDecorationTitle={this.props.treeView.outlineMode}
-            hideResizeHandles={this.props.treeView.outlineMode}
-            onClick={this.onChildClick}
-            focus={this.refocus}
-            ContentScaling={returnOne}
-            onKey={this.onKeyDown}
-            hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)}
-            dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)}
-            ScreenToLocalTransform={this.docTransform}
-            renderDepth={this.props.renderDepth + 1}
-            treeViewDoc={this.props.treeView?.props.Document}
-            rootSelected={returnTrue}
-            docViewPath={this.props.treeView.props.docViewPath}
-            docFilters={returnEmptyFilter}
-            docRangeFilters={returnEmptyFilter}
-            searchFilterDocs={returnEmptyDoclist}
-            ContainingCollectionDoc={this.props.containerCollection}
-            ContainingCollectionView={undefined}
-            addDocument={this.props.addDocument}
-            moveDocument={this.move}
-            removeDocument={this.props.removeDoc}
-            whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
-            addDocTab={this.props.addDocTab}
-            pinToPres={this.props.treeView.props.pinToPres}
-            disableDocBrushing={this.props.treeView.props.disableDocBrushing}
-            bringToFront={returnFalse}
-        />;
-    }
+        return (
+            <DocumentView
+                key={this.doc[Id]}
+                ref={action((r: DocumentView | null) => (this._dref = r))}
+                Document={this.doc}
+                DataDoc={undefined}
+                PanelWidth={panelWidth}
+                PanelHeight={panelHeight}
+                NativeWidth={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfWidth : undefined}
+                NativeHeight={!asText && (this.layoutDoc.type === DocumentType.RTF || this.layoutDoc.type === DocumentType.SLIDER) ? this.rtfHeight : undefined}
+                LayoutTemplateString={asText ? FormattedTextBox.LayoutString('text') : undefined}
+                LayoutTemplate={this.props.treeView.props.childLayoutTemplate}
+                isContentActive={isActive}
+                isDocumentActive={isActive}
+                styleProvider={asText ? this.titleStyleProvider : this.embeddedStyleProvider}
+                hideTitle={asText}
+                fitContentsToBox={returnTrue}
+                hideDecorationTitle={this.props.treeView.outlineMode}
+                hideResizeHandles={this.props.treeView.outlineMode}
+                onClick={this.onChildClick}
+                focus={this.refocus}
+                ContentScaling={returnOne}
+                onKey={this.onKeyDown}
+                hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)}
+                dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)}
+                ScreenToLocalTransform={this.docTransform}
+                renderDepth={this.props.renderDepth + 1}
+                treeViewDoc={this.props.treeView?.props.Document}
+                rootSelected={returnTrue}
+                docViewPath={this.props.treeView.props.docViewPath}
+                docFilters={returnEmptyFilter}
+                docRangeFilters={returnEmptyFilter}
+                searchFilterDocs={returnEmptyDoclist}
+                ContainingCollectionDoc={this.props.containerCollection}
+                ContainingCollectionView={undefined}
+                addDocument={this.props.addDocument}
+                moveDocument={this.move}
+                removeDocument={this.props.removeDoc}
+                whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
+                addDocTab={this.props.addDocTab}
+                pinToPres={this.props.treeView.props.pinToPres}
+                disableDocBrushing={this.props.treeView.props.disableDocBrushing}
+                bringToFront={returnFalse}
+            />
+        );
+    };
 
     // renders the text version of a document as the header.  This is used in the file system mode and in other vanilla tree views.
     @computed get renderTitleAsHeader() {
-        return <>
-            {this.renderBullet}
-            {this.renderTitle}
-        </>;
+        return (
+            <>
+                {this.renderBullet}
+                {this.renderTitle}
+            </>
+        );
     }
 
     // renders the document in the header field instead of a text proxy.
     renderDocumentAsHeader = (asText: boolean) => {
-        return <>
-            {this.renderBullet}
-            {this.renderEmbeddedDocument(asText, this.props.isContentActive)}
-        </>;
-    }
+        return (
+            <>
+                {this.renderBullet}
+                {this.renderEmbeddedDocument(asText, this.props.isContentActive)}
+            </>
+        );
+    };
 
     @computed get renderBorder() {
         const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None);
-        const sortings = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string, label: string } };
-        return <div className={`treeView-border${this.props.treeView.outlineMode ? TreeViewType.outline : ""}`} style={{ borderColor: sortings[sorting]?.color }}>
-            {!this.treeViewOpen ? (null) : this.renderContent}
-        </div>;
+        const sortings = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string; label: string } };
+        return (
+            <div className={`treeView-border${this.props.treeView.outlineMode ? TreeViewType.outline : ''}`} style={{ borderColor: sortings[sorting]?.color }}>
+                {!this.treeViewOpen ? null : this.renderContent}
+            </div>
+        );
     }
 
     onTreeDrop = (de: React.DragEvent) => {
         const pt = [de.clientX, de.clientY];
         const rect = this._header.current!.getBoundingClientRect();
         const before = pt[1] < rect.top + rect.height / 2;
-        const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * .75) || (!before && this.treeViewOpen && this.childDocList.length);
-
-        const docs = this.props.treeView.onTreeDrop(de, (docs: Doc[]) => this.dropDocuments(docs, before, inside, "copy", undefined, false));
-    }
+        const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * 0.75) || (!before && this.treeViewOpen && this.childDocList.length);
 
+        const docs = this.props.treeView.onTreeDrop(de, (docs: Doc[]) => this.dropDocuments(docs, before, inside, 'copy', undefined, false));
+    };
 
     render() {
         TraceMobx();
         const hideTitle = this.doc.treeViewHideHeader || (this.doc.treeViewHideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode;
-        return this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? "<" + this.doc.title + ">" : // just print the title of documents we've previously rendered in this hierarchical path to avoid cycles
-            <div className={`treeView-container${this.props.isContentActive() ? "-active" : ""}`}
+        return this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? (
+            '<' + this.doc.title + '>' // just print the title of documents we've previously rendered in this hierarchical path to avoid cycles
+        ) : (
+            <div
+                className={`treeView-container${this.props.isContentActive() ? '-active' : ''}`}
                 ref={this.createTreeDropTarget}
                 onDrop={this.onTreeDrop}
-            //onPointerDown={e => this.props.isContentActive(true) && SelectionManager.DeselectAll()} // bcz: this breaks entering a text filter in a filterBox since it deselects the filter's target document
-            // onKeyDown={this.onKeyDown}
+                //onPointerDown={e => this.props.isContentActive(true) && SelectionManager.DeselectAll()} // bcz: this breaks entering a text filter in a filterBox since it deselects the filter's target document
+                // onKeyDown={this.onKeyDown}
             >
                 <li className="collection-child">
-                    {hideTitle && this.doc.type !== DocumentType.RTF && !this.doc.treeViewRenderAsBulletHeader ? // should test for prop 'treeViewRenderDocWithBulletAsHeader"
-                        this.renderEmbeddedDocument(false, returnFalse) :
-                        this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader(!this.doc.treeViewRenderAsBulletHeader) : this.renderTitleAsHeader, this._editTitle)}
+                    {hideTitle && this.doc.type !== DocumentType.RTF && !this.doc.treeViewRenderAsBulletHeader // should test for prop 'treeViewRenderDocWithBulletAsHeader"
+                        ? this.renderEmbeddedDocument(false, returnFalse)
+                        : this.renderBulletHeader(hideTitle ? this.renderDocumentAsHeader(!this.doc.treeViewRenderAsBulletHeader) : this.renderTitleAsHeader, this._editTitle)}
                 </li>
-            </div>;
+            </div>
+        );
     }
 
     public static sortDocs(childDocs: Doc[], criterion: string | undefined) {
@@ -883,9 +1083,10 @@ export class TreeView extends React.Component<TreeViewProps> {
         if (criterion !== TreeSort.None) {
             const sortAlphaNum = (a: string, b: string): 0 | 1 | -1 => {
                 const reN = /[0-9]*$/;
-                const aA = a.replace(reN, "") ? a.replace(reN, "") : +a; // get rid of trailing numbers
-                const bA = b.replace(reN, "") ? b.replace(reN, "") : +b;
-                if (aA === bA) {  // if header string matches, then compare numbers numerically
+                const aA = a.replace(reN, '') ? a.replace(reN, '') : +a; // get rid of trailing numbers
+                const bA = b.replace(reN, '') ? b.replace(reN, '') : +b;
+                if (aA === bA) {
+                    // if header string matches, then compare numbers numerically
                     const aN = parseInt(a.match(reN)![0], 10);
                     const bN = parseInt(b.match(reN)![0], 10);
                     return aN === bN ? 0 : aN > bN ? 1 : -1;
@@ -894,11 +1095,11 @@ export class TreeView extends React.Component<TreeViewProps> {
                 }
             };
             docs.sort(function (d1, d2): 0 | 1 | -1 {
-                const a = (criterion === TreeSort.Up ? d2 : d1);
-                const b = (criterion === TreeSort.Up ? d1 : d2);
-                const first = a[criterion === TreeSort.Zindex ? "zIndex" : "title"];
-                const second = b[criterion === TreeSort.Zindex ? "zIndex" : "title"];
-                if (typeof first === 'number' && typeof second === 'number') return (first - second) > 0 ? 1 : -1;
+                const a = criterion === TreeSort.Up ? d2 : d1;
+                const b = criterion === TreeSort.Up ? d1 : d2;
+                const first = a[criterion === TreeSort.Zindex ? 'zIndex' : 'title'];
+                const second = b[criterion === TreeSort.Zindex ? 'zIndex' : 'title'];
+                if (typeof first === 'number' && typeof second === 'number') return first - second > 0 ? 1 : -1;
                 if (typeof first === 'string' && typeof second === 'string') return sortAlphaNum(first, second);
                 return criterion ? 1 : -1;
             });
@@ -934,11 +1135,11 @@ export class TreeView extends React.Component<TreeViewProps> {
         dontRegisterView: boolean | undefined,
         observerHeight: (ref: any) => void,
         unobserveHeight: (ref: any) => void,
-        contextMenuItems: ({ script: ScriptField, filter: ScriptField, label: string, icon: string }[]),
+        contextMenuItems: { script: ScriptField; filter: ScriptField; label: string; icon: string }[],
         // TODO: [AL] add these
         AddToMap?: (treeViewDoc: Doc, index: number[]) => Doc[],
         RemFromMap?: (treeViewDoc: Doc, index: number[]) => Doc[],
-        hierarchyIndex?: number[],
+        hierarchyIndex?: number[]
     ) {
         const viewSpecScript = Cast(containerCollection.viewSpecScript, ScriptField);
         if (viewSpecScript) {
@@ -948,68 +1149,77 @@ export class TreeView extends React.Component<TreeViewProps> {
         const docs = TreeView.sortDocs(childDocs, StrCast(containerCollection.treeViewSortCriterion, TreeSort.None));
         const rowWidth = () => panelWidth() - treeBulletWidth();
         const treeViewRefs = new Map<Doc, TreeView | undefined>();
-        return docs.filter(child => child instanceof Doc).map((child, i) => {
-            const pair = Doc.GetLayoutDataDocPair(containerCollection, dataDoc, child);
-            if (!pair.layout || pair.data instanceof Promise) {
-                return (null);
-            }
-
-            const dentDoc = (editTitle: boolean, newParent: Doc, addAfter: Doc | undefined, parent: TreeView | CollectionTreeView | undefined) => {
-                if (parent instanceof TreeView && parent.props.treeView.fileSysMode && !newParent.isFolder) return;
-                const fieldKey = Doc.LayoutFieldKey(newParent);
-                if (remove && fieldKey && Cast(newParent[fieldKey], listSpec(Doc)) !== undefined) {
-                    remove(child);
-                    FormattedTextBox.SelectOnLoad = child[Id];
-                    TreeView._editTitleOnLoad = editTitle ? { id: child[Id], parent } : undefined;
-                    Doc.AddDocToList(newParent, fieldKey, child, addAfter, false);
-                    newParent.treeViewOpen = true;
-                    child.context = treeView.Document;
+        return docs
+            .filter(child => child instanceof Doc)
+            .map((child, i) => {
+                const pair = Doc.GetLayoutDataDocPair(containerCollection, dataDoc, child);
+                if (!pair.layout || pair.data instanceof Promise) {
+                    return null;
                 }
-            };
-            const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1]));
-            const outdent = parentCollectionDoc?._viewType !== CollectionViewType.Tree ? undefined : ((editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView.props.parentTreeView : undefined));
-            const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false);
-            const childLayout = Doc.Layout(pair.layout);
-            const rowHeight = () => {
-                const aspect = Doc.NativeAspect(childLayout);
-                return (aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]());
-            };
-            return <TreeView key={child[Id]} ref={r => treeViewRefs.set(child, r ? r : undefined)}
-                document={pair.layout}
-                dataDoc={pair.data}
-                containerCollection={containerCollection}
-                prevSibling={docs[i]}
-                // TODO: [AL] add these
-                hierarchyIndex={hierarchyIndex ? [...hierarchyIndex, i + 1] : undefined}
-                AddToMap={AddToMap}
-                RemFromMap={RemFromMap}
-                treeView={treeView}
-                indentDocument={indent}
-                outdentDocument={outdent}
-                onCheckedClick={onCheckedClick}
-                onChildClick={onChildClick}
-                renderDepth={renderDepth}
-                removeDoc={StrCast(containerCollection.freezeChildren).includes("remove") ? undefined : remove}
-                addDocument={addDocument}
-                styleProvider={styleProvider}
-                panelWidth={rowWidth}
-                panelHeight={rowHeight}
-                dontRegisterView={dontRegisterView}
-                moveDocument={move}
-                dropAction={dropAction}
-                addDocTab={addDocTab}
-                ScreenToLocalTransform={screenToLocalXf}
-                isContentActive={isContentActive}
-                treeViewHideHeaderFields={treeViewHideHeaderFields}
-                renderedIds={renderedIds}
-                skipFields={skipFields}
-                firstLevel={firstLevel}
-                whenChildContentsActiveChanged={whenChildContentsActiveChanged}
-                parentTreeView={parentTreeView}
-                observeHeight={observerHeight}
-                unobserveHeight={unobserveHeight}
-                contextMenuItems={contextMenuItems}
-            />;
-        });
+
+                const dentDoc = (editTitle: boolean, newParent: Doc, addAfter: Doc | undefined, parent: TreeView | CollectionTreeView | undefined) => {
+                    if (parent instanceof TreeView && parent.props.treeView.fileSysMode && !newParent.isFolder) return;
+                    const fieldKey = Doc.LayoutFieldKey(newParent);
+                    if (remove && fieldKey && Cast(newParent[fieldKey], listSpec(Doc)) !== undefined) {
+                        remove(child);
+                        FormattedTextBox.SelectOnLoad = child[Id];
+                        TreeView._editTitleOnLoad = editTitle ? { id: child[Id], parent } : undefined;
+                        Doc.AddDocToList(newParent, fieldKey, child, addAfter, false);
+                        newParent.treeViewOpen = true;
+                        child.context = treeView.Document;
+                    }
+                };
+                const indent = i === 0 ? undefined : (editTitle: boolean) => dentDoc(editTitle, docs[i - 1], undefined, treeViewRefs.get(docs[i - 1]));
+                const outdent =
+                    parentCollectionDoc?._viewType !== CollectionViewType.Tree
+                        ? undefined
+                        : (editTitle: boolean) => dentDoc(editTitle, parentCollectionDoc, containerPrevSibling, parentTreeView instanceof TreeView ? parentTreeView.props.parentTreeView : undefined);
+                const addDocument = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => add(doc, relativeTo ?? docs[i], before !== undefined ? before : false);
+                const childLayout = Doc.Layout(pair.layout);
+                const rowHeight = () => {
+                    const aspect = Doc.NativeAspect(childLayout);
+                    return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]();
+                };
+                return (
+                    <TreeView
+                        key={child[Id]}
+                        ref={r => treeViewRefs.set(child, r ? r : undefined)}
+                        document={pair.layout}
+                        dataDoc={pair.data}
+                        containerCollection={containerCollection}
+                        prevSibling={docs[i]}
+                        // TODO: [AL] add these
+                        hierarchyIndex={hierarchyIndex ? [...hierarchyIndex, i + 1] : undefined}
+                        AddToMap={AddToMap}
+                        RemFromMap={RemFromMap}
+                        treeView={treeView}
+                        indentDocument={indent}
+                        outdentDocument={outdent}
+                        onCheckedClick={onCheckedClick}
+                        onChildClick={onChildClick}
+                        renderDepth={renderDepth}
+                        removeDoc={StrCast(containerCollection.freezeChildren).includes('remove') ? undefined : remove}
+                        addDocument={addDocument}
+                        styleProvider={styleProvider}
+                        panelWidth={rowWidth}
+                        panelHeight={rowHeight}
+                        dontRegisterView={dontRegisterView}
+                        moveDocument={move}
+                        dropAction={dropAction}
+                        addDocTab={addDocTab}
+                        ScreenToLocalTransform={screenToLocalXf}
+                        isContentActive={isContentActive}
+                        treeViewHideHeaderFields={treeViewHideHeaderFields}
+                        renderedIds={renderedIds}
+                        skipFields={skipFields}
+                        firstLevel={firstLevel}
+                        whenChildContentsActiveChanged={whenChildContentsActiveChanged}
+                        parentTreeView={parentTreeView}
+                        observeHeight={observerHeight}
+                        unobserveHeight={unobserveHeight}
+                        contextMenuItems={contextMenuItems}
+                    />
+                );
+            });
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 9de2cfcf9..a0ebe4cdc 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -1,13 +1,12 @@
-import { Doc, Field, FieldResult, HeightSym, WidthSym } from "../../../../fields/Doc";
-import { Id, ToString } from "../../../../fields/FieldSymbols";
-import { ObjectField } from "../../../../fields/ObjectField";
-import { RefField } from "../../../../fields/RefField";
-import { listSpec } from "../../../../fields/Schema";
-import { Cast, NumCast, StrCast } from "../../../../fields/Types";
-import { aggregateBounds } from "../../../../Utils";
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import React = require("react");
-import { ColorScheme } from "../../../util/SettingsManager";
+import { Doc, Field, FieldResult, HeightSym, WidthSym } from '../../../../fields/Doc';
+import { Id, ToString } from '../../../../fields/FieldSymbols';
+import { ObjectField } from '../../../../fields/ObjectField';
+import { RefField } from '../../../../fields/RefField';
+import { listSpec } from '../../../../fields/Schema';
+import { Cast, NumCast, StrCast } from '../../../../fields/Types';
+import { aggregateBounds } from '../../../../Utils';
+import { ColorScheme } from '../../../util/SettingsManager';
+import React = require('react');
 
 export interface ViewDefBounds {
     type: string;
@@ -25,7 +24,7 @@ export interface ViewDefBounds {
     color?: string;
     opacity?: number;
     replica?: string;
-    pair?: { layout: Doc, data?: Doc };
+    pair?: { layout: Doc; data?: Doc };
 }
 
 export interface PoolData {
@@ -40,7 +39,7 @@ export interface PoolData {
     transition?: string;
     highlight?: boolean;
     replica: string;
-    pair: { layout: Doc, data?: Doc };
+    pair: { layout: Doc; data?: Doc };
 }
 
 export interface ViewDefResult {
@@ -48,7 +47,7 @@ export interface ViewDefResult {
     bounds?: ViewDefBounds;
 }
 function toLabel(target: FieldResult<Field>) {
-    if (typeof target === "number" || Number(target)) {
+    if (typeof target === 'number' || Number(target)) {
         const truncated = Number(Number(target).toFixed(0));
         const precise = Number(Number(target).toFixed(2));
         return truncated === precise ? Number(target).toFixed(0) : Number(target).toFixed(2);
@@ -60,16 +59,16 @@ function toLabel(target: FieldResult<Field>) {
 }
 /**
  * Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
- * 
+ *
  * @param {String} text The text to be rendered.
  * @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
- * 
+ *
  * @see https://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
  */
 function getTextWidth(text: string, font: string): number {
     // re-use canvas object for better performance
-    const canvas = (getTextWidth as any).canvas || ((getTextWidth as any).canvas = document.createElement("canvas"));
-    const context = canvas.getContext("2d");
+    const canvas = (getTextWidth as any).canvas || ((getTextWidth as any).canvas = document.createElement('canvas'));
+    const context = canvas.getContext('2d');
     context.font = font;
     const metrics = context.measureText(text);
     return metrics.width;
@@ -81,14 +80,7 @@ interface PivotColumn {
     filters: string[];
 }
 
-export function computerPassLayout(
-    poolData: Map<string, PoolData>,
-    pivotDoc: Doc,
-    childPairs: { layout: Doc, data?: Doc }[],
-    panelDim: number[],
-    viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
-    engineProps: any
-) {
+export function computerPassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
     const docMap = new Map<string, PoolData>();
     childPairs.forEach(({ layout, data }, i) => {
         docMap.set(layout[Id], {
@@ -97,60 +89,49 @@ export function computerPassLayout(
             width: layout[WidthSym](),
             height: layout[HeightSym](),
             pair: { layout, data },
-            replica: ""
+            replica: '',
         });
     });
     return normalizeResults(panelDim, 12, docMap, poolData, viewDefsToJSX, [], 0, []);
 }
 
-export function computerStarburstLayout(
-    poolData: Map<string, PoolData>,
-    pivotDoc: Doc,
-    childPairs: { layout: Doc, data?: Doc }[],
-    panelDim: number[],
-    viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
-    engineProps: any
-) {
+export function computerStarburstLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
     const mustFit = pivotDoc[WidthSym]() !== panelDim[0]; // if a panel size is set that's not the same as the pivot doc's size, then assume this is in a panel for a content fitting view (like a grid) in which case everything must be scaled to stay within the panel
     const docMap = new Map<string, PoolData>();
-    const docSize = mustFit ? panelDim[0] * .33 : 75; // assume an icon sized at 75
+    const docSize = mustFit ? panelDim[0] * 0.33 : 75; // assume an icon sized at 75
     const burstRadius = mustFit ? panelDim : [NumCast(pivotDoc._starburstRadius, panelDim[0]) - docSize, NumCast(pivotDoc._starburstRadius, panelDim[1]) - docSize];
     const scaleDim = [burstRadius[0] * 2 + docSize, burstRadius[1] * 2 + docSize];
     childPairs.forEach(({ layout, data }, i) => {
-        const docSize = layout.layoutKey === "layout_icon" ? (mustFit ? panelDim[0] * .33 : 75) : 400; // assume a icon sized at 75
-        const deg = i / childPairs.length * Math.PI * 2;
+        const docSize = layout.layoutKey === 'layout_icon' ? (mustFit ? panelDim[0] * 0.33 : 75) : 400; // assume a icon sized at 75
+        const deg = (i / childPairs.length) * Math.PI * 2;
         docMap.set(layout[Id], {
             x: Math.cos(deg) * burstRadius[0] - docSize / 2,
-            y: Math.sin(deg) * burstRadius[1] - docSize * layout[HeightSym]() / layout[WidthSym]() / 2,
-            width: docSize,//layout[WidthSym](),
-            height: docSize * layout[HeightSym]() / layout[WidthSym](),
+            y: Math.sin(deg) * burstRadius[1] - (docSize * layout[HeightSym]()) / layout[WidthSym]() / 2,
+            width: docSize, //layout[WidthSym](),
+            height: (docSize * layout[HeightSym]()) / layout[WidthSym](),
             zIndex: NumCast(layout.zIndex),
             pair: { layout, data },
-            replica: ""
+            replica: '',
         });
     });
-    const divider = { type: "div", color: "transparent", x: -burstRadius[0], y: 0, width: 15, height: 15, payload: undefined };
+    const divider = { type: 'div', color: 'transparent', x: -burstRadius[0], y: 0, width: 15, height: 15, payload: undefined };
     return normalizeResults(scaleDim, 12, docMap, poolData, viewDefsToJSX, [], 0, [divider]);
 }
 
-
-export function computePivotLayout(
-    poolData: Map<string, PoolData>,
-    pivotDoc: Doc,
-    childPairs: { layout: Doc, data?: Doc }[],
-    panelDim: number[],
-    viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
-    engineProps: any
-) {
+export function computePivotLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps: any) {
     const docMap = new Map<string, PoolData>();
-    const fieldKey = "data";
+    const fieldKey = 'data';
     const pivotColumnGroups = new Map<FieldResult<Field>, PivotColumn>();
 
     let nonNumbers = 0;
-    const pivotFieldKey = toLabel(engineProps?.pivotField ?? pivotDoc._pivotField) || "author";
+    const pivotFieldKey = toLabel(engineProps?.pivotField ?? pivotDoc._pivotField) || 'author';
     childPairs.map(pair => {
-        const lval = pivotFieldKey === "#" || pivotFieldKey === "tags" ? Array.from(Object.keys(Doc.GetProto(pair.layout))).filter(k => k.startsWith("#")).map(k => k.substring(1)) :
-            Cast(pair.layout[pivotFieldKey], listSpec("string"), null);
+        const lval =
+            pivotFieldKey === '#' || pivotFieldKey === 'tags'
+                ? Array.from(Object.keys(Doc.GetProto(pair.layout)))
+                      .filter(k => k.startsWith('#'))
+                      .map(k => k.substring(1))
+                : Cast(pair.layout[pivotFieldKey], listSpec('string'), null);
 
         const num = toNumber(pair.layout[pivotFieldKey]);
         if (num === undefined || Number.isNaN(num)) {
@@ -166,7 +147,7 @@ export function computePivotLayout(
         } else if (val) {
             !pivotColumnGroups.get(val) && pivotColumnGroups.set(val, { docs: [], filters: [val], replicas: [] });
             pivotColumnGroups.get(val)!.docs.push(pair.layout);
-            pivotColumnGroups.get(val)!.replicas.push("");
+            pivotColumnGroups.get(val)!.replicas.push('');
         } else {
             docMap.set(pair.layout[Id], {
                 x: 0,
@@ -175,11 +156,11 @@ export function computePivotLayout(
                 width: 0,
                 height: 0,
                 pair,
-                replica: ""
+                replica: '',
             });
         }
     });
-    const pivotNumbers = nonNumbers / childPairs.length < .1;
+    const pivotNumbers = nonNumbers / childPairs.length < 0.1;
     if (pivotColumnGroups.size > 10) {
         const arrayofKeys = Array.from(pivotColumnGroups.keys());
         const sortedKeys = pivotNumbers ? arrayofKeys.sort((n1: FieldResult, n2: FieldResult) => toNumber(n1)! - toNumber(n2)!) : arrayofKeys.sort();
@@ -196,9 +177,11 @@ export function computePivotLayout(
             }
         }
     }
-    const fontSize = NumCast(pivotDoc[fieldKey + "-timelineFontSize"], panelDim[1] > 58 ? 20 : Math.max(7, panelDim[1] / 3));
+    const fontSize = NumCast(pivotDoc[fieldKey + '-timelineFontSize'], panelDim[1] > 58 ? 20 : Math.max(7, panelDim[1] / 3));
     const desc = `${fontSize}px ${getComputedStyle(document.body).fontFamily}`;
-    const textlen = Array.from(pivotColumnGroups.keys()).map(c => getTextWidth(toLabel(c), desc)).reduce((p, c) => Math.max(p, c), 0 as number);
+    const textlen = Array.from(pivotColumnGroups.keys())
+        .map(c => getTextWidth(toLabel(c), desc))
+        .reduce((p, c) => Math.max(p, c), 0 as number);
     const max_text = Math.min(Math.ceil(textlen / 120) * 28, panelDim[1] / 2);
     const maxInColumn = Array.from(pivotColumnGroups.values()).reduce((p, s) => Math.max(p, s.docs.length), 1);
 
@@ -222,7 +205,7 @@ export function computePivotLayout(
     const groupNames: ViewDefBounds[] = [];
 
     const expander = 1.05;
-    const gap = .15;
+    const gap = 0.15;
     const maxColHeight = pivotAxisWidth * expander * Math.ceil(maxInColumn / numCols);
     let x = 0;
     const sortedPivotKeys = pivotNumbers ? Array.from(pivotColumnGroups.keys()).sort((n1: FieldResult, n2: FieldResult) => toNumber(n1)! - toNumber(n2)!) : Array.from(pivotColumnGroups.keys()).sort();
@@ -232,14 +215,14 @@ export function computePivotLayout(
         let xCount = 0;
         const text = toLabel(key);
         groupNames.push({
-            type: "text",
+            type: 'text',
             text,
             x,
             y: pivotAxisWidth,
             width: pivotAxisWidth * expander * numCols,
             height: max_text,
             fontSize,
-            payload: val
+            payload: val,
         });
         val.docs.forEach((doc, i) => {
             const layoutDoc = Doc.Layout(doc);
@@ -249,13 +232,13 @@ export function computePivotLayout(
                 hgt = pivotAxisWidth;
                 wid = (Doc.NativeAspect(layoutDoc) || 1) * pivotAxisWidth;
             }
-            docMap.set(doc[Id] + (val.replicas || ""), {
-                x: x + xCount * pivotAxisWidth * expander + (pivotAxisWidth - wid) / 2 + (val.docs.length < numCols ? (numCols - val.docs.length) * pivotAxisWidth / 2 : 0),
+            docMap.set(doc[Id] + (val.replicas || ''), {
+                x: x + xCount * pivotAxisWidth * expander + (pivotAxisWidth - wid) / 2 + (val.docs.length < numCols ? ((numCols - val.docs.length) * pivotAxisWidth) / 2 : 0),
                 y: -y + (pivotAxisWidth - hgt) / 2,
                 width: wid,
                 height: hgt,
                 pair: { layout: doc },
-                replica: val.replicas[i]
+                replica: val.replicas[i],
             });
             xCount++;
             if (xCount >= numCols) {
@@ -266,13 +249,14 @@ export function computePivotLayout(
         x += pivotAxisWidth * (numCols * expander + gap);
     });
 
-    const dividers = sortedPivotKeys.map((key, i) =>
-    ({
-        type: "div", color: "lightGray",
-        x: i * pivotAxisWidth * (numCols * expander + gap) - pivotAxisWidth * (expander - 1) / 2,
-        y: -maxColHeight + pivotAxisWidth, width: pivotAxisWidth * numCols * expander,
+    const dividers = sortedPivotKeys.map((key, i) => ({
+        type: 'div',
+        color: 'lightGray',
+        x: i * pivotAxisWidth * (numCols * expander + gap) - (pivotAxisWidth * (expander - 1)) / 2,
+        y: -maxColHeight + pivotAxisWidth,
+        width: pivotAxisWidth * numCols * expander,
         height: maxColHeight,
-        payload: pivotColumnGroups.get(key)!.filters
+        payload: pivotColumnGroups.get(key)!.filters,
     }));
     groupNames.push(...dividers);
     return normalizeResults(panelDim, max_text, docMap, poolData, viewDefsToJSX, groupNames, 0, []);
@@ -282,24 +266,17 @@ function toNumber(val: FieldResult<Field>) {
     return val === undefined ? undefined : NumCast(val, Number(StrCast(val)));
 }
 
-export function computeTimelineLayout(
-    poolData: Map<string, PoolData>,
-    pivotDoc: Doc,
-    childPairs: { layout: Doc, data?: Doc }[],
-    panelDim: number[],
-    viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[],
-    engineProps?: any
-) {
-    const fieldKey = "data";
+export function computeTimelineLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[], engineProps?: any) {
+    const fieldKey = 'data';
     const pivotDateGroups = new Map<number, Doc[]>();
     const docMap = new Map<string, PoolData>();
     const groupNames: ViewDefBounds[] = [];
     const timelineFieldKey = Field.toString(pivotDoc._pivotField as Field);
-    const curTime = toNumber(pivotDoc[fieldKey + "-timelineCur"]);
-    const curTimeSpan = Cast(pivotDoc[fieldKey + "-timelineSpan"], "number", null);
-    const minTimeReq = curTimeSpan === undefined ? Cast(pivotDoc[fieldKey + "-timelineMinReq"], "number", null) : curTime && (curTime - curTimeSpan);
-    const maxTimeReq = curTimeSpan === undefined ? Cast(pivotDoc[fieldKey + "-timelineMaxReq"], "number", null) : curTime && (curTime + curTimeSpan);
-    const fontSize = NumCast(pivotDoc[fieldKey + "-timelineFontSize"], panelDim[1] > 58 ? 20 : Math.max(7, panelDim[1] / 3));
+    const curTime = toNumber(pivotDoc[fieldKey + '-timelineCur']);
+    const curTimeSpan = Cast(pivotDoc[fieldKey + '-timelineSpan'], 'number', null);
+    const minTimeReq = curTimeSpan === undefined ? Cast(pivotDoc[fieldKey + '-timelineMinReq'], 'number', null) : curTime && curTime - curTimeSpan;
+    const maxTimeReq = curTimeSpan === undefined ? Cast(pivotDoc[fieldKey + '-timelineMaxReq'], 'number', null) : curTime && curTime + curTimeSpan;
+    const fontSize = NumCast(pivotDoc[fieldKey + '-timelineFontSize'], panelDim[1] > 58 ? 20 : Math.max(7, panelDim[1] / 3));
     const fontHeight = panelDim[1] > 58 ? 30 : panelDim[1] / 2;
     const findStack = (time: number, stack: number[]) => {
         const index = stack.findIndex(val => val === undefined || val < x);
@@ -325,8 +302,8 @@ export function computeTimelineLayout(
         }
     }
     setTimeout(() => {
-        pivotDoc[fieldKey + "-timelineMin"] = minTime = minTimeReq ? Math.min(minTimeReq, minTime) : minTime;
-        pivotDoc[fieldKey + "-timelineMax"] = maxTime = maxTimeReq ? Math.max(maxTimeReq, maxTime) : maxTime;
+        pivotDoc[fieldKey + '-timelineMin'] = minTime = minTimeReq ? Math.min(minTimeReq, minTime) : minTime;
+        pivotDoc[fieldKey + '-timelineMax'] = maxTime = maxTimeReq ? Math.max(maxTimeReq, maxTime) : maxTime;
     }, 0);
 
     if (maxTime === minTime) {
@@ -340,10 +317,10 @@ export function computeTimelineLayout(
     let prevKey = Math.floor(minTime);
 
     if (sortedKeys.length && scaling * (sortedKeys[0] - prevKey) > 25) {
-        groupNames.push({ type: "text", text: toLabel(prevKey), x: x, y: 0, height: fontHeight, fontSize, payload: undefined });
+        groupNames.push({ type: 'text', text: toLabel(prevKey), x: x, y: 0, height: fontHeight, fontSize, payload: undefined });
     }
     if (!sortedKeys.length && curTime !== undefined) {
-        groupNames.push({ type: "text", text: toLabel(curTime), x: (curTime - minTime) * scaling, zIndex: 1000, color: "orange", y: 0, height: fontHeight, fontSize, payload: undefined });
+        groupNames.push({ type: 'text', text: toLabel(curTime), x: (curTime - minTime) * scaling, zIndex: 1000, color: 'orange', y: 0, height: fontHeight, fontSize, payload: undefined });
     }
 
     const pivotAxisWidth = NumCast(pivotDoc.pivotTimeWidth, panelDim[1] / 2.5);
@@ -351,26 +328,26 @@ export function computeTimelineLayout(
     let zind = 0;
     sortedKeys.forEach(key => {
         if (curTime !== undefined && curTime > prevKey && curTime <= key) {
-            groupNames.push({ type: "text", text: toLabel(curTime), x: (curTime - minTime) * scaling, y: 0, zIndex: 1000, color: "orange", height: fontHeight, fontSize, payload: key });
+            groupNames.push({ type: 'text', text: toLabel(curTime), x: (curTime - minTime) * scaling, y: 0, zIndex: 1000, color: 'orange', height: fontHeight, fontSize, payload: key });
         }
         const keyDocs = pivotDateGroups.get(key)!;
         x += scaling * (key - prevKey);
         const stack = findStack(x, stacking);
         prevKey = key;
         if (!stack && (curTime === undefined || Math.abs(x - (curTime - minTime) * scaling) > pivotAxisWidth)) {
-            groupNames.push({ type: "text", text: toLabel(key), x: x, y: stack * 25, height: fontHeight, fontSize, payload: undefined });
+            groupNames.push({ type: 'text', text: toLabel(key), x: x, y: stack * 25, height: fontHeight, fontSize, payload: undefined });
         }
         layoutDocsAtTime(keyDocs, key);
     });
     if (sortedKeys.length && curTime !== undefined && curTime > sortedKeys[sortedKeys.length - 1]) {
         x = (curTime - minTime) * scaling;
-        groupNames.push({ type: "text", text: toLabel(curTime), x: x, y: 0, zIndex: 1000, color: "orange", height: fontHeight, fontSize, payload: undefined });
+        groupNames.push({ type: 'text', text: toLabel(curTime), x: x, y: 0, zIndex: 1000, color: 'orange', height: fontHeight, fontSize, payload: undefined });
     }
     if (Math.ceil(maxTime - minTime) * scaling > x + 25) {
-        groupNames.push({ type: "text", text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
+        groupNames.push({ type: 'text', text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
     }
 
-    const divider = { type: "div", color: CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? "dimgray" : "black", x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
+    const divider = { type: 'div', color: Doc.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'dimgray' : 'black', x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
     return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]);
 
     function layoutDocsAtTime(keyDocs: Doc[], key: number) {
@@ -384,13 +361,14 @@ export function computeTimelineLayout(
                 wid = (Doc.NativeAspect(layoutDoc) || 1) * pivotAxisWidth;
             }
             docMap.set(doc[Id], {
-                x: x, y: -Math.sqrt(stack) * pivotAxisWidth / 2 - pivotAxisWidth + (pivotAxisWidth - hgt) / 2,
-                zIndex: (curTime === key ? 1000 : zind++),
+                x: x,
+                y: (-Math.sqrt(stack) * pivotAxisWidth) / 2 - pivotAxisWidth + (pivotAxisWidth - hgt) / 2,
+                zIndex: curTime === key ? 1000 : zind++,
                 highlight: curTime === key,
-                width: wid / (Math.max(stack, 1)),
-                height: hgt / (Math.max(stack, 1)),
+                width: wid / Math.max(stack, 1),
+                height: hgt / Math.max(stack, 1),
                 pair: { layout: doc },
-                replica: ""
+                replica: '',
             });
             stacking[stack] = x + pivotAxisWidth;
         });
@@ -407,41 +385,49 @@ function normalizeResults(
     minWidth: number,
     extras: ViewDefBounds[]
 ): ViewDefResult[] {
-    const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height }) as ViewDefBounds);
+    const grpEles = groupNames.map(gn => ({ x: gn.x, y: gn.y, width: gn.width, height: gn.height } as ViewDefBounds));
     const docEles = Array.from(docMap.entries()).map(ele => ele[1]);
-    const aggBounds = aggregateBounds(extras.concat(grpEles.concat(docEles.map(de => ({ ...de, type: "doc", payload: "" })))).filter(e => e.zIndex !== -99), 0, 0);
+    const aggBounds = aggregateBounds(
+        extras.concat(grpEles.concat(docEles.map(de => ({ ...de, type: 'doc', payload: '' })))).filter(e => e.zIndex !== -99),
+        0,
+        0
+    );
     aggBounds.r = aggBounds.x + Math.max(minWidth, aggBounds.r - aggBounds.x);
     const wscale = panelDim[0] / (aggBounds.r - aggBounds.x);
-    let scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] ? (panelDim[1]) / (aggBounds.b - aggBounds.y) : wscale;
+    let scale = wscale * (aggBounds.b - aggBounds.y) > panelDim[1] ? panelDim[1] / (aggBounds.b - aggBounds.y) : wscale;
     if (Number.isNaN(scale)) scale = 1;
 
-    Array.from(docMap.entries()).filter(ele => ele[1].pair).map(ele => {
-        const newPosRaw = ele[1];
-        if (newPosRaw) {
-            const newPos = {
-                x: newPosRaw.x * scale,
-                y: newPosRaw.y * scale,
-                z: newPosRaw.z,
-                replica: newPosRaw.replica,
-                highlight: newPosRaw.highlight,
-                zIndex: newPosRaw.zIndex,
-                width: (newPosRaw.width || 0) * scale,
-                height: newPosRaw.height! * scale,
-                pair: ele[1].pair
-            };
-            poolData.set(newPos.pair.layout[Id] + (newPos.replica || ""), { transition: "all 1s", ...newPos });
-        }
-    });
+    Array.from(docMap.entries())
+        .filter(ele => ele[1].pair)
+        .map(ele => {
+            const newPosRaw = ele[1];
+            if (newPosRaw) {
+                const newPos = {
+                    x: newPosRaw.x * scale,
+                    y: newPosRaw.y * scale,
+                    z: newPosRaw.z,
+                    replica: newPosRaw.replica,
+                    highlight: newPosRaw.highlight,
+                    zIndex: newPosRaw.zIndex,
+                    width: (newPosRaw.width || 0) * scale,
+                    height: newPosRaw.height! * scale,
+                    pair: ele[1].pair,
+                };
+                poolData.set(newPos.pair.layout[Id] + (newPos.replica || ''), { transition: 'all 1s', ...newPos });
+            }
+        });
 
-    return viewDefsToJSX(extras.concat(groupNames).map(gname => ({
-        type: gname.type,
-        text: gname.text,
-        x: gname.x * scale,
-        y: gname.y * scale,
-        color: gname.color,
-        width: gname.width === undefined ? undefined : gname.width * scale,
-        height: gname.height === -1 ? 1 : gname.type === "text" ? Math.max(fontHeight * scale, (gname.height || 0) * scale) : (gname.height || 0) * scale,
-        fontSize: gname.fontSize,
-        payload: gname.payload
-    })));
+    return viewDefsToJSX(
+        extras.concat(groupNames).map(gname => ({
+            type: gname.type,
+            text: gname.text,
+            x: gname.x * scale,
+            y: gname.y * scale,
+            color: gname.color,
+            width: gname.width === undefined ? undefined : gname.width * scale,
+            height: gname.height === -1 ? 1 : gname.type === 'text' ? Math.max(fontHeight * scale, (gname.height || 0) * scale) : (gname.height || 0) * scale,
+            fontSize: gname.fontSize,
+            payload: gname.payload,
+        }))
+    );
 }
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 5f890c810..d979ef961 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -1,19 +1,18 @@
-import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, Field } from "../../../../fields/Doc";
-import { Id } from "../../../../fields/FieldSymbols";
-import { List } from "../../../../fields/List";
-import { Cast, NumCast } from "../../../../fields/Types";
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, Field } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { List } from '../../../../fields/List';
+import { Cast, NumCast } from '../../../../fields/Types';
 import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
-import { LinkManager } from "../../../util/LinkManager";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { SnappingManager } from "../../../util/SnappingManager";
-import { DocumentView } from "../../nodes/DocumentView";
-import "./CollectionFreeFormLinkView.scss";
-import React = require("react");
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { Colors } from "../../global/globalEnums";
-
+import { LinkManager } from '../../../util/LinkManager';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { SettingsManager } from '../../../util/SettingsManager';
+import { SnappingManager } from '../../../util/SnappingManager';
+import { Colors } from '../../global/globalEnums';
+import { DocumentView } from '../../nodes/DocumentView';
+import './CollectionFreeFormLinkView.scss';
+import React = require('react');
 
 export interface CollectionFreeFormLinkViewProps {
     A: DocumentView;
@@ -27,31 +26,41 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
     @observable _start = 0;
     _anchorDisposer: IReactionDisposer | undefined;
     _timeout: NodeJS.Timeout | undefined;
-    componentWillUnmount() { this._anchorDisposer?.(); }
-    @action timeout = action(() => (Date.now() < this._start++ + 1000) && (this._timeout = setTimeout(this.timeout, 25)));
+    componentWillUnmount() {
+        this._anchorDisposer?.();
+    }
+    @action timeout = action(() => Date.now() < this._start++ + 1000 && (this._timeout = setTimeout(this.timeout, 25)));
     componentDidMount() {
-        this._anchorDisposer = reaction(() => [
-            this.props.A.props.ScreenToLocalTransform(),
-            Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
-            Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?._highlights,
-            this.props.B.props.ScreenToLocalTransform(),
-            Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
-            Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?._highlights,
-        ],
+        this._anchorDisposer = reaction(
+            () => [
+                this.props.A.props.ScreenToLocalTransform(),
+                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
+                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor1, Doc, null)?.annotationOn, Doc, null)?._highlights,
+                this.props.B.props.ScreenToLocalTransform(),
+                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?.scrollTop,
+                Cast(Cast(Cast(this.props.A.rootDoc, Doc, null)?.anchor2, Doc, null)?.annotationOn, Doc, null)?._highlights,
+            ],
             action(() => {
                 this._start = Date.now();
                 this._timeout && clearTimeout(this._timeout);
                 this._timeout = setTimeout(this.timeout, 25);
                 setTimeout(this.placeAnchors);
-            })
-            , { fireImmediately: true });
+            }),
+            { fireImmediately: true }
+        );
     }
     placeAnchors = () => {
         const { A, B, LinkDocs } = this.props;
         const linkDoc = LinkDocs[0];
         if (SnappingManager.GetIsDragging() || !A.ContentDiv || !B.ContentDiv) return;
-        setTimeout(action(() => this._opacity = 0.75), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
-        setTimeout(action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line.
+        setTimeout(
+            action(() => (this._opacity = 0.75)),
+            0
+        ); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
+        setTimeout(
+            action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)),
+            750
+        ); // this will unhighlight the link line.
         const a = A.ContentDiv.getBoundingClientRect();
         const b = B.ContentDiv.getBoundingClientRect();
         const { left: aleft, top: atop, width: awidth, height: aheight } = A.ContentDiv.parentElement!.getBoundingClientRect();
@@ -60,7 +69,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         const bpt = Utils.closestPtBetweenRectangles(bleft, btop, bwidth, bheight, aleft, atop, awidth, aheight, apt.point.x, apt.point.y);
 
         // really hacky stuff to make the LinkAnchorBox display where we want it to:
-        //   if there's an element in the DOM with a classname containing a link anchor's id, 
+        //   if there's an element in the DOM with a classname containing a link anchor's id,
         //   then that DOM element is a hyperlink source for the current anchor and we want to place our link box at it's top right
         //   otherwise, we just use the computed nearest point on the document boundary to the target Document
         const targetAhyperlink = Array.from(window.document.getElementsByClassName((linkDoc.anchor1 as Doc)[Id])).lastElement();
@@ -68,8 +77,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         if ((!targetAhyperlink && !a.width) || (!targetBhyperlink && !b.width)) return;
         if (!targetAhyperlink) {
             if (linkDoc.linkAutoMove) {
-                linkDoc.anchor1_x = (apt.point.x - aleft) / awidth * 100;
-                linkDoc.anchor1_y = (apt.point.y - atop) / aheight * 100;
+                linkDoc.anchor1_x = ((apt.point.x - aleft) / awidth) * 100;
+                linkDoc.anchor1_y = ((apt.point.y - atop) / aheight) * 100;
             }
         } else {
             const m = targetAhyperlink.getBoundingClientRect();
@@ -78,13 +87,13 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
             const mpy = mp[1] / A.props.PanelHeight();
             if (mpx >= 0 && mpx <= 1) linkDoc.anchor1_x = mpx * 100;
             if (mpy >= 0 && mpy <= 1) linkDoc.anchor1_y = mpy * 100;
-            if (getComputedStyle(targetAhyperlink).fontSize === "0px") linkDoc.opacity = 0;
+            if (getComputedStyle(targetAhyperlink).fontSize === '0px') linkDoc.opacity = 0;
             else linkDoc.opacity = 1;
         }
         if (!targetBhyperlink) {
             if (linkDoc.linkAutoMove) {
-                linkDoc.anchor2_x = (bpt.point.x - bleft) / bwidth * 100;
-                linkDoc.anchor2_y = (bpt.point.y - btop) / bheight * 100;
+                linkDoc.anchor2_x = ((bpt.point.x - bleft) / bwidth) * 100;
+                linkDoc.anchor2_y = ((bpt.point.y - btop) / bheight) * 100;
             }
         } else {
             const m = targetBhyperlink.getBoundingClientRect();
@@ -93,80 +102,86 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
             const mpy = mp[1] / B.props.PanelHeight();
             if (mpx >= 0 && mpx <= 1) linkDoc.anchor2_x = mpx * 100;
             if (mpy >= 0 && mpy <= 1) linkDoc.anchor2_y = mpy * 100;
-            if (getComputedStyle(targetBhyperlink).fontSize === "0px") linkDoc.opacity = 0;
+            if (getComputedStyle(targetBhyperlink).fontSize === '0px') linkDoc.opacity = 0;
             else linkDoc.opacity = 1;
         }
-    }
-
+    };
 
     pointerDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, (e, down, delta) => {
-            this.props.LinkDocs[0].linkOffsetX = NumCast(this.props.LinkDocs[0].linkOffsetX) + delta[0];
-            this.props.LinkDocs[0].linkOffsetY = NumCast(this.props.LinkDocs[0].linkOffsetY) + delta[1];
-            return false;
-        }, emptyFunction, () => {
-            // OverlayView.Instance.addElement(
-            //     <LinkEditor sourceDoc={this.props.A.props.Document} linkDoc={this.props.LinkDocs[0]}
-            //         showLinks={action(() => { })}
-            //     />, { x: 300, y: 300 });
-        });
-
-
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            (e, down, delta) => {
+                this.props.LinkDocs[0].linkOffsetX = NumCast(this.props.LinkDocs[0].linkOffsetX) + delta[0];
+                this.props.LinkDocs[0].linkOffsetY = NumCast(this.props.LinkDocs[0].linkOffsetY) + delta[1];
+                return false;
+            },
+            emptyFunction,
+            () => {
+                // OverlayView.Instance.addElement(
+                //     <LinkEditor sourceDoc={this.props.A.props.Document} linkDoc={this.props.LinkDocs[0]}
+                //         showLinks={action(() => { })}
+                //     />, { x: 300, y: 300 });
+            }
+        );
+    };
 
     visibleY = (el: any) => {
         let rect = el.getBoundingClientRect();
-        const top = rect.top, height = rect.height;
+        const top = rect.top,
+            height = rect.height;
         var el = el.parentNode;
         while (el && el !== document.body) {
             rect = el.getBoundingClientRect?.();
             if (rect?.width) {
-                if (top <= rect.bottom === false && getComputedStyle(el).overflow === "hidden") return rect.bottom;
+                if (top <= rect.bottom === false && getComputedStyle(el).overflow === 'hidden') return rect.bottom;
                 // Check if the element is out of view due to a container scrolling
-                if ((top + height) <= rect.top && getComputedStyle(el).overflow === "hidden") return rect.top;
+                if (top + height <= rect.top && getComputedStyle(el).overflow === 'hidden') return rect.top;
             }
             el = el.parentNode;
         }
         // Check its within the document viewport
         return top; //top <= document.documentElement.clientHeight && getComputedStyle(document.documentElement).overflow === "hidden";
-    }
+    };
     visibleX = (el: any) => {
         let rect = el.getBoundingClientRect();
-        const left = rect.left, width = rect.width;
+        const left = rect.left,
+            width = rect.width;
         var el = el.parentNode;
         while (el && el !== document.body) {
             rect = el?.getBoundingClientRect();
             if (rect?.width) {
-                if (left <= rect.right === false && getComputedStyle(el).overflow === "hidden") return rect.right;
+                if (left <= rect.right === false && getComputedStyle(el).overflow === 'hidden') return rect.right;
                 // Check if the element is out of view due to a container scrolling
-                if ((left + width) <= rect.left && getComputedStyle(el).overflow === "hidden") return rect.left;
+                if (left + width <= rect.left && getComputedStyle(el).overflow === 'hidden') return rect.left;
             }
             el = el.parentNode;
         }
         // Check its within the document viewport
         return left; //top <= document.documentElement.clientHeight && getComputedStyle(document.documentElement).overflow === "hidden";
-    }
+    };
 
     @action
     toggleProperties = () => {
-        if (CurrentUserUtils.propertiesWidth > 0) {
-            CurrentUserUtils.propertiesWidth = 0;
+        if (SettingsManager.propertiesWidth > 0) {
+            SettingsManager.propertiesWidth = 0;
         } else {
-            CurrentUserUtils.propertiesWidth = 250;
+            SettingsManager.propertiesWidth = 250;
         }
-    }
+    };
 
     onClickLine = () => {
         SelectionManager.SelectSchemaViewDoc(this.props.LinkDocs[0], true);
         this.toggleProperties();
-    }
+    };
 
     @computed.struct get renderData() {
-        this._start; SnappingManager.GetIsDragging();
+        this._start;
+        SnappingManager.GetIsDragging();
         const { A, B, LinkDocs } = this.props;
         if (!A.ContentDiv || !B.ContentDiv || !LinkDocs.length) return undefined;
-        const acont = A.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
-        const bcont = B.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
+        const acont = A.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
+        const bcont = B.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
         const adiv = acont.length ? acont[0] : A.ContentDiv;
         const bdiv = bcont.length ? bcont[0] : B.ContentDiv;
         for (let apdiv = adiv; apdiv; apdiv = apdiv.parentElement as any) if ((apdiv as any).hidden) return;
@@ -185,11 +200,11 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         const pt2 = [bleft + b.width / 2, btop + b.width / 2];
         const pt1vec = [(bDocBounds.left + bDocBounds.right) / 2 - pt1[0], (bDocBounds.top + bDocBounds.bottom) / 2 - pt1[1]];
         const pt2vec = [(aDocBounds.left + aDocBounds.right) / 2 - pt2[0], (aDocBounds.top + aDocBounds.bottom) / 2 - pt2[1]];
-        const pt1len = Math.sqrt((pt1vec[0] * pt1vec[0]) + (pt1vec[1] * pt1vec[1]));
-        const pt2len = Math.sqrt((pt2vec[0] * pt2vec[0]) + (pt2vec[1] * pt2vec[1]));
+        const pt1len = Math.sqrt(pt1vec[0] * pt1vec[0] + pt1vec[1] * pt1vec[1]);
+        const pt2len = Math.sqrt(pt2vec[0] * pt2vec[0] + pt2vec[1] * pt2vec[1]);
         const ptlen = Math.sqrt((pt1[0] - pt2[0]) * (pt1[0] - pt2[0]) + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1])) / 2;
-        const pt1norm = clipped ? [0, 0] : [pt1vec[0] / pt1len * ptlen, pt1vec[1] / pt1len * ptlen];
-        const pt2norm = clipped ? [0, 0] : [pt2vec[0] / pt2len * ptlen, pt2vec[1] / pt2len * ptlen];
+        const pt1norm = clipped ? [0, 0] : [(pt1vec[0] / pt1len) * ptlen, (pt1vec[1] / pt1len) * ptlen];
+        const pt2norm = clipped ? [0, 0] : [(pt2vec[0] / pt2len) * ptlen, (pt2vec[1] / pt2len) * ptlen];
         const pt1normlen = Math.sqrt(pt1norm[0] * pt1norm[0] + pt1norm[1] * pt1norm[1]) || 1;
         const pt2normlen = Math.sqrt(pt2norm[0] * pt2norm[0] + pt2norm[1] * pt2norm[1]) || 1;
         const pt1normalized = [pt1norm[0] / pt1normlen, pt1norm[1] / pt1normlen];
@@ -203,7 +218,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
     }
 
     render() {
-        if (!this.renderData) return (null);
+        if (!this.renderData) return null;
 
         const { a, b, pt1norm, pt2norm, aActive, bActive, textX, textY, pt1, pt2 } = this.renderData;
         LinkManager.currentLink = this.props.LinkDocs[0];
@@ -216,31 +231,37 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
         const linkSize = currRelationshipIndex === -1 || currRelationshipIndex >= linkRelationshipSizes.length ? -1 : linkRelationshipSizes[currRelationshipIndex];
 
         //access stroke color using index of the relationship in the color list (default black)
-        const stroke = currRelationshipIndex === -1 || currRelationshipIndex >= linkColorList.length ? "black" : linkColorList[currRelationshipIndex];
+        const stroke = currRelationshipIndex === -1 || currRelationshipIndex >= linkColorList.length ? 'black' : linkColorList[currRelationshipIndex];
         // const hexStroke = this.rgbToHex(stroke)
 
         //calculate stroke width/thickness based on the relative importance of the relationshipship (i.e. how many links the relationship has)
         //thickness varies linearly from 3px to 12px for increasing link count
-        const strokeWidth = linkSize === -1 ? "3px" : Math.floor(2 + 10 * (linkSize / Math.max(...linkRelationshipSizes))) + "px";
+        const strokeWidth = linkSize === -1 ? '3px' : Math.floor(2 + 10 * (linkSize / Math.max(...linkRelationshipSizes))) + 'px';
 
         if (this.props.LinkDocs[0].displayArrow === undefined) {
             this.props.LinkDocs[0].displayArrow = false;
         }
 
-        return this.props.LinkDocs[0].opacity === 0 || !a.width || !b.width || ((!this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<>
-            <defs>
-                <marker id="arrowhead" markerWidth="4" markerHeight="3"
-                    refX="0" refY="1.5" orient="auto">
-                    <polygon points="0 0, 3 1.5, 0 3" fill={Colors.DARK_GRAY} />
-                </marker>
-            </defs>
-            <path className="collectionfreeformlinkview-linkLine" style={{ pointerEvents: "all", opacity: this._opacity, stroke: SelectionManager.SelectedSchemaDoc() === this.props.LinkDocs[0] ? Colors.MEDIUM_BLUE : stroke, strokeWidth }}
-                onClick={this.onClickLine}
-                d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`}
-                markerEnd={this.props.LinkDocs[0].displayArrow ? "url(#arrowhead)" : ""} />
-            {textX === undefined ? (null) : <text className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown} >
-                {Field.toString(this.props.LinkDocs[0].description as any as Field)}
-            </text>}
-        </>);
+        return this.props.LinkDocs[0].opacity === 0 || !a.width || !b.width || (!this.props.LinkDocs[0].linkDisplay && !aActive && !bActive) ? null : (
+            <>
+                <defs>
+                    <marker id="arrowhead" markerWidth="4" markerHeight="3" refX="0" refY="1.5" orient="auto">
+                        <polygon points="0 0, 3 1.5, 0 3" fill={Colors.DARK_GRAY} />
+                    </marker>
+                </defs>
+                <path
+                    className="collectionfreeformlinkview-linkLine"
+                    style={{ pointerEvents: 'all', opacity: this._opacity, stroke: SelectionManager.SelectedSchemaDoc() === this.props.LinkDocs[0] ? Colors.MEDIUM_BLUE : stroke, strokeWidth }}
+                    onClick={this.onClickLine}
+                    d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`}
+                    markerEnd={this.props.LinkDocs[0].displayArrow ? 'url(#arrowhead)' : ''}
+                />
+                {textX === undefined ? null : (
+                    <text className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown}>
+                        {Field.toString(this.props.LinkDocs[0].description as any as Field)}
+                    </text>
+                )}
+            </>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
index 9f6938e67..9e8d92d7d 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.tsx
@@ -1,79 +1,82 @@
-import { computed } from "mobx";
-import { observer } from "mobx-react";
+import { computed } from 'mobx';
+import { observer } from 'mobx-react';
 import * as mobxUtils from 'mobx-utils';
-import CursorField from "../../../../fields/CursorField";
-import { FieldResult } from "../../../../fields/Doc";
-import { List } from "../../../../fields/List";
-import { listSpec } from "../../../../fields/Schema";
-import { Cast } from "../../../../fields/Types";
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { CollectionViewProps } from "../CollectionView";
-import "./CollectionFreeFormView.scss";
-import React = require("react");
-import v5 = require("uuid/v5");
+import CursorField from '../../../../fields/CursorField';
+import { Doc, FieldResult } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { List } from '../../../../fields/List';
+import { listSpec } from '../../../../fields/Schema';
+import { Cast } from '../../../../fields/Types';
+import { CollectionViewProps } from '../CollectionView';
+import './CollectionFreeFormView.scss';
+import React = require('react');
+import v5 = require('uuid/v5');
 
 @observer
 export class CollectionFreeFormRemoteCursors extends React.Component<CollectionViewProps> {
-
     @computed protected get cursors(): CursorField[] {
         const doc = this.props.Document;
 
         let cursors: FieldResult<List<CursorField>>;
-        const { id } = CurrentUserUtils;
+        const id = Doc.UserDoc()[Id];
         if (!id || !(cursors = Cast(doc.cursors, listSpec(CursorField)))) {
             return [];
         }
         const now = mobxUtils.now();
-        return (cursors || []).filter(({ data: { metadata } }) => metadata.id !== id && (now - metadata.timestamp) < 1000);
+        return (cursors || []).filter(({ data: { metadata } }) => metadata.id !== id && now - metadata.timestamp < 1000);
     }
 
     @computed get renderedCursors() {
-        return this.cursors.map(({ data: { metadata, position: { x, y } } }) => {
-            return (
-                <div key={metadata.id} className="collectionFreeFormRemoteCursors-cont"
-                    style={{ transform: `translate(${x - 10}px, ${y - 10}px)` }}
-                >
-                    <canvas className="collectionFreeFormRemoteCursors-canvas"
-                        ref={(el) => {
-                            if (el) {
-                                const ctx = el.getContext('2d');
-                                if (ctx) {
-                                    ctx.fillStyle = "#" + v5(metadata.id, v5.URL).substring(0, 6).toUpperCase() + "22";
-                                    ctx.fillRect(0, 0, 20, 20);
+        return this.cursors.map(
+            ({
+                data: {
+                    metadata,
+                    position: { x, y },
+                },
+            }) => {
+                return (
+                    <div key={metadata.id} className="collectionFreeFormRemoteCursors-cont" style={{ transform: `translate(${x - 10}px, ${y - 10}px)` }}>
+                        <canvas
+                            className="collectionFreeFormRemoteCursors-canvas"
+                            ref={el => {
+                                if (el) {
+                                    const ctx = el.getContext('2d');
+                                    if (ctx) {
+                                        ctx.fillStyle = '#' + v5(metadata.id, v5.URL).substring(0, 6).toUpperCase() + '22';
+                                        ctx.fillRect(0, 0, 20, 20);
 
-                                    ctx.fillStyle = "black";
-                                    ctx.lineWidth = 0.5;
+                                        ctx.fillStyle = 'black';
+                                        ctx.lineWidth = 0.5;
 
-                                    ctx.beginPath();
+                                        ctx.beginPath();
 
-                                    ctx.moveTo(10, 0);
-                                    ctx.lineTo(10, 8);
+                                        ctx.moveTo(10, 0);
+                                        ctx.lineTo(10, 8);
 
-                                    ctx.moveTo(10, 20);
-                                    ctx.lineTo(10, 12);
+                                        ctx.moveTo(10, 20);
+                                        ctx.lineTo(10, 12);
 
-                                    ctx.moveTo(0, 10);
-                                    ctx.lineTo(8, 10);
+                                        ctx.moveTo(0, 10);
+                                        ctx.lineTo(8, 10);
 
-                                    ctx.moveTo(20, 10);
-                                    ctx.lineTo(12, 10);
+                                        ctx.moveTo(20, 10);
+                                        ctx.lineTo(12, 10);
 
-                                    ctx.stroke();
+                                        ctx.stroke();
+                                    }
                                 }
-                            }
-                        }}
-                        width={20}
-                        height={20}
-                    />
-                    <p className="collectionFreeFormRemoteCursors-symbol">
-                        {metadata.identifier[0].toUpperCase()}
-                    </p>
-                </div>
-            );
-        });
+                            }}
+                            width={20}
+                            height={20}
+                        />
+                        <p className="collectionFreeFormRemoteCursors-symbol">{metadata.identifier[0].toUpperCase()}</p>
+                    </div>
+                );
+            }
+        );
     }
 
     render() {
         return this.renderedCursors;
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8444c9119..07ea26346 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -18,13 +18,13 @@ import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
 import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
 import { Docs, DocUtils } from '../../../documents/Documents';
-import { DocumentType } from '../../../documents/DocumentTypes';
-import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
+import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager, dropActionType } from '../../../util/DragManager';
 import { HistoryUtil } from '../../../util/History';
 import { InteractionUtils } from '../../../util/InteractionUtils';
 import { RecordingApi } from '../../../util/RecordingApi';
+import { ReplayMovements } from '../../../util/ReplayMovements';
 import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { ColorScheme } from '../../../util/SettingsManager';
@@ -48,7 +48,6 @@ import { StyleProp } from '../../StyleProvider';
 import { CollectionDockingView } from '../CollectionDockingView';
 import { CollectionSubView } from '../CollectionSubView';
 import { TreeViewType } from '../CollectionTreeView';
-import { CollectionViewType } from '../CollectionView';
 import { TabDocView } from '../TabDocView';
 import { computePivotLayout, computerPassLayout, computerStarburstLayout, computeTimelineLayout, PoolData, ViewDefBounds, ViewDefResult } from './CollectionFreeFormLayoutEngines';
 import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCursors';
@@ -56,8 +55,6 @@ import './CollectionFreeFormView.scss';
 import { MarqueeView } from './MarqueeView';
 import React = require('react');
 import e = require('connect-flash');
-import { ReplayMovements } from '../../../util/ReplayMovements';
-
 
 export type collectionFreeformViewProps = {
     annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox)
@@ -517,7 +514,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) &&
                 !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)
             ) {
-                switch (CurrentUserUtils.ActiveTool) {
+                switch (Doc.ActiveTool) {
                     case InkTool.Highlighter:
                         break;
                     // TODO: nda - this where we want to create the new "writingDoc" collection that we add strokes to
@@ -556,7 +553,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     this.addMoveListeners();
                     this.removeEndListeners();
                     this.addEndListeners();
-                    if (CurrentUserUtils.ActiveTool === InkTool.None) {
+                    if (Doc.ActiveTool === InkTool.None) {
                         this._lastX = pt.pageX;
                         this._lastY = pt.pageY;
                         e.preventDefault();
@@ -577,14 +574,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
             case GestureUtils.Gestures.Stroke:
                 const points = ge.points;
                 const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
-                const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points, {
+                const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), Doc.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points, {
                     title: 'ink stroke',
                     x: B.x - ActiveInkWidth() / 2,
                     y: B.y - ActiveInkWidth() / 2,
                     _width: B.width + ActiveInkWidth(),
                     _height: B.height + ActiveInkWidth(),
                 });
-                if (CurrentUserUtils.ActiveTool === InkTool.Write) {
+                if (Doc.ActiveTool === InkTool.Write) {
                     this.unprocessedDocs.push(inkDoc);
                     CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
                 }
@@ -775,7 +772,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     onPointerMove = (e: PointerEvent): void => {
         if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return;
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
-            CurrentUserUtils.ActiveTool = InkTool.None;
+            Doc.ActiveTool = InkTool.None;
             if (this.props.isContentActive(true)) e.stopPropagation();
         } else if (!e.cancelBubble) {
             if (this.tryDragCluster(e, this._hitCluster)) {
@@ -907,7 +904,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         if (!e.cancelBubble) {
             const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
             if (myTouches[0]) {
-                if (CurrentUserUtils.ActiveTool === InkTool.None) {
+                if (Doc.ActiveTool === InkTool.None) {
                     if (this.tryDragCluster(e, this._hitCluster)) {
                         e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
                         e.preventDefault();
@@ -1061,8 +1058,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     onPointerWheel = (e: React.WheelEvent): void => {
-        if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline)
-            return;
+        if (this.layoutDoc._Transform || (this.layoutDoc._fitWidth && this.layoutDoc.nativeHeight) || DocListCast(Doc.MyOverlayDocs?.data).includes(this.props.Document) || this.props.Document.treeViewOutlineMode === TreeViewType.outline) return;
         if (!e.ctrlKey && this.props.Document.scrollHeight !== undefined) {
             // things that can scroll vertically should do that instead of zooming
             e.stopPropagation();
@@ -1079,7 +1075,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         Doc.UserDoc()?.presentationMode === 'recording' && RecordingApi.Instance.setRecordingFFView(this);
         // TODO: make this based off the specific recording FFView
         Doc.UserDoc()?.presentationMode === 'none' && RecordingApi.Instance.setPlayFFView(this);
-        
+
         // TODO: zzz + michael to figure out this merge in case of strange behaviour
         // if (Doc.UserDoc()?.presentationMode === 'watching') {
         //     RecordingApi.Instance.pauseVideoAndMovements();
@@ -1118,7 +1114,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                 else if (ranges.yrange.max <= panY - panelDim[1] / 2) panY = ranges.yrange.min - panelDim[1] / 2;
             }
         }
-        if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.Document)) {
+        if (!this.layoutDoc._lockedTransform || LightboxView.LightboxDoc || DocListCast(Doc.MyOverlayDocs?.data).includes(this.Document)) {
             this._viewTransition = panTime;
             const scale = this.getLocalTransform().inverse().Scale;
             const minScale = NumCast(this.rootDoc._viewScaleMin, 1);
@@ -2218,7 +2214,7 @@ class CollectionFreeFormBackgroundGrid extends React.Component<CollectionFreeFor
         const renderGridSpace = gridSpace * this.props.zoomScaling();
         const w = this.props.PanelWidth() + 2 * renderGridSpace;
         const h = this.props.PanelHeight() + 2 * renderGridSpace;
-        const strokeStyle = CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'rgba(255,255,255,0.5)' : 'rgba(0, 0,0,0.5)';
+        const strokeStyle = Doc.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'rgba(255,255,255,0.5)' : 'rgba(0, 0,0,0.5)';
         return (
             <canvas
                 className="collectionFreeFormView-grid"
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index ab8a34d5a..4513ffb39 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -1,36 +1,33 @@
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import { AclAdmin, AclAugment, AclEdit, DataSym, Doc, DocListCastAsync, Opt } from "../../../../fields/Doc";
-import { Id } from "../../../../fields/FieldSymbols";
-import { InkData, InkField, InkTool } from "../../../../fields/InkField";
-import { List } from "../../../../fields/List";
-import { RichTextField } from "../../../../fields/RichTextField";
-import { SchemaHeaderField } from "../../../../fields/SchemaHeaderField";
-import { Cast, DocCast, FieldValue, NumCast, StrCast } from "../../../../fields/Types";
-import { ImageField } from "../../../../fields/URLField";
-import { GetEffectiveAcl } from "../../../../fields/util";
-import { intersectRect, returnFalse, Utils } from "../../../../Utils";
-import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
-import { Docs, DocumentOptions, DocUtils } from "../../../documents/Documents";
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { Transform } from "../../../util/Transform";
-import { undoBatch, UndoManager } from "../../../util/UndoManager";
-import { ContextMenu } from "../../ContextMenu";
-import { FormattedTextBox } from "../../nodes/formattedText/FormattedTextBox";
-import { PinViewProps, PresBox } from "../../nodes/trails/PresBox";
-import { PresMovement } from "../../nodes/trails/PresEnums";
-import { VideoBox } from "../../nodes/VideoBox";
-import { pasteImageBitmap } from "../../nodes/WebBoxRenderer";
-import { PreviewCursor } from "../../PreviewCursor";
-import { CollectionDockingView } from "../CollectionDockingView";
-import { SubCollectionViewProps } from "../CollectionSubView";
-import { TreeView } from "../TreeView";
-import { MarqueeOptionsMenu } from "./MarqueeOptionsMenu";
-import "./MarqueeView.scss";
-import React = require("react");
-import { TabDocView } from "../TabDocView";
+import { action, computed, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import { AclAdmin, AclAugment, AclEdit, DataSym, Doc, Opt } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { InkData, InkField, InkTool } from '../../../../fields/InkField';
+import { List } from '../../../../fields/List';
+import { RichTextField } from '../../../../fields/RichTextField';
+import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
+import { Cast, DocCast, FieldValue, NumCast, StrCast } from '../../../../fields/Types';
+import { ImageField } from '../../../../fields/URLField';
+import { GetEffectiveAcl } from '../../../../fields/util';
+import { intersectRect, returnFalse, Utils } from '../../../../Utils';
+import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
+import { Docs, DocumentOptions, DocUtils } from '../../../documents/Documents';
+import { DocumentType } from '../../../documents/DocumentTypes';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { Transform } from '../../../util/Transform';
+import { undoBatch, UndoManager } from '../../../util/UndoManager';
+import { ContextMenu } from '../../ContextMenu';
+import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
+import { PinViewProps, PresBox } from '../../nodes/trails/PresBox';
+import { VideoBox } from '../../nodes/VideoBox';
+import { pasteImageBitmap } from '../../nodes/WebBoxRenderer';
+import { PreviewCursor } from '../../PreviewCursor';
+import { SubCollectionViewProps } from '../CollectionSubView';
+import { TabDocView } from '../TabDocView';
+import { TreeView } from '../TreeView';
+import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
+import './MarqueeView.scss';
+import React = require('react');
 
 interface MarqueeViewProps {
     getContainerTransform: () => Transform;
@@ -46,15 +43,14 @@ interface MarqueeViewProps {
 }
 
 export interface MarqueeViewBounds {
-	left: number;
-	top: number;
-	width: number;
-	height: number;
+    left: number;
+    top: number;
+    width: number;
+    height: number;
 }
 
 @observer
-export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps>
-{
+export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps> {
     private _commandExecuted = false;
     @observable _lastX: number = 0;
     @observable _lastY: number = 0;
@@ -64,18 +60,26 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     @observable _lassoPts: [number, number][] = [];
     @observable _lassoFreehand: boolean = false;
 
-    @computed get Transform() { return this.props.getTransform(); }
+    @computed get Transform() {
+        return this.props.getTransform();
+    }
     @computed get Bounds() {
         // nda - ternary argument to transformPoint is returning the lower of the downX/Y and lastX/Y and passing in as args x,y
         const topLeft = this.Transform.transformPoint(this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY);
         // nda - args to transformDirection is just x and y diff btw downX/Y and lastX/Y
         const size = this.Transform.transformDirection(this._lastX - this._downX, this._lastY - this._downY);
-		const bounds:MarqueeViewBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) }
+        const bounds: MarqueeViewBounds = { left: topLeft[0], top: topLeft[1], width: Math.abs(size[0]), height: Math.abs(size[1]) };
         return bounds;
     }
-    get inkDoc() { return this.props.Document; }
-    get ink() { return Cast(this.props.Document.ink, InkField); }
-    set ink(value: Opt<InkField>) { this.props.Document.ink = value; }
+    get inkDoc() {
+        return this.props.Document;
+    }
+    get ink() {
+        return Cast(this.props.Document.ink, InkField);
+    }
+    set ink(value: Opt<InkField>) {
+        this.props.Document.ink = value;
+    }
 
     componentDidMount() {
         this.props.setPreviewCursor?.(this.setPreviewCursor);
@@ -84,14 +88,14 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     @action
     cleanupInteractions = (all: boolean = false, hideMarquee: boolean = true) => {
         if (all) {
-            document.removeEventListener("pointerup", this.onPointerUp, true);
-            document.removeEventListener("pointermove", this.onPointerMove, true);
+            document.removeEventListener('pointerup', this.onPointerUp, true);
+            document.removeEventListener('pointermove', this.onPointerMove, true);
         }
-        document.removeEventListener("keydown", this.marqueeCommand, true);
+        document.removeEventListener('keydown', this.marqueeCommand, true);
         hideMarquee && this.hideMarquee();
 
         this._lassoPts = [];
-    }
+    };
 
     @undoBatch
     @action
@@ -100,76 +104,75 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         // tslint:disable-next-line:prefer-const
         const cm = ContextMenu.Instance;
         const [x, y] = this.Transform.transformPoint(this._downX, this._downY);
-        if (e.key === "?") {
-            cm.setDefaultItem("?", (str: string) => this.props.addDocTab(
-                Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: "bing", useCors: true }), "add:right"));
+        if (e.key === '?') {
+            cm.setDefaultItem('?', (str: string) => this.props.addDocTab(Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: 'bing', useCors: true }), 'add:right'));
 
             cm.displayMenu(this._downX, this._downY, undefined, true);
             e.stopPropagation();
-        } else
-            if (e.key === "u" && this.props.ungroup) {
-                e.stopPropagation();
-                this.props.ungroup();
-            }
-            else if (e.key === ":") {
-                DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument || returnFalse, x, y);
+        } else if (e.key === 'u' && this.props.ungroup) {
+            e.stopPropagation();
+            this.props.ungroup();
+        } else if (e.key === ':') {
+            DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument || returnFalse, x, y);
 
-                cm.displayMenu(this._downX, this._downY, undefined, true);
-                e.stopPropagation();
-            } else if (e.key === "a" && (e.ctrlKey || e.metaKey)) {
-                e.preventDefault();
-                this.props.selectDocuments(this.props.activeDocuments());
-                e.stopPropagation();
-            } else if (e.key === "q" && e.ctrlKey) {
-                e.preventDefault();
-                (async () => {
-                    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) {
-                            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;
-                        }
+            cm.displayMenu(this._downX, this._downY, undefined, true);
+            e.stopPropagation();
+        } else if (e.key === 'a' && (e.ctrlKey || e.metaKey)) {
+            e.preventDefault();
+            this.props.selectDocuments(this.props.activeDocuments());
+            e.stopPropagation();
+        } else if (e.key === 'q' && e.ctrlKey) {
+            e.preventDefault();
+            (async () => {
+                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
+                    ) {
+                        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;
                     }
-                    let ypos = y;
-                    ns.map(line => {
-                        const indent = line.search(/\S|$/);
-                        const newBox = Docs.Create.TextDocument(line, { _width: 200, _height: 35, x: x + indent / 3 * 10, y: ypos, title: line });
-                        this.props.addDocument?.(newBox);
-                        ypos += 40 * this.Transform.Scale;
-                    });
-                })();
-                e.stopPropagation();
-            } else if (e.key === "b" && e.ctrlKey) {
-                document.body.focus(); // so that we can access the clipboard without an error
-                setTimeout(() =>
-                    pasteImageBitmap((data: any, error: any) => {
-                        error && console.log(error);
-                        data && VideoBox.convertDataUri(data, this.props.Document[Id] + "-thumb-frozen").then(returnedfilename => {
-                            this.props.Document["thumb-frozen"] = new ImageField(returnedfilename);
+                }
+                let ypos = y;
+                ns.map(line => {
+                    const indent = line.search(/\S|$/);
+                    const newBox = Docs.Create.TextDocument(line, { _width: 200, _height: 35, x: x + (indent / 3) * 10, y: ypos, title: line });
+                    this.props.addDocument?.(newBox);
+                    ypos += 40 * this.Transform.Scale;
+                });
+            })();
+            e.stopPropagation();
+        } else if (e.key === 'b' && e.ctrlKey) {
+            document.body.focus(); // so that we can access the clipboard without an error
+            setTimeout(() =>
+                pasteImageBitmap((data: any, error: any) => {
+                    error && console.log(error);
+                    data &&
+                        VideoBox.convertDataUri(data, this.props.Document[Id] + '-thumb-frozen').then(returnedfilename => {
+                            this.props.Document['thumb-frozen'] = new ImageField(returnedfilename);
                         });
-                    }));
-            } else if (e.key === "s" && e.ctrlKey) {
-                e.preventDefault();
-                const slide = DocUtils.copyDragFactory(DocCast(Doc.UserDoc().emptySlide))!;
-                slide.x = x;
-                slide.y = y;
-                FormattedTextBox.SelectOnLoad = slide[Id];
-                TreeView._editTitleOnLoad = { id: slide[Id], parent: undefined };
-                this.props.addDocument?.(slide);
-                e.stopPropagation();
-            } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views().length < 2) {
-                FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this.props.childLayoutString ? e.key : "";
-                FormattedTextBox.LiveTextUndo = UndoManager.StartBatch("live text batch");
-                this.props.addLiveTextDocument(CurrentUserUtils.GetNewTextDoc("-typed text-", x, y, 200, 100, this.props.xPadding === 0));
-                e.stopPropagation();
-            }
-    }
+                })
+            );
+        } else if (e.key === 's' && e.ctrlKey) {
+            e.preventDefault();
+            const slide = DocUtils.copyDragFactory(DocCast(Doc.UserDoc().emptySlide))!;
+            slide.x = x;
+            slide.y = y;
+            FormattedTextBox.SelectOnLoad = slide[Id];
+            TreeView._editTitleOnLoad = { id: slide[Id], parent: undefined };
+            this.props.addDocument?.(slide);
+            e.stopPropagation();
+        } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views().length < 2) {
+            FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this.props.childLayoutString ? e.key : '';
+            FormattedTextBox.LiveTextUndo = UndoManager.StartBatch('live text batch');
+            this.props.addLiveTextDocument(DocUtils.GetNewTextDoc('-typed text-', x, y, 200, 100, this.props.xPadding === 0));
+            e.stopPropagation();
+        }
+    };
     //heuristically converts pasted text into a table.
     // assumes each entry is separated by a tab
     // skips all rows until it gets to a row with more than one entry
@@ -178,26 +181,26 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
     //         any row that has only one column is a section header-- this header is then added as a column to subsequent rows until the next header
     // assumes each cell is a string or a number
     pasteTable(ns: string[], x: number, y: number) {
-        while (ns.length > 0 && ns[0].split("\t").length < 2) {
+        while (ns.length > 0 && ns[0].split('\t').length < 2) {
             ns.splice(0, 1);
         }
         if (ns.length > 0) {
-            const columns = ns[0].split("\t");
+            const columns = ns[0].split('\t');
             const docList: Doc[] = [];
-            let groupAttr: string | number = "";
+            let groupAttr: string | number = '';
             const rowProto = new Doc();
             rowProto.title = rowProto.Id;
             rowProto._width = 200;
             rowProto.isPrototype = true;
             for (let i = 1; i < ns.length - 1; i++) {
-                const values = ns[i].split("\t");
+                const values = ns[i].split('\t');
                 if (values.length === 1 && columns.length > 1) {
                     groupAttr = values[0];
                     continue;
                 }
                 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));
+                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;
                 }
@@ -206,7 +209,13 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 doc._width = 200;
                 docList.push(doc);
             }
-            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 });
+            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);
         }
@@ -227,10 +236,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 // }
                 // bcz: do we need this?   it kills the context menu on the main collection if !altKey
                 // e.stopPropagation();
-            }
-            else PreviewCursor.Visible = false;
+            } else PreviewCursor.Visible = false;
         }
-    }
+    };
 
     @action
     onPointerMove = (e: PointerEvent): void => {
@@ -238,8 +246,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         this._lastY = e.pageY;
         this._lassoPts.push([e.clientX, e.clientY]);
         if (!e.cancelBubble) {
-            if (Math.abs(this._lastX - this._downX) > Utils.DRAG_THRESHOLD ||
-                Math.abs(this._lastY - this._downY) > Utils.DRAG_THRESHOLD) {
+            if (Math.abs(this._lastX - this._downX) > Utils.DRAG_THRESHOLD || Math.abs(this._lastY - this._downY) > Utils.DRAG_THRESHOLD) {
                 if (!this._commandExecuted) {
                     this.showMarquee();
                 }
@@ -253,7 +260,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         if (PresBox.startMarquee) {
             e.stopPropagation();
         }
-    }
+    };
 
     @action
     onPointerUp = (e: PointerEvent): void => {
@@ -270,14 +277,14 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         const hideMarquee = () => {
             this.hideMarquee();
             MarqueeOptionsMenu.Instance.fadeOut(true);
-            document.removeEventListener("pointerdown", hideMarquee);
-            document.removeEventListener("wheel", hideMarquee);
+            document.removeEventListener('pointerdown', hideMarquee);
+            document.removeEventListener('wheel', hideMarquee);
         };
         if (PresBox.startMarquee) {
             this.pinWithView();
             PresBox.startMarquee = false;
         }
-        if (!this._commandExecuted && (Math.abs(this.Bounds.height * this.Bounds.width) > 100) && !PresBox.startMarquee) {
+        if (!this._commandExecuted && Math.abs(this.Bounds.height * this.Bounds.width) > 100 && !PresBox.startMarquee) {
             MarqueeOptionsMenu.Instance.createCollection = this.collection;
             MarqueeOptionsMenu.Instance.delete = this.delete;
             MarqueeOptionsMenu.Instance.summarize = this.summary;
@@ -285,19 +292,22 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             MarqueeOptionsMenu.Instance.hideMarquee = this.hideMarquee;
             MarqueeOptionsMenu.Instance.jumpTo(e.clientX, e.clientY);
             MarqueeOptionsMenu.Instance.pinWithView = this.pinWithView;
-            document.addEventListener("pointerdown", hideMarquee);
-            document.addEventListener("wheel", hideMarquee);
+            document.addEventListener('pointerdown', hideMarquee);
+            document.addEventListener('wheel', hideMarquee);
         } else {
             this.hideMarquee();
         }
         this.cleanupInteractions(true, this._commandExecuted);
 
         e.altKey && e.preventDefault();
-    }
+    };
 
     clearSelection() {
-        if (window.getSelection) { window.getSelection()?.removeAllRanges(); }
-        else if (document.getSelection()) { document.getSelection()?.empty(); }
+        if (window.getSelection) {
+            window.getSelection()?.removeAllRanges();
+        } else if (document.getSelection()) {
+            document.getSelection()?.empty();
+        }
     }
 
     setPreviewCursor = action((x: number, y: number, drag: boolean, hide: boolean) => {
@@ -312,9 +322,9 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             this._commandExecuted = false;
             PreviewCursor.Visible = false;
             this.cleanupInteractions(true);
-            document.addEventListener("pointermove", this.onPointerMove, true);
-            document.addEventListener("pointerup", this.onPointerUp, true);
-            document.addEventListener("keydown", this.marqueeCommand, true);
+            document.addEventListener('pointermove', this.onPointerMove, true);
+            document.addEventListener('pointerup', this.onPointerUp, true);
+            document.addEventListener('keydown', this.marqueeCommand, true);
         } else {
             this._downX = x;
             this._downY = y;
@@ -328,9 +338,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
 
     @action
     onClick = (e: React.MouseEvent): void => {
-        if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
-            Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
-            if (CurrentUserUtils.ActiveTool === InkTool.None) {
+        if (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
+            if (Doc.ActiveTool === InkTool.None) {
                 if (!(e.nativeEvent as any).marqueeHit) {
                     (e.nativeEvent as any).marqueeHit = true;
                     if (!this.props.trySelectCluster(e.shiftKey)) {
@@ -339,17 +348,22 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 }
             }
             // let the DocumentView stopPropagation of this event when it selects this document
-        } else {  // why do we get a click event when the cursor have moved a big distance?
+        } else {
+            // why do we get a click event when the cursor have moved a big distance?
             // let's cut it off here so no one else has to deal with it.
             e.stopPropagation();
         }
-    }
+    };
 
     @action
-    showMarquee = () => { this._visible = true; }
+    showMarquee = () => {
+        this._visible = true;
+    };
 
     @action
-    hideMarquee = () => { this._visible = false; }
+    hideMarquee = () => {
+        this._visible = false;
+    };
 
     @undoBatch
     @action
@@ -361,16 +375,18 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         this.cleanupInteractions(false);
         MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
-    }
+    };
 
     getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, makeGroup: Opt<boolean>) => {
-        const newCollection = creator ? creator(selected, { title: "nested stack", }) : ((doc: Doc) => {
-            Doc.GetProto(doc).data = new List<Doc>(selected);
-            Doc.GetProto(doc).title = makeGroup ? "grouping" : "nested freeform";
-            !this.props.isAnnotationOverlay && Doc.AddDocToList(CurrentUserUtils.MyFileOrphans, undefined, Doc.GetProto(doc));
-            doc._panX = doc._panY = 0;
-            return doc;
-        })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
+        const newCollection = creator
+            ? creator(selected, { title: 'nested stack' })
+            : ((doc: Doc) => {
+                  Doc.GetProto(doc).data = new List<Doc>(selected);
+                  Doc.GetProto(doc).title = makeGroup ? 'grouping' : 'nested freeform';
+                  !this.props.isAnnotationOverlay && Doc.AddDocToList(Doc.MyFileOrphans, undefined, Doc.GetProto(doc));
+                  doc._panX = doc._panY = 0;
+                  return doc;
+              })(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
         newCollection.system = undefined;
         newCollection._width = this.Bounds.width;
         newCollection._height = this.Bounds.height;
@@ -378,7 +394,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         newCollection.forceActive = makeGroup;
         newCollection.x = this.Bounds.left;
         newCollection.y = this.Bounds.top;
-        selected.forEach(d => d.context = newCollection);
+        selected.forEach(d => (d.context = newCollection));
         this.hideMarquee();
         return newCollection;
     });
@@ -393,74 +409,76 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         this.props.selectDocuments([newCollection]);
         MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
-    }
-
-	/**
-	 * This triggers the TabDocView.PinDoc method which is the universal method
-	 * used to pin documents to the currently active presentation trail. 
-	 * 
-	 * This one is unique in that it includes the bounds associated with marquee view. 
-	 */
+    };
+
+    /**
+     * This triggers the TabDocView.PinDoc method which is the universal method
+     * used to pin documents to the currently active presentation trail.
+     *
+     * This one is unique in that it includes the bounds associated with marquee view.
+     */
     @undoBatch
     @action
     pinWithView = async () => {
         const scale = Math.min(this.props.PanelWidth() / this.Bounds.width, this.props.PanelHeight() / this.Bounds.height);
-		const doc = this.props.Document;
-		const viewOptions:PinViewProps = {
-			bounds: this.Bounds,
-			scale: scale
-		};
-		TabDocView.PinDoc(doc, {pinWithView: viewOptions});
-		MarqueeOptionsMenu.Instance.fadeOut(true);
+        const doc = this.props.Document;
+        const viewOptions: PinViewProps = {
+            bounds: this.Bounds,
+            scale: scale,
+        };
+        TabDocView.PinDoc(doc, { pinWithView: viewOptions });
+        MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
-    }
+    };
 
     @undoBatch
     @action
     collection = (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean) => {
         const selected = this.marqueeSelect(false);
-        if (e instanceof KeyboardEvent ? "cg".includes(e.key) : true) {
-            selected.map(action(d => {
-                const dx = NumCast(d.x);
-                const dy = NumCast(d.y);
-                delete d.x;
-                delete d.y;
-                delete d.activeFrame;
-                delete d._timecodeToShow;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-                delete d._timecodeToHide;  // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
-                d.x = dx - this.Bounds.left - this.Bounds.width / 2;
-                d.y = dy - this.Bounds.top - this.Bounds.height / 2;
-                return d;
-            }));
+        if (e instanceof KeyboardEvent ? 'cg'.includes(e.key) : true) {
+            selected.map(
+                action(d => {
+                    const dx = NumCast(d.x);
+                    const dy = NumCast(d.y);
+                    delete d.x;
+                    delete d.y;
+                    delete d.activeFrame;
+                    delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+                    delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection
+                    d.x = dx - this.Bounds.left - this.Bounds.width / 2;
+                    d.y = dy - this.Bounds.top - this.Bounds.height / 2;
+                    return d;
+                })
+            );
             this.props.removeDocument?.(selected);
         }
         // TODO: nda - this is the code to actually get a new grouped collection
-        const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === "t" ? Docs.Create.StackingDocument : undefined, group);
+        const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === 't' ? Docs.Create.StackingDocument : undefined, group);
         this.props.addDocument?.(newCollection);
         this.props.selectDocuments([newCollection]);
         MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
-    }
+    };
 
     @undoBatch
     @action
     syntaxHighlight = (e: KeyboardEvent | React.PointerEvent | undefined) => {
         const selected = this.marqueeSelect(false);
-        if (e instanceof KeyboardEvent ? e.key === "i" : true) {
+        if (e instanceof KeyboardEvent ? e.key === 'i' : true) {
             const inks = selected.filter(s => s.type === DocumentType.INK);
             const setDocs = selected.filter(s => s.type === DocumentType.RTF && s.color);
-            const sets = setDocs.map((sd) => Cast(sd.data, RichTextField)?.Text as string);
+            const sets = setDocs.map(sd => Cast(sd.data, RichTextField)?.Text as string);
             const colors = setDocs.map(sd => FieldValue(sd.color) as string);
             const wordToColor = new Map<string, string>();
-            sets.forEach((st: string, i: number) => st.split(",").forEach(word => wordToColor.set(word, colors[i])));
+            sets.forEach((st: string, i: number) => st.split(',').forEach(word => wordToColor.set(word, colors[i])));
             const strokes: InkData[] = [];
             inks.filter(i => Cast(i.data, InkField)).forEach(i => {
                 const d = Cast(i.data, InkField, null);
-                const left = Math.min(...d?.inkData.map(pd => pd.X) ?? [0]);
-                const top = Math.min(...d?.inkData.map(pd => pd.Y) ?? [0]);
+                const left = Math.min(...(d?.inkData.map(pd => pd.X) ?? [0]));
+                const top = Math.min(...(d?.inkData.map(pd => pd.Y) ?? [0]));
                 strokes.push(d.inkData.map(pd => ({ X: pd.X + NumCast(i.x) - left, Y: pd.Y + NumCast(i.y) - top })));
             });
-            CognitiveServices.Inking.Appliers.InterpretStrokes(strokes).then((results) => {
+            CognitiveServices.Inking.Appliers.InterpretStrokes(strokes).then(results => {
                 // const wordResults = results.filter((r: any) => r.category === "inkWord");
                 // for (const word of wordResults) {
                 //     const indices: number[] = word.strokeIds;
@@ -501,12 +519,12 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 //         }
                 //     });
                 // }
-                const lines = results.filter((r: any) => r.category === "line");
-                const text = lines.map((l: any) => l.recognizedText).join("\r\n");
+                const lines = results.filter((r: any) => r.category === 'line');
+                const text = lines.map((l: any) => l.recognizedText).join('\r\n');
                 this.props.addDocument?.(Docs.Create.TextDocument(text, { _width: this.Bounds.width, _height: this.Bounds.height, x: this.Bounds.left + this.Bounds.width, y: this.Bounds.top, title: text }));
             });
         }
-    }
+    };
 
     @undoBatch
     @action
@@ -517,15 +535,15 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             d.y = NumCast(d.y) - this.Bounds.top;
             return d;
         });
-        const summary = Docs.Create.TextDocument("", { backgroundColor: "#e2ad32", x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitContentsToBox: true, _showSidebar: true, title: "overview" });
-        const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: "transparent" });
-        DocUtils.MakeLink({ doc: summary }, { doc: portal }, "summary of:summarized by", "");
+        const summary = Docs.Create.TextDocument('', { backgroundColor: '#e2ad32', x: this.Bounds.left, y: this.Bounds.top, isPushpin: true, _width: 200, _height: 200, _fitContentsToBox: true, _showSidebar: true, title: 'overview' });
+        const portal = Docs.Create.FreeformDocument(selected, { x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: 'transparent' });
+        DocUtils.MakeLink({ doc: summary }, { doc: portal }, 'summary of:summarized by', '');
 
         portal.hidden = true;
         this.props.addDocument?.(portal);
         this.props.addLiveTextDocument(summary);
         MarqueeOptionsMenu.Instance.fadeOut(true);
-    }
+    };
 
     @action
     background = (e: KeyboardEvent | React.PointerEvent | undefined) => {
@@ -534,33 +552,33 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         MarqueeOptionsMenu.Instance.fadeOut(true);
         this.hideMarquee();
         setTimeout(() => this.props.selectDocuments([newCollection]));
-    }
+    };
 
     @undoBatch
     marqueeCommand = action((e: KeyboardEvent) => {
         if (this._commandExecuted || (e as any).propagationIsStopped) {
             return;
         }
-        if (e.key === "Backspace" || e.key === "Delete" || e.key === "d") {
+        if (e.key === 'Backspace' || e.key === 'Delete' || e.key === 'd') {
             this._commandExecuted = true;
             e.stopPropagation();
             (e as any).propagationIsStopped = true;
             this.delete();
             e.stopPropagation();
         }
-        if ("cbtsSpg".indexOf(e.key) !== -1) {
+        if ('cbtsSpg'.indexOf(e.key) !== -1) {
             this._commandExecuted = true;
             e.stopPropagation();
             e.preventDefault();
             (e as any).propagationIsStopped = true;
-            if (e.key === "g") this.collection(e, true);
-            if (e.key === "c" || e.key === "t") this.collection(e);
-            if (e.key === "s" || e.key === "S") this.summary(e);
-            if (e.key === "b") this.background(e);
-            if (e.key === "p") this.pileup(e);
+            if (e.key === 'g') this.collection(e, true);
+            if (e.key === 'c' || e.key === 't') this.collection(e);
+            if (e.key === 's' || e.key === 'S') this.summary(e);
+            if (e.key === 'b') this.background(e);
+            if (e.key === 'p') this.pileup(e);
             this.cleanupInteractions(false);
         }
-        if (e.key === "r" || e.key === " ") {
+        if (e.key === 'r' || e.key === ' ') {
             this._commandExecuted = true;
             e.stopPropagation();
             e.preventDefault();
@@ -568,18 +586,17 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         }
     });
 
-    touchesLine(r1: { left: number, top: number, width: number, height: number }) {
+    touchesLine(r1: { left: number; top: number; width: number; height: number }) {
         for (const lassoPt of this._lassoPts) {
             const topLeft = this.Transform.transformPoint(lassoPt[0], lassoPt[1]);
-            if (r1.left < topLeft[0] && topLeft[0] < r1.left + r1.width &&
-                r1.top < topLeft[1] && topLeft[1] < r1.top + r1.height) {
+            if (r1.left < topLeft[0] && topLeft[0] < r1.left + r1.width && r1.top < topLeft[1] && topLeft[1] < r1.top + r1.height) {
                 return true;
             }
         }
         return false;
     }
 
-    boundingShape(r1: { left: number, top: number, width: number, height: number }) {
+    boundingShape(r1: { left: number; top: number; width: number; height: number }) {
         const xs = this._lassoPts.map(pair => pair[0]);
         const ys = this._lassoPts.map(pair => pair[1]);
         const tl = this.Transform.transformPoint(Math.min(...xs), Math.min(...ys));
@@ -592,10 +609,10 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
             let hasRight = false;
             for (const lassoPt of this._lassoPts) {
                 const truePoint = this.Transform.transformPoint(lassoPt[0], lassoPt[1]);
-                hasLeft = hasLeft || (truePoint[0] > tl[0] && truePoint[0] < r1.left) && (truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height);
-                hasTop = hasTop || (truePoint[1] > tl[1] && truePoint[1] < r1.top) && (truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width);
-                hasRight = hasRight || (truePoint[0] < br[0] && truePoint[0] > r1.left + r1.width) && (truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height);
-                hasBottom = hasBottom || (truePoint[1] < br[1] && truePoint[1] > r1.top + r1.height) && (truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width);
+                hasLeft = hasLeft || (truePoint[0] > tl[0] && truePoint[0] < r1.left && truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height);
+                hasTop = hasTop || (truePoint[1] > tl[1] && truePoint[1] < r1.top && truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width);
+                hasRight = hasRight || (truePoint[0] < br[0] && truePoint[0] > r1.left + r1.width && truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height);
+                hasBottom = hasBottom || (truePoint[1] < br[1] && truePoint[1] > r1.top + r1.height && truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width);
                 if (hasTop && hasLeft && hasBottom && hasRight) {
                     return true;
                 }
@@ -615,9 +632,20 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
                 (this.touchesLine(bounds) || this.boundingShape(bounds)) && selection.push(doc);
             }
         };
-        this.props.activeDocuments().filter(doc => !doc.z && !doc._lockedPosition).map(selectFunc);
-        if (!selection.length && selectBackgrounds) this.props.activeDocuments().filter(doc => doc.z === undefined).map(selectFunc);
-        if (!selection.length) this.props.activeDocuments().filter(doc => doc.z !== undefined).map(selectFunc);
+        this.props
+            .activeDocuments()
+            .filter(doc => !doc.z && !doc._lockedPosition)
+            .map(selectFunc);
+        if (!selection.length && selectBackgrounds)
+            this.props
+                .activeDocuments()
+                .filter(doc => doc.z === undefined)
+                .map(selectFunc);
+        if (!selection.length)
+            this.props
+                .activeDocuments()
+                .filter(doc => doc.z !== undefined)
+                .map(selectFunc);
         return selection;
     }
 
@@ -625,31 +653,42 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
         const cpt = this._lassoFreehand || !this._visible ? [0, 0] : [this._downX < this._lastX ? this._downX : this._lastX, this._downY < this._lastY ? this._downY : this._lastY];
         const p = this.props.getContainerTransform().transformPoint(cpt[0], cpt[1]);
         const v = this._lassoFreehand ? [0, 0] : this.props.getContainerTransform().transformDirection(this._lastX - this._downX, this._lastY - this._downY);
-        return <div className="marquee" style={{
-            transform: `translate(${p[0]}px, ${p[1]}px)`,
-            width: Math.abs(v[0]),
-            height: Math.abs(v[1]),
-            zIndex: 2000
-        }}> {this._lassoFreehand ?
-            <svg height={2000} width={2000}>
-                <polyline points={this._lassoPts.reduce((s, pt) => s + pt[0] + "," + pt[1] + " ", "")} fill="none" stroke="black" strokeWidth="1" strokeDasharray="3" />
-            </svg>
-            :
-            <span className="marquee-legend" />}
-        </div>;
+        return (
+            <div
+                className="marquee"
+                style={{
+                    transform: `translate(${p[0]}px, ${p[1]}px)`,
+                    width: Math.abs(v[0]),
+                    height: Math.abs(v[1]),
+                    zIndex: 2000,
+                }}>
+                {' '}
+                {this._lassoFreehand ? (
+                    <svg height={2000} width={2000}>
+                        <polyline points={this._lassoPts.reduce((s, pt) => s + pt[0] + ',' + pt[1] + ' ', '')} fill="none" stroke="black" strokeWidth="1" strokeDasharray="3" />
+                    </svg>
+                ) : (
+                    <span className="marquee-legend" />
+                )}
+            </div>
+        );
     }
 
     render() {
-        return <div className="marqueeView"
-            style={{
-                overflow: StrCast(this.props.Document._overflow),
-                cursor: [InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool) || this._visible || PresBox.startMarquee ? "crosshair" : "pointer"
-            }}
-
-            onDragOver={e => e.preventDefault()}
-            onScroll={(e) => e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0} onClick={this.onClick} onPointerDown={this.onPointerDown}>
-            {this._visible ? this.marqueeDiv : null}
-            {this.props.children}
-        </div>;
+        return (
+            <div
+                className="marqueeView"
+                style={{
+                    overflow: StrCast(this.props.Document._overflow),
+                    cursor: [InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool) || this._visible || PresBox.startMarquee ? 'crosshair' : 'pointer',
+                }}
+                onDragOver={e => e.preventDefault()}
+                onScroll={e => (e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0)}
+                onClick={this.onClick}
+                onPointerDown={this.onPointerDown}>
+                {this._visible ? this.marqueeDiv : null}
+                {this.props.children}
+            </div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 8adfdc70b..0d7d67dd8 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -7,20 +7,17 @@ import { Doc, HeightSym, Opt, WidthSym } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, returnEmptyDoclist, returnTrue, Utils } from '../../../../Utils';
-import { DocUtils } from '../../../documents/Documents';
-import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
+import { CollectionViewType } from '../../../documents/DocumentTypes';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
 import { Transform } from '../../../util/Transform';
 import { Colors, Shadows } from '../../global/globalEnums';
-import { AudioBox } from '../../nodes/AudioBox';
 import { DocumentLinksButton } from '../../nodes/DocumentLinksButton';
 import { DocumentView } from '../../nodes/DocumentView';
 import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup';
 import { StyleProp } from '../../StyleProvider';
 import { CollectionStackedTimeline } from '../CollectionStackedTimeline';
 import { CollectionSubView } from '../CollectionSubView';
-import { CollectionViewType } from '../CollectionView';
 import './CollectionLinearView.scss';
 
 /**
@@ -228,7 +225,7 @@ export class CollectionLinearView extends CollectionSubView() {
                         }}>
                         {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
                     </div>
-                    {!DocumentLinksButton.StartLink || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? null : (
+                    {!DocumentLinksButton.StartLink || this.layoutDoc !== Doc.MyDockedBtns ? null : (
                         <span className="bottomPopup-background" style={{ pointerEvents: 'all' }} onPointerDown={e => e.stopPropagation()}>
                             <span className="bottomPopup-text">
                                 Creating link from:{' '}
@@ -263,7 +260,7 @@ export class CollectionLinearView extends CollectionSubView() {
                             </Tooltip>
                         </span>
                     )}
-                    {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== CurrentUserUtils.MyDockedBtns ? null : (
+                    {!CollectionStackedTimeline.CurrentlyPlaying || !CollectionStackedTimeline.CurrentlyPlaying.length || this.layoutDoc !== Doc.MyDockedBtns ? null : (
                         <span className="bottomPopup-background">
                             <span className="bottomPopup-text">
                                 Currently playing:
diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx
index 1e7f4f10b..ed856a4ab 100644
--- a/src/client/views/linking/LinkMenuItem.tsx
+++ b/src/client/views/linking/LinkMenuItem.tsx
@@ -11,6 +11,7 @@ import { DocumentType } from '../../documents/DocumentTypes';
 import { DocumentManager } from '../../util/DocumentManager';
 import { DragManager } from '../../util/DragManager';
 import { Hypothesis } from '../../util/HypothesisUtils';
+import { LinkFollower } from '../../util/LinkFollower';
 import { LinkManager } from '../../util/LinkManager';
 import { undoBatch } from '../../util/UndoManager';
 import { DocumentView } from '../nodes/DocumentView';
@@ -118,7 +119,7 @@ export class LinkMenuItem extends React.Component<LinkMenuItemProps> {
                             : undefined;
 
                     if (focusDoc) this.props.docView.ComponentView?.scrollFocus?.(focusDoc, true);
-                    LinkManager.FollowLink(this.props.linkDoc, this.props.sourceDoc, this.props.docView.props, false);
+                    LinkFollower.FollowLink(this.props.linkDoc, this.props.sourceDoc, this.props.docView.props, false);
                 }
             }
         );
diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx
index a6f6bd35f..0bcb68f82 100644
--- a/src/client/views/linking/LinkPopup.tsx
+++ b/src/client/views/linking/LinkPopup.tsx
@@ -1,17 +1,16 @@
 import { action, observable } from 'mobx';
-import { observer } from "mobx-react";
+import { observer } from 'mobx-react';
 import { EditorView } from 'prosemirror-view';
+import { Doc } from '../../../fields/Doc';
 import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../Utils';
 import { DocUtils } from '../../documents/Documents';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { Transform } from '../../util/Transform';
 import { undoBatch } from '../../util/UndoManager';
 import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
 import { SearchBox } from '../search/SearchBox';
 import { DefaultStyleProvider } from '../StyleProvider';
 import './LinkPopup.scss';
-import React = require("react");
-import { Doc, Opt } from '../../../fields/Doc';
+import React = require('react');
 
 interface LinkPopupProps {
     showPopup: boolean;
@@ -23,33 +22,30 @@ interface LinkPopupProps {
 }
 
 /**
- * Popup component for creating links from text to Dash documents 
+ * Popup component for creating links from text to Dash documents
  */
 
 @observer
 export class LinkPopup extends React.Component<LinkPopupProps> {
-    @observable private linkURL: string = "";
+    @observable private linkURL: string = '';
     @observable public view?: EditorView;
 
-
-
     // TODO: should check for valid URL
     @undoBatch
     makeLinkToURL = (target: string, lcoation: string) => {
-        ((this.view as any)?.TextView as FormattedTextBox).makeLinkAnchor(undefined, "onRadd:rightight", target, target);
-    }
+        ((this.view as any)?.TextView as FormattedTextBox).makeLinkAnchor(undefined, 'onRadd:rightight', target, target);
+    };
 
     @action
     onLinkChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         this.linkURL = e.target.value;
-    }
-
+    };
 
     getPWidth = () => 500;
     getPHeight = () => 500;
 
     render() {
-        const popupVisibility = this.props.showPopup ? "block" : "none";
+        const popupVisibility = this.props.showPopup ? 'block' : 'none';
         const linkDoc = this.props.linkFrom ? this.props.linkFrom : undefined;
         return (
             <div className="linkPopup-container" style={{ display: popupVisibility }}>
@@ -68,8 +64,8 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
                         className="linkPopup-searchBox searchBox-input" /> */}
 
                     <SearchBox
-                        Document={CurrentUserUtils.MySearcher}
-                        DataDoc={CurrentUserUtils.MySearcher}
+                        Document={Doc.MySearcher}
+                        DataDoc={Doc.MySearcher}
                         linkFrom={linkDoc}
                         linkSearch={true}
                         fieldKey="data"
@@ -96,9 +92,10 @@ export class LinkPopup extends React.Component<LinkPopupProps> {
                         docRangeFilters={returnEmptyFilter}
                         searchFilterDocs={returnEmptyDoclist}
                         ContainingCollectionView={undefined}
-                        ContainingCollectionDoc={undefined} />
+                        ContainingCollectionDoc={undefined}
+                    />
                 </div>
             </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index c42c2306a..8437736ae 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -1,31 +1,29 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { DateField } from "../../../fields/DateField";
-import { Doc, DocListCast } from "../../../fields/Doc";
-import { ComputedField } from "../../../fields/ScriptField";
-import { Cast, DateCast, NumCast } from "../../../fields/Types";
-import { AudioField, nullAudio } from "../../../fields/URLField";
-import { emptyFunction, formatTime, OmitKeys, returnFalse, setupMoveUpEvents } from "../../../Utils";
-import { DocUtils } from "../../documents/Documents";
-import { Networking } from "../../Network";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { DragManager } from "../../util/DragManager";
-import { undoBatch } from "../../util/UndoManager";
-import { CollectionStackedTimeline, TrimScope } from "../collections/CollectionStackedTimeline";
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
-import "./AudioBox.scss";
-import { FieldView, FieldViewProps } from "./FieldView";
-
+import React = require('react');
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, IReactionDisposer, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { DateField } from '../../../fields/DateField';
+import { Doc, DocListCast } from '../../../fields/Doc';
+import { ComputedField } from '../../../fields/ScriptField';
+import { Cast, DateCast, NumCast } from '../../../fields/Types';
+import { AudioField, nullAudio } from '../../../fields/URLField';
+import { emptyFunction, formatTime, OmitKeys, returnFalse, setupMoveUpEvents } from '../../../Utils';
+import { DocUtils } from '../../documents/Documents';
+import { Networking } from '../../Network';
+import { DragManager } from '../../util/DragManager';
+import { undoBatch } from '../../util/UndoManager';
+import { CollectionStackedTimeline, TrimScope } from '../collections/CollectionStackedTimeline';
+import { ContextMenu } from '../ContextMenu';
+import { ContextMenuProps } from '../ContextMenuItem';
+import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
+import './AudioBox.scss';
+import { FieldView, FieldViewProps } from './FieldView';
 
 /**
  * AudioBox
  * Main component: AudioBox.tsx
  * Supporting Components: CollectionStackedTimeline, AudioWaveform
- * 
+ *
  * AudioBox is a node that supports the recording and playback of audio files in Dash.
  * When an audio file is importeed into Dash, it is immediately rendered as an AudioBox document.
  * When a blank AudioBox node is created in Dash, audio recording controls are displayed and the user can start a recording which can be paused or stopped, and can use dictation to create a text transcript.
@@ -34,24 +32,23 @@ import { FieldView, FieldViewProps } from "./FieldView";
  * User can trim audio: nondestructive, just sets new bounds for playback and rendering timelin
  */
 
-
 // used as a wrapper class for MediaStream from MediaDevices API
 declare class MediaRecorder {
     constructor(e: any); // whatever MediaRecorder has
 }
 
 enum media_state {
-    PendingRecording = "pendingRecording",
-    Recording = "recording",
-    Paused = "paused",
-    Playing = "playing"
+    PendingRecording = 'pendingRecording',
+    Recording = 'recording',
+    Paused = 'paused',
+    Playing = 'playing',
 }
 
-
 @observer
 export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
-
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(AudioBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(AudioBox, fieldKey);
+    }
     public static Enabled = false;
 
     static topControlsHeight = 30; // height of upper controls above timeline
@@ -73,27 +70,41 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     @observable _muted: boolean = false;
     @observable _paused: boolean = false; // is recording paused
     // @observable rawDuration: number = 0; // computed from the length of the audio element when loaded
-    @computed get recordingStart() { return DateCast(this.dataDoc[this.fieldKey + "-recordingStart"])?.date.getTime(); }
-    @computed get rawDuration() { return NumCast(this.dataDoc[`${this.fieldKey}-duration`]); } // bcz: shouldn't be needed since it's computed from audio element
+    @computed get recordingStart() {
+        return DateCast(this.dataDoc[this.fieldKey + '-recordingStart'])?.date.getTime();
+    }
+    @computed get rawDuration() {
+        return NumCast(this.dataDoc[`${this.fieldKey}-duration`]);
+    } // bcz: shouldn't be needed since it's computed from audio element
     // mehek: not 100% sure but i think due to the order in which things are loaded this is necessary ^^
     // if you get rid of it and set the value to 0 the timeline and waveform will set their bounds incorrectly
 
-    @computed get miniPlayer() { return this.props.PanelHeight() < 50; } // used to collapse timeline when node is shrunk
-    @computed get links() { return DocListCast(this.dataDoc.links); }
-    @computed get mediaState() { return this.dataDoc.mediaState as media_state; }
-    @computed get path() { // returns the path of the audio file
-        const path = Cast(this.props.Document[this.fieldKey], AudioField, null)?.url.href || "";
-        return path === nullAudio ? "" : path;
+    @computed get miniPlayer() {
+        return this.props.PanelHeight() < 50;
+    } // used to collapse timeline when node is shrunk
+    @computed get links() {
+        return DocListCast(this.dataDoc.links);
+    }
+    @computed get mediaState() {
+        return this.dataDoc.mediaState as media_state;
+    }
+    @computed get path() {
+        // returns the path of the audio file
+        const path = Cast(this.props.Document[this.fieldKey], AudioField, null)?.url.href || '';
+        return path === nullAudio ? '' : path;
+    }
+    set mediaState(value) {
+        this.dataDoc.mediaState = value;
     }
-    set mediaState(value) { this.dataDoc.mediaState = value; }
-
-    @computed get timeline() { return this._stackedTimeline; } // returns CollectionStackedTimeline ref
 
+    @computed get timeline() {
+        return this._stackedTimeline;
+    } // returns CollectionStackedTimeline ref
 
     componentWillUnmount() {
         this.removeCurrentlyPlaying();
         this._dropDisposer?.();
-        Object.values(this._disposers).forEach((disposer) => disposer?.());
+        Object.values(this._disposers).forEach(disposer => disposer?.());
 
         this.mediaState === media_state.Recording && this.stopRecording();
     }
@@ -110,14 +121,10 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         }
     }
 
-
     getLinkData(l: Doc) {
         let la1 = l.anchor1 as Doc;
         let la2 = l.anchor2 as Doc;
-        const linkTime =
-            this.timeline?.anchorStart(la2) ||
-            this.timeline?.anchorStart(la1) ||
-            0;
+        const linkTime = this.timeline?.anchorStart(la2) || this.timeline?.anchorStart(la1) || 0;
         if (Doc.AreProtosEqual(la1, this.dataDoc)) {
             la1 = l.anchor2 as Doc;
             la2 = l.anchor1 as Doc;
@@ -126,20 +133,17 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     }
 
     getAnchor = () => {
-        return CollectionStackedTimeline.createAnchor(
-            this.rootDoc,
-            this.dataDoc,
-            this.annotationKey,
-            "_timecodeToShow" /* audioStart */,
-            "_timecodeToHide" /* audioEnd */,
-            this._ele?.currentTime ||
-            Cast(this.props.Document._currentTimecode, "number", null) ||
-            (this.mediaState === media_state.Recording
-                ? (Date.now() - (this.recordingStart || 0)) / 1000
-                : undefined)
-        ) || this.rootDoc;
-    }
-
+        return (
+            CollectionStackedTimeline.createAnchor(
+                this.rootDoc,
+                this.dataDoc,
+                this.annotationKey,
+                '_timecodeToShow' /* audioStart */,
+                '_timecodeToHide' /* audioEnd */,
+                this._ele?.currentTime || Cast(this.props.Document._currentTimecode, 'number', null) || (this.mediaState === media_state.Recording ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined)
+            ) || this.rootDoc
+        );
+    };
 
     // updates timecode and shows it in timeline, follows links at time
     @action
@@ -148,24 +152,23 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             this.links
                 .map(l => this.getLinkData(l))
                 .forEach(({ la1, la2, linkTime }) => {
-                    if (linkTime > NumCast(this.layoutDoc._currentTimecode) &&
-                        linkTime < this._ele!.currentTime) {
+                    if (linkTime > NumCast(this.layoutDoc._currentTimecode) && linkTime < this._ele!.currentTime) {
                         Doc.linkFollowHighlight(la1);
                     }
                 });
             this.layoutDoc._currentTimecode = this._ele.currentTime;
             this.timeline?.scrollToTime(NumCast(this.layoutDoc._currentTimecode));
         }
-    }
+    };
 
     // play back the audio from seekTimeInSeconds, fullPlay tells whether clip is being played to end vs link range
     @action
     playFrom = (seekTimeInSeconds: number, endTime?: number, fullPlay: boolean = false) => {
         clearTimeout(this._play); // abort any previous clip ending
-        if (Number.isNaN(this._ele?.duration)) { // audio element isn't loaded yet... wait 1/2 second and try again
+        if (Number.isNaN(this._ele?.duration)) {
+            // audio element isn't loaded yet... wait 1/2 second and try again
             setTimeout(() => this.playFrom(seekTimeInSeconds, endTime), 500);
-        }
-        else if (this.timeline && this._ele && AudioBox.Enabled) {
+        } else if (this.timeline && this._ele && AudioBox.Enabled) {
             // trimBounds override requested playback bounds
             const end = Math.min(this.timeline.trimEnd, endTime ?? this.timeline.trimEnd);
             const start = Math.max(this.timeline.trimStart, seekTimeInSeconds);
@@ -175,21 +178,18 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 this._ele.play();
                 this.mediaState = media_state.Playing;
                 this.addCurrentlyPlaying();
-                this._play = setTimeout(
-                    () => {
-                        // need to keep track of if end of clip is reached so on next play, clip restarts
-                        if (fullPlay) this._finished = true;
-                        // removes from currently playing if playback has reached end of range marker
-                        else this.removeCurrentlyPlaying();
-                        this.Pause();
-                    },
-                    (end - start) * 1000);
+                this._play = setTimeout(() => {
+                    // need to keep track of if end of clip is reached so on next play, clip restarts
+                    if (fullPlay) this._finished = true;
+                    // removes from currently playing if playback has reached end of range marker
+                    else this.removeCurrentlyPlaying();
+                    this.Pause();
+                }, (end - start) * 1000);
             } else {
                 this.Pause();
             }
         }
-    }
-
+    };
 
     // removes from currently playing display
     @action
@@ -198,7 +198,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc);
             index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1);
         }
-    }
+    };
 
     // adds doc to currently playing display
     @action
@@ -209,8 +209,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         if (CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc) === -1) {
             CollectionStackedTimeline.CurrentlyPlaying.push(this.layoutDoc);
         }
-    }
-
+    };
 
     // update the recording time
     updateRecordTime = () => {
@@ -220,13 +219,13 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 this.layoutDoc._currentTimecode = (new Date().getTime() - this._recordStart - this._pausedTime) / 1000;
             }
         }
-    }
+    };
 
     // starts recording
     recordAudioAnnotation = async () => {
         this._stream = await navigator.mediaDevices.getUserMedia({ audio: true });
         this._recorder = new MediaRecorder(this._stream);
-        this.dataDoc[this.fieldKey + "-recordingStart"] = new DateField();
+        this.dataDoc[this.fieldKey + '-recordingStart'] = new DateField();
         DocUtils.ActiveRecordings.push(this);
         this._recorder.ondataavailable = async (e: any) => {
             const [{ result }] = await Networking.UploadFilesToServer(e.data);
@@ -235,11 +234,11 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             }
         };
         this._recordStart = new Date().getTime();
-        runInAction(() => this.mediaState = media_state.Recording);
+        runInAction(() => (this.mediaState = media_state.Recording));
         setTimeout(this.updateRecordTime);
         this._recorder.start();
         setTimeout(this.stopRecording, 60 * 60 * 1000); // stop after an hour
-    }
+    };
 
     // stops recording
     @action
@@ -249,52 +248,59 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             this._recorder = undefined;
             const now = new Date().getTime();
             this._paused && (this._pausedTime += now - this._pauseStart);
-            this.dataDoc[this.fieldKey + "-duration"] = (now - this._recordStart - this._pausedTime) / 1000;
+            this.dataDoc[this.fieldKey + '-duration'] = (now - this._recordStart - this._pausedTime) / 1000;
             this.mediaState = media_state.Paused;
             this._stream?.getAudioTracks()[0].stop();
             const ind = DocUtils.ActiveRecordings.indexOf(this);
             ind !== -1 && DocUtils.ActiveRecordings.splice(ind, 1);
         }
-    }
-
+    };
 
     // context menu
     specificContextMenu = (e: React.MouseEvent): void => {
         const funcs: ContextMenuProps[] = [];
         funcs.push({
-            description: (this.layoutDoc.hideAnchors ? "Don't hide" : "Hide") + " anchors",
-            event: e => this.layoutDoc.hideAnchors = !this.layoutDoc.hideAnchors,
-            icon: "expand-arrows-alt",
+            description: (this.layoutDoc.hideAnchors ? "Don't hide" : 'Hide') + ' anchors',
+            event: e => (this.layoutDoc.hideAnchors = !this.layoutDoc.hideAnchors),
+            icon: 'expand-arrows-alt',
         });
         funcs.push({
-            description: (this.layoutDoc.dontAutoFollowLinks ? "" : "Don't") + " follow links when encountered",
-            event: e => this.layoutDoc.dontAutoFollowLinks = !this.layoutDoc.dontAutoFollowLinks,
-            icon: "expand-arrows-alt",
+            description: (this.layoutDoc.dontAutoFollowLinks ? '' : "Don't") + ' follow links when encountered',
+            event: e => (this.layoutDoc.dontAutoFollowLinks = !this.layoutDoc.dontAutoFollowLinks),
+            icon: 'expand-arrows-alt',
         });
         funcs.push({
-            description: (this.layoutDoc.dontAutoPlayFollowedLinks ? "" : "Don't") + " play when link is selected",
-            event: e => this.layoutDoc.dontAutoPlayFollowedLinks = !this.layoutDoc.dontAutoPlayFollowedLinks,
-            icon: "expand-arrows-alt",
+            description: (this.layoutDoc.dontAutoPlayFollowedLinks ? '' : "Don't") + ' play when link is selected',
+            event: e => (this.layoutDoc.dontAutoPlayFollowedLinks = !this.layoutDoc.dontAutoPlayFollowedLinks),
+            icon: 'expand-arrows-alt',
         });
         funcs.push({
-            description: (this.layoutDoc.autoPlayAnchors ? "Don't auto" : "Auto") + " play anchors onClick",
-            event: e => this.layoutDoc.autoPlayAnchors = !this.layoutDoc.autoPlayAnchors,
-            icon: "expand-arrows-alt",
+            description: (this.layoutDoc.autoPlayAnchors ? "Don't auto" : 'Auto') + ' play anchors onClick',
+            event: e => (this.layoutDoc.autoPlayAnchors = !this.layoutDoc.autoPlayAnchors),
+            icon: 'expand-arrows-alt',
         });
         ContextMenu.Instance?.addItem({
-            description: "Options...",
+            description: 'Options...',
             subitems: funcs,
-            icon: "asterisk",
+            icon: 'asterisk',
         });
-    }
-
+    };
 
     // button for starting and stopping the recording
     Record = (e: React.PointerEvent) => {
-        e.button === 0 && !e.ctrlKey && setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => {
-            this._recorder ? this.stopRecording() : this.recordAudioAnnotation();
-        }), false);
-    }
+        e.button === 0 &&
+            !e.ctrlKey &&
+            setupMoveUpEvents(
+                this,
+                e,
+                returnFalse,
+                returnFalse,
+                action(() => {
+                    this._recorder ? this.stopRecording() : this.recordAudioAnnotation();
+                }),
+                false
+            );
+    };
 
     // for play button
     Play = (e?: any) => {
@@ -314,7 +320,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
             this.playFrom(start, this.timeline.trimEnd, true);
         }
-    }
+    };
 
     // pause play back
     @action
@@ -327,60 +333,73 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             if (!this._finished) clearTimeout(this._play);
             this.removeCurrentlyPlaying();
         }
-    }
+    };
 
     // for dictation button, creates a text document for dictation
     onFile = (e: any) => {
-        setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => {
-            const newDoc = CurrentUserUtils.GetNewTextDoc(
-                "",
-                NumCast(this.rootDoc.x),
-                NumCast(this.rootDoc.y) +
-                NumCast(this.layoutDoc._height) +
-                10,
-                NumCast(this.layoutDoc._width),
-                2 * NumCast(this.layoutDoc._height)
-            );
-            Doc.GetProto(newDoc).recordingSource = this.dataDoc;
-            Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.fieldKey}-recordingStart"]`);
-            Doc.GetProto(newDoc).mediaState = ComputedField.MakeFunction("self.recordingSource.mediaState");
-            if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.rootDoc)) {
-                newDoc.x = this.rootDoc.x;
-                newDoc.y = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
-                Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, newDoc);
-            } else {
-                this.props.addDocument?.(newDoc);
-            }
-        }), false);
-    }
-
+        setupMoveUpEvents(
+            this,
+            e,
+            returnFalse,
+            returnFalse,
+            action(() => {
+                const newDoc = DocUtils.GetNewTextDoc('', NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
+                Doc.GetProto(newDoc).recordingSource = this.dataDoc;
+                Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.fieldKey}-recordingStart"]`);
+                Doc.GetProto(newDoc).mediaState = ComputedField.MakeFunction('self.recordingSource.mediaState');
+                if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.rootDoc)) {
+                    newDoc.x = this.rootDoc.x;
+                    newDoc.y = NumCast(this.rootDoc.y) + NumCast(this.rootDoc._height);
+                    Doc.AddDocToList(Doc.MyOverlayDocs, undefined, newDoc);
+                } else {
+                    this.props.addDocument?.(newDoc);
+                }
+            }),
+            false
+        );
+    };
 
     // sets <audio> ref for updating time
     setRef = (e: HTMLAudioElement | null) => {
-        e?.addEventListener("timeupdate", this.timecodeChanged);
-        e?.addEventListener("ended", () => { this._finished = true; this.Pause(); });
+        e?.addEventListener('timeupdate', this.timecodeChanged);
+        e?.addEventListener('ended', () => {
+            this._finished = true;
+            this.Pause();
+        });
         this._ele = e;
-    }
-
+    };
 
     // pause the time during recording phase
     recordPause = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => {
-            this._pauseStart = new Date().getTime();
-            this._paused = true;
-            this._recorder.pause();
-        }), false);
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            returnFalse,
+            returnFalse,
+            action(() => {
+                this._pauseStart = new Date().getTime();
+                this._paused = true;
+                this._recorder.pause();
+            }),
+            false
+        );
+    };
 
     // continue the recording
     recordPlay = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, returnFalse, returnFalse, action(() => {
-            this._paused = false;
-            this._pausedTime += new Date().getTime() - this._pauseStart;
-            this._recorder.resume();
-        }), false);
-    }
-
+        setupMoveUpEvents(
+            this,
+            e,
+            returnFalse,
+            returnFalse,
+            action(() => {
+                this._paused = false;
+                this._pausedTime += new Date().getTime() - this._pauseStart;
+                this._recorder.resume();
+            }),
+            false
+        );
+    };
 
     // plays link
     playLink = (link: Doc) => {
@@ -392,8 +411,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             }
         } else {
             this.links
-                .filter((l) => l.anchor1 === link || l.anchor2 === link)
-                .forEach((l) => {
+                .filter(l => l.anchor1 === link || l.anchor2 === link)
+                .forEach(l => {
                     const { la1, la2 } = this.getLinkData(l);
                     const startTime = this.timeline?.anchorStart(la1) || this.timeline?.anchorStart(la2);
                     const endTime = this.timeline?.anchorEnd(la1) || this.timeline?.anchorEnd(la2);
@@ -406,17 +425,14 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     }
                 });
         }
-    }
-
+    };
 
     @action
-    timelineWhenChildContentsActiveChanged = (isActive: boolean) =>
-        this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive)
+    timelineWhenChildContentsActiveChanged = (isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive));
 
-    timelineScreenToLocal = () =>
-        this.props.ScreenToLocalTransform().translate(0, -AudioBox.bottomControlsHeight)
+    timelineScreenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -AudioBox.bottomControlsHeight);
 
-    setPlayheadTime = (time: number) => this._ele!.currentTime = this.layoutDoc._currentTimecode = time;
+    setPlayheadTime = (time: number) => (this._ele!.currentTime = this.layoutDoc._currentTimecode = time);
 
     playing = () => this.mediaState === media_state.Playing;
 
@@ -424,7 +440,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     // timeline dimensions
     timelineWidth = () => this.props.PanelWidth();
-    timelineHeight = () => (this.props.PanelHeight() - (AudioBox.topControlsHeight + AudioBox.bottomControlsHeight));
+    timelineHeight = () => this.props.PanelHeight() - (AudioBox.topControlsHeight + AudioBox.bottomControlsHeight);
 
     // ends trim, hides trim controls and displays new clip
     @undoBatch
@@ -432,32 +448,38 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this.Pause();
         this.setPlayheadTime(Math.max(Math.min(this.timeline?.trimEnd || 0, this._ele!.currentTime), this.timeline?.trimStart || 0));
         this.timeline?.StopTrimming();
-    }
+    };
 
     // displays trim controls to start trimming clip
     startTrim = (scope: TrimScope) => {
         this.Pause();
         this.timeline?.StartTrimming(scope);
-    }
+    };
 
     // for trim button, double click displays full clip, single displays curr trim bounds
     onClipPointerDown = (e: React.PointerEvent) => {
         e.stopPropagation();
-        this.timeline && setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => {
-            if (doubleTap) {
-                this.startTrim(TrimScope.All);
-            } else if (this.timeline) {
-                this.Pause();
-                this.timeline.IsTrimming !== TrimScope.None ? this.finishTrim() : this.startTrim(TrimScope.Clip);
-            }
-        }));
-    }
-
+        this.timeline &&
+            setupMoveUpEvents(
+                this,
+                e,
+                returnFalse,
+                returnFalse,
+                action((e: PointerEvent, doubleTap?: boolean) => {
+                    if (doubleTap) {
+                        this.startTrim(TrimScope.All);
+                    } else if (this.timeline) {
+                        this.Pause();
+                        this.timeline.IsTrimming !== TrimScope.None ? this.finishTrim() : this.startTrim(TrimScope.Clip);
+                    }
+                })
+            );
+    };
 
     // for zoom slider, sets timeline waveform zoom
     zoom = (zoom: number) => {
         this.timeline?.setZoom(zoom);
-    }
+    };
 
     // for volume slider sets volume
     @action
@@ -469,7 +491,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 this.toggleMute();
             }
         }
-    }
+    };
 
     // toggles audio muted
     @action
@@ -478,135 +500,156 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             this._muted = !this._muted;
             this._ele.muted = this._muted;
         }
-    }
-
+    };
 
     setupTimelineDrop = (r: HTMLDivElement | null) => {
         if (r && this.timeline) {
             this._dropDisposer?.();
-            this._dropDisposer = DragManager.MakeDropTarget(r,
+            this._dropDisposer = DragManager.MakeDropTarget(
+                r,
                 (e, de) => {
                     const [xp, yp] = this.props.ScreenToLocalTransform().transformPoint(de.x, de.y);
                     de.complete.docDragData && this.timeline.internalDocDrop(e, de, de.complete.docDragData, xp);
                 },
-                this.layoutDoc, undefined);
+                this.layoutDoc,
+                undefined
+            );
         }
-    }
-
+    };
 
     // UI for recording, initially displayed when new audio created in Dash
     @computed get recordingControls() {
-        return <div className="audiobox-recorder">
-            <div className="audiobox-dictation" onPointerDown={this.onFile}>
-                <FontAwesomeIcon
-                    size="2x"
-                    icon="file-alt" />
-            </div>
-            {[media_state.Recording, media_state.Playing].includes(this.mediaState) ?
-                <div className="recording-controls" onClick={e => e.stopPropagation()}>
-                    <div className="record-button" onPointerDown={this.Record}>
-                        <FontAwesomeIcon
-                            size="2x"
-                            icon="stop" />
-                    </div>
-                    <div className="record-button" onPointerDown={this._paused ? this.recordPlay : this.recordPause}>
-                        <FontAwesomeIcon
-                            size="2x"
-                            icon={this._paused ? "play" : "pause"} />
+        return (
+            <div className="audiobox-recorder">
+                <div className="audiobox-dictation" onPointerDown={this.onFile}>
+                    <FontAwesomeIcon size="2x" icon="file-alt" />
+                </div>
+                {[media_state.Recording, media_state.Playing].includes(this.mediaState) ? (
+                    <div className="recording-controls" onClick={e => e.stopPropagation()}>
+                        <div className="record-button" onPointerDown={this.Record}>
+                            <FontAwesomeIcon size="2x" icon="stop" />
+                        </div>
+                        <div className="record-button" onPointerDown={this._paused ? this.recordPlay : this.recordPause}>
+                            <FontAwesomeIcon size="2x" icon={this._paused ? 'play' : 'pause'} />
+                        </div>
+                        <div className="record-timecode">{formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode)))}</div>
                     </div>
-                    <div className="record-timecode">
-                        {formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode)))}
+                ) : (
+                    <div className="audiobox-start-record" onPointerDown={this.Record}>
+                        <FontAwesomeIcon icon="microphone" />
+                        RECORD
                     </div>
-                </div>
-                :
-                <div className="audiobox-start-record" onPointerDown={this.Record}>
-                    <FontAwesomeIcon icon="microphone" />
-                    RECORD
-                </div>}
-        </div>;
+                )}
+            </div>
+        );
     }
 
     // UI for playback, displayed for imported or recorded clips, hides timeline and collapses controls when node is shrunk vertically
     @computed get playbackControls() {
-        return <div className="audiobox-file" style={{
-            pointerEvents: this._isAnyChildContentActive || this.props.isContentActive() ? "all" : "none",
-            flexDirection: this.miniPlayer ? "row" : "column",
-            justifyContent: this.miniPlayer ? "flex-start" : "space-between"
-        }}>
-            <div className="audiobox-controls">
-                <div className="controls-left">
-                    <div className="audiobox-button"
-                        title={this.mediaState === media_state.Paused ? "play" : "pause"}
-                        onPointerDown={this.mediaState === media_state.Paused ? this.Play : (e) => { e.stopPropagation(); this.Pause(); }}>
-                        <FontAwesomeIcon icon={this.mediaState === media_state.Paused ? "play" : "pause"} size={"1x"} />
+        return (
+            <div
+                className="audiobox-file"
+                style={{
+                    pointerEvents: this._isAnyChildContentActive || this.props.isContentActive() ? 'all' : 'none',
+                    flexDirection: this.miniPlayer ? 'row' : 'column',
+                    justifyContent: this.miniPlayer ? 'flex-start' : 'space-between',
+                }}>
+                <div className="audiobox-controls">
+                    <div className="controls-left">
+                        <div
+                            className="audiobox-button"
+                            title={this.mediaState === media_state.Paused ? 'play' : 'pause'}
+                            onPointerDown={
+                                this.mediaState === media_state.Paused
+                                    ? this.Play
+                                    : e => {
+                                          e.stopPropagation();
+                                          this.Pause();
+                                      }
+                            }>
+                            <FontAwesomeIcon icon={this.mediaState === media_state.Paused ? 'play' : 'pause'} size={'1x'} />
+                        </div>
+
+                        {!this.miniPlayer && (
+                            <div className="audiobox-button" title={this.timeline?.IsTrimming !== TrimScope.None ? 'finish' : 'trim'} onPointerDown={this.onClipPointerDown}>
+                                <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? 'check' : 'cut'} size={'1x'} />
+                            </div>
+                        )}
                     </div>
-
-                    {!this.miniPlayer &&
-                        <div className="audiobox-button"
-                            title={this.timeline?.IsTrimming !== TrimScope.None ? "finish" : "trim"}
-                            onPointerDown={this.onClipPointerDown}>
-                            <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? "check" : "cut"} size={"1x"} />
-                        </div>}
-                </div>
-                <div className="controls-right">
-                    <div className="audiobox-button"
-                        title={this._muted ? "unmute" : "mute"}
-                        onPointerDown={(e) => { e.stopPropagation(); this.toggleMute(); }}>
-                        <FontAwesomeIcon icon={this._muted ? "volume-mute" : "volume-up"} />
+                    <div className="controls-right">
+                        <div
+                            className="audiobox-button"
+                            title={this._muted ? 'unmute' : 'mute'}
+                            onPointerDown={e => {
+                                e.stopPropagation();
+                                this.toggleMute();
+                            }}>
+                            <FontAwesomeIcon icon={this._muted ? 'volume-mute' : 'volume-up'} />
+                        </div>
+                        <input
+                            type="range"
+                            step="0.1"
+                            min="0"
+                            max="1"
+                            value={this._muted ? 0 : this._volume}
+                            className="toolbar-slider volume"
+                            onPointerDown={(e: React.PointerEvent) => {
+                                e.stopPropagation();
+                            }}
+                            onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setVolume(Number(e.target.value))}
+                        />
                     </div>
-                    <input type="range" step="0.1" min="0" max="1" value={this._muted ? 0 : this._volume}
-                        className="toolbar-slider volume"
-                        onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
-                        onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setVolume(Number(e.target.value))}
-                    />
-                </div>
-            </div>
-
-            <div className="audiobox-playback" style={{ width: this.miniPlayer ? 0 : "100%" }}>
-                <div className="audiobox-timeline">
-                    {this.renderTimeline}
                 </div>
-            </div>
-
-            {this.audio}
 
-            <div className="audiobox-timecodes">
-                <div className="timecode-current">
-                    {this.timeline && formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode) - NumCast(this.timeline.clipStart)))}
+                <div className="audiobox-playback" style={{ width: this.miniPlayer ? 0 : '100%' }}>
+                    <div className="audiobox-timeline">{this.renderTimeline}</div>
                 </div>
-                {this.miniPlayer ?
-                    <div>/</div>
-                    :
-                    <div className="bottom-controls-middle">
-                        <FontAwesomeIcon icon="search-plus" />
-                        <input type="range" step="0.1" min="1" max="5" value={this.timeline?._zoomFactor}
-                            className="toolbar-slider" id="zoom-slider"
-                            onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
-                            onChange={(e: React.ChangeEvent<HTMLInputElement>) => { this.zoom(Number(e.target.value)); }}
-                        />
-                    </div>}
 
-                <div className="timecode-duration">
-                    {this.timeline && formatTime(Math.round(this.timeline.clipDuration))}
+                {this.audio}
+
+                <div className="audiobox-timecodes">
+                    <div className="timecode-current">{this.timeline && formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode) - NumCast(this.timeline.clipStart)))}</div>
+                    {this.miniPlayer ? (
+                        <div>/</div>
+                    ) : (
+                        <div className="bottom-controls-middle">
+                            <FontAwesomeIcon icon="search-plus" />
+                            <input
+                                type="range"
+                                step="0.1"
+                                min="1"
+                                max="5"
+                                value={this.timeline?._zoomFactor}
+                                className="toolbar-slider"
+                                id="zoom-slider"
+                                onPointerDown={(e: React.PointerEvent) => {
+                                    e.stopPropagation();
+                                }}
+                                onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
+                                    this.zoom(Number(e.target.value));
+                                }}
+                            />
+                        </div>
+                    )}
+
+                    <div className="timecode-duration">{this.timeline && formatTime(Math.round(this.timeline.clipDuration))}</div>
                 </div>
             </div>
-
-
-        </div>;
+        );
     }
 
     // gets CollectionStackedTimeline
     @computed get renderTimeline() {
         return (
             <CollectionStackedTimeline
-                ref={action((r: any) => this._stackedTimeline = r)}
-                {...OmitKeys(this.props, ["CollectionFreeFormDocumentView"]).omit}
+                ref={action((r: any) => (this._stackedTimeline = r))}
+                {...OmitKeys(this.props, ['CollectionFreeFormDocumentView']).omit}
                 fieldKey={this.annotationKey}
-                dictationKey={this.fieldKey + "-dictation"}
+                dictationKey={this.fieldKey + '-dictation'}
                 mediaPath={this.path}
                 renderDepth={this.props.renderDepth + 1}
-                startTag={"_timecodeToShow" /* audioStart */}
-                endTag={"_timecodeToHide" /* audioEnd */}
+                startTag={'_timecodeToShow' /* audioStart */}
+                endTag={'_timecodeToHide' /* audioEnd */}
                 bringToFront={emptyFunction}
                 CollectionView={undefined}
                 playFrom={this.playFrom}
@@ -631,26 +674,22 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     // returns the html audio element
     @computed get audio() {
-        return <audio ref={this.setRef}
-            className={`audiobox-control${this.props.isContentActive() ? "-interactive" : ""}`}
-            onLoadedData={action(e =>
-                (this._ele?.duration && this._ele?.duration !== Infinity) &&
-                (this.dataDoc[this.fieldKey + "-duration"] = this._ele.duration)
-            )}
-        >
-            <source src={this.path} type="audio/mpeg" />
-            Not supported.
-        </audio>;
+        return (
+            <audio
+                ref={this.setRef}
+                className={`audiobox-control${this.props.isContentActive() ? '-interactive' : ''}`}
+                onLoadedData={action(e => this._ele?.duration && this._ele?.duration !== Infinity && (this.dataDoc[this.fieldKey + '-duration'] = this._ele.duration))}>
+                <source src={this.path} type="audio/mpeg" />
+                Not supported.
+            </audio>
+        );
     }
 
     render() {
-        return <div
-            ref={this.setupTimelineDrop}
-            className="audiobox-container"
-            onContextMenu={this.specificContextMenu}
-            style={{ pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined }}
-        >
-            {!this.path ? this.recordingControls : this.playbackControls}
-        </div>;
+        return (
+            <div ref={this.setupTimelineDrop} className="audiobox-container" onContextMenu={this.specificContextMenu} style={{ pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined }}>
+                {!this.path ? this.recordingControls : this.playbackControls}
+            </div>
+        );
     }
 }
diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx
index 6d05fca5e..c229a966a 100644
--- a/src/client/views/nodes/ColorBox.tsx
+++ b/src/client/views/nodes/ColorBox.tsx
@@ -1,23 +1,24 @@
-import React = require("react");
-import { action } from "mobx";
-import { observer } from "mobx-react";
+import React = require('react');
+import { action } from 'mobx';
+import { observer } from 'mobx-react';
 import { ColorState, SketchPicker } from 'react-color';
 import { Doc, HeightSym, WidthSym } from '../../../fields/Doc';
-import { InkTool } from "../../../fields/InkField";
-import { StrCast } from "../../../fields/Types";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { SelectionManager } from "../../util/SelectionManager";
-import { undoBatch } from "../../util/UndoManager";
-import { ViewBoxBaseComponent } from "../DocComponent";
-import { ActiveInkColor, ActiveInkWidth, SetActiveInkColor, SetActiveInkWidth } from "../InkingStroke";
-import "./ColorBox.scss";
+import { InkTool } from '../../../fields/InkField';
+import { StrCast } from '../../../fields/Types';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { SelectionManager } from '../../util/SelectionManager';
+import { undoBatch } from '../../util/UndoManager';
+import { ViewBoxBaseComponent } from '../DocComponent';
+import { ActiveInkColor, ActiveInkWidth, SetActiveInkColor, SetActiveInkWidth } from '../InkingStroke';
+import './ColorBox.scss';
 import { FieldView, FieldViewProps } from './FieldView';
-import { RichTextMenu } from "./formattedText/RichTextMenu";
+import { RichTextMenu } from './formattedText/RichTextMenu';
 
 @observer
 export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ColorBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(ColorBox, fieldKey);
+    }
 
     @undoBatch
     @action
@@ -25,17 +26,22 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
         SetActiveInkColor(color.hex);
 
         SelectionManager.Views().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.isTemplateForField ? view.props.Document : Doc.GetProto(view.props.Document);
+            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.isTemplateForField
+                    ? view.props.Document
+                    : Doc.GetProto(view.props.Document);
             if (targetDoc) {
-                if (view.props.LayoutTemplate?.() || view.props.LayoutTemplateString) {  // this situation typically occurs when you have a link dot 
-                    targetDoc.backgroundColor = color.hex;  // bcz: don't know how to change the color of an inline template...
-                }
-                else if (RichTextMenu.Instance?.TextViewFieldKey && window.getSelection()?.toString() !== "") {
-                    Doc.Layout(view.props.Document)[RichTextMenu.Instance.TextViewFieldKey + "-color"] = color.hex;
+                if (view.props.LayoutTemplate?.() || view.props.LayoutTemplateString) {
+                    // this situation typically occurs when you have a link dot
+                    targetDoc.backgroundColor = color.hex; // bcz: don't know how to change the color of an inline template...
+                } else if (RichTextMenu.Instance?.TextViewFieldKey && window.getSelection()?.toString() !== '') {
+                    Doc.Layout(view.props.Document)[RichTextMenu.Instance.TextViewFieldKey + '-color'] = color.hex;
                 } else {
-                    Doc.Layout(view.props.Document)._backgroundColor = color.hex + (color.rgb.a ? Math.round(color.rgb.a * 255).toString(16) : ""); // '_backgroundColor' is template specific.  'backgroundColor' would apply to all templates, but has no UI at the moment
+                    Doc.Layout(view.props.Document)._backgroundColor = color.hex + (color.rgb.a ? Math.round(color.rgb.a * 255).toString(16) : ''); // '_backgroundColor' is template specific.  'backgroundColor' would apply to all templates, but has no UI at the moment
                 }
             }
         });
@@ -43,24 +49,34 @@ export class ColorBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     render() {
         const scaling = Math.min(this.layoutDoc.fitWidth ? 10000 : this.props.PanelHeight() / this.rootDoc[HeightSym](), this.props.PanelWidth() / this.rootDoc[WidthSym]());
-        return <div className={`colorBox-container${this.isContentActive() ? "-interactive" : ""}`}
-            onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()} onClick={e => e.stopPropagation()}
-            style={{ transform: `scale(${scaling})`, width: `${100 * scaling}%`, height: `${100 * scaling}%` }} >
-
-            <SketchPicker
-                onChange={c => CurrentUserUtils.ActiveTool === InkTool.None && ColorBox.switchColor(c)}
-                color={StrCast(SelectionManager.Views()?.[0]?.rootDoc?._backgroundColor, ActiveInkColor())}
-                presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986',
-                    '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
-            />
+        return (
+            <div
+                className={`colorBox-container${this.isContentActive() ? '-interactive' : ''}`}
+                onPointerDown={e => e.button === 0 && !e.ctrlKey && e.stopPropagation()}
+                onClick={e => e.stopPropagation()}
+                style={{ transform: `scale(${scaling})`, width: `${100 * scaling}%`, height: `${100 * scaling}%` }}>
+                <SketchPicker
+                    onChange={c => Doc.ActiveTool === InkTool.None && ColorBox.switchColor(c)}
+                    color={StrCast(SelectionManager.Views()?.[0]?.rootDoc?._backgroundColor, ActiveInkColor())}
+                    presetColors={['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']}
+                />
 
-            <div style={{ width: this.props.PanelWidth() / scaling, display: "flex", paddingTop: "10px" }}>
-                <div> {ActiveInkWidth()}</div>
-                <input type="range" defaultValue={ActiveInkWidth()} min={1} max={100} onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
-                    SetActiveInkWidth(e.target.value);
-                    SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.strokeWidth = Number(e.target.value));
-                }} />
+                <div style={{ width: this.props.PanelWidth() / scaling, display: 'flex', paddingTop: '10px' }}>
+                    <div> {ActiveInkWidth()}</div>
+                    <input
+                        type="range"
+                        defaultValue={ActiveInkWidth()}
+                        min={1}
+                        max={100}
+                        onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
+                            SetActiveInkWidth(e.target.value);
+                            SelectionManager.Views()
+                                .filter(i => StrCast(i.rootDoc.type) === DocumentType.INK)
+                                .map(i => (i.rootDoc.strokeWidth = Number(e.target.value)));
+                        }}
+                    />
+                </div>
             </div>
-        </div>;
+        );
     }
 }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7569b209d..f89c65052 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1,56 +1,57 @@
-import { IconProp } from "@fortawesome/fontawesome-svg-core";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, StrListCast, WidthSym } from "../../../fields/Doc";
+import { IconProp } from '@fortawesome/fontawesome-svg-core';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
 import { Document } from '../../../fields/documentSchemas';
 import { Id } from '../../../fields/FieldSymbols';
 import { InkTool } from '../../../fields/InkField';
-import { List } from "../../../fields/List";
-import { ObjectField } from "../../../fields/ObjectField";
-import { listSpec } from "../../../fields/Schema";
+import { List } from '../../../fields/List';
+import { ObjectField } from '../../../fields/ObjectField';
+import { listSpec } from '../../../fields/Schema';
 import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, Cast, ImageCast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
-import { AudioField } from "../../../fields/URLField";
+import { BoolCast, Cast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { AudioField } from '../../../fields/URLField';
 import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util';
 import { MobileInterface } from '../../../mobile/MobileInterface';
-import { emptyFunction, hasDescendantTarget, lightOrDark, OmitKeys, returnEmptyString, returnTrue, returnFalse, returnVal, simulateMouseClick, Utils } from "../../../Utils";
+import { emptyFunction, hasDescendantTarget, lightOrDark, OmitKeys, returnEmptyString, returnFalse, returnTrue, returnVal, simulateMouseClick, Utils } from '../../../Utils';
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
-import { Docs, DocUtils } from "../../documents/Documents";
-import { DocumentType } from '../../documents/DocumentTypes';
-import { Networking } from "../../Network";
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
-import { DocumentManager } from "../../util/DocumentManager";
-import { DragManager, dropActionType } from "../../util/DragManager";
+import { DocServer } from '../../DocServer';
+import { Docs, DocUtils } from '../../documents/Documents';
+import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
+import { Networking } from '../../Network';
+import { DocumentManager } from '../../util/DocumentManager';
+import { DragManager, dropActionType } from '../../util/DragManager';
 import { InteractionUtils } from '../../util/InteractionUtils';
+import { LinkFollower } from '../../util/LinkFollower';
 import { LinkManager } from '../../util/LinkManager';
 import { ScriptingGlobals } from '../../util/ScriptingGlobals';
-import { SelectionManager } from "../../util/SelectionManager";
+import { SelectionManager } from '../../util/SelectionManager';
+import { SettingsManager } from '../../util/SettingsManager';
 import { SharingManager } from '../../util/SharingManager';
 import { SnappingManager } from '../../util/SnappingManager';
-import { Transform } from "../../util/Transform";
-import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { CollectionView, CollectionViewType } from '../collections/CollectionView';
-import { ContextMenu } from "../ContextMenu";
+import { Transform } from '../../util/Transform';
+import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { CollectionView } from '../collections/CollectionView';
+import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
-import { DocComponent } from "../DocComponent";
+import { DocComponent } from '../DocComponent';
 import { EditableView } from '../EditableView';
-import { InkingStroke } from "../InkingStroke";
-import { LightboxView } from "../LightboxView";
-import { StyleProp } from "../StyleProvider";
-import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView";
-import { DocumentContentsView } from "./DocumentContentsView";
+import { InkingStroke } from '../InkingStroke';
+import { LightboxView } from '../LightboxView';
+import { StyleProp } from '../StyleProvider';
+import { CollectionFreeFormDocumentView } from './CollectionFreeFormDocumentView';
+import { DocumentContentsView } from './DocumentContentsView';
 import { DocumentLinksButton } from './DocumentLinksButton';
-import "./DocumentView.scss";
-import { FormattedTextBox } from "./formattedText/FormattedTextBox";
+import './DocumentView.scss';
+import { FieldViewProps } from './FieldView';
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
 import { LinkAnchorBox } from './LinkAnchorBox';
-import { LinkDocPreview } from "./LinkDocPreview";
+import { LinkDocPreview } from './LinkDocPreview';
 import { RadialMenu } from './RadialMenu';
-import { ScriptingBox } from "./ScriptingBox";
+import { ScriptingBox } from './ScriptingBox';
 import { PresBox } from './trails/PresBox';
-import React = require("react");
-import { DocServer } from "../../DocServer";
-import { FieldViewProps } from "./FieldView";
+import React = require('react');
 const { Howl } = require('howler');
 
 interface Window {
@@ -64,16 +65,16 @@ declare class MediaRecorder {
 
 export enum ViewAdjustment {
     resetView = 1,
-    doNothing = 0
+    doNothing = 0,
 }
 
-export const ViewSpecPrefix = "viewSpec";  // field prefix for anchor fields that are immediately copied over to the target document when link is followed.  Other anchor properties will be copied over in the specific setViewSpec() method on their view (which allows for seting preview values instead of writing to the document)
+export const ViewSpecPrefix = 'viewSpec'; // field prefix for anchor fields that are immediately copied over to the target document when link is followed.  Other anchor properties will be copied over in the specific setViewSpec() method on their view (which allows for seting preview values instead of writing to the document)
 
 export interface DocFocusOptions {
     originalTarget?: Doc; // set in JumpToDocument, used by TabDocView to determine whether to fit contents to tab
-    willZoom?: boolean;   // determines whether to zoom in on target document
-    scale?: number;       // percent of containing frame to zoom into document
-    afterFocus?: DocAfterFocusFunc;  // function to call after focusing on a document
+    willZoom?: boolean; // determines whether to zoom in on target document
+    scale?: number; // percent of containing frame to zoom into document
+    afterFocus?: DocAfterFocusFunc; // function to call after focusing on a document
     docTransform?: Transform; // when a document can't be panned and zoomed within its own container (say a group), then we need to continue to move up the render hierarchy to find something that can pan and zoom.  when this happens the docTransform must accumulate all the transforms of each level of the hierarchy
     instant?: boolean; // whether focus should happen instantly (as opposed to smooth zoom)
 }
@@ -84,9 +85,9 @@ export interface DocComponentView {
     updateIcon?: () => void; // updates the icon representation of the document
     getAnchor?: () => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box)
     scrollFocus?: (doc: Doc, smooth: boolean) => Opt<number>; // returns the duration of the focus
-    setViewSpec?: (anchor: Doc, preview: boolean) => void;  // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
+    setViewSpec?: (anchor: Doc, preview: boolean) => void; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
     reverseNativeScaling?: () => boolean; // DocumentView's setup screenToLocal based on the doc having a nativeWidth/Height.  However, some content views (e.g., FreeFormView w/ fitContentsToBox set) may ignore the native dimensions so this flags the DocumentView to not do Nativre scaling.
-    shrinkWrap?: () => void;  // requests a document to display all of its contents with no white space.  currently only implemented (needed?) for freeform views
+    shrinkWrap?: () => void; // requests a document to display all of its contents with no white space.  currently only implemented (needed?) for freeform views
     menuControls?: () => JSX.Element; // controls to display in the top menu bar when the document is selected.
     isAnyChildContentActive?: () => boolean; // is any child content of the document active
     getKeyFrameEditing?: () => boolean; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown)
@@ -99,20 +100,20 @@ export interface DocComponentView {
     annotationKey?: string;
     getTitle?: () => string;
     getScrollHeight?: () => number;
-    getCenter?: (xf: Transform) => { X: number, Y: number };
-    ptToScreen?: (pt: { X: number, Y: number }) => { X: number, Y: number };
-    ptFromScreen?: (pt: { X: number, Y: number }) => { X: number, Y: number };
-    snapPt?: (pt: { X: number, Y: number }, excludeSegs?: number[]) => { nearestPt: { X: number, Y: number }, distance: number };
+    getCenter?: (xf: Transform) => { X: number; Y: number };
+    ptToScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
+    ptFromScreen?: (pt: { X: number; Y: number }) => { X: number; Y: number };
+    snapPt?: (pt: { X: number; Y: number }, excludeSegs?: number[]) => { nearestPt: { X: number; Y: number }; distance: number };
     search?: (str: string, bwd?: boolean, clear?: boolean) => boolean;
 }
-// These props are passed to both FieldViews and DocumentViews 
+// These props are passed to both FieldViews and DocumentViews
 export interface DocumentViewSharedProps {
     fieldKey?: string; // only used by FieldViews but helpful here to allow styleProviders to access fieldKey of FieldViewProps.  In priniciple, passing a fieldKey to a documentView could override or be the default fieldKey for fieldViews
     DocumentView?: () => DocumentView;
     renderDepth: number;
     Document: Doc;
     DataDoc?: Doc;
-    contentBounds?: () => (undefined|{x:number, y:number, r:number, b:number});
+    contentBounds?: () => undefined | { x: number; y: number; r: number; b: number };
     fitContentsToBox?: () => boolean; // used by freeformview to fit its contents to its panel. corresponds to _fitContentsToBox property on a Document
     ContainingCollectionView: Opt<CollectionView>;
     ContainingCollectionDoc: Opt<Doc>;
@@ -137,7 +138,7 @@ export interface DocumentViewSharedProps {
     whenChildContentsActiveChanged: (isActive: boolean) => void;
     rootSelected: (outsideReaction?: boolean) => boolean; // whether the root of a template has been selected
     addDocTab: (doc: Doc, where: string) => boolean;
-    filterAddDocument?: (doc: Doc[]) => boolean;  // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
+    filterAddDocument?: (doc: Doc[]) => boolean; // allows a document that renders a Collection view to filter or modify any documents added to the collection (see PresBox for an example)
     addDocument?: (doc: Doc | Doc[]) => boolean;
     removeDocument?: (doc: Doc | Doc[]) => boolean;
     moveDocument?: (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => boolean;
@@ -161,8 +162,8 @@ export interface DocumentViewSharedProps {
 export interface DocumentViewProps extends DocumentViewSharedProps {
     // properties specific to DocumentViews but not to FieldView
     hideResizeHandles?: boolean; // whether to suppress DocumentDecorations when this document is selected
-    hideTitle?: boolean;  // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
-    hideDecorationTitle?: boolean;  // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
+    hideTitle?: boolean; // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
+    hideDecorationTitle?: boolean; // forces suppression of title. e.g, treeView document labels suppress titles in case they are globally active via settings
     hideDocumentButtonBar?: boolean;
     hideOpenButton?: boolean;
     hideDeleteButton?: boolean;
@@ -172,19 +173,19 @@ export interface DocumentViewProps extends DocumentViewSharedProps {
     contentPointerEvents?: string; // pointer events allowed for content of a document view.  eg. set to "none" in menuSidebar for sharedDocs so that you can select a document, but not interact with its contents
     radialMenu?: String[];
     LayoutTemplateString?: string;
-    dontCenter?: "x" | "y" | "xy";
+    dontCenter?: 'x' | 'y' | 'xy';
     dontScaleFilter?: (doc: Doc) => boolean; // decides whether a document can be scaled to fit its container vs native size with scrolling
     ContentScaling?: () => number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal
     NativeWidth?: () => number;
     NativeHeight?: () => number;
     LayoutTemplate?: () => Opt<Doc>;
-    contextMenuItems?: () => { script: ScriptField, filter?: ScriptField, label: string, icon: string }[];
+    contextMenuItems?: () => { script: ScriptField; filter?: ScriptField; label: string; icon: string }[];
     onClick?: () => ScriptField;
     onDoubleClick?: () => ScriptField;
     onPointerDown?: () => ScriptField;
     onPointerUp?: () => ScriptField;
-    onBrowseClick?: () => (ScriptField | undefined);
-    onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => (boolean | undefined);
+    onBrowseClick?: () => ScriptField | undefined;
+    onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => boolean | undefined;
 }
 
 // these props are only available in DocumentViewIntenral
@@ -219,34 +220,88 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
     @observable _componentView: Opt<DocComponentView>; // needs to be accessed from DocumentView wrapper class
 
-    private get topMost() { return this.props.renderDepth === 0 && !LightboxView.LightboxDoc; }
-    public get displayName() { return "DocumentView(" + this.props.Document.title + ")"; } // this makes mobx trace() statements more descriptive
-    public get ContentDiv() { return this._mainCont.current; }
-    public get LayoutFieldKey() { return Doc.LayoutFieldKey(this.layoutDoc); }
-    @computed get ShowTitle() { return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.ShowTitle) as (Opt<string>); }
-    @computed get ContentScale() { return this.props.ContentScaling?.() || 1; }
-    @computed get thumb() { return ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url.href.replace(".png", "_m.png"); }
-    @computed get hidden() { return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Hidden); }
-    @computed get opacity() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Opacity); }
-    @computed get boxShadow() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BoxShadow); }
-    @computed get borderRounding() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding); }
-    @computed get hideLinkButton() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HideLinkButton + (this.props.isSelected() ? ":selected" : "")); }
-    @computed get widgetDecorations() { return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Decorations + (this.props.isSelected() ? ":selected" : "")); }
-    @computed get backgroundColor() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor); }
-    @computed get docContents() { return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.DocContents); }
-    @computed get headerMargin() { return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0; }
-    @computed get titleHeight() { return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.TitleHeight) || 0; }
-    @computed get pointerEvents() { return this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents + (this.props.isSelected() ? ":selected" : "")); }
-    @computed get finalLayoutKey() { return StrCast(this.Document.layoutKey, "layout"); }
-    @computed get nativeWidth() { return this.props.NativeWidth(); }
-    @computed get nativeHeight() { return this.props.NativeHeight(); }
-    @computed get onClickHandler() { return this.props.onClick?.() ?? (this.props.onBrowseClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null))); }
-    @computed get onDoubleClickHandler() { return this.props.onDoubleClick?.() ?? (Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick); }
-    @computed get onPointerDownHandler() { return this.props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown); }
-    @computed get onPointerUpHandler() { return this.props.onPointerUp?.() ?? ScriptCast(this.Document.onPointerUp); }
-
-    componentWillUnmount() { this.cleanupHandlers(true); }
-    componentDidMount() { this.setupHandlers(); }
+    private get topMost() {
+        return this.props.renderDepth === 0 && !LightboxView.LightboxDoc;
+    }
+    public get displayName() {
+        return 'DocumentView(' + this.props.Document.title + ')';
+    } // this makes mobx trace() statements more descriptive
+    public get ContentDiv() {
+        return this._mainCont.current;
+    }
+    public get LayoutFieldKey() {
+        return Doc.LayoutFieldKey(this.layoutDoc);
+    }
+    @computed get ShowTitle() {
+        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.ShowTitle) as Opt<string>;
+    }
+    @computed get ContentScale() {
+        return this.props.ContentScaling?.() || 1;
+    }
+    @computed get thumb() {
+        return ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url.href.replace('.png', '_m.png');
+    }
+    @computed get hidden() {
+        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Hidden);
+    }
+    @computed get opacity() {
+        return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Opacity);
+    }
+    @computed get boxShadow() {
+        return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BoxShadow);
+    }
+    @computed get borderRounding() {
+        return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
+    }
+    @computed get hideLinkButton() {
+        return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HideLinkButton + (this.props.isSelected() ? ':selected' : ''));
+    }
+    @computed get widgetDecorations() {
+        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Decorations + (this.props.isSelected() ? ':selected' : ''));
+    }
+    @computed get backgroundColor() {
+        return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor);
+    }
+    @computed get docContents() {
+        return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.DocContents);
+    }
+    @computed get headerMargin() {
+        return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0;
+    }
+    @computed get titleHeight() {
+        return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.TitleHeight) || 0;
+    }
+    @computed get pointerEvents() {
+        return this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents + (this.props.isSelected() ? ':selected' : ''));
+    }
+    @computed get finalLayoutKey() {
+        return StrCast(this.Document.layoutKey, 'layout');
+    }
+    @computed get nativeWidth() {
+        return this.props.NativeWidth();
+    }
+    @computed get nativeHeight() {
+        return this.props.NativeHeight();
+    }
+    @computed get onClickHandler() {
+        return this.props.onClick?.() ?? this.props.onBrowseClick?.() ?? Cast(this.Document.onClick, ScriptField, Cast(this.layoutDoc.onClick, ScriptField, null));
+    }
+    @computed get onDoubleClickHandler() {
+        return this.props.onDoubleClick?.() ?? Cast(this.layoutDoc.onDoubleClick, ScriptField, null) ?? this.Document.onDoubleClick;
+    }
+    @computed get onPointerDownHandler() {
+        return this.props.onPointerDown?.() ?? ScriptCast(this.Document.onPointerDown);
+    }
+    @computed get onPointerUpHandler() {
+        return this.props.onPointerUp?.() ?? ScriptCast(this.Document.onPointerUp);
+    }
+
+    componentWillUnmount() {
+        this.cleanupHandlers(true);
+    }
+    componentDidMount() {
+        this.setupHandlers();
+    }
     //componentDidUpdate() { this.setupHandlers(); }
     setupHandlers() {
         this.cleanupHandlers(false);
@@ -268,8 +323,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     handle1PointerHoldStart = (e: Event, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): any => {
         this.removeMoveListeners();
         this.removeEndListeners();
-        document.removeEventListener("pointermove", this.onPointerMove);
-        document.removeEventListener("pointerup", this.onPointerUp);
+        document.removeEventListener('pointermove', this.onPointerMove);
+        document.removeEventListener('pointerup', this.onPointerUp);
         if (RadialMenu.Instance._display === false) {
             this.addHoldMoveListeners();
             this.addHoldEndListeners();
@@ -278,7 +333,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             this._firstX = pt.pageX;
             this._firstY = pt.pageY;
         }
-    }
+    };
 
     handle1PointerHoldMove = (e: Event, me: InteractionUtils.MultiTouchEvent<TouchEvent>): void => {
         const pt = me.touchEvent.touches[me.touchEvent.touches.length - 1];
@@ -289,7 +344,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (Math.abs(pt.pageX - this._firstX) > 150 || Math.abs(pt.pageY - this._firstY) > 150) {
             this.handle1PointerHoldEnd(e, me);
         }
-    }
+    };
 
     handle1PointerHoldEnd = (e: Event, me: InteractionUtils.MultiTouchEvent<TouchEvent>): void => {
         this.removeHoldMoveListeners();
@@ -304,7 +359,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (RadialMenu.Instance.used) {
             this.onContextMenu(undefined, me.touches[0].pageX, me.touches[0].pageY);
         }
-    }
+    };
 
     handle2PointersDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => {
         if (!e.nativeEvent.cancelBubble && !this.props.isSelected()) {
@@ -316,7 +371,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             this.removeEndListeners();
             this.addEndListeners();
         }
-    }
+    };
 
     handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => {
         SelectionManager.DeselectAll();
@@ -326,7 +381,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             this._downX = touch.clientX;
             this._downY = touch.clientY;
             if (!e.nativeEvent.cancelBubble) {
-                if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc._lockedPosition && !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) e.stopPropagation();
+                if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc._lockedPosition && !DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) e.stopPropagation();
                 this.removeMoveListeners();
                 this.addMoveListeners();
                 this.removeEndListeners();
@@ -334,24 +389,23 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 e.stopPropagation();
             }
         }
-    }
+    };
 
     handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
         if (e.cancelBubble && this.props.isDocumentActive?.()) {
             this.removeMoveListeners();
-        }
-        else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
+        } else if (!e.cancelBubble && (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !this.layoutDoc._lockedPosition && !DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
             const touch = me.touchEvent.changedTouches.item(0);
             if (touch && (Math.abs(this._downX - touch.clientX) > 3 || Math.abs(this._downY - touch.clientY) > 3)) {
                 if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler)) {
                     this.cleanUpInteractions();
-                    this.startDragging(this._downX, this._downY, this.Document.dropAction ? this.Document.dropAction as any : e.ctrlKey || e.altKey ? "alias" : undefined);
+                    this.startDragging(this._downX, this._downY, this.Document.dropAction ? (this.Document.dropAction as any) : e.ctrlKey || e.altKey ? 'alias' : undefined);
                 }
             }
             e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
             e.preventDefault();
         }
-    }
+    };
 
     @action
     handle2PointersMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
@@ -362,8 +416,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         const oldPoint2 = this.prevPoints.get(pt2.identifier);
         const pinching = InteractionUtils.Pinning(pt1, pt2, oldPoint1!, oldPoint2!);
         if (pinching !== 0 && oldPoint1 && oldPoint2) {
-            const dW = (Math.abs(pt1.clientX - pt2.clientX) - Math.abs(oldPoint1.clientX - oldPoint2.clientX));
-            const dH = (Math.abs(pt1.clientY - pt2.clientY) - Math.abs(oldPoint1.clientY - oldPoint2.clientY));
+            const dW = Math.abs(pt1.clientX - pt2.clientX) - Math.abs(oldPoint1.clientX - oldPoint2.clientX);
+            const dH = Math.abs(pt1.clientY - pt2.clientY) - Math.abs(oldPoint1.clientY - oldPoint2.clientY);
             const dX = -1 * Math.sign(dW);
             const dY = -1 * Math.sign(dH);
 
@@ -372,33 +426,32 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 const layoutDoc = Document(Doc.Layout(this.props.Document));
                 let nwidth = Doc.NativeWidth(layoutDoc);
                 let nheight = Doc.NativeHeight(layoutDoc);
-                const width = (layoutDoc._width || 0);
-                const height = (layoutDoc._height || (nheight / nwidth * width));
+                const width = layoutDoc._width || 0;
+                const height = layoutDoc._height || (nheight / nwidth) * width;
                 const scale = this.props.ScreenToLocalTransform().Scale * this.ContentScale;
-                const actualdW = Math.max(width + (dW * scale), 20);
-                const actualdH = Math.max(height + (dH * scale), 20);
+                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);
                 const fixedAspect = e.ctrlKey || (nwidth && nheight);
                 if (fixedAspect && (!nwidth || !nheight)) {
-                    Doc.SetNativeWidth(layoutDoc, nwidth = layoutDoc._width || 0);
-                    Doc.SetNativeHeight(layoutDoc, nheight = layoutDoc._height || 0);
+                    Doc.SetNativeWidth(layoutDoc, (nwidth = layoutDoc._width || 0));
+                    Doc.SetNativeHeight(layoutDoc, (nheight = layoutDoc._height || 0));
                 }
                 if (nwidth > 0 && nheight > 0) {
                     if (Math.abs(dW) > Math.abs(dH)) {
                         if (!fixedAspect) {
-                            Doc.SetNativeWidth(layoutDoc, actualdW / (layoutDoc._width || 1) * Doc.NativeWidth(layoutDoc));
+                            Doc.SetNativeWidth(layoutDoc, (actualdW / (layoutDoc._width || 1)) * Doc.NativeWidth(layoutDoc));
                         }
                         layoutDoc._width = actualdW;
-                        if (fixedAspect && !this.props.DocumentView().fitWidth) layoutDoc._height = nheight / nwidth * layoutDoc._width;
+                        if (fixedAspect && !this.props.DocumentView().fitWidth) layoutDoc._height = (nheight / nwidth) * layoutDoc._width;
                         else layoutDoc._height = actualdH;
-                    }
-                    else {
+                    } else {
                         if (!fixedAspect) {
-                            Doc.SetNativeHeight(layoutDoc, actualdH / (layoutDoc._height || 1) * Doc.NativeHeight(doc));
+                            Doc.SetNativeHeight(layoutDoc, (actualdH / (layoutDoc._height || 1)) * Doc.NativeHeight(doc));
                         }
                         layoutDoc._height = actualdH;
-                        if (fixedAspect && !this.props.DocumentView().fitWidth) layoutDoc._width = nwidth / nheight * layoutDoc._height;
+                        if (fixedAspect && !this.props.DocumentView().fitWidth) layoutDoc._width = (nwidth / nheight) * layoutDoc._height;
                         else layoutDoc._width = actualdW;
                     }
                 } else {
@@ -410,7 +463,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             e.stopPropagation();
             e.preventDefault();
         }
-    }
+    };
 
     @action
     onRadialMenu = (e: Event, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>): void => {
@@ -419,19 +472,30 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
         // RadialMenu.Instance.addItem({ description: "Open Fields", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "map-pin", selected: -1 });
         const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]);
-        (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) && RadialMenu.Instance.addItem({ description: "Delete", event: () => { this.props.ContainingCollectionView?.removeDocument(this.props.Document), RadialMenu.Instance.closeMenu(); }, icon: "external-link-square-alt", selected: -1 });
+        (effectiveAcl === AclEdit || effectiveAcl === AclAdmin) &&
+            RadialMenu.Instance.addItem({
+                description: 'Delete',
+                event: () => {
+                    this.props.ContainingCollectionView?.removeDocument(this.props.Document), RadialMenu.Instance.closeMenu();
+                },
+                icon: 'external-link-square-alt',
+                selected: -1,
+            });
         // RadialMenu.Instance.addItem({ description: "Open in a new tab", event: () => this.props.addDocTab(this.props.Document, "add:right"), icon: "trash", selected: -1 });
-        RadialMenu.Instance.addItem({ description: "Pin", event: () => this.props.pinToPres(this.props.Document), icon: "map-pin", selected: -1 });
-        RadialMenu.Instance.addItem({ description: "Open", event: () => MobileInterface.Instance.handleClick(this.props.Document), icon: "trash", selected: -1 });
+        RadialMenu.Instance.addItem({ description: 'Pin', event: () => this.props.pinToPres(this.props.Document), icon: 'map-pin', selected: -1 });
+        RadialMenu.Instance.addItem({ description: 'Open', event: () => MobileInterface.Instance.handleClick(this.props.Document), icon: 'trash', selected: -1 });
 
         SelectionManager.DeselectAll();
-    }
+    };
 
     startDragging(x: number, y: number, dropAction: dropActionType, hideSource = false) {
         if (this._mainCont.current) {
             const dragData = new DragManager.DocumentDragData([this.props.Document]);
             const [left, top] = this.props.ScreenToLocalTransform().scale(this.ContentScale).inverse().transformPoint(0, 0);
-            dragData.offset = this.props.ScreenToLocalTransform().scale(this.ContentScale).transformDirection(x - left, y - top);
+            dragData.offset = this.props
+                .ScreenToLocalTransform()
+                .scale(this.ContentScale)
+                .transformDirection(x - left, y - top);
             dragData.offset[0] = Math.min(this.rootDoc[WidthSym](), dragData.offset[0]);
             dragData.offset[1] = Math.min(this.rootDoc[HeightSym](), dragData.offset[1]);
             dragData.dropAction = dropAction;
@@ -440,8 +504,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             dragData.moveDocument = this.props.moveDocument;
             const ffview = this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
             ffview && runInAction(() => (ffview.ChildDrag = this.props.DocumentView()));
-            DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: hideSource || (!dropAction && !this.layoutDoc.onDragStart) },
-                () => setTimeout(action(() => ffview && (ffview.ChildDrag = undefined)))); // this needs to happen after the drop event is processed.
+            DragManager.StartDocumentDrag([this._mainCont.current], dragData, x, y, { hideSource: hideSource || (!dropAction && !this.layoutDoc.onDragStart) }, () => setTimeout(action(() => ffview && (ffview.ChildDrag = undefined)))); // this needs to happen after the drop event is processed.
             ffview?.setupDragLines(false);
         }
     }
@@ -450,91 +513,114 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (e.altKey && !e.nativeEvent.cancelBubble) {
             e.stopPropagation();
             e.preventDefault();
-            if (e.key === "†" || e.key === "t") {
-                if (!StrCast(this.layoutDoc._showTitle)) this.layoutDoc._showTitle = "title";
+            if (e.key === '†' || e.key === 't') {
+                if (!StrCast(this.layoutDoc._showTitle)) this.layoutDoc._showTitle = 'title';
                 if (!this._titleRef.current) setTimeout(() => this._titleRef.current?.setIsFocused(true), 0);
-                else if (!this._titleRef.current.setIsFocused(true)) { // if focus didn't change, focus on interior text...
+                else if (!this._titleRef.current.setIsFocused(true)) {
+                    // if focus didn't change, focus on interior text...
                     this._titleRef.current?.setIsFocused(false);
                     this._componentView?.setFocus?.();
                 }
             }
         }
-    }
+    };
 
     focus = (anchor: Doc, options?: DocFocusOptions) => {
-        LightboxView.SetCookie(StrCast(anchor["cookies-set"]));
+        LightboxView.SetCookie(StrCast(anchor['cookies-set']));
         // copying over VIEW fields immediately allows the view type to switch to create the right _componentView
-        Array.from(Object.keys(Doc.GetProto(anchor))).filter(key => key.startsWith(ViewSpecPrefix)).forEach(spec => {
-            this.layoutDoc[spec.replace(ViewSpecPrefix, "")] = ((field) => field instanceof ObjectField ? ObjectField.MakeCopy(field) : field)(anchor[spec]);
-        });
+        Array.from(Object.keys(Doc.GetProto(anchor)))
+            .filter(key => key.startsWith(ViewSpecPrefix))
+            .forEach(spec => {
+                this.layoutDoc[spec.replace(ViewSpecPrefix, '')] = (field => (field instanceof ObjectField ? ObjectField.MakeCopy(field) : field))(anchor[spec]);
+            });
         // after  a timeout, the right _componentView should have been created, so call it to update its view spec values
         setTimeout(() => this._componentView?.setViewSpec?.(anchor, LinkDocPreview.LinkInfo ? true : false));
         const focusSpeed = this._componentView?.scrollFocus?.(anchor, options?.instant === false || !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here
-        const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => (options?.afterFocus?.(true) ?? ViewAdjustment.doNothing);
+        const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus?.(true) ?? ViewAdjustment.doNothing;
         this.props.focus(options?.docTransform ? anchor : this.rootDoc, {
-            ...options, afterFocus: (didFocus: boolean) =>
-                new Promise<ViewAdjustment>(res => setTimeout(async () => res(endFocus ? 
-                    await endFocus(didFocus||focusSpeed !== undefined) : 
-                    ViewAdjustment.doNothing), focusSpeed ?? 0)
-                    )
+            ...options,
+            afterFocus: (didFocus: boolean) => new Promise<ViewAdjustment>(res => setTimeout(async () => res(endFocus ? await endFocus(didFocus || focusSpeed !== undefined) : ViewAdjustment.doNothing), focusSpeed ?? 0)),
         });
-
-    }
+    };
     onClick = action((e: React.MouseEvent | React.PointerEvent) => {
-        if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && this.props.renderDepth >= 0 &&
-            (Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD)) {
+        if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && this.props.renderDepth >= 0 && Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
             let stopPropagate = true;
             let preventDefault = true;
             const isScriptBox = () => StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name);
             (this.rootDoc._raiseWhenDragged === undefined ? DragManager.GetRaiseWhenDragged() : this.rootDoc._raiseWhenDragged) && this.props.bringToFront(this.rootDoc);
-            if (this._doubleTap && (this.props.Document.type !== DocumentType.FONTICON || this.onDoubleClickHandler)) {// && !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
+            if (this._doubleTap && (this.props.Document.type !== DocumentType.FONTICON || this.onDoubleClickHandler)) {
+                // && !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
                 if (this._timeout) {
                     clearTimeout(this._timeout);
                     this._pendingDoubleClick = false;
                     this._timeout = undefined;
                 }
-                if (this.onDoubleClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name)) { // bcz: hack? don't execute script if you're clicking on a scripting box itself
+                if (this.onDoubleClickHandler?.script && !StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name)) {
+                    // bcz: hack? don't execute script if you're clicking on a scripting box itself
                     const { clientX, clientY, shiftKey } = e;
-                    const func = () => this.onDoubleClickHandler.script.run({
-                        this: this.layoutDoc,
-                        self: this.rootDoc,
-                        scriptContext: this.props.scriptContext,
-                        thisContainer: this.props.ContainingCollectionDoc,
-                        documentView: this.props.DocumentView(),
-                        clientX, clientY, shiftKey
-                    }, console.log);
-                    UndoManager.RunInBatch(() => func().result?.select === true ? this.props.select(false) : "", "on double click");
+                    const func = () =>
+                        this.onDoubleClickHandler.script.run(
+                            {
+                                this: this.layoutDoc,
+                                self: this.rootDoc,
+                                scriptContext: this.props.scriptContext,
+                                thisContainer: this.props.ContainingCollectionDoc,
+                                documentView: this.props.DocumentView(),
+                                clientX,
+                                clientY,
+                                shiftKey,
+                            },
+                            console.log
+                        );
+                    UndoManager.RunInBatch(() => (func().result?.select === true ? this.props.select(false) : ''), 'on double click');
                 } else if (!Doc.IsSystem(this.rootDoc)) {
-                    UndoManager.RunInBatch(() =>
-                        LightboxView.AddDocTab(this.rootDoc, "lightbox", this.props.LayoutTemplate?.(), this.props.addDocTab)
-                        , "double tap");
+                    UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, 'lightbox', this.props.LayoutTemplate?.(), this.props.addDocTab), 'double tap');
                     SelectionManager.DeselectAll();
                     Doc.UnBrushDoc(this.props.Document);
                 }
-            } else if (this.onClickHandler?.script && !isScriptBox()) { // bcz: hack? don't execute script if you're clicking on a scripting box itself
+            } else if (this.onClickHandler?.script && !isScriptBox()) {
+                // bcz: hack? don't execute script if you're clicking on a scripting box itself
                 const { clientX, clientY, shiftKey } = e;
-                const func = () => this.onClickHandler.script.run({
-                    this: this.layoutDoc,
-                    self: this.rootDoc,
-                    _readOnly_: false,
-                    scriptContext: this.props.scriptContext,
-                    thisContainer: this.props.ContainingCollectionDoc,
-                    documentView: this.props.DocumentView(),
-                    clientX, clientY, shiftKey
-                }, console.log).result?.select === true ? this.props.select(false) : "";
-                const clickFunc = () => this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, "on click");
+                const func = () =>
+                    this.onClickHandler.script.run(
+                        {
+                            this: this.layoutDoc,
+                            self: this.rootDoc,
+                            _readOnly_: false,
+                            scriptContext: this.props.scriptContext,
+                            thisContainer: this.props.ContainingCollectionDoc,
+                            documentView: this.props.DocumentView(),
+                            clientX,
+                            clientY,
+                            shiftKey,
+                        },
+                        console.log
+                    ).result?.select === true
+                        ? this.props.select(false)
+                        : '';
+                const clickFunc = () => (this.props.Document.dontUndo ? func() : UndoManager.RunInBatch(func, 'on click'));
                 if (this.onDoubleClickHandler) {
-                    runInAction(() => this._pendingDoubleClick = true);
-                    this._timeout = setTimeout(() => { this._timeout = undefined; clickFunc(); }, 350);
+                    runInAction(() => (this._pendingDoubleClick = true));
+                    this._timeout = setTimeout(() => {
+                        this._timeout = undefined;
+                        clickFunc();
+                    }, 350);
                 } else clickFunc();
             } else if (this.allLinks && this.Document.type !== DocumentType.LINK && !isScriptBox() && this.Document.isLinkButton && !e.shiftKey && !e.ctrlKey) {
-                this.allLinks.length && LinkManager.FollowLink(undefined, this.props.Document, this.props, e.altKey);
+                this.allLinks.length && LinkFollower.FollowLink(undefined, this.props.Document, this.props, e.altKey);
             } else {
-                if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) {  // onDragStart implies a button doc that we don't want to select when clicking.   RootDocument & isTemplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
+                if ((this.layoutDoc.onDragStart || this.props.Document.rootDocument) && !(e.ctrlKey || e.button > 0)) {
+                    // onDragStart implies a button doc that we don't want to select when clicking.   RootDocument & isTemplaetForField implies we're clicking on part of a template instance and we want to select the whole template, not the part
                     stopPropagate = false; // don't stop propagation for field templates -- want the selection to propagate up to the root document of the template
                 } else {
-                    runInAction(() => this._pendingDoubleClick = true);
-                    this._timeout = setTimeout(action(() => { this._pendingDoubleClick = false; this._timeout = undefined; }), 350);
+                    runInAction(() => (this._pendingDoubleClick = true));
+                    this._timeout = setTimeout(
+                        action(() => {
+                            this._pendingDoubleClick = false;
+                            this._timeout = undefined;
+                        }),
+                        350
+                    );
                     this.props.select(e.ctrlKey || e.shiftKey);
                 }
                 preventDefault = false;
@@ -545,9 +631,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     });
 
     onPointerDown = (e: React.PointerEvent): void => {
-        if (this.rootDoc.type === DocumentType.INK && CurrentUserUtils.ActiveTool === InkTool.Eraser) return;
+        if (this.rootDoc.type === DocumentType.INK && Doc.ActiveTool === InkTool.Eraser) return;
         // continue if the event hasn't been canceled AND we are using a mouse or this has an onClick or onDragStart function (meaning it is a button document)
-        if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool))) {
+        if (!(InteractionUtils.IsType(e, InteractionUtils.MOUSETYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool))) {
             if (!InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
                 e.stopPropagation();
                 if (SelectionManager.IsSelected(this.props.DocumentView(), true) && this.props.Document._viewType !== CollectionViewType.Docking) e.preventDefault(); // goldenlayout needs to be able to move its tabs, so can't preventDefault for it
@@ -557,50 +643,54 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         }
         this._downX = e.clientX;
         this._downY = e.clientY;
-        if ((!e.nativeEvent.cancelBubble || this.onClickHandler || this.layoutDoc.onDragStart) &&
+        if (
+            (!e.nativeEvent.cancelBubble || this.onClickHandler || this.layoutDoc.onDragStart) &&
             // if this is part of a template, let the event go up to the tempalte root unless right/ctrl clicking
-            !(this.props.Document.rootDocument && !(e.ctrlKey || e.button > 0))) {
-            if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) &&
+            !(this.props.Document.rootDocument && !(e.ctrlKey || e.button > 0))
+        ) {
+            if (
+                (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) &&
                 !this.props.onBrowseClick?.() &&
                 !this.Document.ignoreClick &&
                 !e.ctrlKey &&
                 (e.button === 0 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) &&
-                !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
+                !DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)
+            ) {
                 e.stopPropagation();
                 // don't preventDefault anymore.  Goldenlayout, PDF text selection and RTF text selection all need it to go though
                 //if (this.props.isSelected(true) && this.rootDoc.type !== DocumentType.PDF && this.layoutDoc._viewType !== CollectionViewType.Docking) e.preventDefault();
             }
             if (this.props.isDocumentActive?.()) {
-                document.removeEventListener("pointermove", this.onPointerMove);
-                document.addEventListener("pointermove", this.onPointerMove);
+                document.removeEventListener('pointermove', this.onPointerMove);
+                document.addEventListener('pointermove', this.onPointerMove);
             }
-            document.removeEventListener("pointerup", this.onPointerUp);
-            document.addEventListener("pointerup", this.onPointerUp);
+            document.removeEventListener('pointerup', this.onPointerUp);
+            document.addEventListener('pointerup', this.onPointerUp);
         }
-    }
+    };
 
     onPointerMove = (e: PointerEvent): void => {
         if (e.cancelBubble) return;
-        if ((InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool))) return;
+        if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) return;
 
-        if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
+        if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) && !this.layoutDoc._lockedPosition && !DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
             if (Math.abs(this._downX - e.clientX) > 3 || Math.abs(this._downY - e.clientY) > 3) {
                 if (!e.altKey && (!this.topMost || this.layoutDoc.onDragStart || this.onClickHandler) && (e.buttons === 1 || InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE))) {
-                    document.removeEventListener("pointermove", this.onPointerMove);
-                    document.removeEventListener("pointerup", this.onPointerUp);
-                    this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && "alias") || (this.props.dropAction || this.Document.dropAction || undefined) as dropActionType);
+                    document.removeEventListener('pointermove', this.onPointerMove);
+                    document.removeEventListener('pointerup', this.onPointerUp);
+                    this.startDragging(this._downX, this._downY, ((e.ctrlKey || e.altKey) && 'alias') || ((this.props.dropAction || this.Document.dropAction || undefined) as dropActionType));
                 }
             }
             e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document'  however it does mark the event as cancelBubble=true which we test for in the move event handlers
             e.preventDefault();
         }
-    }
+    };
 
     cleanupPointerEvents = () => {
         this.cleanUpInteractions();
-        document.removeEventListener("pointermove", this.onPointerMove);
-        document.removeEventListener("pointerup", this.onPointerUp);
-    }
+        document.removeEventListener('pointermove', this.onPointerMove);
+        document.removeEventListener('pointerup', this.onPointerUp);
+    };
 
     onPointerUp = (e: PointerEvent): void => {
         this.cleanupPointerEvents();
@@ -608,13 +698,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (this.onPointerUpHandler?.script && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
             this.onPointerUpHandler.script.run({ self: this.rootDoc, this: this.layoutDoc }, console.log);
         } else {
-            this._doubleTap = (Date.now() - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2);
+            this._doubleTap = Date.now() - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2;
             // bcz: this is a placeholder.  documents, when selected, should stopPropagation on doubleClicks if they want to keep the DocumentView from getting them
-            if (!this.props.isSelected(true) || ![DocumentType.PDF, DocumentType.RTF].includes(StrCast(this.rootDoc.type) as any)) this._lastTap = Date.now();// don't want to process the start of a double tap if the doucment is selected
+            if (!this.props.isSelected(true) || ![DocumentType.PDF, DocumentType.RTF].includes(StrCast(this.rootDoc.type) as any)) this._lastTap = Date.now(); // don't want to process the start of a double tap if the doucment is selected
         }
-    }
+    };
 
-    @undoBatch @action
+    @undoBatch
+    @action
     toggleFollowLink = (location: Opt<string>, zoom?: boolean, setPushpin?: boolean): void => {
         this.Document.ignoreClick = false;
         if (setPushpin) {
@@ -628,46 +719,48 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             this.Document.followLinkLocation = location;
         } else if (this.Document._isLinkButton && this.onClickHandler) {
             this.Document._isLinkButton = false;
-            this.Document["onClick-rawScript"] = this.dataDoc["onClick-rawScript"] = this.dataDoc.onClick = this.Document.onClick = this.layoutDoc.onClick = undefined;
+            this.Document['onClick-rawScript'] = this.dataDoc['onClick-rawScript'] = this.dataDoc.onClick = this.Document.onClick = this.layoutDoc.onClick = undefined;
         }
-    }
-    @undoBatch @action
+    };
+    @undoBatch
+    @action
     toggleTargetOnClick = (): void => {
         this.Document.ignoreClick = false;
         this.Document._isLinkButton = true;
         this.Document.isPushpin = true;
-    }
-    @undoBatch @action
-    followLinkOnClick = (location: Opt<string>, zoom: boolean,): void => {
+    };
+    @undoBatch
+    @action
+    followLinkOnClick = (location: Opt<string>, zoom: boolean): void => {
         this.Document.ignoreClick = false;
         this.Document._isLinkButton = true;
         this.Document.isPushpin = false;
         this.Document.followLinkZoom = zoom;
         this.Document.followLinkLocation = location;
-    }
-    @undoBatch @action
+    };
+    @undoBatch
+    @action
     selectOnClick = (): void => {
         this.Document.ignoreClick = false;
         this.Document._isLinkButton = false;
         this.Document.isPushpin = false;
         this.Document.onClick = this.layoutDoc.onClick = undefined;
-    }
+    };
     @undoBatch
     noOnClick = (): void => {
         this.Document.ignoreClick = false;
         this.Document._isLinkButton = false;
-    }
+    };
 
     @undoBatch deleteClicked = () => this.props.removeDocument?.(this.props.Document);
-    @undoBatch setToggleDetail = () => this.Document.onClick = ScriptField.MakeScript(`toggleDetail(documentView, "${StrCast(this.Document.layoutKey).replace("layout_", "")}")`, { documentView: "any" });
+    @undoBatch setToggleDetail = () => (this.Document.onClick = ScriptField.MakeScript(`toggleDetail(documentView, "${StrCast(this.Document.layoutKey).replace('layout_', '')}")`, { documentView: 'any' }));
 
-    @undoBatch @action
+    @undoBatch
+    @action
     drop = async (e: Event, de: DragManager.DropEvent) => {
         if (this.props.dontRegisterView || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return;
-        if (this.props.Document === CurrentUserUtils.ActiveDashboard) {
-            alert((e.target as any)?.closest?.("*.lm_content") ?
-                "You can't perform this move most likely because you don't have permission to modify the destination." :
-                "Linking to document tabs not yet supported. Drop link on document content.");
+        if (this.props.Document === Doc.ActiveDashboard) {
+            alert((e.target as any)?.closest?.('*.lm_content') ? "You can't perform this move most likely because you don't have permission to modify the destination." : 'Linking to document tabs not yet supported. Drop link on document content.');
             return;
         }
         const linkdrag = de.complete.annoDragData ?? de.complete.linkDragData;
@@ -682,20 +775,20 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 de.complete.linkDocument = DocUtils.MakeLink({ doc: linkdrag.linkSourceDoc }, { doc: dropDoc }, undefined, undefined, undefined, undefined, [de.x, de.y - 50]);
             }
         }
-    }
+    };
 
     @undoBatch
     @action
     makeIntoPortal = async () => {
         const portalLink = this.allLinks.find(d => d.anchor1 === this.props.Document);
         if (!portalLink) {
-            const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _fitWidth: true, title: StrCast(this.props.Document.title) + " [Portal]" });
-            DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, "portal to:portal from");
+            const portal = Docs.Create.FreeformDocument([], { _width: NumCast(this.layoutDoc._width) + 10, _height: NumCast(this.layoutDoc._height), _fitWidth: true, title: StrCast(this.props.Document.title) + ' [Portal]' });
+            DocUtils.MakeLink({ doc: this.props.Document }, { doc: portal }, 'portal to:portal from');
         }
-        this.Document.followLinkLocation = "inPlace";
+        this.Document.followLinkLocation = 'inPlace';
         this.Document.followLinkZoom = true;
         this.Document._isLinkButton = true;
-    }
+    };
 
     @action
     onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => {
@@ -707,7 +800,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         }
         // the touch onContextMenu is button 0, the pointer onContextMenu is button 2
         if (e) {
-            if (e.button === 0 && !e.ctrlKey || e.isDefaultPrevented()) {
+            if ((e.button === 0 && !e.ctrlKey) || e.isDefaultPrevented()) {
                 e.preventDefault();
                 return;
             }
@@ -715,7 +808,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
             e.stopPropagation();
             e.persist();
 
-            if (!navigator.userAgent.includes("Mozilla") && (Math.abs(this._downX - e?.clientX) > 3 || Math.abs(this._downY - e?.clientY) > 3)) {
+            if (!navigator.userAgent.includes('Mozilla') && (Math.abs(this._downX - e?.clientX) > 3 || Math.abs(this._downY - e?.clientY) > 3)) {
                 return;
             }
         }
@@ -724,17 +817,17 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (!cm || (e as any)?.nativeEvent?.SchemaHandled) return;
 
         if (e && !(e.nativeEvent as any).dash) {
-            const onDisplay = () => setTimeout(() => {
-                DocumentViewInternal.SelectAfterContextMenu && !this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up.  by delaying the selection until hopefully after the pointer up, the context menu will appear.
+            const onDisplay = () =>
                 setTimeout(() => {
-                    const ele = document.elementFromPoint(e.clientX, e.clientY);
-                    simulateMouseClick(ele, e.clientX, e.clientY, e.screenX, e.screenY);
+                    DocumentViewInternal.SelectAfterContextMenu && !this.props.isSelected(true) && SelectionManager.SelectView(this.props.DocumentView(), false); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up.  by delaying the selection until hopefully after the pointer up, the context menu will appear.
+                    setTimeout(() => {
+                        const ele = document.elementFromPoint(e.clientX, e.clientY);
+                        simulateMouseClick(ele, e.clientX, e.clientY, e.screenX, e.screenY);
+                    });
                 });
-            });
-            if (navigator.userAgent.includes("Macintosh")) {
+            if (navigator.userAgent.includes('Macintosh')) {
                 cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15, undefined, undefined, onDisplay);
-            }
-            else {
+            } else {
                 onDisplay();
             }
             return;
@@ -742,232 +835,291 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
         const customScripts = Cast(this.props.Document.contextMenuScripts, listSpec(ScriptField), []);
         StrListCast(this.Document.contextMenuLabels).forEach((label, i) =>
-            cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ documentView: this, this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: "sticky-note" }));
-        this.props.contextMenuItems?.().forEach(item =>
-            item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: item.icon as IconProp }));
+            cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ documentView: this, this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: 'sticky-note' })
+        );
+        this.props
+            .contextMenuItems?.()
+            .forEach(item => item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: item.icon as IconProp }));
 
         if (!this.props.Document.isFolder) {
             const templateDoc = Cast(this.props.Document[StrCast(this.props.Document.layoutKey)], Doc, null);
-            const appearance = cm.findByDescription("UI Controls...");
-            const appearanceItems: ContextMenuProps[] = appearance && "subitems" in appearance ? appearance.subitems : [];
-            !Doc.noviceMode && templateDoc && appearanceItems.push({ description: "Open Template   ", event: () => this.props.addDocTab(templateDoc, "add:right"), icon: "eye" });
-            !Doc.noviceMode && appearanceItems.push({
-                description: "Add a Field", event: () => {
-                    const alias = Doc.MakeAlias(this.rootDoc);
-                    alias.layout = FormattedTextBox.LayoutString("newfield");
-                    alias.title = "newfield";
-                    alias._height = 35;
-                    alias._width = 100;
-                    alias.syncLayoutFieldWithTitle = true;
-                    alias.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc.width);
-                    alias.y = NumCast(this.rootDoc.y);
-                    this.props.addDocument?.(alias);
-                }, icon: "eye"
-            });
-            DocListCast(this.Document.links).length && appearanceItems.splice(0, 0, { description: `${this.layoutDoc.hideLinkButton ? "Show" : "Hide"} Link Button`, event: action(() => this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton), icon: "eye" });
-            !appearance && cm.addItem({ description: "UI Controls...", subitems: appearanceItems, icon: "compass" });
+            const appearance = cm.findByDescription('UI Controls...');
+            const appearanceItems: ContextMenuProps[] = appearance && 'subitems' in appearance ? appearance.subitems : [];
+            !Doc.noviceMode && templateDoc && appearanceItems.push({ description: 'Open Template   ', event: () => this.props.addDocTab(templateDoc, 'add:right'), icon: 'eye' });
+            !Doc.noviceMode &&
+                appearanceItems.push({
+                    description: 'Add a Field',
+                    event: () => {
+                        const alias = Doc.MakeAlias(this.rootDoc);
+                        alias.layout = FormattedTextBox.LayoutString('newfield');
+                        alias.title = 'newfield';
+                        alias._height = 35;
+                        alias._width = 100;
+                        alias.syncLayoutFieldWithTitle = true;
+                        alias.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc.width);
+                        alias.y = NumCast(this.rootDoc.y);
+                        this.props.addDocument?.(alias);
+                    },
+                    icon: 'eye',
+                });
+            DocListCast(this.Document.links).length &&
+                appearanceItems.splice(0, 0, { description: `${this.layoutDoc.hideLinkButton ? 'Show' : 'Hide'} Link Button`, event: action(() => (this.layoutDoc.hideLinkButton = !this.layoutDoc.hideLinkButton)), icon: 'eye' });
+            !appearance && cm.addItem({ description: 'UI Controls...', subitems: appearanceItems, icon: 'compass' });
 
             if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && this.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Tree) {
-                !Doc.noviceMode && appearanceItems.splice(0, 0, { description: `${!this.layoutDoc._showAudio ? "Show" : "Hide"} Audio Button`, event: action(() => this.layoutDoc._showAudio = !this.layoutDoc._showAudio), icon: "microphone" });
-                const existingOnClick = cm.findByDescription("OnClick...");
-                const onClicks: ContextMenuProps[] = existingOnClick && "subitems" in existingOnClick ? existingOnClick.subitems : [];
+                !Doc.noviceMode && appearanceItems.splice(0, 0, { description: `${!this.layoutDoc._showAudio ? 'Show' : 'Hide'} Audio Button`, event: action(() => (this.layoutDoc._showAudio = !this.layoutDoc._showAudio)), icon: 'microphone' });
+                const existingOnClick = cm.findByDescription('OnClick...');
+                const onClicks: ContextMenuProps[] = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : [];
 
-                const zorders = cm.findByDescription("ZOrder...");
-                const zorderItems: ContextMenuProps[] = zorders && "subitems" in zorders ? zorders.subitems : [];
+                const zorders = cm.findByDescription('ZOrder...');
+                const zorderItems: ContextMenuProps[] = zorders && 'subitems' in zorders ? zorders.subitems : [];
                 if (this.props.bringToFront !== emptyFunction) {
-                    zorderItems.push({ description: "Bring to Front", event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, false)), icon: "expand-arrows-alt" });
-                    zorderItems.push({ description: "Send to Back", event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, true)), icon: "expand-arrows-alt" });
-                    zorderItems.push({ description: this.rootDoc._raiseWhenDragged !== false ? "Keep ZIndex when dragged" : "Allow ZIndex to change when dragged", event: undoBatch(action(() => this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined)), icon: "expand-arrows-alt" });
+                    zorderItems.push({ description: 'Bring to Front', event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, false)), icon: 'expand-arrows-alt' });
+                    zorderItems.push({ description: 'Send to Back', event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, true)), icon: 'expand-arrows-alt' });
+                    zorderItems.push({
+                        description: this.rootDoc._raiseWhenDragged !== false ? 'Keep ZIndex when dragged' : 'Allow ZIndex to change when dragged',
+                        event: undoBatch(action(() => (this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined))),
+                        icon: 'expand-arrows-alt',
+                    });
                 }
-                !zorders && cm.addItem({ description: "ZOrder...", noexpand: true, subitems: zorderItems, icon: "compass" });
+                !zorders && cm.addItem({ description: 'ZOrder...', noexpand: true, subitems: zorderItems, icon: 'compass' });
 
-                !Doc.noviceMode && onClicks.push({ description: "Enter Portal", event: this.makeIntoPortal, icon: "window-restore" });
-                !Doc.noviceMode && onClicks.push({ description: "Toggle Detail", event: this.setToggleDetail, icon: "concierge-bell" });
-                this.props.CollectionFreeFormDocumentView && onClicks.push({ description: (this.Document.followLinkZoom ? "Don't" : "") + " zoom following link", event: () => this.Document.followLinkZoom = !this.Document.followLinkZoom, icon: this.Document.ignoreClick ? "unlock" : "lock" });
+                !Doc.noviceMode && onClicks.push({ description: 'Enter Portal', event: this.makeIntoPortal, icon: 'window-restore' });
+                !Doc.noviceMode && onClicks.push({ description: 'Toggle Detail', event: this.setToggleDetail, icon: 'concierge-bell' });
+                this.props.CollectionFreeFormDocumentView &&
+                    onClicks.push({
+                        description: (this.Document.followLinkZoom ? "Don't" : '') + ' zoom following link',
+                        event: () => (this.Document.followLinkZoom = !this.Document.followLinkZoom),
+                        icon: this.Document.ignoreClick ? 'unlock' : 'lock',
+                    });
 
                 if (!this.Document.annotationOn) {
-                    const options = cm.findByDescription("Options...");
-                    const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : [];
-                    !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" });
-
-                    onClicks.push({ description: this.Document.ignoreClick ? "Select" : "Do Nothing", event: () => this.Document.ignoreClick = !this.Document.ignoreClick, icon: this.Document.ignoreClick ? "unlock" : "lock" });
-                    onClicks.push({ description: this.Document.isLinkButton ? "Remove Follow Behavior" : "Follow Link in Place", event: () => this.toggleFollowLink("inPlace", true, false), icon: "link" });
-                    !this.Document.isLinkButton && onClicks.push({ description: "Follow Link on Right", event: () => this.toggleFollowLink("add:right", false, false), icon: "link" });
-                    onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? "Remove Click Behavior" : "Follow Link", event: () => this.toggleFollowLink(undefined, false, false), icon: "link" });
-                    onClicks.push({ description: (this.Document.isPushpin ? "Remove" : "Make") + " Pushpin", event: () => this.toggleFollowLink(undefined, false, true), icon: "map-pin" });
-                    onClicks.push({ description: "Edit onClick Script", event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, "onClick"), "edit onClick"), icon: "terminal" });
-                    !existingOnClick && cm.addItem({ description: "OnClick...", addDivider: true, noexpand: true, subitems: onClicks, icon: "mouse-pointer" });
+                    const options = cm.findByDescription('Options...');
+                    const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : [];
+                    !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' });
+
+                    onClicks.push({ description: this.Document.ignoreClick ? 'Select' : 'Do Nothing', event: () => (this.Document.ignoreClick = !this.Document.ignoreClick), icon: this.Document.ignoreClick ? 'unlock' : 'lock' });
+                    onClicks.push({ description: this.Document.isLinkButton ? 'Remove Follow Behavior' : 'Follow Link in Place', event: () => this.toggleFollowLink('inPlace', true, false), icon: 'link' });
+                    !this.Document.isLinkButton && onClicks.push({ description: 'Follow Link on Right', event: () => this.toggleFollowLink('add:right', false, false), icon: 'link' });
+                    onClicks.push({ description: this.Document.isLinkButton || this.onClickHandler ? 'Remove Click Behavior' : 'Follow Link', event: () => this.toggleFollowLink(undefined, false, false), icon: 'link' });
+                    onClicks.push({ description: (this.Document.isPushpin ? 'Remove' : 'Make') + ' Pushpin', event: () => this.toggleFollowLink(undefined, false, true), icon: 'map-pin' });
+                    onClicks.push({ description: 'Edit onClick Script', event: () => UndoManager.RunInBatch(() => DocUtils.makeCustomViewClicked(this.props.Document, undefined, 'onClick'), 'edit onClick'), icon: 'terminal' });
+                    !existingOnClick && cm.addItem({ description: 'OnClick...', addDivider: true, noexpand: true, subitems: onClicks, icon: 'mouse-pointer' });
                 } else if (DocListCast(this.Document.links).length) {
-                    onClicks.push({ description: "Select on Click", event: () => this.selectOnClick(), icon: "link" });
-                    onClicks.push({ description: "Follow Link on Click", event: () => this.followLinkOnClick(undefined, false), icon: "link" });
-                    onClicks.push({ description: "Toggle Link Target on Click", event: () => this.toggleTargetOnClick(), icon: "map-pin" });
-                    !existingOnClick && cm.addItem({ description: "OnClick...", addDivider: true, subitems: onClicks, icon: "mouse-pointer" });
+                    onClicks.push({ description: 'Select on Click', event: () => this.selectOnClick(), icon: 'link' });
+                    onClicks.push({ description: 'Follow Link on Click', event: () => this.followLinkOnClick(undefined, false), icon: 'link' });
+                    onClicks.push({ description: 'Toggle Link Target on Click', event: () => this.toggleTargetOnClick(), icon: 'map-pin' });
+                    !existingOnClick && cm.addItem({ description: 'OnClick...', addDivider: true, subitems: onClicks, icon: 'mouse-pointer' });
                 }
             }
 
             const funcs: ContextMenuProps[] = [];
             if (!Doc.noviceMode && this.layoutDoc.onDragStart) {
-                funcs.push({ description: "Drag an Alias", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) });
-                funcs.push({ description: "Drag a Copy", icon: "edit", event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) });
-                funcs.push({ description: "Drag Document", icon: "edit", event: () => this.layoutDoc.onDragStart = undefined });
-                cm.addItem({ description: "OnDrag...", noexpand: true, subitems: funcs, icon: "asterisk" });
+                funcs.push({ description: 'Drag an Alias', icon: 'edit', event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getAlias(this.dragFactory)')) });
+                funcs.push({ description: 'Drag a Copy', icon: 'edit', event: () => this.Document.dragFactory && (this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)')) });
+                funcs.push({ description: 'Drag Document', icon: 'edit', event: () => (this.layoutDoc.onDragStart = undefined) });
+                cm.addItem({ description: 'OnDrag...', noexpand: true, subitems: funcs, icon: 'asterisk' });
             }
 
-            const more = cm.findByDescription("More...");
-            const moreItems = more && "subitems" in more ? more.subitems : [];
+            const more = cm.findByDescription('More...');
+            const moreItems = more && 'subitems' in more ? more.subitems : [];
             if (!Doc.IsSystem(this.rootDoc)) {
-                (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && moreItems.push({ description: "Share", event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: "users" });
+                (this.rootDoc._viewType !== CollectionViewType.Docking || !Doc.noviceMode) && moreItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' });
                 if (!Doc.noviceMode) {
-                    moreItems.push({ description: "Make View of Metadata Field", event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: "concierge-bell" });
-                    moreItems.push({ description: `${this.Document._chromeHidden ? "Show" : "Hide"} Chrome`, event: () => this.Document._chromeHidden = !this.Document._chromeHidden, icon: "project-diagram" });
+                    moreItems.push({ description: 'Make View of Metadata Field', event: () => Doc.MakeMetadataFieldTemplate(this.props.Document, this.props.DataDoc), icon: 'concierge-bell' });
+                    moreItems.push({ description: `${this.Document._chromeHidden ? 'Show' : 'Hide'} Chrome`, event: () => (this.Document._chromeHidden = !this.Document._chromeHidden), icon: 'project-diagram' });
 
                     if (Cast(Doc.GetProto(this.props.Document).data, listSpec(Doc))) {
-                        moreItems.push({ description: "Export to Google Photos Album", event: () => GooglePhotos.Export.CollectionToAlbum({ collection: this.props.Document }).then(console.log), icon: "caret-square-right" });
-                        moreItems.push({ description: "Tag Child Images via Google Photos", event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: "caret-square-right" });
-                        moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" });
+                        moreItems.push({ description: 'Export to Google Photos Album', event: () => GooglePhotos.Export.CollectionToAlbum({ collection: this.props.Document }).then(console.log), icon: 'caret-square-right' });
+                        moreItems.push({ description: 'Tag Child Images via Google Photos', event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: 'caret-square-right' });
+                        moreItems.push({ description: 'Write Back Link to Album', event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: 'caret-square-right' });
                     }
-                    moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Doc.globalServerPath(this.props.Document)), icon: "fingerprint" });
+                    moreItems.push({ description: 'Copy ID', event: () => Utils.CopyText(Doc.globalServerPath(this.props.Document)), icon: 'fingerprint' });
                 }
             }
 
-            if (this.props.removeDocument && !Doc.IsSystem(this.rootDoc) && CurrentUserUtils.ActiveDashboard !== this.props.Document) { // need option to gray out menu items ... preferably with a '?' that explains why they're grayed out (eg., no permissions)
-                moreItems.push({ description: "Close", event: this.deleteClicked, icon: "times" });
+            if (this.props.removeDocument && !Doc.IsSystem(this.rootDoc) && Doc.ActiveDashboard !== this.props.Document) {
+                // need option to gray out menu items ... preferably with a '?' that explains why they're grayed out (eg., no permissions)
+                moreItems.push({ description: 'Close', event: this.deleteClicked, icon: 'times' });
             }
-            !more && moreItems.length && cm.addItem({ description: "More...", subitems: moreItems, icon: "compass" });
-
-            const help = cm.findByDescription("Help...");
-            const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : [];
-            helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" });
-            !Doc.noviceMode && helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" });
-            !Doc.noviceMode && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" });
-            !Doc.noviceMode && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" });
-            cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" });
+            !more && moreItems.length && cm.addItem({ description: 'More...', subitems: moreItems, icon: 'compass' });
+
+            const help = cm.findByDescription('Help...');
+            const helpItems: ContextMenuProps[] = help && 'subitems' in help ? help.subitems : [];
+            helpItems.push({ description: 'Show Fields ', event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), 'add:right'), icon: 'layer-group' });
+            !Doc.noviceMode && helpItems.push({ description: 'Text Shortcuts Ctrl+/', event: () => this.props.addDocTab(Docs.Create.PdfDocument('/assets/cheat-sheet.pdf', { _width: 300, _height: 300 }), 'add:right'), icon: 'keyboard' });
+            !Doc.noviceMode && helpItems.push({ description: 'Print Document in Console', event: () => console.log(this.props.Document), icon: 'hand-point-right' });
+            !Doc.noviceMode && helpItems.push({ description: 'Print DataDoc in Console', event: () => console.log(this.props.Document[DataSym]), icon: 'hand-point-right' });
+            cm.addItem({ description: 'Help...', noexpand: true, subitems: helpItems, icon: 'question' });
         }
 
         if (!this.topMost) e?.stopPropagation(); // DocumentViews should stop propagation of this event
         cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15);
-    }
+    };
 
     collectionFilters = () => StrListCast(this.props.Document._docFilters);
     collectionRangeDocFilters = () => StrListCast(this.props.Document._docRangeFilters);
     @computed get showFilterIcon() {
-        return this.collectionFilters().length || this.collectionRangeDocFilters().length ? "hasFilter" :
-            this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined;
+        return this.collectionFilters().length || this.collectionRangeDocFilters().length ? 'hasFilter' : this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? 'inheritsFilter' : undefined;
     }
     rootSelected = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false;
     panelHeight = () => this.props.PanelHeight() - this.headerMargin;
     screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
     contentScaling = () => this.ContentScale;
     onClickFunc = () => this.onClickHandler;
-    setHeight = (height: number) => this.layoutDoc._height = height;
-    setContentView = action((view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view);
+    setHeight = (height: number) => (this.layoutDoc._height = height);
+    setContentView = action((view: { getAnchor?: () => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view));
     isContentActive = (outsideReaction?: boolean) => {
-        return this.props.isContentActive() === false ? false : (
-            CurrentUserUtils.ActiveTool !== InkTool.None ||
-            SnappingManager.GetIsDragging() ||
-            this.rootSelected() ||
-            this.props.Document.forceActive ||
-            this.props.isSelected(outsideReaction) ||
-            this._componentView?.isAnyChildContentActive?.() ||
-            this.props.isContentActive()) ? true : undefined;
-    }
+        return this.props.isContentActive() === false
+            ? false
+            : Doc.ActiveTool !== InkTool.None ||
+              SnappingManager.GetIsDragging() ||
+              this.rootSelected() ||
+              this.props.Document.forceActive ||
+              this.props.isSelected(outsideReaction) ||
+              this._componentView?.isAnyChildContentActive?.() ||
+              this.props.isContentActive()
+            ? true
+            : undefined;
+    };
     @observable _retryThumb = 1;
     thumbShown = () => {
-        return !this.props.isSelected() && LightboxView.LightboxDoc !== this.rootDoc && this.thumb &&
+        return !this.props.isSelected() &&
+            LightboxView.LightboxDoc !== this.rootDoc &&
+            this.thumb &&
             !Doc.AreProtosEqual(DocumentLinksButton.StartLink, this.rootDoc) &&
             !Doc.isBrushedHighlightedDegree(this.props.Document) &&
-            !this._componentView?.isAnyChildContentActive?.() ? true : false;
-    }
+            !this._componentView?.isAnyChildContentActive?.()
+            ? true
+            : false;
+    };
     @computed get contents() {
         TraceMobx();
-        const audioView = !this.layoutDoc._showAudio ? (null) :
-            <div className="documentView-audioBackground" onPointerDown={this.recordAudioAnnotation} onPointerEnter={this.onPointerEnter} >
-                <FontAwesomeIcon className="documentView-audioFont"
-                    style={{ color: [DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]).length ? "blue" : "gray", "green", "red"][this._mediaState] }}
-                    icon={!DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]).length ? "microphone" : "file-audio"} size="sm" />
-            </div>;
-
-        return <div className="documentView-contentsView"
-            style={{
-                pointerEvents: this.props.pointerEvents?.() as any ?? this.rootDoc.layoutKey === "layout_icon" ? "none" :
-                    this.props.contentPointerEvents as any ? this.props.contentPointerEvents as any :
-                        this.rootDoc.type !== DocumentType.INK && this.isContentActive() ? "all" :
-                            "none",
-                height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
-            }}>
-            {!this._retryThumb || !this.thumbShown() ? (null) :
-                <img style={{ background: "white", top: 0, position: "relative" }} src={this.thumb} // + '?d=' + (new Date()).getTime()}
-                    width={this.props.PanelWidth()} height={this.props.PanelHeight()}
-                    onError={(e: any) => {
-                        setTimeout(action(() => this._retryThumb = 0), 0);
-                        setTimeout(action(() => this._retryThumb = 1), 150);
-                    }} />}
-            <DocumentContentsView key={1}
-                {...this.props}
-                docViewPath={this.props.viewPath}
-                thumbShown={this.thumbShown}
-                isHovering={this.isHovering}
-                setContentView={this.setContentView}
-                scaling={this.contentScaling}
-                PanelHeight={this.panelHeight}
-                setHeight={!this.props.suppressSetHeight ? this.setHeight : undefined}
-                isContentActive={this.isContentActive}
-                ScreenToLocalTransform={this.screenToLocal}
-                rootSelected={this.rootSelected}
-                onClick={this.onClickFunc}
-                focus={this.focus}
-                layoutKey={this.finalLayoutKey} />
-            {this.layoutDoc.hideAllLinks ? (null) : this.allLinkEndpoints}
-            {(!this.props.isSelected() && !this._isHovering) || this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? (null) :
-                <DocumentLinksButton View={this.props.DocumentView()}
-                    ContentScaling={this.props.ContentScaling}
-                    Offset={[this.topMost ? 0 : !this.props.isSelected() ? - 15 : -30, undefined, undefined, this.topMost ? 10 : !this.props.isSelected() ? - 15 : -30]} />
-            }
-            {audioView}
-        </div>;
+        const audioView = !this.layoutDoc._showAudio ? null : (
+            <div className="documentView-audioBackground" onPointerDown={this.recordAudioAnnotation} onPointerEnter={this.onPointerEnter}>
+                <FontAwesomeIcon
+                    className="documentView-audioFont"
+                    style={{ color: [DocListCast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations']).length ? 'blue' : 'gray', 'green', 'red'][this._mediaState] }}
+                    icon={!DocListCast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations']).length ? 'microphone' : 'file-audio'}
+                    size="sm"
+                />
+            </div>
+        );
+
+        return (
+            <div
+                className="documentView-contentsView"
+                style={{
+                    pointerEvents:
+                        (this.props.pointerEvents?.() as any) ?? this.rootDoc.layoutKey === 'layout_icon'
+                            ? 'none'
+                            : (this.props.contentPointerEvents as any)
+                            ? (this.props.contentPointerEvents as any)
+                            : this.rootDoc.type !== DocumentType.INK && this.isContentActive()
+                            ? 'all'
+                            : 'none',
+                    height: this.headerMargin ? `calc(100% - ${this.headerMargin}px)` : undefined,
+                }}>
+                {!this._retryThumb || !this.thumbShown() ? null : (
+                    <img
+                        style={{ background: 'white', top: 0, position: 'relative' }}
+                        src={this.thumb} // + '?d=' + (new Date()).getTime()}
+                        width={this.props.PanelWidth()}
+                        height={this.props.PanelHeight()}
+                        onError={(e: any) => {
+                            setTimeout(
+                                action(() => (this._retryThumb = 0)),
+                                0
+                            );
+                            setTimeout(
+                                action(() => (this._retryThumb = 1)),
+                                150
+                            );
+                        }}
+                    />
+                )}
+                <DocumentContentsView
+                    key={1}
+                    {...this.props}
+                    docViewPath={this.props.viewPath}
+                    thumbShown={this.thumbShown}
+                    isHovering={this.isHovering}
+                    setContentView={this.setContentView}
+                    scaling={this.contentScaling}
+                    PanelHeight={this.panelHeight}
+                    setHeight={!this.props.suppressSetHeight ? this.setHeight : undefined}
+                    isContentActive={this.isContentActive}
+                    ScreenToLocalTransform={this.screenToLocal}
+                    rootSelected={this.rootSelected}
+                    onClick={this.onClickFunc}
+                    focus={this.focus}
+                    layoutKey={this.finalLayoutKey}
+                />
+                {this.layoutDoc.hideAllLinks ? null : this.allLinkEndpoints}
+                {(!this.props.isSelected() && !this._isHovering) || this.hideLinkButton || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() ? null : (
+                    <DocumentLinksButton
+                        View={this.props.DocumentView()}
+                        ContentScaling={this.props.ContentScaling}
+                        Offset={[this.topMost ? 0 : !this.props.isSelected() ? -15 : -30, undefined, undefined, this.topMost ? 10 : !this.props.isSelected() ? -15 : -30]}
+                    />
+                )}
+                {audioView}
+            </div>
+        );
     }
 
     get indicatorIcon() {
-        if (this.props.Document["acl-Public"] !== SharingPermissions.None) return "globe-americas";
-        else if (this.props.Document.numGroupsShared || NumCast(this.props.Document.numUsersShared, 0) > 1) return "users";
-        else return "user";
+        if (this.props.Document['acl-Public'] !== SharingPermissions.None) return 'globe-americas';
+        else if (this.props.Document.numGroupsShared || NumCast(this.props.Document.numUsersShared, 0) > 1) return 'users';
+        else return 'user';
     }
 
     @undoBatch
-    hideLinkAnchor = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && (doc.hidden = true), true)
+    hideLinkAnchor = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && (doc.hidden = true), true);
     anchorPanelWidth = () => this.props.PanelWidth() || 1;
     anchorPanelHeight = () => this.props.PanelHeight() || 1;
     anchorStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
         switch (property) {
-            case StyleProp.ShowTitle: return "";
-            case StyleProp.PointerEvents: return "none";
-            case StyleProp.LinkSource: return this.props.Document;// pass the LinkSource to the LinkAnchorBox
-            default: return this.props.styleProvider?.(doc, props, property);
+            case StyleProp.ShowTitle:
+                return '';
+            case StyleProp.PointerEvents:
+                return 'none';
+            case StyleProp.LinkSource:
+                return this.props.Document; // pass the LinkSource to the LinkAnchorBox
+            default:
+                return this.props.styleProvider?.(doc, props, property);
         }
-    }
-    // We need to use allrelatedLinks to get not just links to the document as a whole, but links to 
-    // anchors that are not rendered as DocumentViews (marked as 'unrendered' with their 'annotationOn' set to this document).  e.g., 
+    };
+    // We need to use allrelatedLinks to get not just links to the document as a whole, but links to
+    // anchors that are not rendered as DocumentViews (marked as 'unrendered' with their 'annotationOn' set to this document).  e.g.,
     //     - PDF text regions are rendered as an Annotations without generating a DocumentView, '
     //     - RTF selections are rendered via Prosemirror and have a mark which contains the Document ID for the annotation link
     //     - and links to PDF/Web docs at a certain scroll location never create an explicit view.
     // For each of these, we create LinkAnchorBox's on the border of the DocumentView.
     @computed get directLinks() {
-        TraceMobx(); return LinkManager.Instance.getAllRelatedLinks(this.rootDoc).filter(link =>
-            Doc.AreProtosEqual(link.anchor1 as Doc, this.rootDoc) ||
-            Doc.AreProtosEqual(link.anchor2 as Doc, this.rootDoc) ||
-            ((link.anchor1 as Doc).unrendered && Doc.AreProtosEqual((link.anchor1 as Doc).annotationOn as Doc, this.rootDoc)) ||
-            ((link.anchor2 as Doc).unrendered && Doc.AreProtosEqual((link.anchor2 as Doc).annotationOn as Doc, this.rootDoc))
+        TraceMobx();
+        return LinkManager.Instance.getAllRelatedLinks(this.rootDoc).filter(
+            link =>
+                Doc.AreProtosEqual(link.anchor1 as Doc, this.rootDoc) ||
+                Doc.AreProtosEqual(link.anchor2 as Doc, this.rootDoc) ||
+                ((link.anchor1 as Doc).unrendered && Doc.AreProtosEqual((link.anchor1 as Doc).annotationOn as Doc, this.rootDoc)) ||
+                ((link.anchor2 as Doc).unrendered && Doc.AreProtosEqual((link.anchor2 as Doc).annotationOn as Doc, this.rootDoc))
         );
     }
-    @computed get allLinks() { TraceMobx(); return LinkManager.Instance.getAllRelatedLinks(this.rootDoc); }
-    @computed get allLinkEndpoints() {  // the small blue dots that mark the endpoints of links
+    @computed get allLinks() {
+        TraceMobx();
+        return LinkManager.Instance.getAllRelatedLinks(this.rootDoc);
+    }
+    @computed get allLinkEndpoints() {
+        // the small blue dots that mark the endpoints of links
         TraceMobx();
         if (this.layoutDoc.unrendered || this.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) return null;
-        if (this.rootDoc.type=== DocumentType.PRES || this.rootDoc.type === DocumentType.LINK || this.props.dontRegisterView) return (null);
+        if (this.rootDoc.type === DocumentType.PRES || this.rootDoc.type === DocumentType.LINK || this.props.dontRegisterView) return null;
         const filtered = DocUtils.FilterDocs(this.directLinks, this.props.docFilters?.() ?? [], []).filter(d => !d.hidden);
-        return filtered.map((link, i) =>
+        return filtered.map((link, i) => (
             <div className="documentView-anchorCont" key={link[Id]}>
-                <DocumentView {...this.props}
+                <DocumentView
+                    {...this.props}
                     isContentActive={returnFalse}
                     Document={link}
                     PanelWidth={this.anchorPanelWidth}
@@ -979,80 +1131,87 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     styleProvider={this.anchorStyleProvider}
                     removeDocument={this.hideLinkAnchor}
                     LayoutTemplate={undefined}
-                    LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(link, this.rootDoc)}`)} />
-            </div >);
+                    LayoutTemplateString={LinkAnchorBox.LayoutString(`anchor${Doc.LinkEndpoint(link, this.rootDoc)}`)}
+                />
+            </div>
+        ));
     }
 
     @action
     onPointerEnter = () => {
         const self = this;
-        const audioAnnos = DocListCast(this.dataDoc[this.LayoutFieldKey + "-audioAnnotations"]);
+        const audioAnnos = DocListCast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations']);
         if (audioAnnos && audioAnnos.length && this._mediaState === 0) {
             const anno = audioAnnos[Math.floor(Math.random() * audioAnnos.length)];
-            anno.data instanceof AudioField && new Howl({
-                src: [anno.data.url.href],
-                format: ["mp3"],
-                autoplay: true,
-                loop: false,
-                volume: 0.5,
-                onend: function () {
-                    runInAction(() => self._mediaState = 0);
-                }
-            });
+            anno.data instanceof AudioField &&
+                new Howl({
+                    src: [anno.data.url.href],
+                    format: ['mp3'],
+                    autoplay: true,
+                    loop: false,
+                    volume: 0.5,
+                    onend: function () {
+                        runInAction(() => (self._mediaState = 0));
+                    },
+                });
             this._mediaState = 1;
         }
-    }
+    };
     recordAudioAnnotation = () => {
         let gumStream: any;
         let recorder: any;
         const self = this;
-        navigator.mediaDevices.getUserMedia({
-            audio: true
-        }).then(function (stream) {
-            gumStream = stream;
-            recorder = new MediaRecorder(stream);
-            recorder.ondataavailable = async (e: any) => {
-                const [{ result }] = await Networking.UploadFilesToServer(e.data);
-                if (!(result instanceof Error)) {
-                    const audioDoc = Docs.Create.AudioDocument(result.accessPaths.agnostic.client, { title: "audio test", _width: 200, _height: 32 });
-                    audioDoc.treeViewExpandedView = "layout";
-                    const audioAnnos = Cast(self.dataDoc[self.LayoutFieldKey + "-audioAnnotations"], listSpec(Doc));
-                    if (audioAnnos === undefined) {
-                        self.dataDoc[self.LayoutFieldKey + "-audioAnnotations"] = new List([audioDoc]);
-                    } else {
-                        audioAnnos.push(audioDoc);
+        navigator.mediaDevices
+            .getUserMedia({
+                audio: true,
+            })
+            .then(function (stream) {
+                gumStream = stream;
+                recorder = new MediaRecorder(stream);
+                recorder.ondataavailable = async (e: any) => {
+                    const [{ result }] = await Networking.UploadFilesToServer(e.data);
+                    if (!(result instanceof Error)) {
+                        const audioDoc = Docs.Create.AudioDocument(result.accessPaths.agnostic.client, { title: 'audio test', _width: 200, _height: 32 });
+                        audioDoc.treeViewExpandedView = 'layout';
+                        const audioAnnos = Cast(self.dataDoc[self.LayoutFieldKey + '-audioAnnotations'], listSpec(Doc));
+                        if (audioAnnos === undefined) {
+                            self.dataDoc[self.LayoutFieldKey + '-audioAnnotations'] = new List([audioDoc]);
+                        } else {
+                            audioAnnos.push(audioDoc);
+                        }
                     }
-                }
-            };
-            runInAction(() => self._mediaState = 2);
-            recorder.start();
-            setTimeout(() => {
-                recorder.stop();
-                runInAction(() => self._mediaState = 0);
-                gumStream.getAudioTracks()[0].stop();
-            }, 5000);
-        });
-    }
+                };
+                runInAction(() => (self._mediaState = 2));
+                recorder.start();
+                setTimeout(() => {
+                    recorder.stop();
+                    runInAction(() => (self._mediaState = 0));
+                    gumStream.getAudioTracks()[0].stop();
+                }, 5000);
+            });
+    };
 
-    captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewInternalProps>, property: string) => this.props?.styleProvider?.(doc, props, property + ":caption");
+    captionStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewInternalProps>, property: string) => this.props?.styleProvider?.(doc, props, property + ':caption');
     @computed get innards() {
         TraceMobx();
-        const ffscale = () => (this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.ScreenToLocalTransform().Scale || 1);
-        const showTitle = this.ShowTitle?.split(":")[0];
-        const showTitleHover = this.ShowTitle?.includes(":hover");
+        const ffscale = () => this.props.DocumentView().props.CollectionFreeFormDocumentView?.().props.ScreenToLocalTransform().Scale || 1;
+        const showTitle = this.ShowTitle?.split(':')[0];
+        const showTitleHover = this.ShowTitle?.includes(':hover');
         const showCaption = !this.props.hideCaptions && this.Document._viewType !== CollectionViewType.Carousel ? StrCast(this.layoutDoc._showCaption) : undefined;
-        const captionView = !showCaption ? (null) :
-            <div className="documentView-captionWrapper"
+        const captionView = !showCaption ? null : (
+            <div
+                className="documentView-captionWrapper"
                 style={{
-                    pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : this.isContentActive() || this.props.isDocumentActive?.() ? "all" : undefined,
+                    pointerEvents: this.onClickHandler || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
                     minWidth: 50 * ffscale(),
-                    maxHeight: `max(100%, ${20 * ffscale()}px)`
+                    maxHeight: `max(100%, ${20 * ffscale()}px)`,
                 }}>
-                <FormattedTextBox {...OmitKeys(this.props, ['children']).omit}
+                <FormattedTextBox
+                    {...OmitKeys(this.props, ['children']).omit}
                     yPadding={10}
                     xPadding={10}
                     fieldKey={showCaption}
-                    fontSize={12 * Math.max(1, 2 * ffscale() / 3)}
+                    fontSize={12 * Math.max(1, (2 * ffscale()) / 3)}
                     styleProvider={this.captionStyleProvider}
                     dontRegisterView={true}
                     noSidebar={true}
@@ -1060,192 +1219,272 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                     isContentActive={this.isContentActive}
                     onClick={this.onClickFunc}
                 />
-            </div>;
-        const targetDoc = (showTitle?.startsWith("_") ? this.layoutDoc : this.rootDoc);
-        const background = StrCast(SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor,
-            Doc.UserDoc().showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : "rgba(0,0,0,0.4)");
-        const titleView = !showTitle ? (null) :
-            <div className={`documentView-titleWrapper${showTitleHover ? "-hover" : ""}`} key="title" style={{
-                position: this.headerMargin ? "relative" : "absolute",
-                height: this.titleHeight,
-                color: lightOrDark(background),
-                background,
-                pointerEvents: this.onClickHandler || this.Document.ignoreClick ? "none" : this.isContentActive() || this.props.isDocumentActive?.() ? "all" : undefined,
-            }}>
-                <EditableView ref={this._titleRef}
-                    contents={showTitle.split(";").map(field => field.trim()).map(field => targetDoc[field]?.toString()).join("\\")}
-                    display={"block"}
+            </div>
+        );
+        const targetDoc = showTitle?.startsWith('_') ? this.layoutDoc : this.rootDoc;
+        const background = StrCast(
+            SharingManager.Instance.users.find(users => users.user.email === this.dataDoc.author)?.sharingDoc.userColor,
+            Doc.UserDoc().showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : 'rgba(0,0,0,0.4)'
+        );
+        const titleView = !showTitle ? null : (
+            <div
+                className={`documentView-titleWrapper${showTitleHover ? '-hover' : ''}`}
+                key="title"
+                style={{
+                    position: this.headerMargin ? 'relative' : 'absolute',
+                    height: this.titleHeight,
+                    color: lightOrDark(background),
+                    background,
+                    pointerEvents: this.onClickHandler || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
+                }}>
+                <EditableView
+                    ref={this._titleRef}
+                    contents={showTitle
+                        .split(';')
+                        .map(field => field.trim())
+                        .map(field => targetDoc[field]?.toString())
+                        .join('\\')}
+                    display={'block'}
                     fontSize={10}
-                    GetValue={() => showTitle.split(";").length === 1 ? showTitle + "=" + Field.toString(targetDoc[showTitle.split(";")[0]] as any as Field) : "#" + showTitle}
+                    GetValue={() => (showTitle.split(';').length === 1 ? showTitle + '=' + Field.toString(targetDoc[showTitle.split(';')[0]] as any as Field) : '#' + showTitle)}
                     SetValue={undoBatch((input: string) => {
-                        if (input?.startsWith("#")) {
+                        if (input?.startsWith('#')) {
                             if (this.props.showTitle) {
                                 this.rootDoc._showTitle = input?.substring(1) ? input.substring(1) : undefined;
                             } else {
-                                Doc.UserDoc().showTitle = input?.substring(1) ? input.substring(1) : "creationDate";
+                                Doc.UserDoc().showTitle = input?.substring(1) ? input.substring(1) : 'creationDate';
                             }
                         } else {
-                            var value = input.replace(new RegExp(showTitle + "="), "") as string | number;
-                            if (showTitle !== "title" && Number(value).toString() === value) value = Number(value);
-                            if (showTitle.includes("Date") || showTitle === "author") return true;
+                            var value = input.replace(new RegExp(showTitle + '='), '') as string | number;
+                            if (showTitle !== 'title' && Number(value).toString() === value) value = Number(value);
+                            if (showTitle.includes('Date') || showTitle === 'author') return true;
                             Doc.SetInPlace(targetDoc, showTitle, value, true);
                         }
                         return true;
                     })}
                 />
-            </div>;
-        return this.props.hideTitle || (!showTitle && !showCaption) ?
-            this.contents :
-            <div className="documentView-styleWrapper" >
-                {!this.headerMargin ? <> {this.contents} {titleView} </> : <> {titleView} {this.contents} </>}
+            </div>
+        );
+        return this.props.hideTitle || (!showTitle && !showCaption) ? (
+            this.contents
+        ) : (
+            <div className="documentView-styleWrapper">
+                {!this.headerMargin ? (
+                    <>
+                        {' '}
+                        {this.contents} {titleView}{' '}
+                    </>
+                ) : (
+                    <>
+                        {' '}
+                        {titleView} {this.contents}{' '}
+                    </>
+                )}
                 {captionView}
-            </div>;
+            </div>
+        );
     }
     isHovering = () => this._isHovering;
     @observable _isHovering = false;
-    @observable _: string = "";
+    @observable _: string = '';
     @computed get renderDoc() {
         TraceMobx();
-        const thumb = ImageCast(this.layoutDoc["thumb-frozen"], ImageCast(this.layoutDoc.thumb))?.url.href.replace(".png", "_m.png");
+        const thumb = ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url.href.replace('.png', '_m.png');
         const isButton = this.props.Document.type === DocumentType.FONTICON;
         if (!(this.props.Document instanceof Doc) || GetEffectiveAcl(this.props.Document[DataSym]) === AclPrivate || (this.hidden && !this.props.treeViewDoc)) return null;
-        return this.docContents ??
-            <div className={`documentView-node${this.topMost ? "-topmost" : ""}`}
-                id={this.props.Document[Id]}
-                onPointerEnter={action(() => this._isHovering = true)}
-                onPointerLeave={action(() => this._isHovering = false)}
-                style={{
-                    background: isButton || thumb ? undefined : this.backgroundColor,
-                    opacity: this.opacity,
-                    color: StrCast(this.layoutDoc.color, "inherit"),
-                    fontFamily: StrCast(this.Document._fontFamily, "inherit"),
-                    fontSize: Cast(this.Document._fontSize, "string", null),
-                    transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined,
-                    transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : `transform ${this._animateScaleTime / 1000}s ease-${this._animateScalingTo < 1 ? "in" : "out"}`,
-                }}>
-
-                {this.innards}
-                {this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <div className="documentView-contentBlocker" /> : (null)}
-                {this.widgetDecorations ?? null}
-            </div>;
+        return (
+            this.docContents ?? (
+                <div
+                    className={`documentView-node${this.topMost ? '-topmost' : ''}`}
+                    id={this.props.Document[Id]}
+                    onPointerEnter={action(() => (this._isHovering = true))}
+                    onPointerLeave={action(() => (this._isHovering = false))}
+                    style={{
+                        background: isButton || thumb ? undefined : this.backgroundColor,
+                        opacity: this.opacity,
+                        color: StrCast(this.layoutDoc.color, 'inherit'),
+                        fontFamily: StrCast(this.Document._fontFamily, 'inherit'),
+                        fontSize: Cast(this.Document._fontSize, 'string', null),
+                        transform: this._animateScalingTo ? `scale(${this._animateScalingTo})` : undefined,
+                        transition: !this._animateScalingTo ? StrCast(this.Document.dataTransition) : `transform ${this._animateScaleTime / 1000}s ease-${this._animateScalingTo < 1 ? 'in' : 'out'}`,
+                    }}>
+                    {this.innards}
+                    {this.onClickHandler && this.props.ContainingCollectionView?.props.Document._viewType === CollectionViewType.Time ? <div className="documentView-contentBlocker" /> : null}
+                    {this.widgetDecorations ?? null}
+                </div>
+            )
+        );
     }
     render() {
         TraceMobx();
         const highlightIndex = this.props.LayoutTemplateString ? (Doc.IsHighlighted(this.props.Document) ? 6 : Doc.DocBrushStatus.unbrushed) : Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString
-        const highlightColor = ["transparent", "rgb(68, 118, 247)", "rgb(68, 118, 247)", "orange", "lightBlue"][highlightIndex];
-        const highlightStyle = ["solid", "dashed", "solid", "solid", "solid"][highlightIndex];
+        const highlightColor = ['transparent', 'rgb(68, 118, 247)', 'rgb(68, 118, 247)', 'orange', 'lightBlue'][highlightIndex];
+        const highlightStyle = ['solid', 'dashed', 'solid', 'solid', 'solid'][highlightIndex];
         const excludeTypes = !this.props.treeViewDoc ? [DocumentType.FONTICON, DocumentType.INK] : [DocumentType.FONTICON];
         let highlighting = !this.props.disableDocBrushing && highlightIndex && !excludeTypes.includes(this.layoutDoc.type as any) && this.layoutDoc._viewType !== CollectionViewType.Linear;
-        highlighting = highlighting && this.props.focus !== emptyFunction && this.layoutDoc.title !== "[pres element template]";  // bcz: hack to turn off highlighting onsidebar panel documents.  need to flag a document as not highlightable in a more direct way
+        highlighting = highlighting && this.props.focus !== emptyFunction && this.layoutDoc.title !== '[pres element template]'; // bcz: hack to turn off highlighting onsidebar panel documents.  need to flag a document as not highlightable in a more direct way
 
         const borderPath = this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BorderPath) || { path: undefined };
         const internal = PresBox.EffectsProvider(this.layoutDoc, this.renderDoc) || this.renderDoc;
-        const boxShadow = this.props.treeViewDoc ? null : highlighting && this.borderRounding && highlightStyle !== "dashed" ? `0 0 0 ${highlightIndex}px ${highlightColor}` :
-            this.boxShadow || (this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined);
-
-        // Return surrounding highlight 
-        return <div className={`${DocumentView.ROOT_DIV} docView-hack`} ref={this._mainCont}
-            onContextMenu={this.onContextMenu}
-            onKeyDown={this.onKeyDown}
-            onPointerDown={this.onPointerDown}
-            onClick={this.onClick}
-            onPointerEnter={action(e => !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document))}
-            onPointerLeave={action(e => !hasDescendantTarget(e.nativeEvent.x, e.nativeEvent.y, this.ContentDiv) && Doc.UnBrushDoc(this.props.Document))}
-            style={{
-                display: this.hidden ? "inline" : undefined,
-                borderRadius: this.borderRounding,
-                pointerEvents: this.pointerEvents,
-                outline: highlighting && !this.borderRounding ? `${highlightColor} ${highlightStyle} ${highlightIndex}px` : "solid 0px",
-                border: highlighting && this.borderRounding && highlightStyle === "dashed" ? `${highlightStyle} ${highlightColor} ${highlightIndex}px` : undefined,
-                boxShadow,
-                clipPath: borderPath.path ? `path('${borderPath.path}')` : undefined
-            }}>
-            {!borderPath.path ? internal :
-                <>
-                    {/* <div style={{ clipPath: `path('${borderPath.fill}')` }}>
+        const boxShadow = this.props.treeViewDoc
+            ? null
+            : highlighting && this.borderRounding && highlightStyle !== 'dashed'
+            ? `0 0 0 ${highlightIndex}px ${highlightColor}`
+            : this.boxShadow || (this.props.Document.isTemplateForField ? 'black 0.2vw 0.2vw 0.8vw' : undefined);
+
+        // Return surrounding highlight
+        return (
+            <div
+                className={`${DocumentView.ROOT_DIV} docView-hack`}
+                ref={this._mainCont}
+                onContextMenu={this.onContextMenu}
+                onKeyDown={this.onKeyDown}
+                onPointerDown={this.onPointerDown}
+                onClick={this.onClick}
+                onPointerEnter={action(e => !SnappingManager.GetIsDragging() && Doc.BrushDoc(this.props.Document))}
+                onPointerLeave={action(e => !hasDescendantTarget(e.nativeEvent.x, e.nativeEvent.y, this.ContentDiv) && Doc.UnBrushDoc(this.props.Document))}
+                style={{
+                    display: this.hidden ? 'inline' : undefined,
+                    borderRadius: this.borderRounding,
+                    pointerEvents: this.pointerEvents,
+                    outline: highlighting && !this.borderRounding ? `${highlightColor} ${highlightStyle} ${highlightIndex}px` : 'solid 0px',
+                    border: highlighting && this.borderRounding && highlightStyle === 'dashed' ? `${highlightStyle} ${highlightColor} ${highlightIndex}px` : undefined,
+                    boxShadow,
+                    clipPath: borderPath.path ? `path('${borderPath.path}')` : undefined,
+                }}>
+                {!borderPath.path ? (
+                    internal
+                ) : (
+                    <>
+                        {/* <div style={{ clipPath: `path('${borderPath.fill}')` }}>
                         {internal}
                     </div> */}
-                    {internal}
-                    <div key="border2" className="documentView-customBorder" style={{ pointerEvents: "none" }} >
-                        <svg style={{ overflow: "visible" }} viewBox={`0 0 ${this.props.PanelWidth()} ${this.props.PanelHeight()}`}>
-                            <path d={borderPath.path} style={{ stroke: "black", fill: "transparent", strokeWidth: borderPath.width }} />
-                        </svg>
-                    </div>
-                </>
-            }
-            {this.showFilterIcon ?
-                <FontAwesomeIcon icon={"filter"} size="lg"
-                    style={{ position: 'absolute', top: '1%', right: '1%', cursor: "pointer", padding: 1, color: this.showFilterIcon === "hasFilter" ? '#18c718bd' : "orange", zIndex: 1 }}
-                    onPointerDown={action(e => { this.props.select(false); CurrentUserUtils.propertiesWidth = 250; e.stopPropagation(); })}
-                />
-                : (null)}
-        </div>;
+                        {internal}
+                        <div key="border2" className="documentView-customBorder" style={{ pointerEvents: 'none' }}>
+                            <svg style={{ overflow: 'visible' }} viewBox={`0 0 ${this.props.PanelWidth()} ${this.props.PanelHeight()}`}>
+                                <path d={borderPath.path} style={{ stroke: 'black', fill: 'transparent', strokeWidth: borderPath.width }} />
+                            </svg>
+                        </div>
+                    </>
+                )}
+                {this.showFilterIcon ? (
+                    <FontAwesomeIcon
+                        icon={'filter'}
+                        size="lg"
+                        style={{ position: 'absolute', top: '1%', right: '1%', cursor: 'pointer', padding: 1, color: this.showFilterIcon === 'hasFilter' ? '#18c718bd' : 'orange', zIndex: 1 }}
+                        onPointerDown={action(e => {
+                            this.props.select(false);
+                            SettingsManager.propertiesWidth = 250;
+                            e.stopPropagation();
+                        })}
+                    />
+                ) : null}
+            </div>
+        );
     }
 }
 
 @observer
 export class DocumentView extends React.Component<DocumentViewProps> {
-    public static ROOT_DIV = "documentView-effectsWrapper";
-    public get displayName() { return "DocumentView(" + this.props.Document?.title + ")"; } // this makes mobx trace() statements more descriptive
+    public static ROOT_DIV = 'documentView-effectsWrapper';
+    public get displayName() {
+        return 'DocumentView(' + this.props.Document?.title + ')';
+    } // this makes mobx trace() statements more descriptive
     public ContentRef = React.createRef<HTMLDivElement>();
     private _disposers: { [name: string]: IReactionDisposer } = {};
 
     public static showBackLinks(linkSource: Doc) {
-        const docid = Doc.CurrentUserEmail + Doc.GetProto(linkSource)[Id] + "-pivotish";
+        const docid = Doc.CurrentUserEmail + Doc.GetProto(linkSource)[Id] + '-pivotish';
         DocServer.GetRefField(docid).then(docx => {
             const rootAlias = () => {
                 const rootAlias = Doc.MakeAlias(linkSource);
                 rootAlias.x = rootAlias.y = 0;
                 return rootAlias;
             };
-            const linkCollection = ((docx instanceof Doc) && docx) || Docs.Create.StackingDocument([/*rootAlias()*/], { title: linkSource.title + "-pivot", _width: 500, _height: 500, }, docid);
+            const linkCollection =
+                (docx instanceof Doc && docx) ||
+                Docs.Create.StackingDocument(
+                    [
+                        /*rootAlias()*/
+                    ],
+                    { title: linkSource.title + '-pivot', _width: 500, _height: 500 },
+                    docid
+                );
             linkCollection.linkSource = linkSource;
-            if (!linkCollection.reactionScript) linkCollection.reactionScript = ScriptField.MakeScript("updateLinkCollection(self)");
+            if (!linkCollection.reactionScript) linkCollection.reactionScript = ScriptField.MakeScript('updateLinkCollection(self)');
             LightboxView.SetLightboxDoc(linkCollection);
         });
     }
 
     @observable public docView: DocumentViewInternal | undefined | null;
 
-    showContextMenu(pageX:number, pageY:number) { return this.docView?.onContextMenu(undefined, pageX, pageY); }
-    get Document() { return this.props.Document; }
-    get topMost() { return this.props.renderDepth === 0; }
-    get rootDoc() { return this.docView?.rootDoc || this.Document; }
-    get dataDoc() { return this.docView?.dataDoc || this.Document; }
-    get finalLayoutKey() { return this.docView?.finalLayoutKey || "layout"; }
-    get ContentDiv() { return this.docView?.ContentDiv; }
-    get ComponentView() { return this.docView?._componentView; }
-    get allLinks() { return this.docView?.allLinks || []; }
-    get LayoutFieldKey() { return this.docView?.LayoutFieldKey || "layout"; }
-    get fitWidth() { return this.props.fitWidth?.(this.rootDoc) || this.layoutDoc.fitWidth; }
-
-    @computed get docViewPath(): DocumentView[] { return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this]; }
-    @computed get layoutDoc() { return Doc.Layout(this.Document, this.props.LayoutTemplate?.()); }
+    showContextMenu(pageX: number, pageY: number) {
+        return this.docView?.onContextMenu(undefined, pageX, pageY);
+    }
+    get Document() {
+        return this.props.Document;
+    }
+    get topMost() {
+        return this.props.renderDepth === 0;
+    }
+    get rootDoc() {
+        return this.docView?.rootDoc || this.Document;
+    }
+    get dataDoc() {
+        return this.docView?.dataDoc || this.Document;
+    }
+    get finalLayoutKey() {
+        return this.docView?.finalLayoutKey || 'layout';
+    }
+    get ContentDiv() {
+        return this.docView?.ContentDiv;
+    }
+    get ComponentView() {
+        return this.docView?._componentView;
+    }
+    get allLinks() {
+        return this.docView?.allLinks || [];
+    }
+    get LayoutFieldKey() {
+        return this.docView?.LayoutFieldKey || 'layout';
+    }
+    get fitWidth() {
+        return this.props.fitWidth?.(this.rootDoc) || this.layoutDoc.fitWidth;
+    }
+
+    @computed get docViewPath(): DocumentView[] {
+        return this.props.docViewPath ? [...this.props.docViewPath(), this] : [this];
+    }
+    @computed get layoutDoc() {
+        return Doc.Layout(this.Document, this.props.LayoutTemplate?.());
+    }
     @computed get nativeWidth() {
-        return this.docView?._componentView?.reverseNativeScaling?.() ? 0 :
-            returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, !this.fitWidth));
+        return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeWidth?.(), Doc.NativeWidth(this.layoutDoc, this.props.DataDoc, !this.fitWidth));
     }
     @computed get nativeHeight() {
-        return this.docView?._componentView?.reverseNativeScaling?.() ? 0 :
-            returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.fitWidth));
+        return this.docView?._componentView?.reverseNativeScaling?.() ? 0 : returnVal(this.props.NativeHeight?.(), Doc.NativeHeight(this.layoutDoc, this.props.DataDoc, !this.fitWidth));
     }
     @computed get shouldNotScale() {
-        return (this.fitWidth && !this.nativeWidth) ||
-            this.props.dontScaleFilter?.(this.Document) ||
-            this.props.treeViewDoc || [CollectionViewType.Docking].includes(this.Document._viewType as any);
+        return (this.fitWidth && !this.nativeWidth) || this.props.dontScaleFilter?.(this.Document) || this.props.treeViewDoc || [CollectionViewType.Docking].includes(this.Document._viewType as any);
+    }
+    @computed get effectiveNativeWidth() {
+        return this.shouldNotScale ? 0 : this.nativeWidth || NumCast(this.layoutDoc.width);
+    }
+    @computed get effectiveNativeHeight() {
+        return this.shouldNotScale ? 0 : this.nativeHeight || NumCast(this.layoutDoc.height);
     }
-    @computed get effectiveNativeWidth() { return this.shouldNotScale ? 0 : (this.nativeWidth || NumCast(this.layoutDoc.width)); }
-    @computed get effectiveNativeHeight() { return this.shouldNotScale ? 0 : (this.nativeHeight || NumCast(this.layoutDoc.height)); }
     @computed get nativeScaling() {
         if (this.shouldNotScale) return 1;
         const minTextScale = this.Document.type === DocumentType.RTF ? 0.1 : 0;
         if (this.fitWidth || this.props.PanelHeight() / (this.effectiveNativeHeight || 1) > this.props.PanelWidth() / (this.effectiveNativeWidth || 1)) {
-            return Math.max(minTextScale, this.props.PanelWidth() / (this.effectiveNativeWidth || 1));  // width-limited or fitWidth
+            return Math.max(minTextScale, this.props.PanelWidth() / (this.effectiveNativeWidth || 1)); // width-limited or fitWidth
         }
         return Math.max(minTextScale, this.props.PanelHeight() / (this.effectiveNativeHeight || 1)); // height-limited or unscaled
     }
 
-    @computed get panelWidth() { return this.effectiveNativeWidth ? this.effectiveNativeWidth * this.nativeScaling : this.props.PanelWidth(); }
+    @computed get panelWidth() {
+        return this.effectiveNativeWidth ? this.effectiveNativeWidth * this.nativeScaling : this.props.PanelWidth();
+    }
     @computed get panelHeight() {
         if (this.effectiveNativeHeight && !this.layoutDoc.nativeHeightUnfrozen) {
             const scrollHeight = this.fitWidth ? Math.max(this.ComponentView?.getScrollHeight?.() ?? NumCast(this.layoutDoc.scrollHeight)) : 0;
@@ -1253,10 +1492,18 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         }
         return this.props.PanelHeight();
     }
-    @computed get Xshift() { return this.effectiveNativeWidth ? (this.props.PanelWidth() - this.effectiveNativeWidth * this.nativeScaling) / 2 : 0; }
-    @computed get Yshift() { return this.effectiveNativeWidth && this.effectiveNativeHeight && Math.abs(this.Xshift) < 0.001 && !this.layoutDoc.nativeHeightUnfrozen ? Math.max(0, (this.props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2) : 0; }
-    @computed get centeringX() { return this.props.dontCenter?.includes("x") ? 0 : this.Xshift; }
-    @computed get centeringY() { return this.props.dontCenter?.includes("y") ? 0 : this.Yshift; }
+    @computed get Xshift() {
+        return this.effectiveNativeWidth ? (this.props.PanelWidth() - this.effectiveNativeWidth * this.nativeScaling) / 2 : 0;
+    }
+    @computed get Yshift() {
+        return this.effectiveNativeWidth && this.effectiveNativeHeight && Math.abs(this.Xshift) < 0.001 && !this.layoutDoc.nativeHeightUnfrozen ? Math.max(0, (this.props.PanelHeight() - this.effectiveNativeHeight * this.nativeScaling) / 2) : 0;
+    }
+    @computed get centeringX() {
+        return this.props.dontCenter?.includes('x') ? 0 : this.Xshift;
+    }
+    @computed get centeringY() {
+        return this.props.dontCenter?.includes('y') ? 0 : this.Yshift;
+    }
 
     toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.ContentScale, this.props.PanelWidth(), this.props.PanelHeight());
     focus = (doc: Doc, options?: DocFocusOptions) => this.docView?.focus(doc, options);
@@ -1264,23 +1511,23 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         if (!this.docView || !this.docView.ContentDiv || this.props.Document.presBox || this.docView.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) {
             return undefined;
         }
-        const xf = (this.docView?.props.ScreenToLocalTransform().scale(this.nativeScaling)).inverse();
+        const xf = this.docView?.props.ScreenToLocalTransform().scale(this.nativeScaling).inverse();
         const [[left, top], [right, bottom]] = [xf.transformPoint(0, 0), xf.transformPoint(this.panelWidth, this.panelHeight)];
         if (this.docView.props.LayoutTemplateString?.includes(LinkAnchorBox.name)) {
-            const docuBox = this.docView.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
+            const docuBox = this.docView.ContentDiv.getElementsByClassName('linkAnchorBox-cont');
             if (docuBox.length) return { ...docuBox[0].getBoundingClientRect(), center: undefined };
         }
         return { left, top, right, bottom, center: this.ComponentView?.getCenter?.(xf) };
-    }
+    };
 
     public iconify(finished?: () => void) {
         this.ComponentView?.updateIcon?.();
-        const layoutKey = Cast(this.Document.layoutKey, "string", null);
-        if (layoutKey !== "layout_icon") {
-            this.switchViews(true, "icon", finished);
-            if (layoutKey && layoutKey !== "layout" && layoutKey !== "layout_icon") this.Document.deiconifyLayout = layoutKey.replace("layout_", "");
+        const layoutKey = Cast(this.Document.layoutKey, 'string', null);
+        if (layoutKey !== 'layout_icon') {
+            this.switchViews(true, 'icon', finished);
+            if (layoutKey && layoutKey !== 'layout' && layoutKey !== 'layout_icon') this.Document.deiconifyLayout = layoutKey.replace('layout_', '');
         } else {
-            const deiconifyLayout = Cast(this.Document.deiconifyLayout, "string", null);
+            const deiconifyLayout = Cast(this.Document.deiconifyLayout, 'string', null);
             this.switchViews(deiconifyLayout ? true : false, deiconifyLayout, finished);
             this.Document.deiconifyLayout = undefined;
             this.props.bringToFront(this.rootDoc);
@@ -1291,17 +1538,23 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     setCustomView = (custom: boolean, layout: string): void => {
         Doc.setNativeView(this.props.Document);
         custom && DocUtils.makeCustomViewClicked(this.props.Document, Docs.Create.StackingDocument, layout, undefined);
-    }
+    };
     switchViews = action((custom: boolean, view: string, finished?: () => void) => {
-        this.docView && (this.docView._animateScalingTo = 0.1);  // shrink doc
-        setTimeout(action(() => {
-            this.setCustomView(custom, view);
-            this.docView && (this.docView._animateScalingTo = 1); // expand it
-            setTimeout(action(() => {
-                this.docView && (this.docView._animateScalingTo = 0);
-                finished?.();
-            }), this.docView!._animateScaleTime - 10);
-        }), this.docView!._animateScaleTime - 10);
+        this.docView && (this.docView._animateScalingTo = 0.1); // shrink doc
+        setTimeout(
+            action(() => {
+                this.setCustomView(custom, view);
+                this.docView && (this.docView._animateScalingTo = 1); // expand it
+                setTimeout(
+                    action(() => {
+                        this.docView && (this.docView._animateScalingTo = 0);
+                        finished?.();
+                    }),
+                    this.docView!._animateScaleTime - 10
+                );
+            }),
+            this.docView!._animateScaleTime - 10
+        );
     });
 
     startDragging = (x: number, y: number, dropAction: dropActionType, hideSource = false) => this.docView?.startDragging(x, y, dropAction, hideSource);
@@ -1315,7 +1568,11 @@ export class DocumentView extends React.Component<DocumentViewProps> {
     PanelHeight = () => this.panelHeight;
     ContentScale = () => this.nativeScaling;
     selfView = () => this;
-    screenToLocalTransform = () =>this.props.ScreenToLocalTransform().translate(-this.centeringX, -this.centeringY).scale(1 / this.nativeScaling);
+    screenToLocalTransform = () =>
+        this.props
+            .ScreenToLocalTransform()
+            .translate(-this.centeringX, -this.centeringY)
+            .scale(1 / this.nativeScaling);
     componentDidMount() {
         this._disposers.reactionScript = reaction(
             () => ScriptCast(this.rootDoc.reactionScript)?.script?.run({ this: this.props.Document, self: Cast(this.rootDoc, Doc, null) || this.props.Document }).result,
@@ -1341,32 +1598,41 @@ export class DocumentView extends React.Component<DocumentViewProps> {
         const yshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined);
         const isPresTreeElement: boolean = this.props.treeViewDoc?.type === DocumentType.PRES;
         const isButton: boolean = this.props.Document.type === DocumentType.FONTICON || this.props.Document._viewType === CollectionViewType.Linear;
-        return (<div className="contentFittingDocumentView">
-            {!this.props.Document || !this.props.PanelWidth() ? (null) : (
-                <div className="contentFittingDocumentView-previewDoc" ref={this.ContentRef}
-                    style={{
-                        transition: this.props.dataTransition,
-                        position: this.props.Document.isInkMask ? "absolute" : undefined,
-                        transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`,
-                        width: isButton || isPresTreeElement ? "100%" : xshift() ?? `${100 * (this.props.PanelWidth() - this.Xshift * 2) / this.props.PanelWidth()}%`,
-                        height: isButton || this.props.forceAutoHeight ? undefined : yshift() ?? (this.fitWidth ? `${this.panelHeight}px` :
-                            `${100 * this.effectiveNativeHeight / this.effectiveNativeWidth * this.props.PanelWidth() / this.props.PanelHeight()}%`),
-                    }}>
-                    <DocumentViewInternal {...this.props}
-                        DocumentView={this.selfView}
-                        viewPath={this.docViewPathFunc}
-                        PanelWidth={this.PanelWidth}
-                        PanelHeight={this.PanelHeight}
-                        NativeWidth={this.NativeWidth}
-                        NativeHeight={this.NativeHeight}
-                        isSelected={this.isSelected}
-                        select={this.select}
-                        ContentScaling={this.ContentScale}
-                        ScreenToLocalTransform={this.screenToLocalTransform}
-                        focus={this.props.focus || emptyFunction}
-                        ref={action((r: DocumentViewInternal | null) => r && (this.docView = r))} />
-                </div>)}
-        </div>);
+        return (
+            <div className="contentFittingDocumentView">
+                {!this.props.Document || !this.props.PanelWidth() ? null : (
+                    <div
+                        className="contentFittingDocumentView-previewDoc"
+                        ref={this.ContentRef}
+                        style={{
+                            transition: this.props.dataTransition,
+                            position: this.props.Document.isInkMask ? 'absolute' : undefined,
+                            transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`,
+                            width: isButton || isPresTreeElement ? '100%' : xshift() ?? `${(100 * (this.props.PanelWidth() - this.Xshift * 2)) / this.props.PanelWidth()}%`,
+                            height:
+                                isButton || this.props.forceAutoHeight
+                                    ? undefined
+                                    : yshift() ?? (this.fitWidth ? `${this.panelHeight}px` : `${(((100 * this.effectiveNativeHeight) / this.effectiveNativeWidth) * this.props.PanelWidth()) / this.props.PanelHeight()}%`),
+                        }}>
+                        <DocumentViewInternal
+                            {...this.props}
+                            DocumentView={this.selfView}
+                            viewPath={this.docViewPathFunc}
+                            PanelWidth={this.PanelWidth}
+                            PanelHeight={this.PanelHeight}
+                            NativeWidth={this.NativeWidth}
+                            NativeHeight={this.NativeHeight}
+                            isSelected={this.isSelected}
+                            select={this.select}
+                            ContentScaling={this.ContentScale}
+                            ScreenToLocalTransform={this.screenToLocalTransform}
+                            focus={this.props.focus || emptyFunction}
+                            ref={action((r: DocumentViewInternal | null) => r && (this.docView = r))}
+                        />
+                    </div>
+                )}
+            </div>
+        );
     }
 }
 
@@ -1380,7 +1646,7 @@ ScriptingGlobals.add(function deiconifyView(documentView: DocumentView) {
 });
 
 ScriptingGlobals.add(function toggleDetail(dv: DocumentView, detailLayoutKeySuffix: string) {
-    if (dv.Document.layoutKey === "layout_" + detailLayoutKeySuffix) dv.switchViews(false, "layout");
+    if (dv.Document.layoutKey === 'layout_' + detailLayoutKeySuffix) dv.switchViews(false, 'layout');
     else dv.switchViews(true, detailLayoutKeySuffix);
 });
 
@@ -1398,8 +1664,8 @@ ScriptingGlobals.add(function updateLinkCollection(linkCollection: Doc) {
             alias.y = 0;
             alias._lockedPosition = false;
             wid += otherdoc[WidthSym]();
-            Doc.AddDocToList(Doc.GetProto(linkCollection), "data", alias);
+            Doc.AddDocToList(Doc.GetProto(linkCollection), 'data', alias);
         }
     });
     return links;
-});
\ No newline at end of file
+});
diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx
index 17b57cb3b..ff04a293c 100644
--- a/src/client/views/nodes/FilterBox.tsx
+++ b/src/client/views/nodes/FilterBox.tsx
@@ -1,52 +1,80 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, observable, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import Select from "react-select";
-import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt } from "../../../fields/Doc";
-import { List } from "../../../fields/List";
-import { RichTextField } from "../../../fields/RichTextField";
-import { listSpec } from "../../../fields/Schema";
-import { ComputedField, ScriptField } from "../../../fields/ScriptField";
-import { Cast, NumCast, StrCast, DocCast } from "../../../fields/Types";
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from "../../../Utils";
-import { Docs } from "../../documents/Documents";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { UserOptions } from "../../util/GroupManager";
-import { ScriptingGlobals } from "../../util/ScriptingGlobals";
-import { SelectionManager } from "../../util/SelectionManager";
-import { CollectionTreeView } from "../collections/CollectionTreeView";
-import { CollectionView } from "../collections/CollectionView";
-import { ViewBoxBaseComponent } from "../DocComponent";
-import { EditableView } from "../EditableView";
-import { SearchBox } from "../search/SearchBox";
-import { DashboardToggleButton, DefaultStyleProvider, StyleProp } from "../StyleProvider";
-import { DocumentViewProps } from "./DocumentView";
+import React = require('react');
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import Select from 'react-select';
+import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt } from '../../../fields/Doc';
+import { List } from '../../../fields/List';
+import { RichTextField } from '../../../fields/RichTextField';
+import { listSpec } from '../../../fields/Schema';
+import { ComputedField, ScriptField } from '../../../fields/ScriptField';
+import { Cast, DocCast, NumCast, StrCast } from '../../../fields/Types';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../Utils';
+import { Docs, DocumentOptions } from '../../documents/Documents';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { UserOptions } from '../../util/GroupManager';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
+import { SelectionManager } from '../../util/SelectionManager';
+import { CollectionTreeView } from '../collections/CollectionTreeView';
+import { CollectionView } from '../collections/CollectionView';
+import { ViewBoxBaseComponent } from '../DocComponent';
+import { EditableView } from '../EditableView';
+import { SearchBox } from '../search/SearchBox';
+import { DashboardToggleButton, DefaultStyleProvider, StyleProp } from '../StyleProvider';
+import { DocumentViewProps } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
 import './FilterBox.scss';
-const higflyout = require("@hig/flyout");
+const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
 @observer
 export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
-
     constructor(props: Readonly<FieldViewProps>) {
         super(props);
         const targetDoc = FilterBox.targetDoc;
-        if (targetDoc && !targetDoc.currentFilter) targetDoc.currentFilter = CurrentUserUtils.createFilterDoc();
+        if (targetDoc && !targetDoc.currentFilter) targetDoc.currentFilter = FilterBox.createFilterDoc();
+    }
+
+    /// creates a new, empty filter doc
+    static createFilterDoc() {
+        const clearAll = `getProto(self).data = new List([])`;
+        const reqdOpts: DocumentOptions = {
+            _lockedPosition: true,
+            _autoHeight: true,
+            _fitWidth: true,
+            _height: 150,
+            _xPadding: 5,
+            _yPadding: 5,
+            _gridGap: 5,
+            _forceActive: true,
+            title: 'Unnamed Filter',
+            filterBoolean: 'AND',
+            boxShadow: '0 0',
+            childDontRegisterViews: true,
+            targetDropAction: 'same',
+            ignoreClick: true,
+            system: true,
+            childDropAction: 'none',
+            treeViewHideTitle: true,
+            treeViewTruncateTitleWidth: 150,
+            childContextMenuLabels: new List<string>(['Clear All']),
+            childContextMenuScripts: new List<ScriptField>([ScriptField.MakeFunction(clearAll)!]),
+        };
+        return Docs.Create.FilterDocument(reqdOpts);
+    }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(FilterBox, fieldKey);
     }
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FilterBox, fieldKey); }
 
     public _filterSelected = false;
-    public _filterMatch = "matched";
+    public _filterMatch = 'matched';
 
     @computed static get currentContainingCollectionDoc() {
         let docView: any = SelectionManager.Views()[0];
         let doc = docView.Document;
 
-        while (docView && doc && doc.type !== "collection") {
+        while (docView && doc && doc.type !== 'collection') {
             doc = docView.props.ContainingCollectionDoc;
             docView = docView.props.ContainingCollectionView;
         }
@@ -58,7 +86,6 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
     //  * @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection
     //  */
 
-
     // trying to get this to work rather than the version below this
 
     // @computed static get targetDoc() {
@@ -70,31 +97,33 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
     //     // return FilterBox._filterScope === "Current Collection" ? SelectionManager.Views()[0].Document || CollectionDockingView.Instance.props.Document : CollectionDockingView.Instance.props.Document;
     // }
 
-
     /**
-    * @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection
-    */
+     * @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection
+     */
     @computed static get targetDoc() {
-        return SelectionManager.Docs().length ? SelectionManager.Docs()[0] : CurrentUserUtils.ActiveDashboard;
+        return SelectionManager.Docs().length ? SelectionManager.Docs()[0] : Doc.ActiveDashboard;
     }
     @computed static get targetDocChildKey() {
         if (SelectionManager.Views().length) {
-            return SelectionManager.Views()[0].ComponentView?.annotationKey || SelectionManager.Views()[0].ComponentView?.fieldKey || "data";
+            return SelectionManager.Views()[0].ComponentView?.annotationKey || SelectionManager.Views()[0].ComponentView?.fieldKey || 'data';
         }
-        return "data";
+        return 'data';
     }
     @computed static get targetDocChildren() {
-        return DocListCast(FilterBox.targetDoc?.[FilterBox.targetDocChildKey] || CurrentUserUtils.ActiveDashboard?.data);
+        return DocListCast(FilterBox.targetDoc?.[FilterBox.targetDocChildKey] || Doc.ActiveDashboard?.data);
     }
 
     @observable _loaded = false;
     componentDidMount() {
-        reaction(() => DocListCastAsync(this.layoutDoc.data),
-            async (activeTabsAsync) => {
+        reaction(
+            () => DocListCastAsync(this.layoutDoc.data),
+            async activeTabsAsync => {
                 const activeTabs = await activeTabsAsync;
                 activeTabs && (await SearchBox.foreachRecursiveDocAsync(activeTabs, emptyFunction));
-                runInAction(() => this._loaded = true);
-            }, { fireImmediately: true });
+                runInAction(() => (this._loaded = true));
+            },
+            { fireImmediately: true }
+        );
     }
 
     @computed get allDocs() {
@@ -109,16 +138,18 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @computed get _allFacets() {
         // trace();
-        const noviceReqFields = ["author", "tags", "text", "type"];
-        const noviceLayoutFields: string[] = [];//["_curPage"];
+        const noviceReqFields = ['author', 'tags', 'text', 'type'];
+        const noviceLayoutFields: string[] = []; //["_curPage"];
         const noviceFields = [...noviceReqFields, ...noviceLayoutFields];
 
         const keys = new Set<string>(noviceFields);
         this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key)));
-        return Array.from(keys.keys()).filter(key => key[0]).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_")) || noviceFields.includes(key) || !Doc.noviceMode).sort();
+        return Array.from(keys.keys())
+            .filter(key => key[0])
+            .filter(key => key[0] === '#' || key.indexOf('lastModified') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('_')) || noviceFields.includes(key) || !Doc.noviceMode)
+            .sort();
     }
 
-
     /**
      * The current attributes selected to filter based on
      */
@@ -136,26 +167,26 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
     gatherFieldValues(childDocs: Doc[], facetKey: string) {
         const valueSet = new Set<string>();
         let rtFields = 0;
-        childDocs.forEach((d) => {
+        childDocs.forEach(d => {
             const facetVal = d[facetKey];
             if (facetVal instanceof RichTextField) rtFields++;
             valueSet.add(Field.toString(facetVal as Field));
             const fieldKey = Doc.LayoutFieldKey(d);
-            const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
-            const data = d[annos ? fieldKey + "-annotations" : fieldKey];
+            const annos = !Field.toString(Doc.LayoutField(d) as Field).includes('CollectionView');
+            const data = d[annos ? fieldKey + '-annotations' : fieldKey];
             if (data !== undefined) {
                 let subDocs = DocListCast(data);
                 if (subDocs.length > 0) {
                     let newarray: Doc[] = [];
                     while (subDocs.length > 0) {
                         newarray = [];
-                        subDocs.forEach((t) => {
+                        subDocs.forEach(t => {
                             const facetVal = t[facetKey];
                             if (facetVal instanceof RichTextField) rtFields++;
                             facetVal !== undefined && valueSet.add(Field.toString(facetVal as Field));
                             const fieldKey = Doc.LayoutFieldKey(t);
-                            const annos = !Field.toString(Doc.LayoutField(t) as Field).includes("CollectionView");
-                            DocListCast(t[annos ? fieldKey + "-annotations" : fieldKey]).forEach((newdoc) => newarray.push(newdoc));
+                            const annos = !Field.toString(Doc.LayoutField(t) as Field).includes('CollectionView');
+                            DocListCast(t[annos ? fieldKey + '-annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
                         });
                         subDocs = newarray;
                     }
@@ -164,7 +195,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
         });
         return { strings: Array.from(valueSet.keys()), rtFields };
     }
-    removeFilterDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).map(doc => this.removeFilter(StrCast(doc.title))).length ? true : false;
+    removeFilterDoc = (doc: Doc | Doc[]) => ((doc instanceof Doc ? [doc] : doc).map(doc => this.removeFilter(StrCast(doc.title))).length ? true : false);
     public removeFilter = (filterName: string) => {
         const targetDoc = FilterBox.targetDoc;
         if (targetDoc) {
@@ -173,24 +204,24 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const found = attributes.findIndex(doc => doc.title === filterName);
             if (found !== -1) {
                 (filterDoc.data as List<Doc>).splice(found, 1);
-                const docFilter = Cast(targetDoc._docFilters, listSpec("string"));
+                const docFilter = Cast(targetDoc._docFilters, listSpec('string'));
                 if (docFilter) {
                     let index: number;
-                    while ((index = docFilter.findIndex(item => item.split(":")[0] === filterName)) !== -1) {
+                    while ((index = docFilter.findIndex(item => item.split(':')[0] === filterName)) !== -1) {
                         docFilter.splice(index, 1);
                     }
                 }
-                const docRangeFilters = Cast(targetDoc._docRangeFilters, listSpec("string"));
+                const docRangeFilters = Cast(targetDoc._docRangeFilters, listSpec('string'));
                 if (docRangeFilters) {
                     let index: number;
-                    while ((index = docRangeFilters.findIndex(item => item.split(":")[0] === filterName)) !== -1) {
+                    while ((index = docRangeFilters.findIndex(item => item.split(':')[0] === filterName)) !== -1) {
                         docRangeFilters.splice(index, 3);
                     }
                 }
             }
         }
         return true;
-    }
+    };
     /**
      * Responds to clicking the check box in the flyout menu
      */
@@ -205,7 +236,8 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const facetValues = this.gatherFieldValues(targetDocChildren, facetHeader);
 
             let nonNumbers = 0;
-            let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE;
+            let minVal = Number.MAX_VALUE,
+                maxVal = -Number.MAX_VALUE;
             facetValues.strings.map(val => {
                 const num = val ? Number(val) : Number.NaN;
                 if (Number.isNaN(num)) {
@@ -216,40 +248,56 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 }
             });
             let newFacet: Opt<Doc>;
-            if (facetHeader === "text" || facetValues.rtFields / allCollectionDocs.length > 0.1) {
-                newFacet = Docs.Create.TextDocument("", {
-                    title: facetHeader, system: true, target: targetDoc, _width: 100, _height: 25, _stayInCollection: true,
-                    treeViewExpandedView: "layout", _treeViewOpen: true, _forceActive: true, ignoreClick: true,
+            if (facetHeader === 'text' || facetValues.rtFields / allCollectionDocs.length > 0.1) {
+                newFacet = Docs.Create.TextDocument('', {
+                    title: facetHeader,
+                    system: true,
+                    target: targetDoc,
+                    _width: 100,
+                    _height: 25,
+                    _stayInCollection: true,
+                    treeViewExpandedView: 'layout',
+                    _treeViewOpen: true,
+                    _forceActive: true,
+                    ignoreClick: true,
                 });
                 Doc.GetProto(newFacet).forceActive = true; // required for FormattedTextBox to be able to gain focus since it will never be 'selected'
                 Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
                 newFacet._textBoxPaddingX = newFacet._textBoxPaddingY = 4;
                 const scriptText = `setDocFilter(this?.target, "${facetHeader}", text, "match")`;
-                newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: "string" });
-            } else if (facetHeader !== "tags" && nonNumbers / facetValues.strings.length < .1) {
+                newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: 'string' });
+            } else if (facetHeader !== 'tags' && nonNumbers / facetValues.strings.length < 0.1) {
                 newFacet = Docs.Create.SliderDocument({
-                    title: facetHeader, system: true, target: targetDoc, _fitWidth: true, _height: 40, _stayInCollection: true,
-                    treeViewExpandedView: "layout", _treeViewOpen: true, _forceActive: true, _overflow: "visible",
+                    title: facetHeader,
+                    system: true,
+                    target: targetDoc,
+                    _fitWidth: true,
+                    _height: 40,
+                    _stayInCollection: true,
+                    treeViewExpandedView: 'layout',
+                    _treeViewOpen: true,
+                    _forceActive: true,
+                    _overflow: 'visible',
                 });
                 const newFacetField = Doc.LayoutFieldKey(newFacet);
                 const ranged = Doc.readDocRangeFilter(targetDoc, facetHeader);
                 Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
-                const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * .1));
-                const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * .05)));
-                newFacet[newFacetField + "-min"] = ranged === undefined ? extendedMinVal : ranged[0];
-                newFacet[newFacetField + "-max"] = ranged === undefined ? extendedMaxVal : ranged[1];
-                Doc.GetProto(newFacet)[newFacetField + "-minThumb"] = extendedMinVal;
-                Doc.GetProto(newFacet)[newFacetField + "-maxThumb"] = extendedMaxVal;
+                const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1));
+                const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05)));
+                newFacet[newFacetField + '-min'] = ranged === undefined ? extendedMinVal : ranged[0];
+                newFacet[newFacetField + '-max'] = ranged === undefined ? extendedMaxVal : ranged[1];
+                Doc.GetProto(newFacet)[newFacetField + '-minThumb'] = extendedMinVal;
+                Doc.GetProto(newFacet)[newFacetField + '-maxThumb'] = extendedMaxVal;
                 const scriptText = `setDocRangeFilter(this?.target, "${facetHeader}", range)`;
-                newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" });
+                newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: 'number' });
                 newFacet.data = ComputedField.MakeFunction(`readNumFacetData(self.target, self, "${FilterBox.targetDocChildKey}", "${facetHeader}")`);
             } else {
                 newFacet = new Doc();
                 newFacet.system = true;
                 newFacet.title = facetHeader;
                 newFacet.treeViewOpen = true;
-                newFacet.layout = CollectionView.LayoutString("data");
-                newFacet.layoutKey = "layout";
+                newFacet.layout = CollectionView.LayoutString('data');
+                newFacet.layoutKey = 'layout';
                 newFacet.type = DocumentType.COL;
                 newFacet.target = targetDoc;
                 newFacet.data = ComputedField.MakeFunction(`readFacetData(self.target, "${FilterBox.targetDocChildKey}", "${facetHeader}")`);
@@ -257,11 +305,11 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
             newFacet.hideContextMenu = true;
             Doc.AddDocToList(this.dataDoc, this.props.fieldKey, newFacet);
         }
-    }
+    };
 
     @computed get scriptField() {
-        const scriptText = "setDocFilter(this?.target, heading, this.title, checked)";
-        const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name });
+        const scriptText = 'setDocFilter(this?.target, heading, this.title, checked)';
+        const script = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: 'string', checked: 'string', containingTreeView: Doc.name });
         return script ? () => script : undefined;
     }
 
@@ -271,7 +319,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     changeBool = (e: any) => {
         FilterBox.targetDoc && (DocCast(FilterBox.targetDoc.currentFilter).filterBoolean = e.currentTarget.value);
-    }
+    };
 
     /**
      * Changes whether to select matched or unmatched documents
@@ -279,7 +327,7 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     changeMatch = (e: any) => {
         this._filterMatch = e.currentTarget.value;
-    }
+    };
 
     @action
     changeSelected = () => {
@@ -290,36 +338,42 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this._filterSelected = true;
             // helper method to select specified docs
         }
-    }
+    };
 
     FilteringStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps | DocumentViewProps>, property: string) {
-        switch (property.split(":")[0]) {
+        switch (property.split(':')[0]) {
             case StyleProp.Decorations:
                 if (doc && !doc.treeViewHideHeaderFields) {
-                    return <>
-                        <div style={{ marginRight: "5px", fontSize: "10px" }}>
-                            <select className="filterBox-selection">
-                                <option value="Is" key="Is">Is</option>
-                                <option value="Is Not" key="Is Not">Is Not</option>
-                            </select>
-                        </div>
-                        <div className="filterBox-treeView-close" onClick={e => this.removeFilter(StrCast(doc.title))}>
-                            <FontAwesomeIcon icon={"times"} size="sm" />
-                        </div>
-                    </>;
+                    return (
+                        <>
+                            <div style={{ marginRight: '5px', fontSize: '10px' }}>
+                                <select className="filterBox-selection">
+                                    <option value="Is" key="Is">
+                                        Is
+                                    </option>
+                                    <option value="Is Not" key="Is Not">
+                                        Is Not
+                                    </option>
+                                </select>
+                            </div>
+                            <div className="filterBox-treeView-close" onClick={e => this.removeFilter(StrCast(doc.title))}>
+                                <FontAwesomeIcon icon={'times'} size="sm" />
+                            </div>
+                        </>
+                    );
                 }
         }
         return DefaultStyleProvider(doc, props, property);
     }
 
-    suppressChildClick = () => ScriptField.MakeScript("")!;
+    suppressChildClick = () => ScriptField.MakeScript('')!;
 
     /**
      * Adds a filterDoc to the list of saved filters
      */
     saveFilter = () => {
-        Doc.AddDocToList(Doc.UserDoc(), "savedFilters", this.props.Document);
-    }
+        Doc.AddDocToList(Doc.UserDoc(), 'savedFilters', this.props.Document);
+    };
 
     /**
      * Changes the title of the filterDoc
@@ -327,116 +381,106 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
     onTitleValueChange = (val: string) => {
         this.props.Document.title = val || `FilterDoc for ${FilterBox.targetDoc?.title}`;
         return true;
-    }
+    };
 
     /**
      * The flyout from which you can select a saved filter to apply
      */
     @computed get flyoutPanel() {
         return DocListCast(Doc.UserDoc().savedFilters).map(doc => {
-            return <>
-                <div className="filterBox-tempFlyout" onWheel={e => e.stopPropagation()} style={{ height: 50, border: "2px" }} onPointerDown={() => this.props.updateFilterDoc?.(doc)}>
-                    {StrCast(doc.title)}
-                </div>
-            </>;
-        }
-        );
+            return (
+                <>
+                    <div className="filterBox-tempFlyout" onWheel={e => e.stopPropagation()} style={{ height: 50, border: '2px' }} onPointerDown={() => this.props.updateFilterDoc?.(doc)}>
+                        {StrCast(doc.title)}
+                    </div>
+                </>
+            );
+        });
     }
     setTreeHeight = (hgt: number) => {
         this.layoutDoc._height = NumCast(this.layoutDoc._autoHeightMargins) + 150; // need to add all the border sizes together.
-    }
+    };
 
     /**
      * add lock and hide button decorations for the "Dashboards" flyout TreeView
      */
     FilterStyleProvider = (doc: Opt<Doc>, props: Opt<FieldViewProps | DocumentViewProps>, property: string) => {
-        if (property.split(":")[0] === StyleProp.Decorations) {
-            return !doc || doc.treeViewHideHeaderFields ? (null) :
-                DashboardToggleButton(doc, "hidden", "trash", "trash", () => this.removeFilter(StrCast(doc.title)));
+        if (property.split(':')[0] === StyleProp.Decorations) {
+            return !doc || doc.treeViewHideHeaderFields ? null : DashboardToggleButton(doc, 'hidden', 'trash', 'trash', () => this.removeFilter(StrCast(doc.title)));
         }
         return this.props.styleProvider?.(doc, props, property);
-    }
+    };
 
     layoutHeight = () => this.layoutDoc[HeightSym]();
     render() {
         const facetCollection = this.props.Document;
 
         const options = this._allFacets.filter(facet => this.currentFacets.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet }));
-        return this.props.dontRegisterView ? (null) : <div className="filterBox-treeView" style={{ width: "100%" }}>
-
-            <div className="filterBox-title">
-                <EditableView
-                    key="editableView"
-                    contents={this.props.Document.title}
-                    height={24}
-                    fontSize={15}
-                    GetValue={() => StrCast(this.props.Document.title)}
-                    SetValue={this.onTitleValueChange}
-                />
-            </div>
+        return this.props.dontRegisterView ? null : (
+            <div className="filterBox-treeView" style={{ width: '100%' }}>
+                <div className="filterBox-title">
+                    <EditableView key="editableView" contents={this.props.Document.title} height={24} fontSize={15} GetValue={() => StrCast(this.props.Document.title)} SetValue={this.onTitleValueChange} />
+                </div>
 
-            <div className="filterBox-select-bool">
-                <select className="filterBox-selection" onChange={this.changeBool} defaultValue={StrCast((FilterBox.targetDoc?.currentFilter as Doc)?.filterBoolean)}>
-                    <option value="AND" key="AND">AND</option>
-                    <option value="OR" key="OR">OR</option>
-                </select>
-                <div className="filterBox-select-text">filters together</div>
-            </div>
+                <div className="filterBox-select-bool">
+                    <select className="filterBox-selection" onChange={this.changeBool} defaultValue={StrCast((FilterBox.targetDoc?.currentFilter as Doc)?.filterBoolean)}>
+                        <option value="AND" key="AND">
+                            AND
+                        </option>
+                        <option value="OR" key="OR">
+                            OR
+                        </option>
+                    </select>
+                    <div className="filterBox-select-text">filters together</div>
+                </div>
 
-            <div className="filterBox-select">
-                <Select
-                    placeholder="Add a filter..."
-                    options={options}
-                    isMulti={false}
-                    onChange={val => this.facetClick((val as UserOptions).value)}
-                    onKeyDown={e => e.stopPropagation()}
-                    value={null}
-                    closeMenuOnSelect={true}
-                />
-            </div>
+                <div className="filterBox-select">
+                    <Select placeholder="Add a filter..." options={options} isMulti={false} onChange={val => this.facetClick((val as UserOptions).value)} onKeyDown={e => e.stopPropagation()} value={null} closeMenuOnSelect={true} />
+                </div>
 
-            <div className="filterBox-tree" key="tree">
-                <CollectionTreeView
-                    Document={facetCollection}
-                    DataDoc={Doc.GetProto(facetCollection)}
-                    fieldKey={this.props.fieldKey}
-                    CollectionView={undefined}
-                    disableDocBrushing={true}
-                    setHeight={this.setTreeHeight} // if the tree view can trigger the height of the filter box to change, then this needs to be filled in.
-                    docFilters={returnEmptyFilter}
-                    docRangeFilters={returnEmptyFilter}
-                    searchFilterDocs={returnEmptyDoclist}
-                    childDocumentsActive={returnTrue}
-                    ContainingCollectionDoc={this.props.ContainingCollectionDoc}
-                    ContainingCollectionView={this.props.ContainingCollectionView}
-                    PanelWidth={this.props.PanelWidth}
-                    PanelHeight={this.layoutHeight}
-                    rootSelected={this.props.rootSelected}
-                    renderDepth={1}
-                    dropAction={this.props.dropAction}
-                    ScreenToLocalTransform={this.props.ScreenToLocalTransform}
-                    isAnyChildContentActive={returnFalse}
-                    addDocTab={returnFalse}
-                    pinToPres={returnFalse}
-                    isSelected={returnFalse}
-                    select={returnFalse}
-                    bringToFront={emptyFunction}
-                    isContentActive={returnTrue}
-                    whenChildContentsActiveChanged={returnFalse}
-                    treeViewHideTitle={true}
-                    focus={returnFalse}
-                    onCheckedClick={this.scriptField}
-                    treeViewHideHeaderFields={false}
-                    dontRegisterView={true}
-                    styleProvider={this.FilterStyleProvider}
-                    docViewPath={this.props.docViewPath}
-                    scriptContext={this.props.scriptContext}
-                    moveDocument={returnFalse}
-                    removeDocument={this.removeFilterDoc}
-                    addDocument={returnFalse} />
-            </div>
-            <div className="filterBox-bottom">
-                {/* <div className="filterBox-select-matched">
+                <div className="filterBox-tree" key="tree">
+                    <CollectionTreeView
+                        Document={facetCollection}
+                        DataDoc={Doc.GetProto(facetCollection)}
+                        fieldKey={this.props.fieldKey}
+                        CollectionView={undefined}
+                        disableDocBrushing={true}
+                        setHeight={this.setTreeHeight} // if the tree view can trigger the height of the filter box to change, then this needs to be filled in.
+                        docFilters={returnEmptyFilter}
+                        docRangeFilters={returnEmptyFilter}
+                        searchFilterDocs={returnEmptyDoclist}
+                        childDocumentsActive={returnTrue}
+                        ContainingCollectionDoc={this.props.ContainingCollectionDoc}
+                        ContainingCollectionView={this.props.ContainingCollectionView}
+                        PanelWidth={this.props.PanelWidth}
+                        PanelHeight={this.layoutHeight}
+                        rootSelected={this.props.rootSelected}
+                        renderDepth={1}
+                        dropAction={this.props.dropAction}
+                        ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+                        isAnyChildContentActive={returnFalse}
+                        addDocTab={returnFalse}
+                        pinToPres={returnFalse}
+                        isSelected={returnFalse}
+                        select={returnFalse}
+                        bringToFront={emptyFunction}
+                        isContentActive={returnTrue}
+                        whenChildContentsActiveChanged={returnFalse}
+                        treeViewHideTitle={true}
+                        focus={returnFalse}
+                        onCheckedClick={this.scriptField}
+                        treeViewHideHeaderFields={false}
+                        dontRegisterView={true}
+                        styleProvider={this.FilterStyleProvider}
+                        docViewPath={this.props.docViewPath}
+                        scriptContext={this.props.scriptContext}
+                        moveDocument={returnFalse}
+                        removeDocument={this.removeFilterDoc}
+                        addDocument={returnFalse}
+                    />
+                </div>
+                <div className="filterBox-bottom">
+                    {/* <div className="filterBox-select-matched">
                     <input className="filterBox-select-box" type="checkbox"
                         onChange={this.changeSelected} />
                     <div className="filterBox-select-text">select</div>
@@ -447,38 +491,35 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     <div className="filterBox-select-text">documents</div>
                 </div> */}
 
-                <div style={{ display: "flex" }}>
-                    <div className="filterBox-saveWrapper">
-                        <div className="filterBox-saveBookmark"
-                            onPointerDown={this.saveFilter}
-                        >
-                            <div>SAVE</div>
+                    <div style={{ display: 'flex' }}>
+                        <div className="filterBox-saveWrapper">
+                            <div className="filterBox-saveBookmark" onPointerDown={this.saveFilter}>
+                                <div>SAVE</div>
+                            </div>
                         </div>
-                    </div>
-                    <div className="filterBox-saveWrapper">
-                        <div className="filterBox-saveBookmark">
-                            <Flyout className="myFilters-flyout" anchorPoint={anchorPoints.TOP} content={this.flyoutPanel}>
-                                <div>FILTERS</div>
-                            </Flyout>
+                        <div className="filterBox-saveWrapper">
+                            <div className="filterBox-saveBookmark">
+                                <Flyout className="myFilters-flyout" anchorPoint={anchorPoints.TOP} content={this.flyoutPanel}>
+                                    <div>FILTERS</div>
+                                </Flyout>
+                            </div>
                         </div>
-                    </div>
-                    <div className="filterBox-saveWrapper">
-                        <div className="filterBox-saveBookmark"
-                            onPointerDown={this.props.createNewFilterDoc}
-                        >
-                            <div>NEW</div>
+                        <div className="filterBox-saveWrapper">
+                            <div className="filterBox-saveBookmark" onPointerDown={this.props.createNewFilterDoc}>
+                                <div>NEW</div>
+                            </div>
                         </div>
                     </div>
                 </div>
             </div>
-        </div>;
+        );
     }
 }
 
 ScriptingGlobals.add(function determineCheckedState(layoutDoc: Doc, facetHeader: string, facetValue: string) {
-    const docFilters = Cast(layoutDoc._docFilters, listSpec("string"), []);
+    const docFilters = Cast(layoutDoc._docFilters, listSpec('string'), []);
     for (const filter of docFilters) {
-        const fields = filter.split(":"); // split into key:value:modifiers
+        const fields = filter.split(':'); // split into key:value:modifiers
         if (fields[0] === facetHeader && fields[1] === facetValue) {
             return fields[2];
         }
@@ -490,11 +531,17 @@ ScriptingGlobals.add(function readNumFacetData(layoutDoc: Doc, facetDoc: Doc, ch
     const activeTabs = DocListCast(layoutDoc[childKey]);
     SearchBox.foreachRecursiveDoc(activeTabs, (depth: number, doc: Doc) => allCollectionDocs.add(doc));
     const set = new Set<string>();
-    if (facetHeader === "tags") allCollectionDocs.forEach(child => Field.toString(child[facetHeader] as Field).split(":").forEach(key => set.add(key)));
+    if (facetHeader === 'tags')
+        allCollectionDocs.forEach(child =>
+            Field.toString(child[facetHeader] as Field)
+                .split(':')
+                .forEach(key => set.add(key))
+        );
     else allCollectionDocs.forEach(child => set.add(Field.toString(child[facetHeader] as Field)));
     const facetValues = Array.from(set).filter(v => v);
 
-    let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE;
+    let minVal = Number.MAX_VALUE,
+        maxVal = -Number.MAX_VALUE;
     facetValues.map(val => {
         const num = val ? Number(val) : Number.NaN;
         if (!Number.isNaN(num)) {
@@ -504,26 +551,31 @@ ScriptingGlobals.add(function readNumFacetData(layoutDoc: Doc, facetDoc: Doc, ch
     });
     const newFacetField = Doc.LayoutFieldKey(facetDoc);
     const ranged = Doc.readDocRangeFilter(layoutDoc, facetHeader);
-    const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * .1));
-    const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * .05)));
-    facetDoc[newFacetField + "-min"] = ranged === undefined ? extendedMinVal : ranged[0];
-    facetDoc[newFacetField + "-max"] = ranged === undefined ? extendedMaxVal : ranged[1];
-    Doc.GetProto(facetDoc)[newFacetField + "-minThumb"] = extendedMinVal;
-    Doc.GetProto(facetDoc)[newFacetField + "-maxThumb"] = extendedMaxVal;
-})
+    const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1));
+    const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05)));
+    facetDoc[newFacetField + '-min'] = ranged === undefined ? extendedMinVal : ranged[0];
+    facetDoc[newFacetField + '-max'] = ranged === undefined ? extendedMaxVal : ranged[1];
+    Doc.GetProto(facetDoc)[newFacetField + '-minThumb'] = extendedMinVal;
+    Doc.GetProto(facetDoc)[newFacetField + '-maxThumb'] = extendedMaxVal;
+});
 ScriptingGlobals.add(function readFacetData(layoutDoc: Doc, childKey: string, facetHeader: string) {
     const allCollectionDocs = new Set<Doc>();
     const activeTabs = DocListCast(layoutDoc[childKey]);
     SearchBox.foreachRecursiveDoc(activeTabs, (depth: number, doc: Doc) => allCollectionDocs.add(doc));
     const set = new Set<string>();
-    if (facetHeader === "tags") allCollectionDocs.forEach(child => Field.toString(child[facetHeader] as Field).split(":").forEach(key => set.add(key)));
+    if (facetHeader === 'tags')
+        allCollectionDocs.forEach(child =>
+            Field.toString(child[facetHeader] as Field)
+                .split(':')
+                .forEach(key => set.add(key))
+        );
     else allCollectionDocs.forEach(child => set.add(Field.toString(child[facetHeader] as Field)));
     const facetValues = Array.from(set).filter(v => v);
 
     let nonNumbers = 0;
 
     facetValues.map(val => Number.isNaN(Number(val)) && nonNumbers++);
-    const facetValueDocSet = (nonNumbers / facetValues.length > .1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue => {
+    const facetValueDocSet = (nonNumbers / facetValues.length > 0.1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2))).map(facetValue => {
         const doc = new Doc();
         doc.system = true;
         doc.title = facetValue.toString();
@@ -531,8 +583,8 @@ ScriptingGlobals.add(function readFacetData(layoutDoc: Doc, childKey: string, fa
         doc.facetHeader = facetHeader;
         doc.facetValue = facetValue;
         doc.treeViewHideHeaderFields = true;
-        doc.treeViewChecked = ComputedField.MakeFunction("determineCheckedState(self.target, self.facetHeader, self.facetValue)");
+        doc.treeViewChecked = ComputedField.MakeFunction('determineCheckedState(self.target, self.facetHeader, self.facetValue)');
         return doc;
     });
     return new List<Doc>(facetValueDocSet);
-});
\ No newline at end of file
+});
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 60eb48114..ffa839fcb 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,5 +1,5 @@
 import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
-import { observer } from "mobx-react";
+import { observer } from 'mobx-react';
 import { extname } from 'path';
 import { DataSym, Doc, DocListCast, Opt, WidthSym } from '../../../fields/Doc';
 import { Id } from '../../../fields/FieldSymbols';
@@ -14,13 +14,12 @@ import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
 import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils';
 import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices';
-import { Docs, DocUtils } from '../../documents/Documents';
+import { DocUtils } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { Networking } from '../../Network';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
 import { DragManager } from '../../util/DragManager';
 import { undoBatch } from '../../util/UndoManager';
-import { ContextMenu } from "../../views/ContextMenu";
+import { ContextMenu } from '../../views/ContextMenu';
 import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
@@ -29,62 +28,64 @@ import { AnchorMenu } from '../pdf/AnchorMenu';
 import { StyleProp } from '../StyleProvider';
 import { FaceRectangles } from './FaceRectangles';
 import { FieldView, FieldViewProps } from './FieldView';
-import "./ImageBox.scss";
-import React = require("react");
+import './ImageBox.scss';
+import React = require('react');
 
 export const pageSchema = createSchema({
-    googlePhotosUrl: "string",
-    googlePhotosTags: "string"
+    googlePhotosUrl: 'string',
+    googlePhotosTags: 'string',
 });
 const uploadIcons = {
-    idle: "downarrow.png",
-    loading: "loading.gif",
-    success: "greencheck.png",
-    failure: "redx.png"
+    idle: 'downarrow.png',
+    loading: 'loading.gif',
+    success: 'greencheck.png',
+    failure: 'redx.png',
 };
 
 @observer
 export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
-    protected _multiTouchDisposer?: import("../../util/InteractionUtils").InteractionUtils.MultiTouchEventDisposer | undefined;
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ImageBox, fieldKey); }
+    protected _multiTouchDisposer?: import('../../util/InteractionUtils').InteractionUtils.MultiTouchEventDisposer | undefined;
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(ImageBox, fieldKey);
+    }
     private _dropDisposer?: DragManager.DragDropDisposer;
     private _disposers: { [name: string]: IReactionDisposer } = {};
     private _getAnchor: (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => Opt<Doc> = () => undefined;
-    @observable _curSuffix = "";
+    @observable _curSuffix = '';
     @observable _uploadIcon = uploadIcons.idle;
 
     protected createDropTarget = (ele: HTMLDivElement) => {
         this._dropDisposer?.();
         ele && (this._dropDisposer = DragManager.MakeDropTarget(ele, this.drop.bind(this), this.props.Document));
-    }
-    setViewSpec = (anchor: Doc, preview: boolean) => {
-
-    } // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
-
+    };
+    setViewSpec = (anchor: Doc, preview: boolean) => {}; // sets viewing information for a componentview, typically when following a link. 'preview' tells the view to use the values without writing to the document
 
     getAnchor = () => {
         const anchor = this._getAnchor?.(this._savedAnnotations);
         anchor && this.addDocument(anchor);
         return anchor ?? this.rootDoc;
-    }
+    };
 
     componentDidMount() {
         this.props.setContentView?.(this); // bcz: do not remove this.  without it, stepping into an image in the lightbox causes an infinite loop....
-        this._disposers.sizer = reaction(() => (
-            {
+        this._disposers.sizer = reaction(
+            () => ({
                 forceFull: this.props.renderDepth < 1 || this.layoutDoc._showFullRes,
                 scrSize: this.props.ScreenToLocalTransform().inverse().transformDirection(this.nativeSize.nativeWidth, this.nativeSize.nativeHeight)[0] / this.nativeSize.nativeWidth,
-                selected: this.props.isSelected()
+                selected: this.props.isSelected(),
             }),
-            ({ forceFull, scrSize, selected }) => this._curSuffix = this.fieldKey === "icon" ? "_m" : forceFull ? "_o" : scrSize < 0.25 ? "_s" : scrSize < 0.5 ? "_m" : scrSize < 0.8 || !selected ? "_l" : "_o",
-            { fireImmediately: true, delay: 1000 });
-        this._disposers.path = reaction(() => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }),
+            ({ forceFull, scrSize, selected }) => (this._curSuffix = this.fieldKey === 'icon' ? '_m' : forceFull ? '_o' : scrSize < 0.25 ? '_s' : scrSize < 0.5 ? '_m' : scrSize < 0.8 || !selected ? '_l' : '_o'),
+            { fireImmediately: true, delay: 1000 }
+        );
+        this._disposers.path = reaction(
+            () => ({ nativeSize: this.nativeSize, width: this.layoutDoc[WidthSym]() }),
             ({ nativeSize, width }) => {
                 if (true || !this.layoutDoc._height) {
-                    this.layoutDoc._height = width * nativeSize.nativeHeight / nativeSize.nativeWidth;
+                    this.layoutDoc._height = (width * nativeSize.nativeHeight) / nativeSize.nativeWidth;
                 }
             },
-            { fireImmediately: true });
+            { fireImmediately: true }
+        );
     }
 
     componentWillUnmount() {
@@ -96,10 +97,12 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     drop = (e: Event, de: DragManager.DropEvent) => {
         if (de.complete.docDragData) {
             if (de.metaKey) {
-                de.complete.docDragData.droppedDocuments.forEach(action((drop: Doc) => {
-                    Doc.AddDocToList(this.dataDoc, this.fieldKey + "-alternates", drop);
-                    e.stopPropagation();
-                }));
+                de.complete.docDragData.droppedDocuments.forEach(
+                    action((drop: Doc) => {
+                        Doc.AddDocToList(this.dataDoc, this.fieldKey + '-alternates', drop);
+                        e.stopPropagation();
+                    })
+                );
             } else if (de.altKey || !this.dataDoc[this.fieldKey]) {
                 const layoutDoc = de.complete.docDragData?.draggedDocuments[0];
                 const targetField = Doc.LayoutFieldKey(layoutDoc);
@@ -112,20 +115,20 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 }
             }
         }
-    }
+    };
 
     @undoBatch
-    resolution = () => this.layoutDoc._showFullRes = !this.layoutDoc._showFullRes
+    resolution = () => (this.layoutDoc._showFullRes = !this.layoutDoc._showFullRes);
 
     @undoBatch
     rotate = action(() => {
-        const nw = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"]);
-        const nh = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"]);
+        const nw = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth']);
+        const nh = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight']);
         const w = this.layoutDoc._width;
         const h = this.layoutDoc._height;
-        this.dataDoc[this.fieldKey + "-rotation"] = (NumCast(this.dataDoc[this.fieldKey + "-rotation"]) + 90) % 360;
-        this.dataDoc[this.fieldKey + "-nativeWidth"] = nh;
-        this.dataDoc[this.fieldKey + "-nativeHeight"] = nw;
+        this.dataDoc[this.fieldKey + '-rotation'] = (NumCast(this.dataDoc[this.fieldKey + '-rotation']) + 90) % 360;
+        this.dataDoc[this.fieldKey + '-nativeWidth'] = nh;
+        this.dataDoc[this.fieldKey + '-nativeHeight'] = nw;
         this.layoutDoc._width = h;
         this.layoutDoc._height = w;
     });
@@ -134,15 +137,15 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         if (!region) return;
         const cropping = Doc.MakeCopy(region, true);
         Doc.GetProto(region).lockedPosition = true;
-        Doc.GetProto(region).title = "region:" + this.rootDoc.title;
+        Doc.GetProto(region).title = 'region:' + this.rootDoc.title;
         Doc.GetProto(region).isPushpin = true;
         this.addDocument(region);
         const anchx = NumCast(cropping.x);
         const anchy = NumCast(cropping.y);
         const anchw = NumCast(cropping._width);
         const anchh = NumCast(cropping._height);
-        const viewScale = NumCast(this.rootDoc[this.fieldKey + "-nativeWidth"]) / anchw;
-        cropping.title = "crop: " + this.rootDoc.title;
+        const viewScale = NumCast(this.rootDoc[this.fieldKey + '-nativeWidth']) / anchw;
+        cropping.title = 'crop: ' + this.rootDoc.title;
         cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width);
         cropping.y = NumCast(this.rootDoc.y);
         cropping._width = anchw * (this.props.scaling?.() || 1);
@@ -153,70 +156,70 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         croppingProto.isPrototype = true;
         croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO
         croppingProto.type = DocumentType.IMG;
-        croppingProto.layout = ImageBox.LayoutString("data");
+        croppingProto.layout = ImageBox.LayoutString('data');
         croppingProto.data = ObjectField.MakeCopy(this.rootDoc[this.fieldKey] as ObjectField);
-        croppingProto["data-nativeWidth"] = anchw;
-        croppingProto["data-nativeHeight"] = anchh;
+        croppingProto['data-nativeWidth'] = anchw;
+        croppingProto['data-nativeHeight'] = anchh;
         croppingProto.viewScale = viewScale;
         croppingProto.viewScaleMin = viewScale;
         croppingProto.panX = anchx / viewScale;
         croppingProto.panY = anchy / viewScale;
-        croppingProto.panXMin = (anchx) / viewScale;
-        croppingProto.panXMax = (anchw) / viewScale;
-        croppingProto.panYMin = (anchy) / viewScale;
-        croppingProto.panYMax = (anchh) / viewScale;
+        croppingProto.panXMin = anchx / viewScale;
+        croppingProto.panXMax = anchw / viewScale;
+        croppingProto.panYMin = anchy / viewScale;
+        croppingProto.panYMax = anchh / viewScale;
         if (addCrop) {
-            DocUtils.MakeLink({ doc: region }, { doc: cropping }, "cropped image", "");
+            DocUtils.MakeLink({ doc: region }, { doc: cropping }, 'cropped image', '');
         }
         this.props.bringToFront(cropping);
         return cropping;
-    }
+    };
 
     specificContextMenu = (e: React.MouseEvent): void => {
         const field = Cast(this.dataDoc[this.fieldKey], ImageField);
         if (field) {
             const funcs: ContextMenuProps[] = [];
-            funcs.push({ description: "Rotate Clockwise 90", event: this.rotate, icon: "expand-arrows-alt" });
-            funcs.push({ description: `Show ${this.layoutDoc._showFullRes ? "Dynamic Res" : "Full Res"}`, event: this.resolution, icon: "expand-arrows-alt" });
-            funcs.push({ description: "Copy path", event: () => Utils.CopyText(this.choosePath(field.url)), icon: "expand-arrows-alt" });
+            funcs.push({ description: 'Rotate Clockwise 90', event: this.rotate, icon: 'expand-arrows-alt' });
+            funcs.push({ description: `Show ${this.layoutDoc._showFullRes ? 'Dynamic Res' : 'Full Res'}`, event: this.resolution, icon: 'expand-arrows-alt' });
+            funcs.push({ description: 'Copy path', event: () => Utils.CopyText(this.choosePath(field.url)), icon: 'expand-arrows-alt' });
             if (!Doc.noviceMode) {
-                funcs.push({ description: "Export to Google Photos", event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: "caret-square-right" });
+                funcs.push({ description: 'Export to Google Photos', event: () => GooglePhotos.Transactions.UploadImages([this.props.Document]), icon: 'caret-square-right' });
 
-                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" });
+                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' });
                 //modes.push({ description: "Recommend", event: this.extractText, icon: "brain" });
-                !existingAnalyze && ContextMenu.Instance?.addItem({ description: "Analyzers...", subitems: modes, icon: "hand-point-right" });
+                !existingAnalyze && ContextMenu.Instance?.addItem({ description: 'Analyzers...', subitems: modes, icon: 'hand-point-right' });
             }
 
-            ContextMenu.Instance?.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
+            ContextMenu.Instance?.addItem({ description: 'Options...', subitems: funcs, icon: 'asterisk' });
         }
-    }
+    };
 
     extractFaces = () => {
         const converter = (results: any) => {
             return results.map((face: CognitiveServices.Image.Face) => Doc.Get.FromJson({ data: face, title: `Face: ${face.faceId}` })!);
         };
-        this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.fieldKey + "-faces"], this.url, Service.Face, converter);
-    }
+        this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.fieldKey + '-faces'], this.url, Service.Face, converter);
+    };
 
     generateMetadata = (threshold: Confidence = Confidence.Excellent) => {
         const converter = (results: any) => {
-            const tagDoc = new Doc;
+            const tagDoc = new Doc();
             const tagsList = new List();
             results.tags.map((tag: Tag) => {
                 tagsList.push(tag.name);
-                const sanitized = tag.name.replace(" ", "_");
+                const sanitized = tag.name.replace(' ', '_');
                 tagDoc[sanitized] = ComputedField.MakeFunction(`(${tag.confidence} >= this.confidence) ? ${tag.confidence} : "${ComputedField.undefined}"`);
             });
-            this.dataDoc[this.fieldKey + "-generatedTags"] = tagsList;
-            tagDoc.title = "Generated Tags Doc";
+            this.dataDoc[this.fieldKey + '-generatedTags'] = tagsList;
+            tagDoc.title = 'Generated Tags Doc';
             tagDoc.confidence = threshold;
             return tagDoc;
         };
-        this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.fieldKey + "-generatedTagsDoc"], this.url, Service.ComputerVision, converter);
-    }
+        this.url && CognitiveServices.Image.Appliers.ProcessImage(this.dataDoc, [this.fieldKey + '-generatedTagsDoc'], this.url, Service.ComputerVision, converter);
+    };
 
     @computed private get url() {
         const data = Cast(this.dataDoc[this.fieldKey], ImageField);
@@ -225,9 +228,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     choosePath(url: URL) {
         const lower = url.href.toLowerCase();
-        if (url.protocol === "data") return url.href;
+        if (url.protocol === 'data') return url.href;
         if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href);
-        if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) return url.href;  //Why is this here
+        if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) return url.href; //Why is this here
 
         const ext = extname(url.href);
         return url.href.replace(ext, this._curSuffix + ext);
@@ -235,40 +238,36 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     considerGooglePhotosLink = () => {
         const remoteUrl = this.dataDoc.googlePhotosUrl;
-        return !remoteUrl ? (null) : (<img draggable={false}
-            style={{ transformOrigin: "bottom right" }}
-            id={"google-photos"}
-            src={"/assets/google_photos.png"}
-            onClick={() => window.open(remoteUrl)}
-        />);
-    }
+        return !remoteUrl ? null : <img draggable={false} style={{ transformOrigin: 'bottom right' }} id={'google-photos'} src={'/assets/google_photos.png'} onClick={() => window.open(remoteUrl)} />;
+    };
 
     considerGooglePhotosTags = () => {
         const tags = this.dataDoc.googlePhotosTags;
-        return !tags ? (null) : (<img id={"google-tags"} src={"/assets/google_tags.png"} />);
-    }
+        return !tags ? null : <img id={'google-tags'} src={'/assets/google_tags.png'} />;
+    };
 
     @computed
     private get considerDownloadIcon() {
         const data = this.dataDoc[this.fieldKey];
         if (!(data instanceof ImageField)) {
-            return (null);
+            return null;
         }
         const primary = data.url.href;
         if (primary.includes(window.location.origin)) {
-            return (null);
+            return null;
         }
         return (
             <img
-                id={"upload-icon"} draggable={false}
-                style={{ transformOrigin: "bottom right" }}
+                id={'upload-icon'}
+                draggable={false}
+                style={{ transformOrigin: 'bottom right' }}
                 src={`/assets/${this._uploadIcon}`}
                 onClick={async () => {
                     const { dataDoc } = this;
                     const { success, failure, idle, loading } = uploadIcons;
-                    runInAction(() => this._uploadIcon = loading);
-                    const [{ accessPaths }] = await Networking.PostToServer("/uploadRemoteImage", { sources: [primary] });
-                    dataDoc[this.props.fieldKey + "-originalUrl"] = primary;
+                    runInAction(() => (this._uploadIcon = loading));
+                    const [{ accessPaths }] = await Networking.PostToServer('/uploadRemoteImage', { sources: [primary] });
+                    dataDoc[this.props.fieldKey + '-originalUrl'] = primary;
                     let succeeded = true;
                     let data: ImageField | undefined;
                     try {
@@ -276,13 +275,16 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     } catch {
                         succeeded = false;
                     }
-                    runInAction(() => this._uploadIcon = succeeded ? success : failure);
-                    setTimeout(action(() => {
-                        this._uploadIcon = idle;
-                        if (data) {
-                            dataDoc[this.fieldKey] = data;
-                        }
-                    }), 2000);
+                    runInAction(() => (this._uploadIcon = succeeded ? success : failure));
+                    setTimeout(
+                        action(() => {
+                            this._uploadIcon = idle;
+                            if (data) {
+                                dataDoc[this.fieldKey] = data;
+                            }
+                        }),
+                        2000
+                    );
                 }}
             />
         );
@@ -290,18 +292,21 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     @computed get nativeSize() {
         TraceMobx();
-        const nativeWidth = NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"], NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"], 500));
-        const nativeHeight = NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"], NumCast(this.layoutDoc[this.fieldKey + "-nativeHeight"], 1));
-        const nativeOrientation = NumCast(this.dataDoc[this.fieldKey + "-nativeOrientation"], 1);
+        const nativeWidth = NumCast(this.dataDoc[this.fieldKey + '-nativeWidth'], NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth'], 500));
+        const nativeHeight = NumCast(this.dataDoc[this.fieldKey + '-nativeHeight'], NumCast(this.layoutDoc[this.fieldKey + '-nativeHeight'], 1));
+        const nativeOrientation = NumCast(this.dataDoc[this.fieldKey + '-nativeOrientation'], 1);
         return { nativeWidth, nativeHeight, nativeOrientation };
     }
 
     @computed get paths() {
         const field = Cast(this.dataDoc[this.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
-        const alts = DocListCast(this.dataDoc[this.fieldKey + "-alternates"]); // retrieve alternate documents that may be rendered as alternate images
-        const altpaths = alts.map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url).filter(url => url).map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
+        const alts = DocListCast(this.dataDoc[this.fieldKey + '-alternates']); // retrieve alternate documents that may be rendered as alternate images
+        const altpaths = alts
+            .map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url)
+            .filter(url => url)
+            .map(url => this.choosePath(url)); // access the primary layout data of the alternate documents
         const paths = field ? [this.choosePath(field.url), ...altpaths] : altpaths;
-        return paths.length ? paths : [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
+        return paths.length ? paths : [Utils.CorsProxy('http://www.cs.brown.edu/~bcz/noImage.png')];
     }
 
     @computed get content() {
@@ -310,39 +315,35 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         const srcpath = this.paths[0];
         const fadepath = this.paths[Math.min(1, this.paths.length - 1)];
         const { nativeWidth, nativeHeight, nativeOrientation } = this.nativeSize;
-        const rotation = NumCast(this.dataDoc[this.fieldKey + "-rotation"]);
+        const rotation = NumCast(this.dataDoc[this.fieldKey + '-rotation']);
         const aspect = rotation % 180 ? nativeHeight / nativeWidth : 1;
-        let transformOrigin = "center center";
+        let transformOrigin = 'center center';
         let transform = `translate(0%, 0%) rotate(${rotation}deg) scale(${aspect})`;
         if (rotation === 90 || rotation === -270) {
-            transformOrigin = "top left";
+            transformOrigin = 'top left';
             transform = `translate(100%, 0%) rotate(${rotation}deg) scale(${aspect})`;
         } else if (rotation === 180) {
             transform = `rotate(${rotation}deg) scale(${aspect})`;
         } else if (rotation === 270 || rotation === -90) {
-            transformOrigin = "right top";
+            transformOrigin = 'right top';
             transform = `translate(-100%, 0%) rotate(${rotation}deg) scale(${aspect})`;
         }
 
-        return <div className="imageBox-cont" key={this.layoutDoc[Id]} ref={this.createDropTarget} onPointerDown={this.marqueeDown}>
-            <div className="imageBox-fader" style={{ overflow: Array.from(this.props.docViewPath?.()).slice(-1)[0].fitWidth ? "auto" : undefined }} >
-                <img key="paths"
-                    src={srcpath}
-                    style={{ transform, transformOrigin }}
-                    draggable={false}
-                    width={nativeWidth} />
-                {fadepath === srcpath ? (null) :
-                    <div className={`imageBox-fadeBlocker${this.props.isHovering?.() ? "-hover" : ""}`}>
-                        <img className="imageBox-fadeaway" key={"fadeaway"}
-                            src={fadepath}
-                            style={{ transform, transformOrigin }} draggable={false}
-                            width={nativeWidth} />
-                    </div>}
+        return (
+            <div className="imageBox-cont" key={this.layoutDoc[Id]} ref={this.createDropTarget} onPointerDown={this.marqueeDown}>
+                <div className="imageBox-fader" style={{ overflow: Array.from(this.props.docViewPath?.()).slice(-1)[0].fitWidth ? 'auto' : undefined }}>
+                    <img key="paths" src={srcpath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} />
+                    {fadepath === srcpath ? null : (
+                        <div className={`imageBox-fadeBlocker${this.props.isHovering?.() ? '-hover' : ''}`}>
+                            <img className="imageBox-fadeaway" key={'fadeaway'} src={fadepath} style={{ transform, transformOrigin }} draggable={false} width={nativeWidth} />
+                        </div>
+                    )}
+                </div>
+                {this.considerDownloadIcon}
+                {this.considerGooglePhotosLink()}
+                <FaceRectangles document={this.dataDoc} color={'#0000FF'} backgroundColor={'#0000FF'} />
             </div>
-            {this.considerDownloadIcon}
-            {this.considerGooglePhotosLink()}
-            <FaceRectangles document={this.dataDoc} color={"#0000FF"} backgroundColor={"#0000FF"} />
-        </div>;
+        );
     }
 
     screenToLocalTransform = this.props.ScreenToLocalTransform;
@@ -357,66 +358,79 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return <div className="imageBox-annotationLayer" style={{ height: this.props.PanelHeight() }} ref={this._annotationLayer} />;
     }
     marqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._viewScale,1) <= NumCast(this.rootDoc.viewScaleMin,1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
-            setupMoveUpEvents(this, e, action(e => {
-                MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
-                this._marqueeing = [e.clientX, e.clientY];
-                return true;
-            }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false);
+        if (!e.altKey && e.button === 0 && NumCast(this.rootDoc._viewScale, 1) <= NumCast(this.rootDoc.viewScaleMin, 1) && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+            setupMoveUpEvents(
+                this,
+                e,
+                action(e => {
+                    MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+                    this._marqueeing = [e.clientX, e.clientY];
+                    return true;
+                }),
+                returnFalse,
+                () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations),
+                false
+            );
         }
-    }
+    };
     @action
     finishMarquee = () => {
         this._getAnchor = AnchorMenu.Instance?.GetAnchor;
         this._marqueeing = undefined;
         this.props.select(false);
-    }
+    };
     savedAnnotations = () => this._savedAnnotations;
 
     render() {
         TraceMobx();
         const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
-        const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / (this.props.scaling?.() || 1)}px` : borderRad;
-        return (<div className="imageBox" onContextMenu={this.specificContextMenu} ref={this._mainCont}
-            style={{
-                width: this.props.PanelWidth() ? undefined : `100%`,
-                height: this.props.PanelWidth() ? undefined : `100%`,
-                pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined,
-                borderRadius
-            }} >
-            <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
-                renderDepth={this.props.renderDepth + 1}
-                fieldKey={this.annotationKey}
-                CollectionView={undefined}
-                isAnnotationOverlay={true}
-                annotationLayerHostsContent={true}
-                PanelWidth={this.props.PanelWidth}
-                PanelHeight={this.props.PanelHeight}
-                ScreenToLocalTransform={this.screenToLocalTransform}
-                select={emptyFunction}
-                scaling={returnOne}
-                whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                removeDocument={this.removeDocument}
-                moveDocument={this.moveDocument}
-                addDocument={this.addDocument}>
-                {this.contentFunc}
-            </CollectionFreeFormView>
-            {this.annotationLayer}
-            {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
-                <MarqueeAnnotator
-                    rootDoc={this.rootDoc}
-                    scrollTop={0}
-                    down={this._marqueeing}
-                    scaling={this.props.scaling}
-                    docView={this.props.docViewPath().slice(-1)[0]}
-                    addDocument={this.addDocument}
-                    finishMarquee={this.finishMarquee}
-                    savedAnnotations={this.savedAnnotations}
-                    annotationLayer={this._annotationLayer.current}
-                    mainCont={this._mainCont.current}
-                    anchorMenuCrop={this.crop}
-                />}
-        </div >);
+        const borderRadius = borderRad?.includes('px') ? `${Number(borderRad.split('px')[0]) / (this.props.scaling?.() || 1)}px` : borderRad;
+        return (
+            <div
+                className="imageBox"
+                onContextMenu={this.specificContextMenu}
+                ref={this._mainCont}
+                style={{
+                    width: this.props.PanelWidth() ? undefined : `100%`,
+                    height: this.props.PanelWidth() ? undefined : `100%`,
+                    pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined,
+                    borderRadius,
+                }}>
+                <CollectionFreeFormView
+                    {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                    renderDepth={this.props.renderDepth + 1}
+                    fieldKey={this.annotationKey}
+                    CollectionView={undefined}
+                    isAnnotationOverlay={true}
+                    annotationLayerHostsContent={true}
+                    PanelWidth={this.props.PanelWidth}
+                    PanelHeight={this.props.PanelHeight}
+                    ScreenToLocalTransform={this.screenToLocalTransform}
+                    select={emptyFunction}
+                    scaling={returnOne}
+                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                    removeDocument={this.removeDocument}
+                    moveDocument={this.moveDocument}
+                    addDocument={this.addDocument}>
+                    {this.contentFunc}
+                </CollectionFreeFormView>
+                {this.annotationLayer}
+                {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
+                    <MarqueeAnnotator
+                        rootDoc={this.rootDoc}
+                        scrollTop={0}
+                        down={this._marqueeing}
+                        scaling={this.props.scaling}
+                        docView={this.props.docViewPath().slice(-1)[0]}
+                        addDocument={this.addDocument}
+                        finishMarquee={this.finishMarquee}
+                        savedAnnotations={this.savedAnnotations}
+                        annotationLayer={this._annotationLayer.current}
+                        mainCont={this._mainCont.current}
+                        anchorMenuCrop={this.crop}
+                    />
+                )}
+            </div>
+        );
     }
-
 }
diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx
index 7fd289a97..85a8622ec 100644
--- a/src/client/views/nodes/LinkAnchorBox.tsx
+++ b/src/client/views/nodes/LinkAnchorBox.tsx
@@ -1,30 +1,31 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable } from "mobx";
-import { observer } from "mobx-react";
-import { Doc } from "../../../fields/Doc";
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { TraceMobx } from "../../../fields/util";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc } from '../../../fields/Doc';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { TraceMobx } from '../../../fields/util';
 import { emptyFunction, setupMoveUpEvents, Utils } from '../../../Utils';
-import { DragManager } from "../../util/DragManager";
-import { LinkManager } from "../../util/LinkManager";
-import { SelectionManager } from "../../util/SelectionManager";
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import { ViewBoxBaseComponent } from "../DocComponent";
-import { LinkEditor } from "../linking/LinkEditor";
-import { StyleProp } from "../StyleProvider";
-import { FieldView, FieldViewProps } from "./FieldView";
-import "./LinkAnchorBox.scss";
-import { LinkDocPreview } from "./LinkDocPreview";
-import React = require("react");
-const higflyout = require("@hig/flyout");
+import { DragManager } from '../../util/DragManager';
+import { LinkFollower } from '../../util/LinkFollower';
+import { SelectionManager } from '../../util/SelectionManager';
+import { ContextMenu } from '../ContextMenu';
+import { ContextMenuProps } from '../ContextMenuItem';
+import { ViewBoxBaseComponent } from '../DocComponent';
+import { LinkEditor } from '../linking/LinkEditor';
+import { StyleProp } from '../StyleProvider';
+import { FieldView, FieldViewProps } from './FieldView';
+import './LinkAnchorBox.scss';
+import { LinkDocPreview } from './LinkDocPreview';
+import React = require('react');
+const higflyout = require('@hig/flyout');
 export const { anchorPoints } = higflyout;
 export const Flyout = higflyout.default;
 
-
 @observer
 export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LinkAnchorBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(LinkAnchorBox, fieldKey);
+    }
     _doubleTap = false;
     _lastTap: number = 0;
     _ref = React.createRef<HTMLDivElement>();
@@ -38,7 +39,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     onPointerDown = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, false);
-    }
+    };
     onPointerMove = action((e: PointerEvent, down: number[], delta: number[]) => {
         const cdiv = this._ref && this._ref.current && this._ref.current.parentElement;
         if (!this._isOpen && cdiv) {
@@ -47,20 +48,20 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
             const separation = Math.sqrt((pt[0] - e.clientX) * (pt[0] - e.clientX) + (pt[1] - e.clientY) * (pt[1] - e.clientY));
             if (separation > 100) {
                 const dragData = new DragManager.DocumentDragData([this.rootDoc]);
-                dragData.dropAction = "alias";
-                dragData.removeDropProperties = ["anchor1_x", "anchor1_y", "anchor2_x", "anchor2_y", "isLinkButton"];
+                dragData.dropAction = 'alias';
+                dragData.removeDropProperties = ['anchor1_x', 'anchor1_y', 'anchor2_x', 'anchor2_y', 'isLinkButton'];
                 DragManager.StartDocumentDrag([this._ref.current!], dragData, pt[0], pt[1]);
                 return true;
             } else {
-                this.rootDoc[this.fieldKey + "_x"] = (pt[0] - bounds.left) / bounds.width * 100;
-                this.rootDoc[this.fieldKey + "_y"] = (pt[1] - bounds.top) / bounds.height * 100;
+                this.rootDoc[this.fieldKey + '_x'] = ((pt[0] - bounds.left) / bounds.width) * 100;
+                this.rootDoc[this.fieldKey + '_y'] = ((pt[1] - bounds.top) / bounds.height) * 100;
             }
         }
         return false;
     });
     @action
     onClick = (e: React.MouseEvent) => {
-        if ((e.button === 2 || e.ctrlKey || !this.layoutDoc.isLinkButton)) {
+        if (e.button === 2 || e.ctrlKey || !this.layoutDoc.isLinkButton) {
             this.props.select(false);
         }
         if (!this._doubleTap && !e.ctrlKey && e.button < 2) {
@@ -68,10 +69,13 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this._editing = true;
             anchorContainerDoc && this.props.bringToFront(anchorContainerDoc, false);
             if (anchorContainerDoc && !this.layoutDoc.onClick && !this._isOpen) {
-                this._timeout = setTimeout(action(() => {
-                    LinkManager.FollowLink(this.rootDoc, anchorContainerDoc, this.props, false);
-                    this._editing = false;
-                }), 300 - (Date.now() - this._lastTap));
+                this._timeout = setTimeout(
+                    action(() => {
+                        LinkFollower.FollowLink(this.rootDoc, anchorContainerDoc, this.props, false);
+                        this._editing = false;
+                    }),
+                    300 - (Date.now() - this._lastTap)
+                );
                 e.stopPropagation();
             }
         } else {
@@ -81,17 +85,17 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.openLinkEditor(e);
             e.stopPropagation();
         }
-    }
+    };
 
     openLinkDocOnRight = (e: React.MouseEvent) => {
-        this.props.addDocTab(this.rootDoc, "add:right");
-    }
+        this.props.addDocTab(this.rootDoc, 'add:right');
+    };
     openLinkTargetOnRight = (e: React.MouseEvent) => {
         const alias = Doc.MakeAlias(Cast(this.layoutDoc[this.fieldKey], Doc, null));
         alias._isLinkButton = undefined;
-        alias.layoutKey = "layout";
-        this.props.addDocTab(alias, "add:right");
-    }
+        alias.layoutKey = 'layout';
+        this.props.addDocTab(alias, 'add:right');
+    };
     @action
     openLinkEditor = action((e: React.MouseEvent) => {
         SelectionManager.DeselectAll();
@@ -100,56 +104,67 @@ export class LinkAnchorBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     specificContextMenu = (e: React.MouseEvent): void => {
         const funcs: ContextMenuProps[] = [];
-        funcs.push({ description: "Open Link Target on Right", event: () => this.openLinkTargetOnRight(e), icon: "eye" });
-        funcs.push({ description: "Open Link on Right", event: () => this.openLinkDocOnRight(e), icon: "eye" });
-        funcs.push({ description: "Open Link Editor", event: () => this.openLinkEditor(e), icon: "eye" });
-        funcs.push({ description: "Toggle Always Show Link", event: () => this.props.Document.linkDisplay = !this.props.Document.linkDisplay, icon: "eye" });
+        funcs.push({ description: 'Open Link Target on Right', event: () => this.openLinkTargetOnRight(e), icon: 'eye' });
+        funcs.push({ description: 'Open Link on Right', event: () => this.openLinkDocOnRight(e), icon: 'eye' });
+        funcs.push({ description: 'Open Link Editor', event: () => this.openLinkEditor(e), icon: 'eye' });
+        funcs.push({ description: 'Toggle Always Show Link', event: () => (this.props.Document.linkDisplay = !this.props.Document.linkDisplay), icon: 'eye' });
 
-        ContextMenu.Instance.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
-    }
+        ContextMenu.Instance.addItem({ description: 'Options...', subitems: funcs, icon: 'asterisk' });
+    };
 
     render() {
         TraceMobx();
         const small = this.props.PanelWidth() <= 1; // this happens when rendered in a treeView
-        const x = NumCast(this.rootDoc[this.fieldKey + "_x"], 100);
-        const y = NumCast(this.rootDoc[this.fieldKey + "_y"], 100);
+        const x = NumCast(this.rootDoc[this.fieldKey + '_x'], 100);
+        const y = NumCast(this.rootDoc[this.fieldKey + '_y'], 100);
         const linkSource = this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.LinkSource);
-        const background = this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.BackgroundColor + ":anchor");
-        const anchor = this.fieldKey === "anchor1" ? "anchor2" : "anchor1";
-        const anchorScale = !this.dataDoc[this.fieldKey + "-useLinkSmallAnchor"] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : .25;
+        const background = this.props.styleProvider?.(this.dataDoc, this.props, StyleProp.BackgroundColor + ':anchor');
+        const anchor = this.fieldKey === 'anchor1' ? 'anchor2' : 'anchor1';
+        const anchorScale = !this.dataDoc[this.fieldKey + '-useLinkSmallAnchor'] && (x === 0 || x === 100 || y === 0 || y === 100) ? 1 : 0.25;
 
         const targetTitle = StrCast((this.dataDoc[anchor] as Doc)?.title);
         const flyout = (
             <div className="linkAnchorBoxBox-flyout" title=" " onPointerOver={() => Doc.UnBrushDoc(this.rootDoc)}>
-                <LinkEditor sourceDoc={Cast(this.dataDoc[this.fieldKey], Doc, null)} hideback={true} linkDoc={this.rootDoc} showLinks={action(() => { })} />
-                {!this._forceOpen ? (null) : <div className="linkAnchorBox-linkCloser" onPointerDown={action(() => this._isOpen = this._editing = this._forceOpen = false)}>
-                    <FontAwesomeIcon color="dimgray" icon={"times"} size={"sm"} />
-                </div>}
+                <LinkEditor sourceDoc={Cast(this.dataDoc[this.fieldKey], Doc, null)} hideback={true} linkDoc={this.rootDoc} showLinks={action(() => {})} />
+                {!this._forceOpen ? null : (
+                    <div className="linkAnchorBox-linkCloser" onPointerDown={action(() => (this._isOpen = this._editing = this._forceOpen = false))}>
+                        <FontAwesomeIcon color="dimgray" icon={'times'} size={'sm'} />
+                    </div>
+                )}
+            </div>
+        );
+        return (
+            <div
+                className={`linkAnchorBox-cont${small ? '-small' : ''}`}
+                onPointerLeave={LinkDocPreview.Clear}
+                onPointerEnter={e =>
+                    LinkDocPreview.SetLinkInfo({
+                        docProps: this.props,
+                        linkSrc: linkSource,
+                        linkDoc: this.rootDoc,
+                        showHeader: true,
+                        location: [e.clientX, e.clientY + 20],
+                    })
+                }
+                onPointerDown={this.onPointerDown}
+                onClick={this.onClick}
+                title={targetTitle}
+                onContextMenu={this.specificContextMenu}
+                ref={this._ref}
+                style={{
+                    background,
+                    left: `calc(${x}% - ${small ? 2.5 : 7.5}px)`,
+                    top: `calc(${y}% - ${small ? 2.5 : 7.5}px)`,
+                    transform: `scale(${anchorScale})`,
+                }}>
+                {!this._editing && !this._forceOpen ? null : (
+                    <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout} open={this._forceOpen ? true : undefined} onOpen={() => (this._isOpen = true)} onClose={action(() => (this._isOpen = this._forceOpen = this._editing = false))}>
+                        <span className="linkAnchorBox-button">
+                            <FontAwesomeIcon icon={'eye'} size={'lg'} />
+                        </span>
+                    </Flyout>
+                )}
             </div>
         );
-        return <div className={`linkAnchorBox-cont${small ? "-small" : ""}`}
-            onPointerLeave={LinkDocPreview.Clear}
-            onPointerEnter={e => LinkDocPreview.SetLinkInfo({
-                docProps: this.props,
-                linkSrc: linkSource,
-                linkDoc: this.rootDoc,
-                showHeader: true,
-                location: [e.clientX, e.clientY + 20]
-            })}
-            onPointerDown={this.onPointerDown} onClick={this.onClick} title={targetTitle} onContextMenu={this.specificContextMenu}
-            ref={this._ref}
-            style={{
-                background,
-                left: `calc(${x}% - ${small ? 2.5 : 7.5}px)`,
-                top: `calc(${y}% - ${small ? 2.5 : 7.5}px)`,
-                transform: `scale(${anchorScale})`
-            }} >
-            {!this._editing && !this._forceOpen ? (null) :
-                <Flyout anchorPoint={anchorPoints.LEFT_TOP} content={flyout} open={this._forceOpen ? true : undefined} onOpen={() => this._isOpen = true} onClose={action(() => this._isOpen = this._forceOpen = this._editing = false)}>
-                    <span className="linkAnchorBox-button" >
-                        <FontAwesomeIcon icon={"eye"} size={"lg"} />
-                    </span>
-                </Flyout>}
-        </div>;
     }
 }
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 47d5c662e..c2fb5d4ec 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -16,6 +16,7 @@ import './LinkDocPreview.scss';
 import React = require('react');
 import { DocumentType } from '../../documents/DocumentTypes';
 import { DragManager } from '../../util/DragManager';
+import { LinkFollower } from '../../util/LinkFollower';
 
 interface LinkDocPreviewProps {
     linkDoc?: Doc;
@@ -145,7 +146,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
     followLink = (e: React.PointerEvent) => {
         if (this._linkDoc && this._linkSrc) {
             LinkDocPreview.Clear();
-            LinkManager.FollowLink(this._linkDoc, this._linkSrc, this.props.docProps, false);
+            LinkFollower.FollowLink(this._linkDoc, this._linkSrc, this.props.docProps, false);
         } else if (this.props.hrefs?.length) {
             this.props.docProps?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true }), 'add:right');
         }
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 18a6b5453..bf4c029b2 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -10,7 +10,6 @@ import { NumCast, StrCast } from '../../../../fields/Types';
 import { TraceMobx } from '../../../../fields/util';
 import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
-import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
 import { DragManager } from '../../../util/DragManager';
 import { SnappingManager } from '../../../util/SnappingManager';
 import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
@@ -491,7 +490,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
 
     @action
     onMarqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
             setupMoveUpEvents(
                 this,
                 e,
@@ -591,7 +590,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         //     moveDocument={this.moveDocument}
         //     addDocument={this.sidebarAddDocument}
         //     childPointerEvents={true}
-        //     pointerEvents={CurrentUserUtils.ActiveTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
+        //     pointerEvents={Doc.ActiveTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />;
         return (
             <div className="mapBox" ref={this._ref}>
                 {/*console.log(apiKey)*/}
diff --git a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
index bdf0f9d64..72569135b 100644
--- a/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
+++ b/src/client/views/nodes/MapBox/MapBoxInfoWindow.tsx
@@ -1,19 +1,17 @@
 import { InfoWindow } from '@react-google-maps/api';
 import { action, computed } from 'mobx';
-import { observer } from "mobx-react";
-import * as React from "react";
+import { observer } from 'mobx-react';
+import * as React from 'react';
 import { Doc } from '../../../../fields/Doc';
 import { Id } from '../../../../fields/FieldSymbols';
 import { emptyFunction, OmitKeys, returnAll, returnEmptyFilter, returnFalse, returnOne, returnTrue, returnZero, setupMoveUpEvents } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
-import { DocumentType } from '../../../documents/DocumentTypes';
+import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
 import { CollectionStackingView } from '../../collections/CollectionStackingView';
-import { CollectionViewType } from '../../collections/CollectionView';
 import { ViewBoxAnnotatableProps } from '../../DocComponent';
 import { FieldViewProps } from '../FieldView';
 import { FormattedTextBox } from '../formattedText/FormattedTextBox';
-import "./MapBox.scss";
-
+import './MapBox.scss';
 
 interface MapBoxInfoWindowProps {
     place: Doc;
@@ -22,69 +20,74 @@ interface MapBoxInfoWindowProps {
     isAnyChildContentActive: () => boolean;
 }
 @observer
-export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & ViewBoxAnnotatableProps & FieldViewProps>{
-
+export class MapBoxInfoWindow extends React.Component<MapBoxInfoWindowProps & ViewBoxAnnotatableProps & FieldViewProps> {
     @action
     private handleInfoWindowClose = () => {
         if (this.props.place.infoWindowOpen) {
             this.props.place.infoWindowOpen = false;
         }
         this.props.place.infoWindowOpen = false;
-    }
+    };
 
     addNoteClick = (e: React.PointerEvent) => {
         setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => {
-            const newBox = Docs.Create.TextDocument("Note", { _autoHeight: true });
-            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
-            Doc.AddDocToList(this.props.place, "data", newBox);
+            const newBox = Docs.Create.TextDocument('Note', { _autoHeight: true });
+            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
+            Doc.AddDocToList(this.props.place, 'data', newBox);
             this._stack?.scrollToBottom();
             e.stopPropagation();
             e.preventDefault();
         });
-    }
-
+    };
 
     _stack: CollectionStackingView | null | undefined;
-    childFitWidth = (doc:Doc) => doc.type === DocumentType.RTF;
-    addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.AddDocToList(this.props.place, "data", d), true as boolean);
-    removeDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, "data", d), true as boolean);
+    childFitWidth = (doc: Doc) => doc.type === DocumentType.RTF;
+    addDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.AddDocToList(this.props.place, 'data', d), true as boolean);
+    removeDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((p, d) => p && Doc.RemoveDocFromList(this.props.place, 'data', d), true as boolean);
     render() {
-        return <InfoWindow anchor={this.props.markerMap[this.props.place[Id]]} onCloseClick={this.handleInfoWindowClose} >
-            <div className="mapbox-infowindow">
-                <div style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }}>
-                    <CollectionStackingView
-                        ref={r => this._stack = r}
-                        {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
-                        Document={this.props.place}
-                        DataDoc={undefined}
-                        fieldKey="data"
-                        CollectionView={undefined}
-                        NativeWidth={returnZero}
-                        NativeHeight={returnZero}
-                        docFilters={returnEmptyFilter}
-                        setHeight={emptyFunction}
-                        isAnnotationOverlay={false}
-                        select={emptyFunction}
-                        scaling={returnOne}
-                        isContentActive={returnTrue}
-                        chromeHidden={true}
-                        rootSelected={returnFalse}
-                        childHideResizeHandles={returnTrue}
-                        childHideDecorationTitle={returnTrue}
-                        childFitWidth={this.childFitWidth}
-                        // childDocumentsActive={returnFalse}
-                        removeDocument={this.removeDoc}
-                        addDocument={this.addDoc}
-                        renderDepth={this.props.renderDepth + 1}
-                        viewType={CollectionViewType.Stacking}
-                        pointerEvents={returnAll}
-                    />
-                </div>
-                <hr />
-                <div onPointerDown={this.addNoteClick} onClick={e => { e.stopPropagation(); e.preventDefault(); }} >
-                    Add Note
+        return (
+            <InfoWindow anchor={this.props.markerMap[this.props.place[Id]]} onCloseClick={this.handleInfoWindowClose}>
+                <div className="mapbox-infowindow">
+                    <div style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }}>
+                        <CollectionStackingView
+                            ref={r => (this._stack = r)}
+                            {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                            Document={this.props.place}
+                            DataDoc={undefined}
+                            fieldKey="data"
+                            CollectionView={undefined}
+                            NativeWidth={returnZero}
+                            NativeHeight={returnZero}
+                            docFilters={returnEmptyFilter}
+                            setHeight={emptyFunction}
+                            isAnnotationOverlay={false}
+                            select={emptyFunction}
+                            scaling={returnOne}
+                            isContentActive={returnTrue}
+                            chromeHidden={true}
+                            rootSelected={returnFalse}
+                            childHideResizeHandles={returnTrue}
+                            childHideDecorationTitle={returnTrue}
+                            childFitWidth={this.childFitWidth}
+                            // childDocumentsActive={returnFalse}
+                            removeDocument={this.removeDoc}
+                            addDocument={this.addDoc}
+                            renderDepth={this.props.renderDepth + 1}
+                            viewType={CollectionViewType.Stacking}
+                            pointerEvents={returnAll}
+                        />
+                    </div>
+                    <hr />
+                    <div
+                        onPointerDown={this.addNoteClick}
+                        onClick={e => {
+                            e.stopPropagation();
+                            e.preventDefault();
+                        }}>
+                        Add Note
+                    </div>
                 </div>
-            </div>
-        </InfoWindow>;
+            </InfoWindow>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 0c7d7dc31..942072524 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -1,32 +1,31 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import React = require('react');
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 // import { Canvas } from '@react-three/fiber';
-import { computed, observable, runInAction } from "mobx";
-import { observer } from "mobx-react";
+import { computed, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
 // import { BufferAttribute, Camera, Vector2, Vector3 } from 'three';
-import { DateField } from "../../../fields/DateField";
-import { Doc, HeightSym, WidthSym } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
-import { ComputedField } from "../../../fields/ScriptField";
-import { Cast, NumCast } from "../../../fields/Types";
-import { AudioField, VideoField } from "../../../fields/URLField";
-import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, OmitKeys, returnFalse, returnOne } from "../../../Utils";
-import { DocUtils } from "../../documents/Documents";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { Networking } from "../../Network";
-import { CaptureManager } from "../../util/CaptureManager";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
-import { CollectionStackedTimeline } from "../collections/CollectionStackedTimeline";
-import { ContextMenu } from "../ContextMenu";
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
+import { DateField } from '../../../fields/DateField';
+import { Doc, HeightSym, WidthSym } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { ComputedField } from '../../../fields/ScriptField';
+import { Cast, NumCast } from '../../../fields/Types';
+import { AudioField, VideoField } from '../../../fields/URLField';
+import { TraceMobx } from '../../../fields/util';
+import { emptyFunction, OmitKeys, returnFalse, returnOne } from '../../../Utils';
+import { DocUtils } from '../../documents/Documents';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { Networking } from '../../Network';
+import { CaptureManager } from '../../util/CaptureManager';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
+import { CollectionStackedTimeline } from '../collections/CollectionStackedTimeline';
+import { ContextMenu } from '../ContextMenu';
+import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
 import { FieldView, FieldViewProps } from './FieldView';
-import { FormattedTextBox } from "./formattedText/FormattedTextBox";
-import "./ScreenshotBox.scss";
-import { VideoBox } from "./VideoBox";
+import { FormattedTextBox } from './formattedText/FormattedTextBox';
+import './ScreenshotBox.scss';
+import { VideoBox } from './VideoBox';
 declare class MediaRecorder {
-      constructor(e: any, options?: any);  // whatever MediaRecorder has
+    constructor(e: any, options?: any); // whatever MediaRecorder has
 }
 
 // interface VideoTileProps {
@@ -107,226 +106,238 @@ declare class MediaRecorder {
 
 @observer
 export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
-      public static LayoutString(fieldKey: string) { return FieldView.LayoutString(ScreenshotBox, fieldKey); }
-      private _audioRec: any;
-      private _videoRec: any;
-      @observable private _videoRef: HTMLVideoElement | null = null;
-      @observable _screenCapture = false;
-      @computed get recordingStart() { return Cast(this.dataDoc[this.props.fieldKey + "-recordingStart"], DateField)?.date.getTime(); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(ScreenshotBox, fieldKey);
+    }
+    private _audioRec: any;
+    private _videoRec: any;
+    @observable private _videoRef: HTMLVideoElement | null = null;
+    @observable _screenCapture = false;
+    @computed get recordingStart() {
+        return Cast(this.dataDoc[this.props.fieldKey + '-recordingStart'], DateField)?.date.getTime();
+    }
 
-      constructor(props: any) {
-            super(props);
-            if (this.rootDoc.videoWall) {
-                  this.rootDoc.nativeWidth = undefined;
-                  this.rootDoc.nativeHeight = undefined;
-                  this.layoutDoc.popOff = 0;
-                  this.layoutDoc.popOut = 1;
-            } else {
-                  this.setupDictation();
-            }
-      }
-      getAnchor = () => {
-            const startTime = Cast(this.layoutDoc._currentTimecode, "number", null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
-            return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow", "_timecodeToHide",
-                  startTime, startTime === undefined ? undefined : startTime + 3)
-                  || this.rootDoc;
-      }
+    constructor(props: any) {
+        super(props);
+        if (this.rootDoc.videoWall) {
+            this.rootDoc.nativeWidth = undefined;
+            this.rootDoc.nativeHeight = undefined;
+            this.layoutDoc.popOff = 0;
+            this.layoutDoc.popOut = 1;
+        } else {
+            this.setupDictation();
+        }
+    }
+    getAnchor = () => {
+        const startTime = Cast(this.layoutDoc._currentTimecode, 'number', null) || (this._videoRec ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined);
+        return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, '_timecodeToShow', '_timecodeToHide', startTime, startTime === undefined ? undefined : startTime + 3) || this.rootDoc;
+    };
 
-      videoLoad = () => {
-            const aspect = this._videoRef!.videoWidth / this._videoRef!.videoHeight;
-            const nativeWidth = Doc.NativeWidth(this.layoutDoc);
-            const nativeHeight = Doc.NativeHeight(this.layoutDoc);
-            if (!nativeWidth || !nativeHeight) {
-                  if (!nativeWidth) Doc.SetNativeWidth(this.dataDoc, 1200);
-                  Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 1200) / aspect);
-                  this.layoutDoc._height = (this.layoutDoc[WidthSym]() || 0) / aspect;
-            }
-      }
+    videoLoad = () => {
+        const aspect = this._videoRef!.videoWidth / this._videoRef!.videoHeight;
+        const nativeWidth = Doc.NativeWidth(this.layoutDoc);
+        const nativeHeight = Doc.NativeHeight(this.layoutDoc);
+        if (!nativeWidth || !nativeHeight) {
+            if (!nativeWidth) Doc.SetNativeWidth(this.dataDoc, 1200);
+            Doc.SetNativeHeight(this.dataDoc, (nativeWidth || 1200) / aspect);
+            this.layoutDoc._height = (this.layoutDoc[WidthSym]() || 0) / aspect;
+        }
+    };
 
-      componentDidMount() {
-            this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = 0;
-            this.props.setContentView?.(this); // this tells the DocumentView that this ScreenshotBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
-            // this.rootDoc.videoWall && reaction(() => ({ width: this.props.PanelWidth(), height: this.props.PanelHeight() }),
-            //     ({ width, height }) => {
-            //         if (this._camera) {
-            //             const angle = -Math.abs(1 - width / height);
-            //             const xz = [0, (this._numScreens - 2) / Math.abs(1 + angle)];
-            //             this._camera.position.set(this._numScreens / 2 + xz[1] * Math.sin(angle), this._numScreens / 2, xz[1] * Math.cos(angle));
-            //             this._camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
-            //             (this._camera as any).updateProjectionMatrix();
-            //         }
-            //     });
-      }
-      componentWillUnmount() {
-            const ind = DocUtils.ActiveRecordings.indexOf(this);
-            ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1));
-      }
-
-      specificContextMenu = (e: React.MouseEvent): void => {
-            const subitems = [{ description: "Screen Capture", event: this.toggleRecording, icon: "expand-arrows-alt" as any }];
-            ContextMenu.Instance.addItem({ description: "Options...", subitems, icon: "video" });
-      }
+    componentDidMount() {
+        this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = 0;
+        this.props.setContentView?.(this); // this tells the DocumentView that this ScreenshotBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link.
+        // this.rootDoc.videoWall && reaction(() => ({ width: this.props.PanelWidth(), height: this.props.PanelHeight() }),
+        //     ({ width, height }) => {
+        //         if (this._camera) {
+        //             const angle = -Math.abs(1 - width / height);
+        //             const xz = [0, (this._numScreens - 2) / Math.abs(1 + angle)];
+        //             this._camera.position.set(this._numScreens / 2 + xz[1] * Math.sin(angle), this._numScreens / 2, xz[1] * Math.cos(angle));
+        //             this._camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
+        //             (this._camera as any).updateProjectionMatrix();
+        //         }
+        //     });
+    }
+    componentWillUnmount() {
+        const ind = DocUtils.ActiveRecordings.indexOf(this);
+        ind !== -1 && DocUtils.ActiveRecordings.splice(ind, 1);
+    }
 
-      @computed get content() {
-            if (this.rootDoc.videoWall) return (null);
-            return <video className={"videoBox-content"} key="video"
-                  ref={r => {
-                        this._videoRef = r;
-                        setTimeout(() => {
-                              if (this.rootDoc.mediaState === "pendingRecording" && this._videoRef) {
-                                    this.toggleRecording();
-                              }
-                        }, 100);
-                  }}
-                  autoPlay={this._screenCapture}
-                  style={{ width: this._screenCapture ? "100%" : undefined, height: this._screenCapture ? "100%" : undefined }}
-                  onCanPlay={this.videoLoad}
-                  controls={true}
-                  onClick={e => e.preventDefault()}>
-                  <source type="video/mp4" />
-                  Not supported.
-            </video>;
-      }
+    specificContextMenu = (e: React.MouseEvent): void => {
+        const subitems = [{ description: 'Screen Capture', event: this.toggleRecording, icon: 'expand-arrows-alt' as any }];
+        ContextMenu.Instance.addItem({ description: 'Options...', subitems, icon: 'video' });
+    };
 
-      // _numScreens = 5;
-      // _camera: Camera | undefined;
-      // @observable _raised = [] as { coord: Vector2, off: Vector3 }[];
-      // @action setRaised = (r: { coord: Vector2, off: Vector3 }[]) => this._raised = r;
-      @computed get threed() {
-            // if (this.rootDoc.videoWall) {
-            //     const screens: any[] = [];
-            //     const colors = ["yellow", "red", "orange", "brown", "maroon", "gray"];
-            //     let count = 0;
-            //     numberRange(this._numScreens).forEach(x => numberRange(this._numScreens).forEach(y => screens.push(
-            //         <VideoTile rootDoc={this.rootDoc} color={colors[count++ % colors.length]} x={x} y={y} raised={this._raised} setRaised={this.setRaised} />)));
-            //     return <Canvas key="canvas" id="CANCAN" style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }} gl={{ antialias: false }} colorManagement={false} onCreated={props => {
-            //         this._camera = props.camera;
-            //         props.camera.position.set(this._numScreens / 2, this._numScreens / 2, this._numScreens - 2);
-            //         props.camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
-            //     }}>
-            //         {/* <ambientLight />*/}
-            //         <pointLight position={[10, 10, 10]} intensity={1} />
-            //         {screens}
-            //     </ Canvas>;
-            // }
-            return (null);
-      }
-      toggleRecording = async () => {
-            if (!this._screenCapture) {
-                  this._audioRec = new MediaRecorder(await navigator.mediaDevices.getUserMedia({ audio: true }));
-                  const aud_chunks: any = [];
-                  this._audioRec.ondataavailable = (e: any) => aud_chunks.push(e.data);
-                  this._audioRec.onstop = async (e: any) => {
-                        const [{ result }] = await Networking.UploadFilesToServer(aud_chunks);
-                        if (!(result instanceof Error)) {
-                              this.dataDoc[this.props.fieldKey + "-audio"] = new AudioField(result.accessPaths.agnostic.client);
+    @computed get content() {
+        if (this.rootDoc.videoWall) return null;
+        return (
+            <video
+                className={'videoBox-content'}
+                key="video"
+                ref={r => {
+                    this._videoRef = r;
+                    setTimeout(() => {
+                        if (this.rootDoc.mediaState === 'pendingRecording' && this._videoRef) {
+                            this.toggleRecording();
                         }
-                  };
-                  this._videoRef!.srcObject = await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
-                  this._videoRec = new MediaRecorder(this._videoRef!.srcObject);
-                  const vid_chunks: any = [];
-                  this._videoRec.onstart = () => this.dataDoc[this.props.fieldKey + "-recordingStart"] = new DateField(new Date());
-                  this._videoRec.ondataavailable = (e: any) => vid_chunks.push(e.data);
-                  this._videoRec.onstop = async (e: any) => {
-                        console.log("screenshotbox: upload")
-                        const file = new File(vid_chunks, `${this.rootDoc[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() });
-                        const [{ result }] = await Networking.UploadFilesToServer(file);
-                        this.dataDoc[this.fieldKey + "-duration"] = (new Date().getTime() - this.recordingStart!) / 1000;
-                        if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox
-                              this.dataDoc.type = DocumentType.VID;
-                              this.layoutDoc.layout = VideoBox.LayoutString(this.fieldKey);
-                              this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = undefined;
-                              this.layoutDoc._fitWidth = undefined;
-                              this.dataDoc[this.props.fieldKey] = new VideoField(result.accessPaths.agnostic.client);
-                        } else alert("video conversion failed");
-                  };
-                  this._audioRec.start();
-                  this._videoRec.start();
-                  runInAction(() => {
-                        this._screenCapture = true;
-                        this.dataDoc.mediaState = "recording";
-                  });
-                  DocUtils.ActiveRecordings.push(this);
-            } else {
-                  this._audioRec?.stop();
-                  this._videoRec?.stop();
-                  runInAction(() => {
-                        this._screenCapture = false;
-                        this.dataDoc.mediaState = "paused";
-                  });
-                  const ind = DocUtils.ActiveRecordings.indexOf(this);
-                  ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1));
+                    }, 100);
+                }}
+                autoPlay={this._screenCapture}
+                style={{ width: this._screenCapture ? '100%' : undefined, height: this._screenCapture ? '100%' : undefined }}
+                onCanPlay={this.videoLoad}
+                controls={true}
+                onClick={e => e.preventDefault()}>
+                <source type="video/mp4" />
+                Not supported.
+            </video>
+        );
+    }
+
+    // _numScreens = 5;
+    // _camera: Camera | undefined;
+    // @observable _raised = [] as { coord: Vector2, off: Vector3 }[];
+    // @action setRaised = (r: { coord: Vector2, off: Vector3 }[]) => this._raised = r;
+    @computed get threed() {
+        // if (this.rootDoc.videoWall) {
+        //     const screens: any[] = [];
+        //     const colors = ["yellow", "red", "orange", "brown", "maroon", "gray"];
+        //     let count = 0;
+        //     numberRange(this._numScreens).forEach(x => numberRange(this._numScreens).forEach(y => screens.push(
+        //         <VideoTile rootDoc={this.rootDoc} color={colors[count++ % colors.length]} x={x} y={y} raised={this._raised} setRaised={this.setRaised} />)));
+        //     return <Canvas key="canvas" id="CANCAN" style={{ width: this.props.PanelWidth(), height: this.props.PanelHeight() }} gl={{ antialias: false }} colorManagement={false} onCreated={props => {
+        //         this._camera = props.camera;
+        //         props.camera.position.set(this._numScreens / 2, this._numScreens / 2, this._numScreens - 2);
+        //         props.camera.lookAt(this._numScreens / 2, this._numScreens / 2, 0);
+        //     }}>
+        //         {/* <ambientLight />*/}
+        //         <pointLight position={[10, 10, 10]} intensity={1} />
+        //         {screens}
+        //     </ Canvas>;
+        // }
+        return null;
+    }
+    toggleRecording = async () => {
+        if (!this._screenCapture) {
+            this._audioRec = new MediaRecorder(await navigator.mediaDevices.getUserMedia({ audio: true }));
+            const aud_chunks: any = [];
+            this._audioRec.ondataavailable = (e: any) => aud_chunks.push(e.data);
+            this._audioRec.onstop = async (e: any) => {
+                const [{ result }] = await Networking.UploadFilesToServer(aud_chunks);
+                if (!(result instanceof Error)) {
+                    this.dataDoc[this.props.fieldKey + '-audio'] = new AudioField(result.accessPaths.agnostic.client);
+                }
+            };
+            this._videoRef!.srcObject = await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
+            this._videoRec = new MediaRecorder(this._videoRef!.srcObject);
+            const vid_chunks: any = [];
+            this._videoRec.onstart = () => (this.dataDoc[this.props.fieldKey + '-recordingStart'] = new DateField(new Date()));
+            this._videoRec.ondataavailable = (e: any) => vid_chunks.push(e.data);
+            this._videoRec.onstop = async (e: any) => {
+                console.log('screenshotbox: upload');
+                const file = new File(vid_chunks, `${this.rootDoc[Id]}.mkv`, { type: vid_chunks[0].type, lastModified: Date.now() });
+                const [{ result }] = await Networking.UploadFilesToServer(file);
+                this.dataDoc[this.fieldKey + '-duration'] = (new Date().getTime() - this.recordingStart!) / 1000;
+                if (!(result instanceof Error)) {
+                    // convert this screenshotBox into normal videoBox
+                    this.dataDoc.type = DocumentType.VID;
+                    this.layoutDoc.layout = VideoBox.LayoutString(this.fieldKey);
+                    this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = undefined;
+                    this.layoutDoc._fitWidth = undefined;
+                    this.dataDoc[this.props.fieldKey] = new VideoField(result.accessPaths.agnostic.client);
+                } else alert('video conversion failed');
+            };
+            this._audioRec.start();
+            this._videoRec.start();
+            runInAction(() => {
+                this._screenCapture = true;
+                this.dataDoc.mediaState = 'recording';
+            });
+            DocUtils.ActiveRecordings.push(this);
+        } else {
+            this._audioRec?.stop();
+            this._videoRec?.stop();
+            runInAction(() => {
+                this._screenCapture = false;
+                this.dataDoc.mediaState = 'paused';
+            });
+            const ind = DocUtils.ActiveRecordings.indexOf(this);
+            ind !== -1 && DocUtils.ActiveRecordings.splice(ind, 1);
 
-                  CaptureManager.Instance.open(this.rootDoc);
-            }
-      }
+            CaptureManager.Instance.open(this.rootDoc);
+        }
+    };
 
-      setupDictation = () => {
-            if (this.dataDoc[this.fieldKey + "-dictation"]) return;
-            const dictationText = CurrentUserUtils.GetNewTextDoc("dictation",
-                  NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10,
-                  NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
-            dictationText._autoHeight = false;
-            const dictationTextProto = Doc.GetProto(dictationText);
-            dictationTextProto.recordingSource = this.dataDoc;
-            dictationTextProto.recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}-recordingStart"]`);
-            dictationTextProto.mediaState = ComputedField.MakeFunction("self.recordingSource.mediaState");
-            this.dataDoc[this.fieldKey + "-dictation"] = dictationText;
-      }
-      contentFunc = () => [this.threed, this.content];
-      videoPanelHeight = () => NumCast(this.dataDoc[this.fieldKey + "-nativeHeight"], this.layoutDoc[HeightSym]()) / NumCast(this.dataDoc[this.fieldKey + "-nativeWidth"], this.layoutDoc[WidthSym]()) * this.props.PanelWidth();
-      formattedPanelHeight = () => Math.max(0, this.props.PanelHeight() - this.videoPanelHeight());
-      render() {
-            TraceMobx();
-            return <div className="videoBox" onContextMenu={this.specificContextMenu} style={{ width: "100%", height: "100%" }} >
-                  <div className="videoBox-viewer" >
-                        <div style={{ position: "relative", height: this.videoPanelHeight() }}>
-                              <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
-                                    PanelHeight={this.videoPanelHeight}
-                                    PanelWidth={this.props.PanelWidth}
-                                    focus={this.props.focus}
-                                    isSelected={this.props.isSelected}
-                                    isAnnotationOverlay={true}
-                                    select={emptyFunction}
-                                    isContentActive={returnFalse}
-                                    scaling={returnOne}
-                                    whenChildContentsActiveChanged={emptyFunction}
-                                    removeDocument={returnFalse}
-                                    moveDocument={returnFalse}
-                                    addDocument={returnFalse}
-                                    CollectionView={undefined}
-                                    ScreenToLocalTransform={this.props.ScreenToLocalTransform}
-                                    renderDepth={this.props.renderDepth + 1}
-                                    ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
-                                    {this.contentFunc}
-                              </CollectionFreeFormView></div>
-                        <div style={{ position: "relative", height: this.formattedPanelHeight() }}>
-                              {!(this.dataDoc[this.fieldKey + "-dictation"] instanceof Doc) ? (null) :
-                                    <FormattedTextBox {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
-                                          Document={this.dataDoc[this.fieldKey + "-dictation"]}
-                                          fieldKey={"text"}
-                                          PanelHeight={this.formattedPanelHeight}
-                                          isAnnotationOverlay={true}
-                                          select={emptyFunction}
-                                          isContentActive={emptyFunction}
-                                          scaling={returnOne}
-                                          xPadding={25}
-                                          yPadding={10}
-                                          whenChildContentsActiveChanged={emptyFunction}
-                                          removeDocument={returnFalse}
-                                          moveDocument={returnFalse}
-                                          addDocument={returnFalse}
-                                          CollectionView={undefined}
-                                          renderDepth={this.props.renderDepth + 1}
-                                          ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
-                                    </FormattedTextBox>}
-                        </div>
-                  </div>
-                  {!this.props.isSelected() ? (null) : <div className="screenshotBox-uiButtons">
-                        <div className="screenshotBox-recorder" key="snap" onPointerDown={this.toggleRecording} >
-                              <FontAwesomeIcon icon="file" size="lg" />
+    setupDictation = () => {
+        if (this.dataDoc[this.fieldKey + '-dictation']) return;
+        const dictationText = DocUtils.GetNewTextDoc('dictation', NumCast(this.rootDoc.x), NumCast(this.rootDoc.y) + NumCast(this.layoutDoc._height) + 10, NumCast(this.layoutDoc._width), 2 * NumCast(this.layoutDoc._height));
+        dictationText._autoHeight = false;
+        const dictationTextProto = Doc.GetProto(dictationText);
+        dictationTextProto.recordingSource = this.dataDoc;
+        dictationTextProto.recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}-recordingStart"]`);
+        dictationTextProto.mediaState = ComputedField.MakeFunction('self.recordingSource.mediaState');
+        this.dataDoc[this.fieldKey + '-dictation'] = dictationText;
+    };
+    contentFunc = () => [this.threed, this.content];
+    videoPanelHeight = () => (NumCast(this.dataDoc[this.fieldKey + '-nativeHeight'], this.layoutDoc[HeightSym]()) / NumCast(this.dataDoc[this.fieldKey + '-nativeWidth'], this.layoutDoc[WidthSym]())) * this.props.PanelWidth();
+    formattedPanelHeight = () => Math.max(0, this.props.PanelHeight() - this.videoPanelHeight());
+    render() {
+        TraceMobx();
+        return (
+            <div className="videoBox" onContextMenu={this.specificContextMenu} style={{ width: '100%', height: '100%' }}>
+                <div className="videoBox-viewer">
+                    <div style={{ position: 'relative', height: this.videoPanelHeight() }}>
+                        <CollectionFreeFormView
+                            {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight']).omit}
+                            PanelHeight={this.videoPanelHeight}
+                            PanelWidth={this.props.PanelWidth}
+                            focus={this.props.focus}
+                            isSelected={this.props.isSelected}
+                            isAnnotationOverlay={true}
+                            select={emptyFunction}
+                            isContentActive={returnFalse}
+                            scaling={returnOne}
+                            whenChildContentsActiveChanged={emptyFunction}
+                            removeDocument={returnFalse}
+                            moveDocument={returnFalse}
+                            addDocument={returnFalse}
+                            CollectionView={undefined}
+                            ScreenToLocalTransform={this.props.ScreenToLocalTransform}
+                            renderDepth={this.props.renderDepth + 1}
+                            ContainingCollectionDoc={this.props.ContainingCollectionDoc}>
+                            {this.contentFunc}
+                        </CollectionFreeFormView>
+                    </div>
+                    <div style={{ position: 'relative', height: this.formattedPanelHeight() }}>
+                        {!(this.dataDoc[this.fieldKey + '-dictation'] instanceof Doc) ? null : (
+                            <FormattedTextBox
+                                {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight']).omit}
+                                Document={this.dataDoc[this.fieldKey + '-dictation']}
+                                fieldKey={'text'}
+                                PanelHeight={this.formattedPanelHeight}
+                                isAnnotationOverlay={true}
+                                select={emptyFunction}
+                                isContentActive={emptyFunction}
+                                scaling={returnOne}
+                                xPadding={25}
+                                yPadding={10}
+                                whenChildContentsActiveChanged={emptyFunction}
+                                removeDocument={returnFalse}
+                                moveDocument={returnFalse}
+                                addDocument={returnFalse}
+                                CollectionView={undefined}
+                                renderDepth={this.props.renderDepth + 1}
+                                ContainingCollectionDoc={this.props.ContainingCollectionDoc}></FormattedTextBox>
+                        )}
+                    </div>
+                </div>
+                {!this.props.isSelected() ? null : (
+                    <div className="screenshotBox-uiButtons">
+                        <div className="screenshotBox-recorder" key="snap" onPointerDown={this.toggleRecording}>
+                            <FontAwesomeIcon icon="file" size="lg" />
                         </div>
-                  </div>}
-            </div >;
-      }
-}
\ No newline at end of file
+                    </div>
+                )}
+            </div>
+        );
+    }
+}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index e833c7e30..b1f7d8023 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -1,44 +1,42 @@
-import React = require("react");
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, untracked } from "mobx";
-import { observer } from "mobx-react";
-import { basename } from "path";
+import React = require('react');
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, untracked } from 'mobx';
+import { observer } from 'mobx-react';
+import { basename } from 'path';
 import * as rp from 'request-promise';
-import { Doc, DocListCast, HeightSym, WidthSym } from "../../../fields/Doc";
-import { InkTool } from "../../../fields/InkField";
-import { List } from "../../../fields/List";
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { AudioField, ImageField, VideoField } from "../../../fields/URLField";
-import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from "../../../Utils";
-import { Docs, DocUtils } from "../../documents/Documents";
-import { DocumentType } from "../../documents/DocumentTypes";
-import { Networking } from "../../Network";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { DocumentManager } from "../../util/DocumentManager";
-import { SelectionManager } from "../../util/SelectionManager";
-import { SnappingManager } from "../../util/SnappingManager";
-import { undoBatch } from "../../util/UndoManager";
-import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
-import { CollectionStackedTimeline, TrimScope } from "../collections/CollectionStackedTimeline";
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
-import { DocumentDecorations } from "../DocumentDecorations";
-import { MarqueeAnnotator } from "../MarqueeAnnotator";
-import { AnchorMenu } from "../pdf/AnchorMenu";
-import { StyleProp } from "../StyleProvider";
+import { Doc, DocListCast, HeightSym, WidthSym } from '../../../fields/Doc';
+import { InkTool } from '../../../fields/InkField';
+import { List } from '../../../fields/List';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { AudioField, ImageField, VideoField } from '../../../fields/URLField';
+import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils';
+import { Docs, DocUtils } from '../../documents/Documents';
+import { DocumentType } from '../../documents/DocumentTypes';
+import { Networking } from '../../Network';
+import { DocumentManager } from '../../util/DocumentManager';
+import { ReplayMovements } from '../../util/ReplayMovements';
+import { SelectionManager } from '../../util/SelectionManager';
+import { SnappingManager } from '../../util/SnappingManager';
+import { undoBatch } from '../../util/UndoManager';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
+import { CollectionStackedTimeline, TrimScope } from '../collections/CollectionStackedTimeline';
+import { ContextMenu } from '../ContextMenu';
+import { ContextMenuProps } from '../ContextMenuItem';
+import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
+import { DocumentDecorations } from '../DocumentDecorations';
+import { MarqueeAnnotator } from '../MarqueeAnnotator';
+import { AnchorMenu } from '../pdf/AnchorMenu';
+import { StyleProp } from '../StyleProvider';
 import { FieldView, FieldViewProps } from './FieldView';
-import "./VideoBox.scss";
-import { Presentation } from "../../util/TrackMovements";
-import { RecordingBox } from "./RecordingBox";
-import { ReplayMovements } from "../../util/ReplayMovements";
+import { RecordingBox } from './RecordingBox';
+import './VideoBox.scss';
 const path = require('path');
 
 /**
  * VideoBox
  * Main component: VideoBox.tsx
  * Supporting Components: CollectionStackedTimeline
- * 
+ *
  * VideoBox is a node that supports the playback of video files in Dash.
  * When a video file or YouTube video is importeed into Dash, it is immediately rendered as a VideoBox document.
  * CollectionStackedTimline handles AudioBox and VideoBox shared behavior, but VideoBox handles playing, pausing, etc because it contains <video> element
@@ -48,7 +46,9 @@ const path = require('path');
 
 @observer
 export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(VideoBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(VideoBox, fieldKey);
+    }
     /**
      * Uploads an image buffer to the server and stores with specified filename.  by default the image
      * is stored at multiple resolutions each retrieved by using the filename appended with _o, _s, _m, _l (indicating original, small, medium, or large)
@@ -58,20 +58,19 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
      */
     public static async convertDataUri(imageUri: string, returnedFilename: string, nosuffix = false, replaceRootFilename?: string) {
         try {
-            const posting = Utils.prepend("/uploadURI");
+            const posting = Utils.prepend('/uploadURI');
             const returnedUri = await rp.post(posting, {
                 body: {
                     uri: imageUri,
                     name: returnedFilename,
                     nosuffix,
-                    replaceRootFilename
+                    replaceRootFilename,
                 },
                 json: true,
             });
             return returnedUri;
-
         } catch (e) {
-            console.log("VideoBox :" + e);
+            console.log('VideoBox :' + e);
         }
     }
 
@@ -103,27 +102,29 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     @observable _finished: boolean = false; // has playback reached end of clip
     @observable _volume: number = 1;
     @observable _muted: boolean = false;
-    @observable _controlsTransform?: { X: number, Y: number };
+    @observable _controlsTransform?: { X: number; Y: number };
     @observable _controlsVisible: boolean = true;
     @observable _scrubbing: boolean = false;
 
-    @computed get links() { return DocListCast(this.dataDoc.links); }
-    @computed get heightPercent() { return NumCast(this.layoutDoc._timelineHeightPercent, 100); } // current percent of video relative to VideoBox height
+    @computed get links() {
+        return DocListCast(this.dataDoc.links);
+    }
+    @computed get heightPercent() {
+        return NumCast(this.layoutDoc._timelineHeightPercent, 100);
+    } // current percent of video relative to VideoBox height
     // @computed get rawDuration() { return NumCast(this.dataDoc[this.fieldKey + "-duration"]); }
     @observable rawDuration: number = 0;
 
-
     @computed get youtubeVideoId() {
         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("/")) : "";
+        return field && field.url.href.indexOf('youtube') !== -1 ? ((arr: string[]) => arr[arr.length - 1])(field.url.href.split('/')) : '';
     }
 
-
     // returns the path of the audio file
     @computed get audiopath() {
         const field = Cast(this.props.Document[this.props.fieldKey + '-audio'], AudioField, null);
         const vfield = Cast(this.dataDoc[this.fieldKey], VideoField, null);
-        return field?.url.href ?? vfield?.url.href ?? "";
+        return field?.url.href ?? vfield?.url.href ?? '';
     }
 
     // returns the presentation data if it exists, null otherwise
@@ -132,10 +133,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         return data ? JSON.parse(data) : null;
     }
 
-    @computed private get timeline() { return this._stackedTimeline; }
-    private get transition() { return this._clicking ? "left 0.5s, width 0.5s, height 0.5s" : ""; } // css transition for hiding/showing timeline
-    public get player(): HTMLVideoElement | null { return this._videoRef; }
-
+    @computed private get timeline() {
+        return this._stackedTimeline;
+    }
+    private get transition() {
+        return this._clicking ? 'left 0.5s, width 0.5s, height 0.5s' : '';
+    } // css transition for hiding/showing timeline
+    public get player(): HTMLVideoElement | null {
+        return this._videoRef;
+    }
 
     componentDidMount() {
         this.props.setContentView?.(this); // this tells the DocumentView that this VideoBox is the "content" of the document.  this allows the DocumentView to indirectly call getAnchor() on the VideoBox when making a link.
@@ -150,7 +156,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             }
         }
         this.player && this.setPlayheadTime(0);
-        document.addEventListener("keydown", this.keyEvents, true);
+        document.addEventListener('keydown', this.keyEvents, true);
 
         if (this.presentation) {
             ReplayMovements.Instance.setVideoBox(this);
@@ -161,9 +167,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this.removeCurrentlyPlaying();
         this.Pause();
         Object.keys(this._disposers).forEach(d => this._disposers[d]?.());
-        document.removeEventListener("keydown", this.keyEvents, true);
+        document.removeEventListener('keydown', this.keyEvents, true);
 
-         if (this.presentation) {
+        if (this.presentation) {
             ReplayMovements.Instance.removeVideoBox();
         }
     }
@@ -173,20 +179,23 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     keyEvents = (e: KeyboardEvent) => {
         if (
             // need to include range inputs because after dragging time slider it becomes target element
-            !(e.target instanceof HTMLInputElement && !(e.target.type === "range")) &&
+            !(e.target instanceof HTMLInputElement && !(e.target.type === 'range')) &&
             this.props.isSelected(true)
         ) {
             switch (e.key) {
-                case "ArrowLeft":
-                case "ArrowRight":
+                case 'ArrowLeft':
+                case 'ArrowRight':
                     clearTimeout(this._controlsFadeTimer);
                     this._scrubbing = true;
-                    this._controlsFadeTimer = setTimeout(action(() => this._scrubbing = false), 500);
+                    this._controlsFadeTimer = setTimeout(
+                        action(() => (this._scrubbing = false)),
+                        500
+                    );
                     e.stopPropagation();
                     break;
             }
         }
-    }
+    };
 
     // plays video
     @action public Play = (update: boolean = true) => {
@@ -210,18 +219,18 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 update && this._youtubePlayer?.playVideo();
                 this._youtubePlayer && !this._playTimer && (this._playTimer = setInterval(this.updateTimecode, 5));
             } catch (e) {
-                console.log("Video Play Exception:", e);
+                console.log('Video Play Exception:', e);
             }
         }
         this.updateTimecode();
-    }
+    };
 
     // goes to time
     @action public Seek(time: number) {
         try {
             this._youtubePlayer?.seekTo(Math.round(time), true);
         } catch (e) {
-            console.log("Video Seek Exception:", e);
+            console.log('Video Seek Exception:', e);
         }
         this.player && (this.player.currentTime = time);
         this._audioPlayer && (this._audioPlayer.currentTime = time);
@@ -242,7 +251,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             this._youtubePlayer && this._playTimer && clearInterval(this._playTimer);
             this._youtubePlayer?.seekTo(this._youtubePlayer?.getCurrentTime(), true);
         } catch (e) {
-            console.log("Video Pause Exception:", e);
+            console.log('Video Pause Exception:', e);
         }
         this._youtubePlayer && SelectionManager.DeselectAll(); // if we don't deselect the player, then we get an annoying YouTube spinner I guess telling us we're paused.
         this._playTimer = undefined;
@@ -251,24 +260,23 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             clearTimeout(this._playRegionTimer); // if paused in the middle of playback, prevents restart on next play
         }
         this._playRegionTimer = undefined;
-    }
+    };
 
     // toggles video full screen
     @action public FullScreen = () => {
         if (document.fullscreenElement === this._contentRef) {
             this._fullScreen = false;
             this.player && this._contentRef && document.exitFullscreen();
-        }
-        else {
+        } else {
             this._fullScreen = true;
             this.player && this._contentRef && this._contentRef.requestFullscreen();
         }
         try {
-            this._youtubePlayer && this.props.addDocTab(this.rootDoc, "add");
+            this._youtubePlayer && this.props.addDocTab(this.rootDoc, 'add');
         } catch (e) {
-            console.log("Video FullScreen Exception:", e);
+            console.log('Video FullScreen Exception:', e);
         }
-    }
+    };
 
     // fades out controls in fullscreen after mouse stops moving
     @action controlsFade = (e: PointerEvent) => {
@@ -276,10 +284,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         if (!this._scrubbing) {
             clearTimeout(this._controlsFadeTimer);
             this._controlsVisible = true;
-            this._controlsFadeTimer = setTimeout(action(() => this._controlsVisible = false), 3000);
+            this._controlsFadeTimer = setTimeout(
+                action(() => (this._controlsVisible = false)),
+                3000
+            );
         }
-    }
-
+    };
 
     // drag controls around window in fulls screen
     @action controlsDrag = (e: React.PointerEvent) => {
@@ -288,7 +298,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         const eleStyle = getComputedStyle(e.target as Element);
         this._controlsTransform = { X: parseInt(eleStyle.left), Y: parseInt(eleStyle.top) };
 
-        setupMoveUpEvents(e.target,
+        setupMoveUpEvents(
+            e.target,
             e,
             action((e, down, delta) => {
                 if (this._controlsTransform) {
@@ -298,32 +309,35 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 return false;
             }),
             emptyFunction,
-            emptyFunction)
-    }
-
+            emptyFunction
+        );
+    };
 
     // creates and links snapshot photo of current video frame
     @action public Snapshot = (downX?: number, downY?: number, cb?: (filename: string, x: number | undefined, y: number | undefined) => void) => {
         const width = NumCast(this.layoutDoc._width);
         const canvas = document.createElement('canvas');
         canvas.width = 640;
-        canvas.height = 640 * Doc.NativeHeight(this.layoutDoc) / (Doc.NativeWidth(this.layoutDoc) || 1);
-        const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
+        canvas.height = (640 * Doc.NativeHeight(this.layoutDoc)) / (Doc.NativeWidth(this.layoutDoc) || 1);
+        const ctx = canvas.getContext('2d'); //draw image to canvas. scale to target dimensions
         if (ctx) {
             this._videoRef && ctx.drawImage(this._videoRef, 0, 0, canvas.width, canvas.height);
         }
 
         if (!this._videoRef) {
             const b = Docs.Create.LabelDocument({
-                x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y, 1),
-                _width: 150, _height: 50, title: (this.layoutDoc._currentTimecode || 0).toString(),
-                _isLinkButton: true
+                x: NumCast(this.layoutDoc.x) + width,
+                y: NumCast(this.layoutDoc.y, 1),
+                _width: 150,
+                _height: 50,
+                title: (this.layoutDoc._currentTimecode || 0).toString(),
+                _isLinkButton: true,
             });
             this.props.addDocument?.(b);
-            DocUtils.MakeLink({ doc: b }, { doc: this.rootDoc }, "video snapshot");
-            Networking.PostToServer("/youtubeScreenshot", {
+            DocUtils.MakeLink({ doc: b }, { doc: this.rootDoc }, 'video snapshot');
+            Networking.PostToServer('/youtubeScreenshot', {
                 id: this.youtubeVideoId,
-                timecode: this.layoutDoc._currentTimecode
+                timecode: this.layoutDoc._currentTimecode,
             }).then(response => {
                 const resolved = response?.accessPaths?.agnostic?.client;
                 if (resolved) {
@@ -335,49 +349,50 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             //convert to desired file format
             const dataUrl = canvas.toDataURL('image/png'); // can also use 'image/png'
             // if you want to preview the captured image,
-            const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, "");
-            const encodedFilename = encodeURIComponent("snapshot" + retitled + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_"));
+            const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, '');
+            const encodedFilename = encodeURIComponent('snapshot' + retitled + '_' + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, '_'));
             const filename = basename(encodedFilename);
-            VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) =>
-                returnedFilename && (cb ?? this.createRealSummaryLink)(returnedFilename, downX, downY));
+            VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) => returnedFilename && (cb ?? this.createRealSummaryLink)(returnedFilename, downX, downY));
         }
-    }
+    };
 
     updateIcon = () => {
         const makeIcon = (returnedfilename: string) => {
             this.dataDoc.icon = new ImageField(returnedfilename);
-            this.dataDoc["icon-nativeWidth"] = this.layoutDoc[WidthSym]();
-            this.dataDoc["icon-nativeHeight"] = this.layoutDoc[HeightSym]();
+            this.dataDoc['icon-nativeWidth'] = this.layoutDoc[WidthSym]();
+            this.dataDoc['icon-nativeHeight'] = this.layoutDoc[HeightSym]();
         };
         this.Snapshot(undefined, undefined, makeIcon);
-    }
+    };
 
     // creates link for snapshot
     createRealSummaryLink = (imagePath: string, downX?: number, downY?: number) => {
-        const url = !imagePath.startsWith("/") ? Utils.CorsProxy(imagePath) : imagePath;
+        const url = !imagePath.startsWith('/') ? Utils.CorsProxy(imagePath) : imagePath;
         const width = NumCast(this.layoutDoc._width) || 1;
         const height = NumCast(this.layoutDoc._height);
         const imageSummary = Docs.Create.ImageDocument(url, {
-            _nativeWidth: Doc.NativeWidth(this.layoutDoc), _nativeHeight: Doc.NativeHeight(this.layoutDoc),
-            x: NumCast(this.layoutDoc.x) + width, y: NumCast(this.layoutDoc.y), _isLinkButton: true,
-            _width: 150, _height: height / width * 150, title: "--snapshot" + NumCast(this.layoutDoc._currentTimecode) + " image-"
+            _nativeWidth: Doc.NativeWidth(this.layoutDoc),
+            _nativeHeight: Doc.NativeHeight(this.layoutDoc),
+            x: NumCast(this.layoutDoc.x) + width,
+            y: NumCast(this.layoutDoc.y),
+            _isLinkButton: true,
+            _width: 150,
+            _height: (height / width) * 150,
+            title: '--snapshot' + NumCast(this.layoutDoc._currentTimecode) + ' image-',
         });
         Doc.SetNativeWidth(Doc.GetProto(imageSummary), Doc.NativeWidth(this.layoutDoc));
         Doc.SetNativeHeight(Doc.GetProto(imageSummary), Doc.NativeHeight(this.layoutDoc));
         this.props.addDocument?.(imageSummary);
-        const link = DocUtils.MakeLink({ doc: imageSummary }, { doc: this.getAnchor() }, "video snapshot");
+        const link = DocUtils.MakeLink({ doc: imageSummary }, { doc: this.getAnchor() }, 'video snapshot');
         link && (Doc.GetProto(link.anchor2 as Doc).timecodeToHide = NumCast((link.anchor2 as Doc).timecodeToShow) + 3);
-        setTimeout(() =>
-            (downX !== undefined && downY !== undefined) && DocumentManager.Instance.getFirstDocumentView(imageSummary)?.startDragging(downX, downY, "move", true));
-    }
-
+        setTimeout(() => downX !== undefined && downY !== undefined && DocumentManager.Instance.getFirstDocumentView(imageSummary)?.startDragging(downX, downY, 'move', true));
+    };
 
     getAnchor = () => {
-        const timecode = Cast(this.layoutDoc._currentTimecode, "number", null);
+        const timecode = Cast(this.layoutDoc._currentTimecode, 'number', null);
         const marquee = AnchorMenu.Instance.GetAnchor?.();
-        return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow"/* videoStart */, "_timecodeToHide" /* videoEnd */, timecode ? timecode : undefined, undefined, marquee) || this.rootDoc;
-    }
-
+        return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, '_timecodeToShow' /* videoStart */, '_timecodeToHide' /* videoEnd */, timecode ? timecode : undefined, undefined, marquee) || this.rootDoc;
+    };
 
     // sets video info on load
     videoLoad = action(() => {
@@ -387,10 +402,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this.layoutDoc._height = NumCast(this.layoutDoc._width) / aspect;
         if (Number.isFinite(this.player!.duration)) {
             this.rawDuration = this.player!.duration;
-        } else this.rawDuration = NumCast(this.dataDoc[this.fieldKey + "-duration"]);
+        } else this.rawDuration = NumCast(this.dataDoc[this.fieldKey + '-duration']);
     });
 
-
     // updates video time
     @action
     updateTimecode = () => {
@@ -398,10 +412,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         try {
             this._youtubePlayer && (this.layoutDoc._currentTimecode = this._youtubePlayer.getCurrentTime?.());
         } catch (e) {
-            console.log("Video Timecode Exception:", e);
+            console.log('Video Timecode Exception:', e);
         }
-    }
-
+    };
 
     // extracts video thumbnails and saves them as field of doc
     getVideoThumbnails = () => {
@@ -419,23 +432,23 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             const ctx = canvas.getContext('2d');
             ctx?.drawImage(video, 0, 0, canvas.width, canvas.height);
             const imgUrl = canvas.toDataURL();
-            const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, "");
-            const encodedFilename = encodeURIComponent("thumbnail" + retitled + "_" + video.currentTime.toString().replace(/\./, "_"));
+            const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, '');
+            const encodedFilename = encodeURIComponent('thumbnail' + retitled + '_' + video.currentTime.toString().replace(/\./, '_'));
             const filename = basename(encodedFilename);
             thumbnailPromises.push(VideoBox.convertDataUri(imgUrl, filename));
             const newTime = video.currentTime + video.duration / (VideoBox.numThumbnails - 1);
             if (newTime < video.duration) {
                 video.currentTime = newTime;
+            } else {
+                Promise.all(thumbnailPromises).then(thumbnails => {
+                    this.dataDoc.thumbnails = new List<string>(thumbnails);
+                });
             }
-            else {
-                Promise.all(thumbnailPromises).then(thumbnails => { this.dataDoc.thumbnails = new List<string>(thumbnails); });
-            }
-        }
+        };
 
         const field = Cast(this.dataDoc[this.fieldKey], VideoField);
         field && (video.src = field.url.href);
-    }
-
+    };
 
     // sets video element ref
     @action
@@ -446,33 +459,34 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             // @ts-ignore
             // vref.onfullscreenchange = action((e) => this._fullScreen = vref.webkitDisplayingFullscreen);
             this._disposers.reactionDisposer?.();
-            this._disposers.reactionDisposer = reaction(() => NumCast(this.layoutDoc._currentTimecode),
-                time => !this._playing && (vref.currentTime = time), { fireImmediately: true });
+            this._disposers.reactionDisposer = reaction(
+                () => NumCast(this.layoutDoc._currentTimecode),
+                time => !this._playing && (vref.currentTime = time),
+                { fireImmediately: true }
+            );
 
             (!this.dataDoc.thumbnails || this.dataDoc.thumbnails.length != VideoBox.numThumbnails) && this.getVideoThumbnails();
         }
-    }
+    };
 
     // set ref for div that wraps video and controls for fullscreen
     @action
     setContentRef = (cref: HTMLDivElement | null) => {
         this._contentRef = cref;
         if (cref) {
-            cref.onfullscreenchange = action((e) => {
-                this._fullScreen = (document.fullscreenElement === cref);
+            cref.onfullscreenchange = action(e => {
+                this._fullScreen = document.fullscreenElement === cref;
                 this._controlsVisible = true;
                 this._scrubbing = false;
                 clearTimeout(this._controlsFadeTimer);
                 if (this._fullScreen) {
                     document.addEventListener('pointermove', this.controlsFade);
-                }
-                else {
+                } else {
                     document.removeEventListener('pointermove', this.controlsFade);
                 }
             });
         }
-    }
-
+    };
 
     // context menu
     specificContextMenu = (e: React.MouseEvent): void => {
@@ -480,143 +494,185 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         if (field) {
             const url = field.url.href;
             const subitems: ContextMenuProps[] = [];
-            subitems.push({ description: "Full Screen", event: this.FullScreen, icon: "expand" });
-            subitems.push({ description: "Take Snapshot", event: this.Snapshot, icon: "expand-arrows-alt" });
-            this.rootDoc.type === DocumentType.SCREENSHOT && subitems.push({
-                description: "Screen Capture", event: (async () => {
-                    runInAction(() => this._screenCapture = !this._screenCapture);
-                    this._videoRef!.srcObject = !this._screenCapture ? undefined : await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
-                }), icon: "expand-arrows-alt"
+            subitems.push({ description: 'Full Screen', event: this.FullScreen, icon: 'expand' });
+            subitems.push({ description: 'Take Snapshot', event: this.Snapshot, icon: 'expand-arrows-alt' });
+            this.rootDoc.type === DocumentType.SCREENSHOT &&
+                subitems.push({
+                    description: 'Screen Capture',
+                    event: async () => {
+                        runInAction(() => (this._screenCapture = !this._screenCapture));
+                        this._videoRef!.srcObject = !this._screenCapture ? undefined : await (navigator.mediaDevices as any).getDisplayMedia({ video: true });
+                    },
+                    icon: 'expand-arrows-alt',
+                });
+            subitems.push({ description: (this.layoutDoc.dontAutoFollowLinks ? '' : "Don't") + ' follow links when encountered', event: () => (this.layoutDoc.dontAutoFollowLinks = !this.layoutDoc.dontAutoFollowLinks), icon: 'expand-arrows-alt' });
+            subitems.push({
+                description: (this.layoutDoc.dontAutoPlayFollowedLinks ? '' : "Don't") + ' play when link is selected',
+                event: () => (this.layoutDoc.dontAutoPlayFollowedLinks = !this.layoutDoc.dontAutoPlayFollowedLinks),
+                icon: 'expand-arrows-alt',
             });
-            subitems.push({ description: (this.layoutDoc.dontAutoFollowLinks ? "" : "Don't") + " follow links when encountered", event: () => this.layoutDoc.dontAutoFollowLinks = !this.layoutDoc.dontAutoFollowLinks, icon: "expand-arrows-alt" });
-            subitems.push({ description: (this.layoutDoc.dontAutoPlayFollowedLinks ? "" : "Don't") + " play when link is selected", event: () => this.layoutDoc.dontAutoPlayFollowedLinks = !this.layoutDoc.dontAutoPlayFollowedLinks, icon: "expand-arrows-alt" });
-            subitems.push({ description: (this.layoutDoc.autoPlayAnchors ? "Don't auto play" : "Auto play") + " anchors onClick", event: () => this.layoutDoc.autoPlayAnchors = !this.layoutDoc.autoPlayAnchors, icon: "expand-arrows-alt" });
+            subitems.push({ description: (this.layoutDoc.autoPlayAnchors ? "Don't auto play" : 'Auto play') + ' anchors onClick', event: () => (this.layoutDoc.autoPlayAnchors = !this.layoutDoc.autoPlayAnchors), icon: 'expand-arrows-alt' });
             // subitems.push({ description: "Toggle Native Controls", event: action(() => VideoBox._nativeControls = !VideoBox._nativeControls), icon: "expand-arrows-alt" });
             // subitems.push({ description: "Start Trim All", event: () => this.startTrim(TrimScope.All), icon: "expand-arrows-alt" });
             // subitems.push({ description: "Start Trim Clip", event: () => this.startTrim(TrimScope.Clip), icon: "expand-arrows-alt" });
             // subitems.push({ description: "Stop Trim", event: () => this.finishTrim(), icon: "expand-arrows-alt" });
-            subitems.push({ description: "Copy path", event: () => { Utils.CopyText(url); }, icon: "expand-arrows-alt" });
+            subitems.push({
+                description: 'Copy path',
+                event: () => {
+                    Utils.CopyText(url);
+                },
+                icon: 'expand-arrows-alt',
+            });
             // if the videobox was turned from a recording box
-            if (this.dataDoc[this.fieldKey + "-recorded"] === true) {
+            if (this.dataDoc[this.fieldKey + '-recorded'] === true) {
                 subitems.push({
-                      description: "Recreate recording", event: () => {
-                            this.dataDoc.layout = RecordingBox.LayoutString(this.fieldKey);
-                            // delete assoicated video data
-                            this.dataDoc[this.props.fieldKey] = "";
-                            this.dataDoc[this.fieldKey + "-duration"] = "";
-                            // delete assoicated presentation data
-                            this.dataDoc[this.fieldKey + "-presentation"] = "";
-                      }, icon: "expand-arrows-alt"
+                    description: 'Recreate recording',
+                    event: () => {
+                        this.dataDoc.layout = RecordingBox.LayoutString(this.fieldKey);
+                        // delete assoicated video data
+                        this.dataDoc[this.props.fieldKey] = '';
+                        this.dataDoc[this.fieldKey + '-duration'] = '';
+                        // delete assoicated presentation data
+                        this.dataDoc[this.fieldKey + '-presentation'] = '';
+                    },
+                    icon: 'expand-arrows-alt',
                 });
             }
-            ContextMenu.Instance.addItem({ description: "Options...", subitems: subitems, icon: "video" });
+            ContextMenu.Instance.addItem({ description: 'Options...', subitems: subitems, icon: 'video' });
         }
-    }
-
+    };
 
     // ref for updating time
-    setAudioRef = (e: HTMLAudioElement | null) => this._audioPlayer = e;
+    setAudioRef = (e: HTMLAudioElement | null) => (this._audioPlayer = e);
 
     // renders the video and audio
     @computed get content() {
         const field = Cast(this.dataDoc[this.fieldKey], VideoField);
-        const interactive = CurrentUserUtils.ActiveTool !== InkTool.None || !this.props.isSelected() ? "" : "-interactive";
-        const classname = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
-        const opacity = this._scrubbing ? 0.3 : (this._controlsVisible ? 1 : 0);
-        return !field ? <div key="loading">Loading</div> :
-            <div className="videoBox-contentContainer" key="container" style={{ mixBlendMode: "multiply", cursor: this._fullScreen && !this._controlsVisible ? 'none' : 'pointer' }}>
-                <div className={classname} ref={this.setContentRef} onPointerDown={(e) => this._fullScreen && e.stopPropagation()}>
-                    {this._fullScreen && <div className="videoBox-ui" onPointerDown={this.controlsDrag}
-                        style={{ left: this._controlsTransform && this._controlsTransform.X, top: this._controlsTransform && this._controlsTransform.Y, visibility: this._controlsVisible || this._scrubbing ? 'visible' : 'hidden', opacity: opacity }}>
-                        {this.UIButtons}
-                    </div>}
-                    <video key="video" autoPlay={this._screenCapture} ref={this.setVideoRef} style={this._fullScreen ? this.fullScreenSize() : {}}
+        const interactive = Doc.ActiveTool !== InkTool.None || !this.props.isSelected() ? '' : '-interactive';
+        const classname = 'videoBox-content' + (this._fullScreen ? '-fullScreen' : '') + interactive;
+        const opacity = this._scrubbing ? 0.3 : this._controlsVisible ? 1 : 0;
+        return !field ? (
+            <div key="loading">Loading</div>
+        ) : (
+            <div className="videoBox-contentContainer" key="container" style={{ mixBlendMode: 'multiply', cursor: this._fullScreen && !this._controlsVisible ? 'none' : 'pointer' }}>
+                <div className={classname} ref={this.setContentRef} onPointerDown={e => this._fullScreen && e.stopPropagation()}>
+                    {this._fullScreen && (
+                        <div
+                            className="videoBox-ui"
+                            onPointerDown={this.controlsDrag}
+                            style={{
+                                left: this._controlsTransform && this._controlsTransform.X,
+                                top: this._controlsTransform && this._controlsTransform.Y,
+                                visibility: this._controlsVisible || this._scrubbing ? 'visible' : 'hidden',
+                                opacity: opacity,
+                            }}>
+                            {this.UIButtons}
+                        </div>
+                    )}
+                    <video
+                        key="video"
+                        autoPlay={this._screenCapture}
+                        ref={this.setVideoRef}
+                        style={this._fullScreen ? this.fullScreenSize() : {}}
                         onCanPlay={this.videoLoad}
                         controls={VideoBox._nativeControls}
                         onPlay={() => this.Play()}
                         onSeeked={this.updateTimecode}
                         onPause={() => this.Pause()}
-                        onClick={this._fullScreen ? () => this.playing() ? this.Pause() : this.Play() : e => e.preventDefault()}>
+                        onClick={this._fullScreen ? () => (this.playing() ? this.Pause() : this.Play()) : e => e.preventDefault()}>
                         <source src={field.url.href} type="video/mp4" />
                         Not supported.
                     </video>
-                    {!this.audiopath || this.audiopath === field.url.href ? (null) :
-                        <audio ref={this.setAudioRef} className={`audiobox-control${this.props.isContentActive() ? "-interactive" : ""}`}>
+                    {!this.audiopath || this.audiopath === field.url.href ? null : (
+                        <audio ref={this.setAudioRef} className={`audiobox-control${this.props.isContentActive() ? '-interactive' : ''}`}>
                             <source src={this.audiopath} type="audio/mpeg" />
                             Not supported.
-                        </audio>}
+                        </audio>
+                    )}
                 </div>
-            </div>;
+            </div>
+        );
     }
 
-
     @action youtubeIframeLoaded = (e: any) => {
         if (!this._youtubeContentCreated) {
             this._forceCreateYouTubeIFrame = !this._forceCreateYouTubeIFrame;
             return;
-        }
-        else this._youtubeContentCreated = false;
+        } else this._youtubeContentCreated = false;
 
         this.loadYouTube(e.target);
-    }
+    };
 
     loadYouTube = (iframe: any) => {
         let started = true;
-        const onYoutubePlayerStateChange = (event: any) => runInAction(() => {
-            if (started && event.data === YT.PlayerState.PLAYING) {
-                started = false;
-                this._youtubePlayer?.unMute();
-                //this.Pause();
-                return;
-            }
-            if (event.data === YT.PlayerState.PLAYING && !this._playing) this.Play(false);
-            if (event.data === YT.PlayerState.PAUSED && this._playing) this.Pause(false);
-        });
+        const onYoutubePlayerStateChange = (event: any) =>
+            runInAction(() => {
+                if (started && event.data === YT.PlayerState.PLAYING) {
+                    started = false;
+                    this._youtubePlayer?.unMute();
+                    //this.Pause();
+                    return;
+                }
+                if (event.data === YT.PlayerState.PLAYING && !this._playing) this.Play(false);
+                if (event.data === YT.PlayerState.PAUSED && this._playing) this.Pause(false);
+            });
         const onYoutubePlayerReady = (event: any) => {
             this._disposers.reactionDisposer?.();
             this._disposers.youtubeReactionDisposer?.();
-            this._disposers.reactionDisposer = reaction(() => this.layoutDoc._currentTimecode, () => !this._playing && this.Seek(NumCast(this.layoutDoc._currentTimecode)));
+            this._disposers.reactionDisposer = reaction(
+                () => this.layoutDoc._currentTimecode,
+                () => !this._playing && this.Seek(NumCast(this.layoutDoc._currentTimecode))
+            );
             this._disposers.youtubeReactionDisposer = reaction(
-                () => CurrentUserUtils.ActiveTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
-                (interactive) => iframe.style.pointerEvents = interactive ? "all" : "none", { fireImmediately: true });
+                () => Doc.ActiveTool === InkTool.None && this.props.isSelected(true) && !SnappingManager.GetIsDragging() && !DocumentDecorations.Instance.Interacting,
+                interactive => (iframe.style.pointerEvents = interactive ? 'all' : 'none'),
+                { fireImmediately: true }
+            );
         };
-        if (typeof (YT) === undefined) setTimeout(() => this.loadYouTube(iframe), 100);
+        if (typeof YT === undefined) setTimeout(() => this.loadYouTube(iframe), 100);
         else {
             (YT as any)?.ready(() => {
                 this._youtubePlayer = new YT.Player(`${this.youtubeVideoId + this._youtubeIframeId}-player`, {
                     events: {
-                        'onReady': this.props.dontRegisterView ? undefined : onYoutubePlayerReady,
-                        'onStateChange': this.props.dontRegisterView ? undefined : onYoutubePlayerStateChange,
-                    }
+                        onReady: this.props.dontRegisterView ? undefined : onYoutubePlayerReady,
+                        onStateChange: this.props.dontRegisterView ? undefined : onYoutubePlayerStateChange,
+                    },
                 });
             });
         }
-    }
-
+    };
 
     // for play button
-    onPlayDown = () => this._playing ? this.Pause() : this.Play();
+    onPlayDown = () => (this._playing ? this.Pause() : this.Play());
 
     // for fullscreen button
     onFullDown = (e: React.PointerEvent) => {
         this.FullScreen();
         e.stopPropagation();
         e.preventDefault();
-    }
+    };
 
     // for snapshot button
     onSnapshotDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, (e) => {
-            this.Snapshot(e.clientX, e.clientY);
-            return true;
-        }, emptyFunction, () => this.Snapshot());
-    }
+        setupMoveUpEvents(
+            this,
+            e,
+            e => {
+                this.Snapshot(e.clientX, e.clientY);
+                return true;
+            },
+            emptyFunction,
+            () => this.Snapshot()
+        );
+    };
 
     // for show/hide timeline button, transitions between show/hide
     @action
     onTimelineHdlDown = (e: React.PointerEvent) => {
         this._clicking = true;
-        setupMoveUpEvents(this, e,
+        setupMoveUpEvents(
+            this,
+            e,
             action(encodeURIComponent => {
                 this._clicking = false;
                 if (this.props.isContentActive()) {
@@ -626,13 +682,19 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                     this.layoutDoc._timelineHeightPercent = 80;
                 }
                 return false;
-            }), emptyFunction,
+            }),
+            emptyFunction,
             () => {
                 this.layoutDoc._timelineHeightPercent = this.heightPercent !== 100 ? 100 : VideoBox.heightPercent;
-                setTimeout(action(() => this._clicking = false), 500);
-            }, this.props.isContentActive(), this.props.isContentActive());
-    }
-
+                setTimeout(
+                    action(() => (this._clicking = false)),
+                    500
+                );
+            },
+            this.props.isContentActive(),
+            this.props.isContentActive()
+        );
+    };
 
     // removes video from currently playing display
     @action
@@ -641,7 +703,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc);
             index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1);
         }
-    }
+    };
 
     // adds video to currently playing display
     @action
@@ -652,31 +714,36 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         if (CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc) === -1) {
             CollectionStackedTimeline.CurrentlyPlaying.push(this.layoutDoc);
         }
-    }
-
+    };
 
     @computed get youtubeContent() {
         this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
         this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
-        const classname = "videoBox-content-YouTube" + (this._fullScreen ? "-fullScreen" : "");
+        const classname = 'videoBox-content-YouTube' + (this._fullScreen ? '-fullScreen' : '');
         const start = untracked(() => Math.round(NumCast(this.layoutDoc._currentTimecode)));
-        return <iframe key={this._youtubeIframeId} id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
-            onPointerLeave={this.updateTimecode}
-            onLoad={this.youtubeIframeLoaded} className={classname} width={Doc.NativeWidth(this.layoutDoc) || 640} height={Doc.NativeHeight(this.layoutDoc) || 390}
-            src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=0&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._nativeControls ? 1 : 0}`} />;
+        return (
+            <iframe
+                key={this._youtubeIframeId}
+                id={`${this.youtubeVideoId + this._youtubeIframeId}-player`}
+                onPointerLeave={this.updateTimecode}
+                onLoad={this.youtubeIframeLoaded}
+                className={classname}
+                width={Doc.NativeWidth(this.layoutDoc) || 640}
+                height={Doc.NativeHeight(this.layoutDoc) || 390}
+                src={`https://www.youtube.com/embed/${this.youtubeVideoId}?enablejsapi=1&rel=0&showinfo=1&autoplay=0&mute=1&start=${start}&modestbranding=1&controls=${VideoBox._nativeControls ? 1 : 0}`}
+            />
+        );
     }
 
-
     // for annotating, adds doc with time info
     @action.bound
     addDocWithTimecode(doc: Doc | Doc[]): boolean {
         const docs = doc instanceof Doc ? [doc] : doc;
         const curTime = NumCast(this.layoutDoc._currentTimecode);
-        docs.forEach(doc => doc._timecodeToHide = (doc._timecodeToShow = curTime) + 1);
+        docs.forEach(doc => (doc._timecodeToHide = (doc._timecodeToShow = curTime) + 1));
         return this.addDocument(doc);
     }
 
-
     // play back the audio from seekTimeInSeconds, fullPlay tells whether clip is being played to end vs link range
     @action
     playFrom = (seekTimeInSeconds: number, endTime?: number, fullPlay: boolean = false) => {
@@ -684,8 +751,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         this._playRegionTimer = undefined;
         if (Number.isNaN(this.player?.duration)) {
             setTimeout(() => this.playFrom(seekTimeInSeconds, endTime), 500);
-        }
-        else if (this.player) {
+        } else if (this.player) {
             // trimBounds override requested playback bounds
             const end = Math.min(this.timeline?.trimEnd ?? this.rawDuration, endTime ?? this.timeline?.trimEnd ?? this.rawDuration);
             const start = Math.max(this.timeline?.trimStart ?? 0, seekTimeInSeconds);
@@ -698,20 +764,18 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 this._audioPlayer?.play();
                 this._playing = true;
                 this.addCurrentlyPlaying();
-                this._playRegionTimer = setTimeout(
-                    () => {
-                        // need to keep track of if end of clip is reached so on next play, clip restarts
-                        if (fullPlay) this._finished = true;
-                        // removes from currently playing if playback has reached end of range marker
-                        else this.removeCurrentlyPlaying();
-                        this.Pause();
-                    }, playRegionDuration * 1000);
+                this._playRegionTimer = setTimeout(() => {
+                    // need to keep track of if end of clip is reached so on next play, clip restarts
+                    if (fullPlay) this._finished = true;
+                    // removes from currently playing if playback has reached end of range marker
+                    else this.removeCurrentlyPlaying();
+                    this.Pause();
+                }, playRegionDuration * 1000);
             } else {
                 this.Pause();
             }
         }
-    }
-
+    };
 
     // ends trim, hides trim controls and displays new clip
     @undoBatch
@@ -725,22 +789,28 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     startTrim = (scope: TrimScope) => {
         this.Pause();
         this.timeline?.StartTrimming(scope);
-    }
+    };
 
     // for trim button, double click displays full clip, single displays curr trim bounds
     onClipPointerDown = (e: React.PointerEvent) => {
         // if timeline isn't shown, show first then trim
         this.heightPercent >= 100 && this.onTimelineHdlDown(e);
-        this.timeline && setupMoveUpEvents(this, e, returnFalse, returnFalse, action((e: PointerEvent, doubleTap?: boolean) => {
-            if (doubleTap) {
-                this.startTrim(TrimScope.All);
-            } else if (this.timeline) {
-                this.Pause();
-                this.timeline.IsTrimming !== TrimScope.None ? this.finishTrim() : this.startTrim(TrimScope.Clip);
-            }
-        }));
-    }
-
+        this.timeline &&
+            setupMoveUpEvents(
+                this,
+                e,
+                returnFalse,
+                returnFalse,
+                action((e: PointerEvent, doubleTap?: boolean) => {
+                    if (doubleTap) {
+                        this.startTrim(TrimScope.All);
+                    } else if (this.timeline) {
+                        this.Pause();
+                        this.timeline.IsTrimming !== TrimScope.None ? this.finishTrim() : this.startTrim(TrimScope.Clip);
+                    }
+                })
+            );
+    };
 
     // for volume slider sets volume
     @action
@@ -752,7 +822,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
                 this.toggleMute();
             }
         }
-    }
+    };
 
     // toggles video mute
     @action
@@ -761,62 +831,68 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
             this._muted = !this._muted;
             this.player.muted = this._muted;
         }
-    }
-
+    };
 
     // stretches vertically or horizontally depending on video orientation so video fits full screen
     fullScreenSize() {
         if (this._videoRef && this._videoRef.videoHeight / this._videoRef.videoWidth > 1) {
-            return { height: "100%" };
-        }
-        else {
-            return { width: "100%" };
+            return { height: '100%' };
+        } else {
+            return { width: '100%' };
         }
     }
 
-
     // for zoom slider, sets timeline waveform zoom
     zoom = (zoom: number) => {
         this.timeline?.setZoom(zoom);
-    }
-
+    };
 
     // plays link
     playLink = (doc: Doc) => {
-        const startTime = Math.max(0, (this._stackedTimeline?.anchorStart(doc) || 0));
+        const startTime = Math.max(0, this._stackedTimeline?.anchorStart(doc) || 0);
         const endTime = this.timeline?.anchorEnd(doc);
         if (startTime !== undefined) {
             if (!this.layoutDoc.dontAutoPlayFollowedLinks) endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime);
             else this.Seek(startTime);
         }
-    }
-
+    };
 
     // starts marquee selection
     marqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.ActiveTool)) {
-            setupMoveUpEvents(this, e, action(e => {
-                MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
-                this._marqueeing = [e.clientX, e.clientY];
-                return true;
-            }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false);
+        if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(Doc.ActiveTool)) {
+            setupMoveUpEvents(
+                this,
+                e,
+                action(e => {
+                    MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+                    this._marqueeing = [e.clientX, e.clientY];
+                    return true;
+                }),
+                returnFalse,
+                () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations),
+                false
+            );
         }
-    }
+    };
 
     // ends marquee selection
     @action
     finishMarquee = () => {
         this._marqueeing = undefined;
         this.props.select(true);
-    }
+    };
 
-    timelineWhenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged(this._isAnyChildContentActive = isActive));
+    timelineWhenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isAnyChildContentActive = isActive)));
 
-    timelineScreenToLocal = () => this.props.ScreenToLocalTransform().scale(this.scaling()).translate(0, -this.heightPercent / 100 * this.props.PanelHeight());
+    timelineScreenToLocal = () =>
+        this.props
+            .ScreenToLocalTransform()
+            .scale(this.scaling())
+            .translate(0, (-this.heightPercent / 100) * this.props.PanelHeight());
 
-    setPlayheadTime = (time: number) => this.player!.currentTime = this.layoutDoc._currentTimecode = time;
+    setPlayheadTime = (time: number) => (this.player!.currentTime = this.layoutDoc._currentTimecode = time);
 
-    timelineHeight = () => this.props.PanelHeight() * (100 - this.heightPercent) / 100;
+    timelineHeight = () => (this.props.PanelHeight() * (100 - this.heightPercent)) / 100;
 
     playing = () => this._playing;
 
@@ -824,20 +900,22 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
 
     scaling = () => this.props.scaling?.() || 1;
 
-    panelWidth = () => this.props.PanelWidth() * this.heightPercent / 100;
-    panelHeight = () => this.layoutDoc._fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : this.props.PanelHeight() * this.heightPercent / 100;
+    panelWidth = () => (this.props.PanelWidth() * this.heightPercent) / 100;
+    panelHeight = () => (this.layoutDoc._fitWidth ? this.panelWidth() / (Doc.NativeAspect(this.rootDoc) || 1) : (this.props.PanelHeight() * this.heightPercent) / 100);
 
     screenToLocalTransform = () => {
         const offset = (this.props.PanelWidth() - this.panelWidth()) / 2 / this.scaling();
-        return this.props.ScreenToLocalTransform().translate(-offset, 0).scale(100 / this.heightPercent);
-    }
+        return this.props
+            .ScreenToLocalTransform()
+            .translate(-offset, 0)
+            .scale(100 / this.heightPercent);
+    };
 
-    marqueeFitScaling = () => (this.props.scaling?.() || 1) * this.heightPercent / 100;
-    marqueeOffset = () => [this.panelWidth() / 2 * (1 - this.heightPercent / 100) / (this.heightPercent / 100), 0];
+    marqueeFitScaling = () => ((this.props.scaling?.() || 1) * this.heightPercent) / 100;
+    marqueeOffset = () => [((this.panelWidth() / 2) * (1 - this.heightPercent / 100)) / (this.heightPercent / 100), 0];
 
     timelineDocFilter = () => [`_timelineLabel:true,${Utils.noRecursionHack}:x`];
 
-
     // renders video controls
     componentUI = (boundsLeft: number, boundsTop: number) => {
         const bounds = this.props.docViewPath().lastElement().getBounds();
@@ -848,130 +926,157 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
         const width = Math.max(right - left, 100);
         const uiHeight = Math.max(25, Math.min(50, height / 10));
         const uiMargin = Math.min(10, height / 20);
-        const vidHeight = height * this.heightPercent / 100;
+        const vidHeight = (height * this.heightPercent) / 100;
         const yPos = top + vidHeight - uiHeight - uiMargin;
         const xPos = uiHeight / vidHeight > 0.4 ? right + 10 : left + 10;
-        const opacity = this._scrubbing ? 0.3 : (this._controlsVisible ? 1 : 0);
-        return this._fullScreen || (right - left) < 50 ? null : <div className="videoBox-ui-wrapper" style={{ clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)` }}>
-            <div className="videoBox-ui" style={{ left: xPos, top: yPos, height: uiHeight, width: width - 20, transition: this._clicking ? "top 0.5s" : "", opacity: opacity}}>
-                {this.UIButtons}
+        const opacity = this._scrubbing ? 0.3 : this._controlsVisible ? 1 : 0;
+        return this._fullScreen || right - left < 50 ? null : (
+            <div className="videoBox-ui-wrapper" style={{ clip: `rect(${boundsTop}px, 10000px, 10000px, ${boundsLeft}px)` }}>
+                <div className="videoBox-ui" style={{ left: xPos, top: yPos, height: uiHeight, width: width - 20, transition: this._clicking ? 'top 0.5s' : '', opacity: opacity }}>
+                    {this.UIButtons}
+                </div>
             </div>
-        </div>
-    }
+        );
+    };
 
     @computed get UIButtons() {
         const bounds = this.props.docViewPath().lastElement().getBounds();
         const width = (bounds?.right || 0) - (bounds?.left || 0);
         const curTime = NumCast(this.layoutDoc._currentTimecode) - (this.timeline?.clipStart || 0);
-        return <>
-            <div className="videobox-button"
-                title={this._playing ? "play" : "pause"}
-                onPointerDown={this.onPlayDown}>
-                <FontAwesomeIcon icon={this._playing ? "pause" : "play"} />
-            </div>
-
-            {this.timeline && width > 150 && <div className="timecode-controls">
-                <div className="timecode-current">
-                    {formatTime(curTime)}
+        return (
+            <>
+                <div className="videobox-button" title={this._playing ? 'play' : 'pause'} onPointerDown={this.onPlayDown}>
+                    <FontAwesomeIcon icon={this._playing ? 'pause' : 'play'} />
                 </div>
 
-                {this._fullScreen || (this.heightPercent === 100 && width > 200) ?
-                    <div className="timeline-slider">
-                        <input type="range" step="0.1" min={this.timeline.clipStart} max={this.timeline.clipEnd} value={curTime}
-                            className="toolbar-slider time-progress"
-                            onPointerDown={action((e: React.PointerEvent) => { e.stopPropagation(); this._scrubbing = true;})}
-                            onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setPlayheadTime(Number(e.target.value))}
-                            onPointerUp={action((e: React.PointerEvent) => {e.stopPropagation(); this._scrubbing = false;})}
-                        />
+                {this.timeline && width > 150 && (
+                    <div className="timecode-controls">
+                        <div className="timecode-current">{formatTime(curTime)}</div>
+
+                        {this._fullScreen || (this.heightPercent === 100 && width > 200) ? (
+                            <div className="timeline-slider">
+                                <input
+                                    type="range"
+                                    step="0.1"
+                                    min={this.timeline.clipStart}
+                                    max={this.timeline.clipEnd}
+                                    value={curTime}
+                                    className="toolbar-slider time-progress"
+                                    onPointerDown={action((e: React.PointerEvent) => {
+                                        e.stopPropagation();
+                                        this._scrubbing = true;
+                                    })}
+                                    onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setPlayheadTime(Number(e.target.value))}
+                                    onPointerUp={action((e: React.PointerEvent) => {
+                                        e.stopPropagation();
+                                        this._scrubbing = false;
+                                    })}
+                                />
+                            </div>
+                        ) : (
+                            <div>/</div>
+                        )}
+
+                        <div className="timecode-end">{formatTime(this.timeline.clipDuration)}</div>
                     </div>
-                    :
-                    <div>/</div>}
-
-                <div className="timecode-end">
-                    {formatTime(this.timeline.clipDuration)}
-                </div>
-            </div>
-            }
-
-            <div className="videobox-button"
-                title={"full screen"}
-                onPointerDown={this.onFullDown}>
-                <FontAwesomeIcon icon="expand" />
-            </div>
-
-            {
-                !this._fullScreen && width > 300 && <div className="videobox-button"
-                    title={"show timeline"}
-                    onPointerDown={this.onTimelineHdlDown}>
-                    <FontAwesomeIcon icon="eye" />
-                </div>
-            }
+                )}
 
-            {
-                !this._fullScreen && width > 300 && <div className="videobox-button"
-                    title={this.timeline?.IsTrimming !== TrimScope.None ? "finish trimming" : "start trim"}
-                    onPointerDown={this.onClipPointerDown}>
-                    <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? "check" : "cut"} />
+                <div className="videobox-button" title={'full screen'} onPointerDown={this.onFullDown}>
+                    <FontAwesomeIcon icon="expand" />
                 </div>
-            }
 
-            <div className="videobox-button"
-                title={this._muted ? "unmute" : "mute"}
-                onPointerDown={(e) => { e.stopPropagation(); this.toggleMute(); }}>
-                <FontAwesomeIcon icon={this._muted ? "volume-mute" : "volume-up"} />
-            </div>
-            {
-                width > 300 && <input type="range" style={{ width: `min(25%, 50px)` }} step="0.1" min="0" max="1" value={this._muted ? 0 : this._volume}
-                    className="toolbar-slider volume"
-                    onPointerDown={(e: React.PointerEvent) => e.stopPropagation()}
-                    onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setVolume(Number(e.target.value))}
-                />
-            }
+                {!this._fullScreen && width > 300 && (
+                    <div className="videobox-button" title={'show timeline'} onPointerDown={this.onTimelineHdlDown}>
+                        <FontAwesomeIcon icon="eye" />
+                    </div>
+                )}
 
-            {
-                !this._fullScreen && this.heightPercent !== 100 && width > 300 &&
-                <>
-                    <div className="videobox-button" title="zoom">
-                        <FontAwesomeIcon icon="search-plus" />
+                {!this._fullScreen && width > 300 && (
+                    <div className="videobox-button" title={this.timeline?.IsTrimming !== TrimScope.None ? 'finish trimming' : 'start trim'} onPointerDown={this.onClipPointerDown}>
+                        <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? 'check' : 'cut'} />
                     </div>
-                    <input type="range" step="0.1" min="1" max="5" value={this.timeline?._zoomFactor}
-                        className="toolbar-slider zoom"
-                        onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
-                        onChange={(e: React.ChangeEvent<HTMLInputElement>) => { this.zoom(Number(e.target.value)); }}
+                )}
+
+                <div
+                    className="videobox-button"
+                    title={this._muted ? 'unmute' : 'mute'}
+                    onPointerDown={e => {
+                        e.stopPropagation();
+                        this.toggleMute();
+                    }}>
+                    <FontAwesomeIcon icon={this._muted ? 'volume-mute' : 'volume-up'} />
+                </div>
+                {width > 300 && (
+                    <input
+                        type="range"
+                        style={{ width: `min(25%, 50px)` }}
+                        step="0.1"
+                        min="0"
+                        max="1"
+                        value={this._muted ? 0 : this._volume}
+                        className="toolbar-slider volume"
+                        onPointerDown={(e: React.PointerEvent) => e.stopPropagation()}
+                        onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setVolume(Number(e.target.value))}
                     />
-                </>
-            }
-        </>
+                )}
+
+                {!this._fullScreen && this.heightPercent !== 100 && width > 300 && (
+                    <>
+                        <div className="videobox-button" title="zoom">
+                            <FontAwesomeIcon icon="search-plus" />
+                        </div>
+                        <input
+                            type="range"
+                            step="0.1"
+                            min="1"
+                            max="5"
+                            value={this.timeline?._zoomFactor}
+                            className="toolbar-slider zoom"
+                            onPointerDown={(e: React.PointerEvent) => {
+                                e.stopPropagation();
+                            }}
+                            onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
+                                this.zoom(Number(e.target.value));
+                            }}
+                        />
+                    </>
+                )}
+            </>
+        );
     }
 
     // renders CollectionStackedTimeline
     @computed get renderTimeline() {
-        return <div className="videoBox-stackPanel" style={{ transition: this.transition, height: `${100 - this.heightPercent}%` }}>
-            <CollectionStackedTimeline ref={action((r: any) => this._stackedTimeline = r)} {...this.props}
-                fieldKey={this.annotationKey}
-                dictationKey={this.fieldKey + "-dictation"}
-                mediaPath={this.audiopath}
-                renderDepth={this.props.renderDepth + 1}
-                startTag={"_timecodeToShow" /* videoStart */}
-                endTag={"_timecodeToHide" /* videoEnd */}
-                bringToFront={emptyFunction}
-                CollectionView={undefined}
-                playFrom={this.playFrom}
-                setTime={this.setPlayheadTime}
-                playing={this.playing}
-                isAnyChildContentActive={this.isAnyChildContentActive}
-                whenChildContentsActiveChanged={this.timelineWhenChildContentsActiveChanged}
-                moveDocument={this.moveDocument}
-                addDocument={this.addDocument}
-                removeDocument={this.removeDocument}
-                ScreenToLocalTransform={this.timelineScreenToLocal}
-                Play={this.Play}
-                Pause={this.Pause}
-                playLink={this.playLink}
-                PanelHeight={this.timelineHeight}
-                rawDuration={this.rawDuration}
-            />
-        </div>;
+        return (
+            <div className="videoBox-stackPanel" style={{ transition: this.transition, height: `${100 - this.heightPercent}%` }}>
+                <CollectionStackedTimeline
+                    ref={action((r: any) => (this._stackedTimeline = r))}
+                    {...this.props}
+                    fieldKey={this.annotationKey}
+                    dictationKey={this.fieldKey + '-dictation'}
+                    mediaPath={this.audiopath}
+                    renderDepth={this.props.renderDepth + 1}
+                    startTag={'_timecodeToShow' /* videoStart */}
+                    endTag={'_timecodeToHide' /* videoEnd */}
+                    bringToFront={emptyFunction}
+                    CollectionView={undefined}
+                    playFrom={this.playFrom}
+                    setTime={this.setPlayheadTime}
+                    playing={this.playing}
+                    isAnyChildContentActive={this.isAnyChildContentActive}
+                    whenChildContentsActiveChanged={this.timelineWhenChildContentsActiveChanged}
+                    moveDocument={this.moveDocument}
+                    addDocument={this.addDocument}
+                    removeDocument={this.removeDocument}
+                    ScreenToLocalTransform={this.timelineScreenToLocal}
+                    Play={this.Play}
+                    Pause={this.Pause}
+                    playLink={this.playLink}
+                    PanelHeight={this.timelineHeight}
+                    rawDuration={this.rawDuration}
+                />
+            </div>
+        );
     }
 
     // renders annotation layer
@@ -982,59 +1087,72 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
     savedAnnotations = () => this._savedAnnotations;
     render() {
         const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
-        const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / this.scaling()}px` : borderRad;
-        return (<div className="videoBox" onContextMenu={this.specificContextMenu} ref={this._mainCont}
-            style={{
-                pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined,
-                borderRadius,
-                overflow: this.props.docViewPath?.().slice(-1)[0].fitWidth ? "auto" : undefined
-            }} onWheel={e => { e.stopPropagation(); e.preventDefault(); }}>
-            <div className="videoBox-viewer" onPointerDown={this.marqueeDown} >
-                <div style={{
-                    position: "absolute", transition: this.transition,
-                    width: this.panelWidth(),
-                    height: this.panelHeight(),
-                    top: 0,
-                    left: (this.props.PanelWidth() - this.panelWidth()) / 2
+        const borderRadius = borderRad?.includes('px') ? `${Number(borderRad.split('px')[0]) / this.scaling()}px` : borderRad;
+        return (
+            <div
+                className="videoBox"
+                onContextMenu={this.specificContextMenu}
+                ref={this._mainCont}
+                style={{
+                    pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined,
+                    borderRadius,
+                    overflow: this.props.docViewPath?.().slice(-1)[0].fitWidth ? 'auto' : undefined,
+                }}
+                onWheel={e => {
+                    e.stopPropagation();
+                    e.preventDefault();
                 }}>
-                    <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
-                        renderDepth={this.props.renderDepth + 1}
-                        fieldKey={this.annotationKey}
-                        CollectionView={undefined}
-                        isAnnotationOverlay={true}
-                        annotationLayerHostsContent={true}
-                        PanelWidth={this.panelWidth}
-                        PanelHeight={this.panelHeight}
-                        ScreenToLocalTransform={this.screenToLocalTransform}
-                        docFilters={this.timelineDocFilter}
-                        select={emptyFunction}
-                        scaling={returnOne}
-                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                        removeDocument={this.removeDocument}
-                        moveDocument={this.moveDocument}
-                        addDocument={this.addDocWithTimecode}>
-                        {this.contentFunc}
-                    </CollectionFreeFormView>
+                <div className="videoBox-viewer" onPointerDown={this.marqueeDown}>
+                    <div
+                        style={{
+                            position: 'absolute',
+                            transition: this.transition,
+                            width: this.panelWidth(),
+                            height: this.panelHeight(),
+                            top: 0,
+                            left: (this.props.PanelWidth() - this.panelWidth()) / 2,
+                        }}>
+                        <CollectionFreeFormView
+                            {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
+                            renderDepth={this.props.renderDepth + 1}
+                            fieldKey={this.annotationKey}
+                            CollectionView={undefined}
+                            isAnnotationOverlay={true}
+                            annotationLayerHostsContent={true}
+                            PanelWidth={this.panelWidth}
+                            PanelHeight={this.panelHeight}
+                            ScreenToLocalTransform={this.screenToLocalTransform}
+                            docFilters={this.timelineDocFilter}
+                            select={emptyFunction}
+                            scaling={returnOne}
+                            whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                            removeDocument={this.removeDocument}
+                            moveDocument={this.moveDocument}
+                            addDocument={this.addDocWithTimecode}>
+                            {this.contentFunc}
+                        </CollectionFreeFormView>
+                    </div>
+                    {this.annotationLayer}
+                    {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
+                        <MarqueeAnnotator
+                            rootDoc={this.rootDoc}
+                            scrollTop={0}
+                            down={this._marqueeing}
+                            scaling={this.marqueeFitScaling}
+                            docView={this.props.docViewPath().slice(-1)[0]}
+                            containerOffset={this.marqueeOffset}
+                            addDocument={this.addDocWithTimecode}
+                            finishMarquee={this.finishMarquee}
+                            savedAnnotations={this.savedAnnotations}
+                            annotationLayer={this._annotationLayer.current}
+                            mainCont={this._mainCont.current}
+                        />
+                    )}
+                    {this.renderTimeline}
                 </div>
-                {this.annotationLayer}
-                {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
-                    <MarqueeAnnotator
-                        rootDoc={this.rootDoc}
-                        scrollTop={0}
-                        down={this._marqueeing}
-                        scaling={this.marqueeFitScaling}
-                        docView={this.props.docViewPath().slice(-1)[0]}
-                        containerOffset={this.marqueeOffset}
-                        addDocument={this.addDocWithTimecode}
-                        finishMarquee={this.finishMarquee}
-                        savedAnnotations={this.savedAnnotations}
-                        annotationLayer={this._annotationLayer.current}
-                        mainCont={this._mainCont.current}
-                    />}
-                {this.renderTimeline}
             </div>
-        </div >);
+        );
     }
 }
 
-VideoBox._nativeControls = false;
\ No newline at end of file
+VideoBox._nativeControls = false;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index d14af49ea..d97277c2b 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1,48 +1,48 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
 import * as WebRequest from 'web-request';
-import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
-import { HtmlField } from "../../../fields/HtmlField";
-import { InkTool } from "../../../fields/InkField";
-import { List } from "../../../fields/List";
-import { listSpec } from "../../../fields/Schema";
-import { ComputedField } from "../../../fields/ScriptField";
-import { Cast, ImageCast, NumCast, StrCast } from "../../../fields/Types";
-import { ImageField, WebField } from "../../../fields/URLField";
-import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils";
-import { Docs } from "../../documents/Documents";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { ScriptingGlobals } from "../../util/ScriptingGlobals";
-import { SnappingManager } from "../../util/SnappingManager";
-import { undoBatch } from "../../util/UndoManager";
-import { MarqueeOptionsMenu } from "../collections/collectionFreeForm";
-import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
-import { ContextMenu } from "../ContextMenu";
-import { ContextMenuProps } from "../ContextMenuItem";
-import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent";
-import { DocumentDecorations } from "../DocumentDecorations";
-import { Colors } from "../global/globalEnums";
-import { LightboxView } from "../LightboxView";
-import { MarqueeAnnotator } from "../MarqueeAnnotator";
-import { AnchorMenu } from "../pdf/AnchorMenu";
-import { Annotation } from "../pdf/Annotation";
-import { SidebarAnnos } from "../SidebarAnnos";
-import { StyleProp } from "../StyleProvider";
-import { DocumentViewProps } from "./DocumentView";
+import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { HtmlField } from '../../../fields/HtmlField';
+import { InkTool } from '../../../fields/InkField';
+import { List } from '../../../fields/List';
+import { listSpec } from '../../../fields/Schema';
+import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
+import { ImageField, WebField } from '../../../fields/URLField';
+import { TraceMobx } from '../../../fields/util';
+import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils';
+import { Docs, DocUtils } from '../../documents/Documents';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
+import { SnappingManager } from '../../util/SnappingManager';
+import { undoBatch } from '../../util/UndoManager';
+import { MarqueeOptionsMenu } from '../collections/collectionFreeForm';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
+import { ContextMenu } from '../ContextMenu';
+import { ContextMenuProps } from '../ContextMenuItem';
+import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
+import { DocumentDecorations } from '../DocumentDecorations';
+import { Colors } from '../global/globalEnums';
+import { LightboxView } from '../LightboxView';
+import { MarqueeAnnotator } from '../MarqueeAnnotator';
+import { AnchorMenu } from '../pdf/AnchorMenu';
+import { Annotation } from '../pdf/Annotation';
+import { SidebarAnnos } from '../SidebarAnnos';
+import { StyleProp } from '../StyleProvider';
+import { DocumentViewProps } from './DocumentView';
 import { FieldView, FieldViewProps } from './FieldView';
-import { LinkDocPreview } from "./LinkDocPreview";
-import { VideoBox } from "./VideoBox";
-import "./WebBox.scss";
-import React = require("react");
-const { CreateImage } = require("./WebBoxRenderer");
-const _global = (window /* browser */ || global /* node */) as any;
-const htmlToText = require("html-to-text");
+import { LinkDocPreview } from './LinkDocPreview';
+import { VideoBox } from './VideoBox';
+import './WebBox.scss';
+import React = require('react');
+const { CreateImage } = require('./WebBoxRenderer');
+const _global = (window /* browser */ || global) /* node */ as any;
+const htmlToText = require('html-to-text');
 @observer
 export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(WebBox, fieldKey);
+    }
     public static openSidebarWidth = 250;
     public static sidebarResizerWidth = 5;
     private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
@@ -55,9 +55,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     private _getAnchor: (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => Opt<Doc> = () => undefined;
     private _sidebarRef = React.createRef<SidebarAnnos>();
     private _searchRef = React.createRef<HTMLInputElement>();
-    private _searchString = "";
-    @observable private _webUrl = "";  // url of the src parameter of the embedded iframe but not necessarily the rendered page - eg, when following a link, the rendered page changes but we don't wan the src parameter to also change as that would cause an unnecessary re-render.  
-    @observable private _hackHide = false;  // apparently changing the value of the 'sandbox' prop doesn't necessarily apply it to the active iframe.  so thisforces the ifrmae to be rebuilt when allowScripts is toggled
+    private _searchString = '';
+    @observable private _webUrl = ''; // url of the src parameter of the embedded iframe but not necessarily the rendered page - eg, when following a link, the rendered page changes but we don't wan the src parameter to also change as that would cause an unnecessary re-render.
+    @observable private _hackHide = false; // apparently changing the value of the 'sandbox' prop doesn't necessarily apply it to the active iframe.  so thisforces the ifrmae to be rebuilt when allowScripts is toggled
     @observable private _searching: boolean = false;
     @observable private _showSidebar = false;
     @observable private _scrollTimer: any;
@@ -69,22 +69,41 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     @observable private _iframe: HTMLIFrameElement | null = null;
     @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
     @observable private _scrollHeight = NumCast(this.layoutDoc.scrollHeight);
-    @computed get _url() { return this.webField?.toString() || ""; }
-    @computed get _urlHash() { return this._url ? WebBox.urlHash(this._url) + "" : ""; }
-    @computed get scrollHeight() { return Math.max(this.layoutDoc[HeightSym](), this._scrollHeight); }
-    @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); }
-    @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); }
-    @computed get webField() { return Cast(this.dataDoc[this.props.fieldKey], WebField)?.url; }
+    @computed get _url() {
+        return this.webField?.toString() || '';
+    }
+    @computed get _urlHash() {
+        return this._url ? WebBox.urlHash(this._url) + '' : '';
+    }
+    @computed get scrollHeight() {
+        return Math.max(this.layoutDoc[HeightSym](), this._scrollHeight);
+    }
+    @computed get allAnnotations() {
+        return DocListCast(this.dataDoc[this.annotationKey]);
+    }
+    @computed get inlineTextAnnotations() {
+        return this.allAnnotations.filter(a => a.textInlineAnnotations);
+    }
+    @computed get webField() {
+        return Cast(this.dataDoc[this.props.fieldKey], WebField)?.url;
+    }
     @computed get webThumb() {
-        return this.props.thumbShown?.() &&
-            ImageCast(this.layoutDoc["thumb-frozen"],
-                ImageCast(this.layoutDoc.thumbScrollTop === this.layoutDoc._scrollTop && this.layoutDoc.thumbNativeWidth === NumCast(this.layoutDoc.nativeWidth) &&
-                    this.layoutDoc.thumbNativeHeight === NumCast(this.layoutDoc.nativeHeight) ? this.layoutDoc.thumb : undefined))?.url;
+        return (
+            this.props.thumbShown?.() &&
+            ImageCast(
+                this.layoutDoc['thumb-frozen'],
+                ImageCast(
+                    this.layoutDoc.thumbScrollTop === this.layoutDoc._scrollTop && this.layoutDoc.thumbNativeWidth === NumCast(this.layoutDoc.nativeWidth) && this.layoutDoc.thumbNativeHeight === NumCast(this.layoutDoc.nativeHeight)
+                        ? this.layoutDoc.thumb
+                        : undefined
+                )
+            )?.url
+        );
     }
 
     constructor(props: any) {
         super(props);
-        runInAction(() => this._webUrl = this._url);  // setting the weburl will change the src parameter of the embedded iframe and force a navigation to it.
+        runInAction(() => (this._webUrl = this._url)); // setting the weburl will change the src parameter of the embedded iframe and force a navigation to it.
     }
 
     @action
@@ -104,7 +123,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             (this._iframe?.contentWindow as any)?.find(searchString, false, bwd, true);
         }
         return true;
-    }
+    };
     @action
     setScrollPos = (pos: number) => {
         if (!this._outerRef.current || this._outerRef.current.scrollHeight < pos) {
@@ -113,14 +132,14 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             this._outerRef.current.scrollTop = pos;
             this._initialScroll = undefined;
         }
-    }
+    };
 
     lockout = false;
     updateThumb = async () => {
-        const imageBitmap = ImageCast(this.layoutDoc["thumb-frozen"])?.url.href;
+        const imageBitmap = ImageCast(this.layoutDoc['thumb-frozen'])?.url.href;
         const scrollTop = NumCast(this.layoutDoc._scrollTop);
         const nativeWidth = NumCast(this.layoutDoc.nativeWidth);
-        const nativeHeight = nativeWidth * this.props.PanelHeight() / this.props.PanelWidth();
+        const nativeHeight = (nativeWidth * this.props.PanelHeight()) / this.props.PanelWidth();
         if (!this.lockout && this._iframe && !imageBitmap && (scrollTop !== this.layoutDoc.thumbScrollTop || nativeWidth !== this.layoutDoc.thumbNativeWidth || nativeHeight !== this.layoutDoc.thumbNativeHeight)) {
             var htmlString = this._iframe.contentDocument && new XMLSerializer().serializeToString(this._iframe.contentDocument);
             if (!htmlString) {
@@ -128,60 +147,65 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
             this.layoutDoc.thumb = undefined;
             this.lockout = true; // lock to prevent multiple thumb updates.
-            CreateImage(
-                this._webUrl.endsWith("/") ? this._webUrl.substring(0, this._webUrl.length - 1) : this._webUrl,
-                this._iframe.contentDocument?.styleSheets ?? [],
-                htmlString,
-                nativeWidth,
-                nativeHeight,
-                scrollTop
-            ).then
-                ((data_url: any) => {
-                    VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), true, this.layoutDoc[Id] + "-icon").then(
-                        returnedfilename => setTimeout(action(() => {
-                            this.lockout = false;
-                            this.layoutDoc.thumb = new ImageField(returnedfilename);
-                            this.layoutDoc.thumbScrollTop = scrollTop;
-                            this.layoutDoc.thumbNativeWidth = nativeWidth;
-                            this.layoutDoc.thumbNativeHeight = nativeHeight;
-                        }), 500));
+            CreateImage(this._webUrl.endsWith('/') ? this._webUrl.substring(0, this._webUrl.length - 1) : this._webUrl, this._iframe.contentDocument?.styleSheets ?? [], htmlString, nativeWidth, nativeHeight, scrollTop)
+                .then((data_url: any) => {
+                    VideoBox.convertDataUri(data_url, this.layoutDoc[Id] + '-icon' + new Date().getTime(), true, this.layoutDoc[Id] + '-icon').then(returnedfilename =>
+                        setTimeout(
+                            action(() => {
+                                this.lockout = false;
+                                this.layoutDoc.thumb = new ImageField(returnedfilename);
+                                this.layoutDoc.thumbScrollTop = scrollTop;
+                                this.layoutDoc.thumbNativeWidth = nativeWidth;
+                                this.layoutDoc.thumbNativeHeight = nativeHeight;
+                            }),
+                            500
+                        )
+                    );
                 })
                 .catch(function (error: any) {
                     console.error('oops, something went wrong!', error);
                 });
         }
-    }
+    };
     async componentDidMount() {
         this.props.setContentView?.(this); // this tells the DocumentView that this WebBox is the "content" of the document.  this allows the DocumentView to call WebBox relevant methods to configure the UI (eg, show back/forward buttons)
 
         runInAction(() => {
-            this._annotationKeySuffix = () => this._urlHash + "-annotations";
-            const reqdFuncs:{[key:string]: string} = {};
+            this._annotationKeySuffix = () => this._urlHash + '-annotations';
+            const reqdFuncs: { [key: string]: string } = {};
             // bcz: need to make sure that doc.data-annotations points to the currently active web page's annotations (this could/should be when the doc is created)
-            reqdFuncs[this.fieldKey + "-annotations"] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`;
-            reqdFuncs[this.fieldKey + "-sidebar"] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`;
-            CurrentUserUtils.AssignScripts(this.dataDoc, {}, reqdFuncs);
+            reqdFuncs[this.fieldKey + '-annotations'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`;
+            reqdFuncs[this.fieldKey + '-sidebar'] = `copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`;
+            DocUtils.AssignScripts(this.dataDoc, {}, reqdFuncs);
         });
-        reaction(() => this.props.isSelected(true) || this.isAnyChildContentActive() || Doc.isBrushedHighlightedDegree(this.props.Document),
-            async (selected) => {
+        reaction(
+            () => this.props.isSelected(true) || this.isAnyChildContentActive() || Doc.isBrushedHighlightedDegree(this.props.Document),
+            async selected => {
                 if (selected) {
                     this._webPageHasBeenRendered = true;
-                } else if ((!this.props.isContentActive(true) || SnappingManager.GetIsDragging()) &&   // update thumnail when unselected AND (no child annotation is active OR we've started dragging the document in which case no additional deselect will occur so this is the only chance to update the thumbnail)
+                } else if (
+                    (!this.props.isContentActive(true) || SnappingManager.GetIsDragging()) && // update thumnail when unselected AND (no child annotation is active OR we've started dragging the document in which case no additional deselect will occur so this is the only chance to update the thumbnail)
                     !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && // don't create a thumbnail when double-clicking to enter lightbox because thumbnail will be empty
-                    LightboxView.LightboxDoc !== this.rootDoc) { // don't create a thumbnail if entering Lightbox from maximize either, since thumb will be empty.
+                    LightboxView.LightboxDoc !== this.rootDoc
+                ) {
+                    // don't create a thumbnail if entering Lightbox from maximize either, since thumb will be empty.
                     this.updateThumb();
                 }
-            }, { fireImmediately: this.props.isSelected(true) || this.isAnyChildContentActive() || (Doc.isBrushedHighlightedDegreeUnmemoized(this.props.Document) ? true : false) });
+            },
+            { fireImmediately: this.props.isSelected(true) || this.isAnyChildContentActive() || (Doc.isBrushedHighlightedDegreeUnmemoized(this.props.Document) ? true : false) }
+        );
 
-        this._disposers.autoHeight = reaction(() => this.layoutDoc._autoHeight,
+        this._disposers.autoHeight = reaction(
+            () => this.layoutDoc._autoHeight,
             autoHeight => {
                 if (autoHeight) {
-                    this.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]);
-                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]) * (this.props.scaling?.() || 1));
+                    this.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']);
+                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']) * (this.props.scaling?.() || 1));
                 }
-            });
+            }
+        );
 
-        if (this.webField?.href.indexOf("youtube") !== -1) {
+        if (this.webField?.href.indexOf('youtube') !== -1) {
             const youtubeaspect = 400 / 315;
             const nativeWidth = Doc.NativeWidth(this.layoutDoc);
             const nativeHeight = Doc.NativeHeight(this.layoutDoc);
@@ -199,8 +223,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         }
 
-        this._disposers.scrollReaction = reaction(() => NumCast(this.layoutDoc._scrollTop),
-            (scrollTop) => {
+        this._disposers.scrollReaction = reaction(
+            () => NumCast(this.layoutDoc._scrollTop),
+            scrollTop => {
                 const viewTrans = StrCast(this.Document._viewTransition);
                 const durationMiliStr = viewTrans.match(/([0-9]*)ms/);
                 const durationSecStr = viewTrans.match(/([0-9.]*)s/);
@@ -223,13 +248,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             for (let i = 0; i < clientRects.length; i++) {
                 const rect = clientRects.item(i);
                 if (rect && rect.width !== this._mainCont.current.clientWidth) {
-                    const annoBox = document.createElement("div");
-                    annoBox.className = "marqueeAnnotator-annotationBox";
+                    const annoBox = document.createElement('div');
+                    annoBox.className = 'marqueeAnnotator-annotationBox';
                     // transforms the positions from screen onto the pdf div
                     annoBox.style.top = (rect.top + this._mainCont.current.scrollTop).toString();
-                    annoBox.style.left = (rect.left).toString();
-                    annoBox.style.width = (rect.width).toString();
-                    annoBox.style.height = (rect.height).toString();
+                    annoBox.style.left = rect.left.toString();
+                    annoBox.style.width = rect.width.toString();
+                    annoBox.style.height = rect.height.toString();
                     this._annotationLayer.current && MarqueeAnnotator.previewNewAnnotation(this._savedAnnotations, this._annotationLayer.current, annoBox, 1);
                 }
             }
@@ -237,23 +262,22 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         //this._selectionText = selRange.cloneContents().textContent || "";
 
         // clear selection
-        if (sel.empty) sel.empty();// Chrome
-        else if (sel.removeAllRanges) sel.removeAllRanges();  // Firefox
+        if (sel.empty) sel.empty(); // Chrome
+        else if (sel.removeAllRanges) sel.removeAllRanges(); // Firefox
         return this._savedAnnotations;
-    }
+    };
 
     menuControls = () => this.urlEditor; // controls to be added to the top bar when a document of this type is selected
 
     scrollFocus = (doc: Doc, smooth: boolean) => {
         if (StrCast(doc.webUrl) !== this._url) this.submitURL(StrCast(doc.webUrl), !smooth);
-        if (DocListCast(this.props.Document[this.fieldKey + "-sidebar"]).includes(doc) && !this.SidebarShown) {
+        if (DocListCast(this.props.Document[this.fieldKey + '-sidebar']).includes(doc) && !this.SidebarShown) {
             this.toggleSidebar(!smooth);
         }
         if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1;
         if (doc !== this.rootDoc && this._outerRef.current) {
             const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1);
-            const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * .1,
-                Math.max(NumCast(doc.y) + doc[HeightSym](), this.getScrollHeight()));
+            const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * 0.1, Math.max(NumCast(doc.y) + doc[HeightSym](), this.getScrollHeight()));
             if (scrollTo !== undefined && this._initialScroll === undefined) {
                 const focusSpeed = smooth ? 500 : 0;
                 this.goTo(scrollTo, focusSpeed);
@@ -263,22 +287,22 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         }
         return undefined;
-    }
+    };
 
     getAnchor = () => {
         const anchor =
             this._getAnchor(this._savedAnnotations) ??
             Docs.Create.WebanchorDocument(this._url, {
-                title: StrCast(this.rootDoc.title + " " + this.layoutDoc._scrollTop),
+                title: StrCast(this.rootDoc.title + ' ' + this.layoutDoc._scrollTop),
                 y: NumCast(this.layoutDoc._scrollTop),
-                unrendered: true
+                unrendered: true,
             });
         this.addDocumentWrapper(anchor);
         return anchor;
-    }
+    };
 
     _textAnnotationCreator: (() => ObservableMap<number, HTMLDivElement[]>) | undefined;
-    savedAnnotationsCreator: (() => ObservableMap<number, HTMLDivElement[]>) = () => this._textAnnotationCreator?.() || this._savedAnnotations;
+    savedAnnotationsCreator: () => ObservableMap<number, HTMLDivElement[]> = () => this._textAnnotationCreator?.() || this._savedAnnotations;
 
     @action
     iframeUp = (e: PointerEvent) => {
@@ -290,11 +314,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             const sel = this._iframe.contentWindow.getSelection();
             if (sel) {
                 this._textAnnotationCreator = () => this.createTextAnnotation(sel, !sel.isCollapsed ? sel.getRangeAt(0) : undefined);
-                AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX,
-                    e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale);
+                AnchorMenu.Instance.jumpTo(e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale);
             }
         }
-    }
+    };
     @action
     iframeDown = (e: PointerEvent) => {
         const sel = this._iframe?.contentWindow?.getSelection?.();
@@ -303,10 +326,12 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         const word = getWordAtPoint(e.target, e.clientX, e.clientY);
         this._setPreviewCursor?.(e.clientX, e.clientY, false, true);
         MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
-        this._marqueeing = [e.clientX * scale + mainContBounds.translateX,
-        e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale];
-        if (word || (e.target as any || "").className.includes("rangeslider") || (e.target as any)?.onclick || (e.target as any)?.parentNode?.onclick) {
-            setTimeout(action(() => this._marqueeing = undefined), 100); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
+        this._marqueeing = [e.clientX * scale + mainContBounds.translateX, e.clientY * scale + mainContBounds.translateY - NumCast(this.layoutDoc._scrollTop) * scale];
+        if (word || ((e.target as any) || '').className.includes('rangeslider') || (e.target as any)?.onclick || (e.target as any)?.parentNode?.onclick) {
+            setTimeout(
+                action(() => (this._marqueeing = undefined)),
+                100
+            ); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
         } else {
             this._iframeClick = this._iframe ?? undefined;
             this._isAnnotating = true;
@@ -322,13 +347,13 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             ContextMenu.Instance.closeMenu();
             ContextMenu.Instance.setIgnoreEvents(true);
         }
-    }
+    };
 
     getScrollHeight = () => this._scrollHeight;
 
     isFirefox = () => {
-        return "InstallTrigger" in window; // navigator.userAgent.indexOf("Chrome") !== -1;
-    }
+        return 'InstallTrigger' in window; // navigator.userAgent.indexOf("Chrome") !== -1;
+    };
     iframeClick = () => this._iframeClick;
     iframeScaling = () => 1 / this.props.ScreenToLocalTransform().Scale;
 
@@ -338,7 +363,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         if (this._initialScroll !== undefined) {
             this.setScrollPos(this._initialScroll);
         }
-        let requrlraw = decodeURIComponent(iframe?.contentWindow?.location.href.replace(Utils.prepend("") + "/corsProxy/", "") ?? this._url.toString());
+        let requrlraw = decodeURIComponent(iframe?.contentWindow?.location.href.replace(Utils.prepend('') + '/corsProxy/', '') ?? this._url.toString());
         if (requrlraw !== this._url.toString()) {
             if (requrlraw.match(/q=.*&/)?.length && this._url.toString().match(/q=.*&/)?.length) {
                 const matches = requrlraw.match(/[^a-zA-z]q=[^&]*/g);
@@ -346,63 +371,79 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 if (matches) {
                     requrlraw = requrlraw.substring(0, requrlraw.indexOf(newsearch));
                     for (let i = 1; i < Array.from(matches)?.length; i++) {
-                        requrlraw = requrlraw.replace(matches[i], "");
+                        requrlraw = requrlraw.replace(matches[i], '');
                     }
                 }
-                requrlraw = requrlraw.replace(/q=[^&]*/, newsearch.substring(1)).replace("search&", "search?").replace("?gbv=1", "");
+                requrlraw = requrlraw
+                    .replace(/q=[^&]*/, newsearch.substring(1))
+                    .replace('search&', 'search?')
+                    .replace('?gbv=1', '');
             }
             this.submitURL(requrlraw, undefined, true);
         }
         if (iframe?.contentDocument) {
-            iframe.contentDocument.addEventListener("pointerup", this.iframeUp);
-            iframe.contentDocument.addEventListener("pointerdown", this.iframeDown);
+            iframe.contentDocument.addEventListener('pointerup', this.iframeUp);
+            iframe.contentDocument.addEventListener('pointerdown', this.iframeDown);
             this._scrollHeight = Math.max(this._scrollHeight, iframe?.contentDocument.body.scrollHeight);
-            setTimeout(action(() => this._scrollHeight = Math.max(this._scrollHeight, iframe?.contentDocument?.body.scrollHeight || 0)), 5000);
-            iframe.setAttribute("enable-annotation", "true");
-            iframe.contentDocument.addEventListener("click", undoBatch(action((e: MouseEvent) => {
-                let href = "";
-                for (let ele = e.target as any; ele; ele = ele.parentElement) {
-                    href = (typeof (ele.href) === "string" ? ele.href : ele.href?.baseVal) || ele.parentElement?.href || href;
-                }
-                const origin = this.webField?.origin;
-                if (href && origin) {
-                    e.stopPropagation();
-                    setTimeout(() => this.submitURL(href.replace(Utils.prepend(""), origin)));
-                    if (this._outerRef.current) {
-                        this._outerRef.current.scrollTop = NumCast(this.layoutDoc._scrollTop);
-                        this._outerRef.current.scrollLeft = 0;
-                    }
-                }
-            })));
+            setTimeout(
+                action(() => (this._scrollHeight = Math.max(this._scrollHeight, iframe?.contentDocument?.body.scrollHeight || 0))),
+                5000
+            );
+            iframe.setAttribute('enable-annotation', 'true');
+            iframe.contentDocument.addEventListener(
+                'click',
+                undoBatch(
+                    action((e: MouseEvent) => {
+                        let href = '';
+                        for (let ele = e.target as any; ele; ele = ele.parentElement) {
+                            href = (typeof ele.href === 'string' ? ele.href : ele.href?.baseVal) || ele.parentElement?.href || href;
+                        }
+                        const origin = this.webField?.origin;
+                        if (href && origin) {
+                            e.stopPropagation();
+                            setTimeout(() => this.submitURL(href.replace(Utils.prepend(''), origin)));
+                            if (this._outerRef.current) {
+                                this._outerRef.current.scrollTop = NumCast(this.layoutDoc._scrollTop);
+                                this._outerRef.current.scrollLeft = 0;
+                            }
+                        }
+                    })
+                )
+            );
             iframe.contentDocument.addEventListener('wheel', this.iframeWheel, false);
             //iframe.contentDocument.addEventListener('scroll', () => !this.active() && this._iframe && (this._iframe.scrollTop = NumCast(this.layoutDoc._scrollTop), false));
         }
-    }
+    };
 
     @action
     iframeWheel = (e: any) => {
         if (!this._scrollTimer) {
-            this._scrollTimer = setTimeout(action(() => this._scrollTimer = undefined), 250); // this turns events off on the iframe which allows scrolling to change direction smoothly
+            this._scrollTimer = setTimeout(
+                action(() => (this._scrollTimer = undefined)),
+                250
+            ); // this turns events off on the iframe which allows scrolling to change direction smoothly
         }
-    }
+    };
 
     @action
     setDashScrollTop = (scrollTop: number, timeout: number = 250) => {
         const iframeHeight = Math.max(scrollTop, this._scrollHeight - this.panelHeight());
         this._scrollTimer && clearTimeout(this._scrollTimer);
-        this._scrollTimer = setTimeout(action(() => {
-            this._scrollTimer = undefined;
-            const newScrollTop = scrollTop > iframeHeight ? iframeHeight : scrollTop;
-            if (!LinkDocPreview.LinkInfo && this._outerRef.current && newScrollTop !== this.layoutDoc.thumbScrollTop &&
-                (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
-                this.layoutDoc.thumb = undefined;
-                this.layoutDoc.thumbScrollTop = undefined;
-                this.layoutDoc.thumbNativeWidth = undefined;
-                this.layoutDoc.thumbNativeHeight = undefined;
-                this.layoutDoc.scrollTop = this._outerRef.current.scrollTop = newScrollTop;
-            } else if (this._outerRef.current) this._outerRef.current.scrollTop = newScrollTop;
-        }), timeout);
-    }
+        this._scrollTimer = setTimeout(
+            action(() => {
+                this._scrollTimer = undefined;
+                const newScrollTop = scrollTop > iframeHeight ? iframeHeight : scrollTop;
+                if (!LinkDocPreview.LinkInfo && this._outerRef.current && newScrollTop !== this.layoutDoc.thumbScrollTop && (!LightboxView.LightboxDoc || LightboxView.IsLightboxDocView(this.props.docViewPath()))) {
+                    this.layoutDoc.thumb = undefined;
+                    this.layoutDoc.thumbScrollTop = undefined;
+                    this.layoutDoc.thumbNativeWidth = undefined;
+                    this.layoutDoc.thumbNativeHeight = undefined;
+                    this.layoutDoc.scrollTop = this._outerRef.current.scrollTop = newScrollTop;
+                } else if (this._outerRef.current) this._outerRef.current.scrollTop = newScrollTop;
+            }),
+            timeout
+        );
+    };
 
     goTo = (scrollTop: number, duration: number) => {
         if (this._outerRef.current) {
@@ -414,20 +455,20 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this.setDashScrollTop(scrollTop);
             }
         } else this._initialScroll = scrollTop;
-    }
+    };
 
     forward = (checkAvailable?: boolean) => {
-        const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"), []);
-        const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), []);
+        const future = Cast(this.dataDoc[this.fieldKey + '-future'], listSpec('string'), []);
+        const history = Cast(this.dataDoc[this.fieldKey + '-history'], listSpec('string'), []);
         if (checkAvailable) return future.length;
         runInAction(() => {
             if (future.length) {
                 const curUrl = this._url;
-                this.dataDoc[this.fieldKey + "-history"] = new List<string>([...history, this._url]);
+                this.dataDoc[this.fieldKey + '-history'] = new List<string>([...history, this._url]);
                 this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!));
                 if (this._webUrl === this._url) {
                     this._webUrl = curUrl;
-                    setTimeout(action(() => this._webUrl = this._url));
+                    setTimeout(action(() => (this._webUrl = this._url)));
                 } else {
                     this._webUrl = this._url;
                 }
@@ -435,21 +476,21 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         });
         return false;
-    }
+    };
 
     back = (checkAvailable?: boolean) => {
-        const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"));
-        const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), []);
+        const future = Cast(this.dataDoc[this.fieldKey + '-future'], listSpec('string'));
+        const history = Cast(this.dataDoc[this.fieldKey + '-history'], listSpec('string'), []);
         if (checkAvailable) return history.length;
         runInAction(() => {
             if (history.length) {
                 const curUrl = this._url;
-                if (future === undefined) this.dataDoc[this.fieldKey + "-future"] = new List<string>([this._url]);
-                else this.dataDoc[this.fieldKey + "-future"] = new List<string>([...future, this._url]);
+                if (future === undefined) this.dataDoc[this.fieldKey + '-future'] = new List<string>([this._url]);
+                else this.dataDoc[this.fieldKey + '-future'] = new List<string>([...future, this._url]);
                 this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!));
                 if (this._webUrl === this._url) {
                     this._webUrl = curUrl;
-                    setTimeout(action(() => this._webUrl = this._url));
+                    setTimeout(action(() => (this._webUrl = this._url)));
                 } else {
                     this._webUrl = this._url;
                 }
@@ -457,21 +498,26 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             }
         });
         return false;
-    }
+    };
 
     static urlHash = (s: string) => {
-        return Math.abs(s.split('').reduce((a: any, b: any) => { a = ((a << 5) - a) + b.charCodeAt(0); return a & a; }, 0));
-    }
+        return Math.abs(
+            s.split('').reduce((a: any, b: any) => {
+                a = (a << 5) - a + b.charCodeAt(0);
+                return a & a;
+            }, 0)
+        );
+    };
     @action
     submitURL = (newUrl?: string, preview?: boolean, dontUpdateIframe?: boolean) => {
         if (!newUrl) return;
-        if (!newUrl.startsWith("http")) newUrl = "http://" + newUrl;
+        if (!newUrl.startsWith('http')) newUrl = 'http://' + newUrl;
         try {
-            const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"));
-            const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"));
+            const future = Cast(this.dataDoc[this.fieldKey + '-future'], listSpec('string'));
+            const history = Cast(this.dataDoc[this.fieldKey + '-history'], listSpec('string'));
             const url = this.webField?.toString();
             if (url && !preview) {
-                this.dataDoc[this.fieldKey + "-history"] = new List<string>([...(history || []), url]);
+                this.dataDoc[this.fieldKey + '-history'] = new List<string>([...(history || []), url]);
                 this.layoutDoc._scrollTop = 0;
                 if (this._webPageHasBeenRendered) {
                     this.layoutDoc.thumb = undefined;
@@ -486,47 +532,54 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 !dontUpdateIframe && (this._webUrl = this._url);
             }
         } catch (e) {
-            console.log("WebBox URL error:" + this._url);
+            console.log('WebBox URL error:' + this._url);
         }
         return true;
-    }
+    };
 
     onWebUrlDrop = (e: React.DragEvent) => {
         const { dataTransfer } = e;
-        const html = dataTransfer.getData("text/html");
-        const uri = dataTransfer.getData("text/uri-list");
-        const url = uri || html || this._url || "";
-        const newurl = url.startsWith(window.location.origin) ?
-            url.replace(window.location.origin, this._url?.match(/http[s]?:\/\/[^\/]*/)?.[0] || "") : url;
+        const html = dataTransfer.getData('text/html');
+        const uri = dataTransfer.getData('text/uri-list');
+        const url = uri || html || this._url || '';
+        const newurl = url.startsWith(window.location.origin) ? url.replace(window.location.origin, this._url?.match(/http[s]?:\/\/[^\/]*/)?.[0] || '') : url;
         this.submitURL(newurl);
         e.stopPropagation();
-    }
+    };
     onWebUrlValueKeyDown = (e: React.KeyboardEvent) => {
-        e.key === "Enter" && this.submitURL(this._keyInput.current!.value);
+        e.key === 'Enter' && this.submitURL(this._keyInput.current!.value);
         e.stopPropagation();
-    }
+    };
 
     @computed get urlEditor() {
         return (
-            <div className="collectionMenu-webUrlButtons" onDrop={this.onWebUrlDrop} onDragOver={e => e.preventDefault()} >
-                <input className="collectionMenu-urlInput" key={this._url}
+            <div className="collectionMenu-webUrlButtons" onDrop={this.onWebUrlDrop} onDragOver={e => e.preventDefault()}>
+                <input
+                    className="collectionMenu-urlInput"
+                    key={this._url}
                     placeholder="ENTER URL"
                     defaultValue={this._url}
                     onDrop={this.onWebUrlDrop}
                     onDragOver={e => e.preventDefault()}
                     onKeyDown={this.onWebUrlValueKeyDown}
-                    onClick={(e) => {
+                    onClick={e => {
                         this._keyInput.current!.select();
                         e.stopPropagation();
                     }}
                     ref={this._keyInput}
                 />
-                <div style={{ display: "flex", flexDirection: "row", justifyContent: "space-between", maxWidth: "250px", }}>
+                <div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', maxWidth: '250px' }}>
                     <button className="submitUrl" onClick={() => this.submitURL(this._keyInput.current!.value)} onDragOver={e => e.stopPropagation()} onDrop={this.onWebUrlDrop}>
                         GO
                     </button>
-                    <button className="submitUrl" onClick={() => this.back}> <FontAwesomeIcon icon="caret-left" size="lg" /> </button>
-                    <button className="submitUrl" onClick={() => this.forward}> <FontAwesomeIcon icon="caret-right" size="lg" />  </button>
+                    <button className="submitUrl" onClick={() => this.back}>
+                        {' '}
+                        <FontAwesomeIcon icon="caret-left" size="lg" />{' '}
+                    </button>
+                    <button className="submitUrl" onClick={() => this.forward}>
+                        {' '}
+                        <FontAwesomeIcon icon="caret-right" size="lg" />{' '}
+                    </button>
                 </div>
             </div>
         );
@@ -535,48 +588,59 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     specificContextMenu = (e: React.MouseEvent | PointerEvent): void => {
         const cm = ContextMenu.Instance;
         const funcs: ContextMenuProps[] = [];
-        if (!cm.findByDescription("Options...")) {
-            !Doc.noviceMode && funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : "Use") + " Cors", event: () => this.layoutDoc.useCors = !this.layoutDoc.useCors, icon: "snowflake" });
+        if (!cm.findByDescription('Options...')) {
+            !Doc.noviceMode && funcs.push({ description: (this.layoutDoc.useCors ? "Don't Use" : 'Use') + ' Cors', event: () => (this.layoutDoc.useCors = !this.layoutDoc.useCors), icon: 'snowflake' });
             funcs.push({
-                description: (this.layoutDoc.allowScripts ? "Prevent" : "Allow") + " Scripts", event: () => {
+                description: (this.layoutDoc.allowScripts ? 'Prevent' : 'Allow') + ' Scripts',
+                event: () => {
                     this.layoutDoc.allowScripts = !this.layoutDoc.allowScripts;
                     if (this._iframe) {
-                        runInAction(() => this._hackHide = true);
-                        setTimeout(action(() => this._hackHide = false));
+                        runInAction(() => (this._hackHide = true));
+                        setTimeout(action(() => (this._hackHide = false)));
                     }
-                }, icon: "snowflake"
+                },
+                icon: 'snowflake',
             });
             funcs.push({
-                description: (!this.layoutDoc.forceReflow ? "Force" : "Prevent") + " Reflow", event: () => {
+                description: (!this.layoutDoc.forceReflow ? 'Force' : 'Prevent') + ' Reflow',
+                event: () => {
                     const nw = !this.layoutDoc.forceReflow ? undefined : Doc.NativeWidth(this.layoutDoc) - this.sidebarWidth() / (this.props.scaling?.() || 1);
                     this.layoutDoc.forceReflow = !nw;
                     if (nw) {
-                        Doc.SetInPlace(this.layoutDoc, this.fieldKey + "-nativeWidth", nw, true);
+                        Doc.SetInPlace(this.layoutDoc, this.fieldKey + '-nativeWidth', nw, true);
                     }
-                }, icon: "snowflake"
+                },
+                icon: 'snowflake',
             });
-            cm.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" });
+            cm.addItem({ description: 'Options...', subitems: funcs, icon: 'asterisk' });
         }
-    }
+    };
 
     @action
     onMarqueeDown = (e: React.PointerEvent) => {
-        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
-            setupMoveUpEvents(this, e, action(e => {
-                MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
-                this._marqueeing = [e.clientX, e.clientY];
-                return true;
-            }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false);
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+            setupMoveUpEvents(
+                this,
+                e,
+                action(e => {
+                    MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
+                    this._marqueeing = [e.clientX, e.clientY];
+                    return true;
+                }),
+                returnFalse,
+                () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations),
+                false
+            );
         }
-    }
+    };
     @action finishMarquee = (x?: number, y?: number, e?: PointerEvent) => {
         this._getAnchor = AnchorMenu.Instance?.GetAnchor;
         this._marqueeing = undefined;
         this._isAnnotating = false;
         this._iframeClick = undefined;
         const sel = this._iframe?.contentDocument?.getSelection();
-        if (sel?.empty) sel.empty();// Chrome
-        else if (sel?.removeAllRanges) sel.removeAllRanges();  // Firefox
+        if (sel?.empty) sel.empty(); // Chrome
+        else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
         if (x !== undefined && y !== undefined) {
             this._setPreviewCursor?.(x, y, false, false);
             ContextMenu.Instance.closeMenu();
@@ -586,10 +650,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 this.props.docViewPath().lastElement().docView?.onContextMenu(undefined, x, y);
             }
         }
-    }
+    };
 
     @computed get urlContent() {
-        if (this._hackHide || (this.webThumb && (!this._webPageHasBeenRendered && LightboxView.LightboxDoc !== this.rootDoc))) return (null);
+        if (this._hackHide || (this.webThumb && !this._webPageHasBeenRendered && LightboxView.LightboxDoc !== this.rootDoc)) return null;
         this.props.thumbShown?.();
         const field = this.dataDoc[this.props.fieldKey];
         let view;
@@ -597,149 +661,194 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
             view = <span className="webBox-htmlSpan" contentEditable onPointerDown={e => e.stopPropagation()} dangerouslySetInnerHTML={{ __html: field.html }} />;
         } else if (field instanceof WebField) {
             const url = this.layoutDoc.useCors ? Utils.CorsProxy(this._webUrl) : this._webUrl;
-            view = <iframe className="webBox-iframe" enable-annotation={"true"}
-                style={{ pointerEvents: this._scrollTimer ? "none" : undefined }}
-                ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={url} onLoad={this.iframeLoaded}
-                // the 'allow-top-navigation' and 'allow-top-navigation-by-user-activation' attributes are left out to prevent iframes from redirecting the top-level Dash page
-                // sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin allow-scripts"} />;
-                sandbox={`${this.layoutDoc.allowScripts ? "allow-scripts" : ""} allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin`} />;
+            view = (
+                <iframe
+                    className="webBox-iframe"
+                    enable-annotation={'true'}
+                    style={{ pointerEvents: this._scrollTimer ? 'none' : undefined }}
+                    ref={action((r: HTMLIFrameElement | null) => (this._iframe = r))}
+                    src={url}
+                    onLoad={this.iframeLoaded}
+                    // the 'allow-top-navigation' and 'allow-top-navigation-by-user-activation' attributes are left out to prevent iframes from redirecting the top-level Dash page
+                    // sandbox={"allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin allow-scripts"} />;
+                    sandbox={`${this.layoutDoc.allowScripts ? 'allow-scripts' : ''} allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-presentation allow-same-origin`}
+                />
+            );
         } else {
-            view = <iframe className="webBox-iframe" enable-annotation={"true"}
-                style={{ pointerEvents: this._scrollTimer ? "none" : undefined }} // if we allow pointer events when scrolling is on, then reversing direction does not work smoothly
-                ref={action((r: HTMLIFrameElement | null) => this._iframe = r)} src={"https://crossorigin.me/https://cs.brown.edu"} />;
+            view = (
+                <iframe
+                    className="webBox-iframe"
+                    enable-annotation={'true'}
+                    style={{ pointerEvents: this._scrollTimer ? 'none' : undefined }} // if we allow pointer events when scrolling is on, then reversing direction does not work smoothly
+                    ref={action((r: HTMLIFrameElement | null) => (this._iframe = r))}
+                    src={'https://crossorigin.me/https://cs.brown.edu'}
+                />
+            );
         }
-        setTimeout(action(() => {
-            this._scrollHeight = Math.max(this._scrollHeight, this._iframe && this._iframe.contentDocument && this._iframe.contentDocument.body ? this._iframe.contentDocument.body.scrollHeight : 0);
-            if (this._initialScroll === undefined && !this._webPageHasBeenRendered) {
-                this.setScrollPos(NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop)));
-            }
-            this._webPageHasBeenRendered = true;
-        }));
+        setTimeout(
+            action(() => {
+                this._scrollHeight = Math.max(this._scrollHeight, this._iframe && this._iframe.contentDocument && this._iframe.contentDocument.body ? this._iframe.contentDocument.body.scrollHeight : 0);
+                if (this._initialScroll === undefined && !this._webPageHasBeenRendered) {
+                    this.setScrollPos(NumCast(this.layoutDoc.thumbScrollTop, NumCast(this.layoutDoc.scrollTop)));
+                }
+                this._webPageHasBeenRendered = true;
+            })
+        );
         return view;
     }
 
     addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
-        (doc instanceof Doc ? [doc] : doc).forEach(doc => doc.webUrl = this._url);
+        (doc instanceof Doc ? [doc] : doc).forEach(doc => (doc.webUrl = this._url));
         return this.addDocument(doc, annotationKey);
-    }
+    };
 
     sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
         if (!this.layoutDoc._showSidebar) this.toggleSidebar();
         return this.addDocumentWrapper(doc, sidebarKey);
-    }
+    };
     @observable _draggingSidebar = false;
-    sidebarBtnDown = (e: React.PointerEvent, onButton: boolean) => { // onButton determines whether the width of the pdf box changes, or just the ratio of the sidebar to the pdf
-        setupMoveUpEvents(this, e, action((e, down, delta) => {
-            this._draggingSidebar = true;
-            const localDelta = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformDirection(delta[0], delta[1]);
-            const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
-            const nativeHeight = NumCast(this.layoutDoc[this.fieldKey + "-nativeHeight"]);
-            const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
-            const ratio = (curNativeWidth + (onButton ? 1 : -1) * localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
-            if (ratio >= 1) {
-                this.layoutDoc.nativeWidth = nativeWidth * ratio;
-                this.layoutDoc.nativeHeight = nativeHeight * (1 + ratio);
-                onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
-                this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
-            }
-            return false;
-        }), action(() => this._draggingSidebar = false), () => this.toggleSidebar());
-    }
+    sidebarBtnDown = (e: React.PointerEvent, onButton: boolean) => {
+        // onButton determines whether the width of the pdf box changes, or just the ratio of the sidebar to the pdf
+        setupMoveUpEvents(
+            this,
+            e,
+            action((e, down, delta) => {
+                this._draggingSidebar = true;
+                const localDelta = this.props
+                    .ScreenToLocalTransform()
+                    .scale(this.props.scaling?.() || 1)
+                    .transformDirection(delta[0], delta[1]);
+                const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
+                const nativeHeight = NumCast(this.layoutDoc[this.fieldKey + '-nativeHeight']);
+                const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
+                const ratio = (curNativeWidth + ((onButton ? 1 : -1) * localDelta[0]) / (this.props.scaling?.() || 1)) / nativeWidth;
+                if (ratio >= 1) {
+                    this.layoutDoc.nativeWidth = nativeWidth * ratio;
+                    this.layoutDoc.nativeHeight = nativeHeight * (1 + ratio);
+                    onButton && (this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0]);
+                    this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
+                }
+                return false;
+            }),
+            action(() => (this._draggingSidebar = false)),
+            () => this.toggleSidebar()
+        );
+    };
     @observable _previewNativeWidth: Opt<number> = undefined;
     @observable _previewWidth: Opt<number> = undefined;
     toggleSidebar = action((preview: boolean = false) => {
-        var nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
+        var nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
         if (!nativeWidth) {
             const defaultNativeWidth = this.dataDoc[this.fieldKey] instanceof WebField ? 850 : this.Document[WidthSym]();
             Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(this.dataDoc) || defaultNativeWidth);
-            Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || this.Document[HeightSym]() / this.Document[WidthSym]() * defaultNativeWidth);
-            nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
+            Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(this.dataDoc) || (this.Document[HeightSym]() / this.Document[WidthSym]()) * defaultNativeWidth);
+            nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
         }
         const sideratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth : 0) + nativeWidth) / nativeWidth;
         const pdfratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? WebBox.openSidebarWidth + WebBox.sidebarResizerWidth : 0) + nativeWidth) / nativeWidth;
         const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
         if (preview) {
             this._previewNativeWidth = nativeWidth * sideratio;
-            this._previewWidth = this.layoutDoc[WidthSym]() * nativeWidth * sideratio / curNativeWidth;
+            this._previewWidth = (this.layoutDoc[WidthSym]() * nativeWidth * sideratio) / curNativeWidth;
             this._showSidebar = true;
-        }
-        else {
+        } else {
             this.layoutDoc._showSidebar = !this.layoutDoc._showSidebar;
-            this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * pdfratio / curNativeWidth;
+            this.layoutDoc._width = (this.layoutDoc[WidthSym]() * nativeWidth * pdfratio) / curNativeWidth;
             if (!this.layoutDoc._showSidebar && !(this.dataDoc[this.fieldKey] instanceof WebField)) {
-                this.layoutDoc.nativeWidth = this.dataDoc[this.fieldKey + "-nativeWidth"] = undefined;
+                this.layoutDoc.nativeWidth = this.dataDoc[this.fieldKey + '-nativeWidth'] = undefined;
             } else {
                 this.layoutDoc.nativeWidth = nativeWidth * pdfratio;
             }
         }
     });
-    sidebarWidth = () => !this.SidebarShown ? 0 :
-        WebBox.sidebarResizerWidth + (this._previewWidth ? WebBox.openSidebarWidth :
-            (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth))
+    sidebarWidth = () =>
+        !this.SidebarShown ? 0 : WebBox.sidebarResizerWidth + (this._previewWidth ? WebBox.openSidebarWidth : ((NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth()) / NumCast(this.layoutDoc.nativeWidth));
 
     @computed get content() {
-        const interactive = !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && CurrentUserUtils.ActiveTool === InkTool.None && !DocumentDecorations.Instance?.Interacting;
-        return <div className={"webBox-cont" + (interactive ? "-interactive" : "")}
-            onKeyDown={e => e.stopPropagation()}
-            style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]) || `100%` : "100%", }}>
-            {this.urlContent}
-        </div>;
+        const interactive =
+            !this.props.docViewPath().lastElement()?.docView?._pendingDoubleClick && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && Doc.ActiveTool === InkTool.None && !DocumentDecorations.Instance?.Interacting;
+        return (
+            <div className={'webBox-cont' + (interactive ? '-interactive' : '')} onKeyDown={e => e.stopPropagation()} style={{ width: !this.layoutDoc.forceReflow ? NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']) || `100%` : '100%' }}>
+                {this.urlContent}
+            </div>
+        );
     }
 
     @computed get annotationLayer() {
         TraceMobx();
-        return <div className="webBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
-            {this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
-                <Annotation {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />)}
-        </div>;
-
+        return (
+            <div className="webBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
+                {this.inlineTextAnnotations
+                    .sort((a, b) => NumCast(a.y) - NumCast(b.y))
+                    .map(anno => (
+                        <Annotation {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
+                    ))}
+            </div>
+        );
+    }
+    @computed get SidebarShown() {
+        return this._showSidebar || this.layoutDoc._showSidebar ? true : false;
     }
-    @computed get SidebarShown() { return this._showSidebar || this.layoutDoc._showSidebar ? true : false; }
 
     @computed get searchUI() {
-        return <div className="webBox-ui"
-            onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? "flex" : "none" }}>
-            <div className="webBox-overlayCont" onPointerDown={(e) => e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}>
-                <button className="webBox-overlayButton" title={"search"} />
-                <input className="webBox-searchBar" placeholder="Search" ref={this._searchRef} onChange={this.searchStringChanged}
-                    onKeyDown={e => { e.key === "Enter" && this.search(this._searchString, e.shiftKey); e.stopPropagation(); }} />
-                <button className="webBox-search" title="Search" onClick={e => this.search(this._searchString, e.shiftKey)}>
-                    <FontAwesomeIcon icon="search" size="sm" />
+        return (
+            <div className="webBox-ui" onPointerDown={e => e.stopPropagation()} style={{ display: this.props.isContentActive() ? 'flex' : 'none' }}>
+                <div className="webBox-overlayCont" onPointerDown={e => e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}>
+                    <button className="webBox-overlayButton" title={'search'} />
+                    <input
+                        className="webBox-searchBar"
+                        placeholder="Search"
+                        ref={this._searchRef}
+                        onChange={this.searchStringChanged}
+                        onKeyDown={e => {
+                            e.key === 'Enter' && this.search(this._searchString, e.shiftKey);
+                            e.stopPropagation();
+                        }}
+                    />
+                    <button className="webBox-search" title="Search" onClick={e => this.search(this._searchString, e.shiftKey)}>
+                        <FontAwesomeIcon icon="search" size="sm" />
+                    </button>
+                </div>
+                <button
+                    className="webBox-overlayButton"
+                    title={'search'}
+                    onClick={action(() => {
+                        this._searching = !this._searching;
+                        this.search('', false, true);
+                    })}>
+                    <div className="webBox-overlayButton-arrow" onPointerDown={e => e.stopPropagation()} />
+                    <div className="webBox-overlayButton-iconCont" onPointerDown={e => e.stopPropagation()}>
+                        <FontAwesomeIcon icon={this._searching ? 'times' : 'search'} size="lg" />
+                    </div>
                 </button>
             </div>
-            <button className="webBox-overlayButton" title={"search"}
-                onClick={action(() => { this._searching = !this._searching; this.search("", false, true); })} >
-                <div className="webBox-overlayButton-arrow" onPointerDown={(e) => e.stopPropagation()} />
-                <div className="webBox-overlayButton-iconCont" onPointerDown={(e) => e.stopPropagation()}>
-                    <FontAwesomeIcon icon={this._searching ? "times" : "search"} size="lg" />
-                </div>
-            </button>
-        </div>;
+        );
     }
-    searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => this._searchString = e.currentTarget.value;
-    showInfo = action((anno: Opt<Doc>) => this._overlayAnnoInfo = anno);
-    setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => this._setPreviewCursor = func;
+    searchStringChanged = (e: React.ChangeEvent<HTMLInputElement>) => (this._searchString = e.currentTarget.value);
+    showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
+    setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
     panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth() + WebBox.sidebarResizerWidth; // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
     panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
     scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop));
     anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
-    basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter("textInlineAnnotations")];
+    basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter('textInlineAnnotations')];
     transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
     opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
-    childStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => {
+    childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
         if (doc instanceof Doc && property === StyleProp.PointerEvents) {
-            if (doc.textInlineAnnotations) return "none";
+            if (doc.textInlineAnnotations) return 'none';
         }
         return this.props.styleProvider?.(doc, props, property);
-    }
-    pointerEvents = () => !this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none";
-    annotationPointerEvents = () => this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none";
+    };
+    pointerEvents = () => (!this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none');
+    annotationPointerEvents = () => (this._isAnnotating || SnappingManager.GetIsDragging() ? 'all' : 'none');
     render() {
-        const pointerEvents = this.layoutDoc._lockedPosition ? "none" : this.props.pointerEvents?.() as any;
+        const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any);
         const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
         const scale = previewScale * (this.props.scaling?.() || 1);
-        const renderAnnotations = (docFilters?: () => string[]) =>
-            <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
+        const renderAnnotations = (docFilters?: () => string[]) => (
+            <CollectionFreeFormView
+                {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
                 renderDepth={this.props.renderDepth + 1}
                 isAnnotationOverlay={true}
                 fieldKey={this.annotationKey}
@@ -749,7 +858,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 PanelHeight={this.panelHeight}
                 ScreenToLocalTransform={this.scrollXf}
                 scaling={returnOne}
-                dropAction={"alias"}
+                dropAction={'alias'}
                 docFilters={docFilters || this.basicFilter}
                 dontRenderDocuments={docFilters ? false : true}
                 select={emptyFunction}
@@ -760,61 +869,76 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                 moveDocument={this.moveDocument}
                 addDocument={this.addDocument}
                 styleProvider={this.childStyleProvider}
-                childPointerEvents={this.props.isContentActive() ? "all" : undefined}
-                pointerEvents={this.annotationPointerEvents} />;
+                childPointerEvents={this.props.isContentActive() ? 'all' : undefined}
+                pointerEvents={this.annotationPointerEvents}
+            />
+        );
         return (
-            <div className="webBox" ref={this._mainCont}
-                style={{ pointerEvents: this.pointerEvents(), display: this.props.thumbShown?.() ? "none" : undefined }} >
+            <div className="webBox" ref={this._mainCont} style={{ pointerEvents: this.pointerEvents(), display: this.props.thumbShown?.() ? 'none' : undefined }}>
                 <div className="webBox-background" style={{ backgroundColor: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor) }} />
-                <div className="webBox-container" style={{
-                    width: `calc(${100 / scale}% - ${!this.SidebarShown ? 0 : (this.sidebarWidth() - WebBox.sidebarResizerWidth) / scale * (this._previewWidth ? scale : 1)}px)`,
-                    transform: `scale(${scale})`,
-                    pointerEvents
-                }} onContextMenu={this.specificContextMenu}>
-                    <div className={"webBox-outerContent"} ref={this._outerRef}
+                <div
+                    className="webBox-container"
+                    style={{
+                        width: `calc(${100 / scale}% - ${!this.SidebarShown ? 0 : ((this.sidebarWidth() - WebBox.sidebarResizerWidth) / scale) * (this._previewWidth ? scale : 1)}px)`,
+                        transform: `scale(${scale})`,
+                        pointerEvents,
+                    }}
+                    onContextMenu={this.specificContextMenu}>
+                    <div
+                        className={'webBox-outerContent'}
+                        ref={this._outerRef}
                         style={{
                             height: `${100 / scale}%`,
-                            pointerEvents
+                            pointerEvents,
                         }}
-                        onWheel={e => { e.stopPropagation(); e.preventDefault(); }} // block wheel events from propagating since they're handled by the iframe
+                        onWheel={e => {
+                            e.stopPropagation();
+                            e.preventDefault();
+                        }} // block wheel events from propagating since they're handled by the iframe
                         onScroll={e => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)}
-                        onPointerDown={this.onMarqueeDown}
-                    >
-                        <div className={"webBox-innerContent"} style={{ height: this._webPageHasBeenRendered ? NumCast(this.scrollHeight, 50) : "100%", pointerEvents }}>
+                        onPointerDown={this.onMarqueeDown}>
+                        <div className={'webBox-innerContent'} style={{ height: this._webPageHasBeenRendered ? NumCast(this.scrollHeight, 50) : '100%', pointerEvents }}>
                             {this.content}
-                            <div style={{ mixBlendMode: "multiply" }}>
-                                {renderAnnotations(this.transparentFilter)}
-                            </div>
+                            <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
                             {renderAnnotations(this.opaqueFilter)}
-                            {SnappingManager.GetIsDragging() ? (null) : renderAnnotations()}
+                            {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
                             {this.annotationLayer}
                         </div>
                     </div>
-                    {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
-                        <div style={{ transformOrigin: "top left", transform: `scale(${1 / scale})` }}>
-                            <MarqueeAnnotator rootDoc={this.rootDoc}
+                    {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
+                        <div style={{ transformOrigin: 'top left', transform: `scale(${1 / scale})` }}>
+                            <MarqueeAnnotator
+                                rootDoc={this.rootDoc}
                                 iframe={this.isFirefox() ? this.iframeClick : undefined}
                                 iframeScaling={this.isFirefox() ? this.iframeScaling : undefined}
                                 anchorMenuClick={this.anchorMenuClick}
                                 scrollTop={0}
-                                down={this._marqueeing} scaling={returnOne}
+                                down={this._marqueeing}
+                                scaling={returnOne}
                                 addDocument={this.addDocumentWrapper}
                                 docView={this.props.docViewPath().lastElement()}
                                 finishMarquee={this.finishMarquee}
                                 savedAnnotations={this.savedAnnotationsCreator}
                                 annotationLayer={this._annotationLayer.current}
-                                mainCont={this._mainCont.current} /> </div>}
-                </div >
-                <div className="webBox-sideResizer" style={{
-                    display: this.SidebarShown ? undefined : "none",
-                    width: WebBox.sidebarResizerWidth,
-                    left: `calc(100% - ${this.sidebarWidth() - WebBox.sidebarResizerWidth}px)`
-                }}
-                    onPointerDown={e => this.sidebarBtnDown(e, false)} />
-                <SidebarAnnos ref={this._sidebarRef}
+                                mainCont={this._mainCont.current}
+                            />{' '}
+                        </div>
+                    )}
+                </div>
+                <div
+                    className="webBox-sideResizer"
+                    style={{
+                        display: this.SidebarShown ? undefined : 'none',
+                        width: WebBox.sidebarResizerWidth,
+                        left: `calc(100% - ${this.sidebarWidth() - WebBox.sidebarResizerWidth}px)`,
+                    }}
+                    onPointerDown={e => this.sidebarBtnDown(e, false)}
+                />
+                <SidebarAnnos
+                    ref={this._sidebarRef}
                     {...this.props}
                     whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                    fieldKey={this.fieldKey + "-" + this._urlHash}
+                    fieldKey={this.fieldKey + '-' + this._urlHash}
                     rootDoc={this.rootDoc}
                     layoutDoc={this.layoutDoc}
                     dataDoc={this.dataDoc}
@@ -825,17 +949,23 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     moveDocument={this.moveDocument}
                     removeDocument={this.removeDocument}
                 />
-                <div className="webBox-overlayButton-sidebar" key="sidebar" title="Toggle Sidebar"
+                <div
+                    className="webBox-overlayButton-sidebar"
+                    key="sidebar"
+                    title="Toggle Sidebar"
                     style={{
-                        display: !this.props.isContentActive() ? "none" : undefined,
-                        top: StrCast(this.rootDoc._showTitle) === "title" ? 20 : 5,
-                        backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK
+                        display: !this.props.isContentActive() ? 'none' : undefined,
+                        top: StrCast(this.rootDoc._showTitle) === 'title' ? 20 : 5,
+                        backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
                     }}
-                    onPointerDown={e => this.sidebarBtnDown(e, true)} >
-                    <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={"comment-alt"} size="sm" />
+                    onPointerDown={e => this.sidebarBtnDown(e, true)}>
+                    <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
                 </div>
-                {!this.props.isContentActive() ? (null) : this.searchUI}
-            </div>);
+                {!this.props.isContentActive() ? null : this.searchUI}
+            </div>
+        );
     }
 }
-ScriptingGlobals.add(function urlHash(url: string) { return WebBox.urlHash(url); });
\ No newline at end of file
+ScriptingGlobals.add(function urlHash(url: string) {
+    return WebBox.urlHash(url);
+});
diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx
index 539fb5c99..1ce97979e 100644
--- a/src/client/views/nodes/button/FontIconBox.tsx
+++ b/src/client/views/nodes/button/FontIconBox.tsx
@@ -1,7 +1,6 @@
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Tooltip } from '@material-ui/core';
-import { Fragment, Mark, Node, Slice } from 'prosemirror-model';
 import { action, computed, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -12,13 +11,11 @@ import { ScriptField } from '../../../../fields/ScriptField';
 import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
 import { WebField } from '../../../../fields/URLField';
 import { aggregateBounds, Utils } from '../../../../Utils';
-import { DocumentType } from '../../../documents/DocumentTypes';
-import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
+import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
 import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
 import { SelectionManager } from '../../../util/SelectionManager';
 import { undoBatch, UndoManager } from '../../../util/UndoManager';
 import { CollectionFreeFormView } from '../../collections/collectionFreeForm';
-import { CollectionViewType } from '../../collections/CollectionView';
 import { ContextMenu } from '../../ContextMenu';
 import { DocComponent } from '../../DocComponent';
 import { EditableView } from '../../EditableView';
@@ -733,7 +730,7 @@ ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) {
 
 export function checkInksToGroup() {
     // console.log("getting here to inks group");
-    if (CurrentUserUtils.ActiveTool === InkTool.Write) {
+    if (Doc.ActiveTool === InkTool.Write) {
         CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
             // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
             // find all inkDocs in ffView.unprocessedDocs that are within 200 pixels of each other
@@ -746,7 +743,7 @@ export function checkInksToGroup() {
 
 export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
     // TODO nda - if document being added to is a inkGrouping then we can just add to that group
-    if (CurrentUserUtils.ActiveTool === InkTool.Write) {
+    if (Doc.ActiveTool === InkTool.Write) {
         CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => {
             // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those
             const selected = ffView.unprocessedDocs;
@@ -819,30 +816,30 @@ export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) {
 ScriptingGlobals.add(function setActiveTool(tool: string, checkResult?: boolean) {
     InkTranscription.Instance?.createInkGroup();
     if (checkResult) {
-        return (CurrentUserUtils.ActiveTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool ? Colors.MEDIUM_BLUE : 'transparent';
+        return (Doc.ActiveTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool ? Colors.MEDIUM_BLUE : 'transparent';
     }
     if (['circle', 'square', 'line'].includes(tool)) {
         if (GestureOverlay.Instance.InkShape === tool) {
-            CurrentUserUtils.ActiveTool = InkTool.None;
+            Doc.ActiveTool = InkTool.None;
             GestureOverlay.Instance.InkShape = InkTool.None;
         } else {
-            CurrentUserUtils.ActiveTool = InkTool.Pen;
+            Doc.ActiveTool = InkTool.Pen;
             GestureOverlay.Instance.InkShape = tool;
         }
     } else if (tool) {
         // pen or eraser
-        if (CurrentUserUtils.ActiveTool === tool && !GestureOverlay.Instance.InkShape) {
-            CurrentUserUtils.ActiveTool = InkTool.None;
+        if (Doc.ActiveTool === tool && !GestureOverlay.Instance.InkShape) {
+            Doc.ActiveTool = InkTool.None;
         } else if (tool == InkTool.Write) {
             // console.log("write mode selected - create groupDoc here!", tool)
-            CurrentUserUtils.ActiveTool = tool;
+            Doc.ActiveTool = tool;
             GestureOverlay.Instance.InkShape = '';
         } else {
-            CurrentUserUtils.ActiveTool = tool as any;
+            Doc.ActiveTool = tool as any;
             GestureOverlay.Instance.InkShape = '';
         }
     } else {
-        CurrentUserUtils.ActiveTool = InkTool.None;
+        Doc.ActiveTool = InkTool.None;
     }
 });
 
diff --git a/src/client/views/nodes/formattedText/DashDocView.tsx b/src/client/views/nodes/formattedText/DashDocView.tsx
index 9d203b6cc..08f255cab 100644
--- a/src/client/views/nodes/formattedText/DashDocView.tsx
+++ b/src/client/views/nodes/formattedText/DashDocView.tsx
@@ -1,18 +1,17 @@
-import { IReactionDisposer, reaction, observable, action } from 'mobx';
+import { action, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
 import { NodeSelection } from 'prosemirror-state';
+import * as ReactDOM from 'react-dom';
 import { Doc, HeightSym, WidthSym } from '../../../../fields/Doc';
-import { Cast, StrCast, NumCast } from '../../../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, Utils, returnTransparent } from '../../../../Utils';
+import { Cast, NumCast, StrCast } from '../../../../fields/Types';
+import { emptyFunction, returnFalse, Utils } from '../../../../Utils';
 import { DocServer } from '../../../DocServer';
 import { Docs, DocUtils } from '../../../documents/Documents';
-import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
+import { ColorScheme } from '../../../util/SettingsManager';
 import { Transform } from '../../../util/Transform';
 import { DocumentView } from '../DocumentView';
 import { FormattedTextBox } from './FormattedTextBox';
 import React = require('react');
-import * as ReactDOM from 'react-dom';
-import { observer } from 'mobx-react';
-import { ColorScheme } from '../../../util/SettingsManager';
 
 export class DashDocView {
     dom: HTMLSpanElement; // container for label and value
@@ -21,7 +20,7 @@ export class DashDocView {
         this.dom = document.createElement('span');
         this.dom.style.position = 'relative';
         this.dom.style.textIndent = '0';
-        this.dom.style.border = '1px solid ' + StrCast(tbox.layoutDoc.color, CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'dimgray' : 'lightGray');
+        this.dom.style.border = '1px solid ' + StrCast(tbox.layoutDoc.color, Doc.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'dimgray' : 'lightGray');
         this.dom.style.width = node.attrs.width;
         this.dom.style.height = node.attrs.height;
         this.dom.style.display = node.attrs.hidden ? 'none' : 'inline-block';
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 940ed6386..8c8b74560 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -8,7 +8,6 @@ import { SchemaHeaderField } from '../../../../fields/SchemaHeaderField';
 import { ComputedField } from '../../../../fields/ScriptField';
 import { Cast, StrCast } from '../../../../fields/Types';
 import { DocServer } from '../../../DocServer';
-import { CollectionViewType } from '../../collections/CollectionView';
 import './DashFieldView.scss';
 import { FormattedTextBox } from './FormattedTextBox';
 import React = require('react');
@@ -16,6 +15,7 @@ import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils
 import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
 import { Tooltip } from '@material-ui/core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { CollectionViewType } from '../../../documents/DocumentTypes';
 
 export class DashFieldView {
     dom: HTMLDivElement; // container for label and value
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 61940ac09..cfaa428f9 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -25,7 +25,6 @@ import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/G
 import { DocServer } from '../../../DocServer';
 import { Docs, DocUtils } from '../../../documents/Documents';
 import { DocumentType } from '../../../documents/DocumentTypes';
-import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
 import { DictationManager } from '../../../util/DictationManager';
 import { DocumentManager } from '../../../util/DocumentManager';
 import { DragManager } from '../../../util/DragManager';
@@ -259,7 +258,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             e.preventDefault();
             e.stopPropagation();
             const targetCreator = (annotationOn?: Doc) => {
-                const target = CurrentUserUtils.GetNewTextDoc('Note linked to ' + this.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn);
+                const target = DocUtils.GetNewTextDoc('Note linked to ' + this.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn);
                 FormattedTextBox.SelectOnLoad = target[Id];
                 return target;
             };
@@ -386,7 +385,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             var tr = this._editorView.state.tr as any;
             const autoAnch = this._editorView.state.schema.marks.autoLinkAnchor;
             tr = tr.removeMark(0, tr.doc.content.size, autoAnch);
-            DocListCast(CurrentUserUtils.MyPublishedDocs.data).forEach(term => (tr = this.hyperlinkTerm(tr, term, newAutoLinks)));
+            DocListCast(Doc.MyPublishedDocs.data).forEach(term => (tr = this.hyperlinkTerm(tr, term, newAutoLinks)));
             tr = tr.setSelection(new TextSelection(tr.doc.resolve(f), tr.doc.resolve(t)));
             this._editorView?.dispatch(tr);
             oldAutoLinks.filter(oldLink => !newAutoLinks.has(oldLink) && oldLink.anchor2 !== this.rootDoc).forEach(LinkManager.Instance.deleteLink);
@@ -411,7 +410,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             if (!(cfield instanceof ComputedField)) {
                 this.dataDoc.title = prefix + str.substring(0, Math.min(40, str.length)) + (str.length > 40 ? '...' : '');
                 if (str.startsWith('@') && str.length > 1) {
-                    Doc.AddDocToList(CurrentUserUtils.MyPublishedDocs, undefined, this.rootDoc);
+                    Doc.AddDocToList(Doc.MyPublishedDocs, undefined, this.rootDoc);
                 }
             }
         }
@@ -1774,7 +1773,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
             );
         };
         return (
-            <div className={'formattedTextBox-sidebar' + (CurrentUserUtils.ActiveTool !== InkTool.None ? '-inking' : '')} style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+            <div className={'formattedTextBox-sidebar' + (Doc.ActiveTool !== InkTool.None ? '-inking' : '')} style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
                 {renderComponent(StrCast(this.layoutDoc.sidebarViewType))}
             </div>
         );
@@ -1785,7 +1784,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         const selected = active;
         const scale = (this.props.scaling?.() || 1) * NumCast(this.layoutDoc._viewScale, 1);
         const rounded = StrCast(this.layoutDoc.borderRounding) === '100%' ? '-rounded' : '';
-        const interactive = (CurrentUserUtils.ActiveTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition);
+        const interactive = (Doc.ActiveTool === InkTool.None || SnappingManager.GetIsDragging()) && (this.layoutDoc.z || !this.layoutDoc._lockedPosition);
         if (!selected && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide);
         const minimal = this.props.ignoreAutoHeight;
         const paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0);
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 91f8d1efc..292c187e4 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -8,19 +8,19 @@ import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal
 import { Doc, DocListCast, DocListCastAsync, FieldResult } from '../../../../fields/Doc';
 import { InkTool } from '../../../../fields/InkField';
 import { List } from '../../../../fields/List';
-import { PrefetchProxy } from '../../../../fields/Proxy';
 import { listSpec } from '../../../../fields/Schema';
 import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
 import { emptyFunction, returnFalse, returnOne, returnTrue, setupMoveUpEvents } from '../../../../Utils';
 import { Docs } from '../../../documents/Documents';
-import { DocumentType } from '../../../documents/DocumentTypes';
-import { CurrentUserUtils } from '../../../util/CurrentUserUtils';
+import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
 import { DocumentManager } from '../../../util/DocumentManager';
+import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
 import { SelectionManager } from '../../../util/SelectionManager';
+import { SettingsManager } from '../../../util/SettingsManager';
 import { undoBatch, UndoManager } from '../../../util/UndoManager';
 import { CollectionDockingView } from '../../collections/CollectionDockingView';
 import { MarqueeViewBounds } from '../../collections/collectionFreeForm';
-import { CollectionView, CollectionViewType } from '../../collections/CollectionView';
+import { CollectionView } from '../../collections/CollectionView';
 import { TabDocView } from '../../collections/TabDocView';
 import { ViewBoxBaseComponent } from '../../DocComponent';
 import { Colors } from '../../global/globalEnums';
@@ -29,8 +29,6 @@ import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentVie
 import { FieldView, FieldViewProps } from '../FieldView';
 import './PresBox.scss';
 import { PresEffect, PresMovement, PresStatus } from './PresEnums';
-import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
-import { PresElementBox } from '.';
 
 export interface PinProps {
     audioRange?: boolean;
@@ -155,7 +153,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     }
     constructor(props: any) {
         super(props);
-        if ((CurrentUserUtils.ActivePresentation = this.rootDoc)) runInAction(() => (PresBox.Instance = this));
+        if ((Doc.ActivePresentation = this.rootDoc)) runInAction(() => (PresBox.Instance = this));
         this.props.Document.presentationFieldKey = this.fieldKey; // provide info to the presElement script so that it can look up rendering information about the presBox
     }
     @computed get selectedDocumentView() {
@@ -195,7 +193,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         this.layoutDoc._gridGap = 0;
         this.layoutDoc._yMargin = 0;
         this.turnOffEdit(true);
-        DocListCastAsync(CurrentUserUtils.MyTrails.data).then(pres => !pres?.includes(this.rootDoc) && Doc.AddDocToList(CurrentUserUtils.MyTrails, 'data', this.rootDoc));
+        DocListCastAsync(Doc.MyTrails.data).then(pres => !pres?.includes(this.rootDoc) && Doc.AddDocToList(Doc.MyTrails, 'data', this.rootDoc));
         this._disposers.selection = reaction(
             () => SelectionManager.Views(),
             views => views.some(view => view.props.Document === this.rootDoc) && this.updateCurrentPresentation()
@@ -204,8 +202,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @action
     updateCurrentPresentation = (pres?: Doc) => {
-        if (pres) CurrentUserUtils.ActivePresentation = pres;
-        else CurrentUserUtils.ActivePresentation = this.rootDoc;
+        if (pres) Doc.ActivePresentation = pres;
+        else Doc.ActivePresentation = this.rootDoc;
         document.removeEventListener('keydown', PresBox.keyEventsWrapper, true);
         document.addEventListener('keydown', PresBox.keyEventsWrapper, true);
         this._presKeyEventsActive = true;
@@ -648,9 +646,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
      */
     @action
     updateMinimize = async () => {
-        if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
+        if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
             this.layoutDoc.presStatus = PresStatus.Edit;
-            Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, this.rootDoc);
+            Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, this.rootDoc);
             CollectionDockingView.AddSplit(this.rootDoc, 'right');
         } else {
             this.layoutDoc.presStatus = PresStatus.Edit;
@@ -660,7 +658,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
             this.rootDoc.y = pt[1] + 10;
             this.rootDoc._height = 30;
             this.rootDoc._width = 248;
-            Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, this.rootDoc);
+            Doc.AddDocToList(Doc.MyOverlayDocs, undefined, this.rootDoc);
             this.props.removeDocument?.(this.layoutDoc);
         }
     };
@@ -768,7 +766,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
     getTransform = () => this.props.ScreenToLocalTransform().translate(-5, -65); // listBox padding-left and pres-box-cont minHeight
     panelHeight = () => this.props.PanelHeight() - 40;
     isContentActive = (outsideReaction?: boolean) =>
-        CurrentUserUtils.ActiveTool === InkTool.None && !this.layoutDoc._lockedPosition && (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false;
+        Doc.ActiveTool === InkTool.None && !this.layoutDoc._lockedPosition && (this.layoutDoc.forceActive || this.props.isSelected(outsideReaction) || this._isChildActive || this.props.renderDepth === 0) ? true : false;
 
     /**
      * For sorting the array so that the order is maintained when it is dropped.
@@ -918,7 +916,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 }
                 break;
             case 'Escape':
-                if (DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc)) {
+                if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
                     this.updateMinimize();
                 } else if (this.layoutDoc.presStatus === 'edit') {
                     this._selectedArray.clear();
@@ -2714,19 +2712,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
 
     @action
     toggleProperties = () => {
-        if (CurrentUserUtils.propertiesWidth > 0) {
-            CurrentUserUtils.propertiesWidth = 0;
+        if (SettingsManager.propertiesWidth > 0) {
+            SettingsManager.propertiesWidth = 0;
         } else {
-            CurrentUserUtils.propertiesWidth = 250;
+            SettingsManager.propertiesWidth = 250;
         }
     };
 
     @computed get toolbar() {
-        const propIcon = CurrentUserUtils.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left';
-        const propTitle = CurrentUserUtils.propertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel';
+        const propIcon = SettingsManager.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left';
+        const propTitle = SettingsManager.propertiesWidth > 0 ? 'Close Presentation Panel' : 'Open Presentation Panel';
         const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
         const isMini: boolean = this.toolbarWidth <= 100;
-        const presKeyEvents: boolean = this.isPres && this._presKeyEventsActive && this.rootDoc === CurrentUserUtils.ActivePresentation;
+        const presKeyEvents: boolean = this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.ActivePresentation;
         const activeColor = Colors.LIGHT_BLUE;
         const inactiveColor = Colors.WHITE;
         return mode === CollectionViewType.Carousel3D ? null : (
@@ -2776,7 +2774,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                 </>
                             }>
                             <div className="toolbar-button" style={{ position: 'absolute', right: 4, fontSize: 16 }} onClick={this.toggleProperties}>
-                                <FontAwesomeIcon className={'toolbar-thumbtack'} icon={propIcon} style={{ color: CurrentUserUtils.propertiesWidth > 0 ? activeColor : inactiveColor }} />
+                                <FontAwesomeIcon className={'toolbar-thumbtack'} icon={propIcon} style={{ color: SettingsManager.propertiesWidth > 0 ? activeColor : inactiveColor }} />
                             </div>
                         </Tooltip>
                     </>
@@ -3093,10 +3091,10 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         // needed to ensure that the childDocs are loaded for looking up fields
         this.childDocs.slice();
         const mode = StrCast(this.rootDoc._viewType) as CollectionViewType;
-        const presKeyEvents: boolean = this.isPres && this._presKeyEventsActive && this.rootDoc === CurrentUserUtils.ActivePresentation;
+        const presKeyEvents: boolean = this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.ActivePresentation;
         const presEnd: boolean = !this.layoutDoc.presLoop && this.itemIndex === this.childDocs.length - 1;
         const presStart: boolean = !this.layoutDoc.presLoop && this.itemIndex === 0;
-        return DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.rootDoc) ? (
+        return DocListCast(Doc.MyOverlayDocs?.data).includes(this.rootDoc) ? (
             <div className="miniPres" onClick={e => e.stopPropagation()}>
                 <div className="presPanelOverlay" style={{ display: 'inline-flex', height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}>
                     <Tooltip
@@ -3151,7 +3149,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 </div>
             </div>
         ) : (
-            <div className="presBox-cont" style={{ minWidth: DocListCast(CurrentUserUtils.MyOverlayDocs?.data).includes(this.layoutDoc) ? 240 : undefined }}>
+            <div className="presBox-cont" style={{ minWidth: DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc) ? 240 : undefined }}>
                 {this.topPanel}
                 {this.toolbar}
                 {this.newDocumentToolbarDropdown}
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx
index cc7bd5189..1a2054676 100644
--- a/src/client/views/nodes/trails/PresElementBox.tsx
+++ b/src/client/views/nodes/trails/PresElementBox.tsx
@@ -1,52 +1,66 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Tooltip } from "@material-ui/core";
-import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, DocListCast, HeightSym, Opt, WidthSym } from "../../../../fields/Doc";
-import { Id } from "../../../../fields/FieldSymbols";
-import { BoolCast, Cast, DocCast, NumCast, StrCast } from "../../../../fields/Types";
-import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from "../../../../Utils";
-import { Docs, DocUtils } from "../../../documents/Documents";
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
-import { DocumentManager } from "../../../util/DocumentManager";
-import { DragManager } from "../../../util/DragManager";
-import { Transform } from "../../../util/Transform";
-import { undoBatch } from "../../../util/UndoManager";
-import { CollectionViewType } from "../../collections/CollectionView";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../../fields/Doc';
+import { Id } from '../../../../fields/FieldSymbols';
+import { List } from '../../../../fields/List';
+import { Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
+import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
+import { Docs, DocUtils } from '../../../documents/Documents';
+import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
+import { DocumentManager } from '../../../util/DocumentManager';
+import { DragManager } from '../../../util/DragManager';
+import { SettingsManager } from '../../../util/SettingsManager';
+import { Transform } from '../../../util/Transform';
+import { undoBatch } from '../../../util/UndoManager';
 import { ViewBoxBaseComponent } from '../../DocComponent';
-import { EditableView } from "../../EditableView";
-import { Colors } from "../../global/globalEnums";
-import { DocumentView, DocumentViewProps } from "../../nodes/DocumentView";
+import { EditableView } from '../../EditableView';
+import { Colors } from '../../global/globalEnums';
+import { DocumentView, DocumentViewProps } from '../../nodes/DocumentView';
 import { FieldView, FieldViewProps } from '../../nodes/FieldView';
-import { StyleProp } from "../../StyleProvider";
-import { PresBox } from "./PresBox";
-import "./PresElementBox.scss";
-import { PresMovement } from "./PresEnums";
-import React = require("react");
-import { List } from "../../../../fields/List";
+import { StyleProp } from '../../StyleProvider';
+import { PresBox } from './PresBox';
+import './PresElementBox.scss';
+import { PresMovement } from './PresEnums';
+import React = require('react');
 /**
  * This class models the view a document added to presentation will have in the presentation.
  * It involves some functionality for its buttons and options.
  */
 @observer
 export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresElementBox, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(PresElementBox, fieldKey);
+    }
     _heightDisposer: IReactionDisposer | undefined;
 
     @observable _dragging = false;
     // Idea: this boolean will determine whether to automatically show the video when this preselement is selected.
     // @observable static showVideo: boolean = false;
-    @computed get indexInPres() { return DocListCast(this.presBox[StrCast(this.presBox.presFieldKey, "data")]).indexOf(this.rootDoc); }  // the index field is where this document is in the presBox display list (since this value is different for each presentation element, the value can't be stored on the layout template which is used by all display elements)
-    @computed get collapsedHeight() { return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(this.presBox._viewType as any) ? 35 : 31; } // the collapsed height changes depending on the state of the presBox.  We could store this on the presentation element template if it's used by only one presentation - but if it's shared by multiple, then this value must be looked up
-    @computed get presStatus() { return this.presBox.presStatus; }
-    @computed get presBox() { return (this.props.DocumentView?.().props.treeViewDoc ?? this.props.ContainingCollectionDoc)!; }
-    @computed get targetDoc() { return Cast(this.rootDoc.presentationTargetDoc, Doc, null) || this.rootDoc; }
+    @computed get indexInPres() {
+        return DocListCast(this.presBox[StrCast(this.presBox.presFieldKey, 'data')]).indexOf(this.rootDoc);
+    } // the index field is where this document is in the presBox display list (since this value is different for each presentation element, the value can't be stored on the layout template which is used by all display elements)
+    @computed get collapsedHeight() {
+        return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(this.presBox._viewType as any) ? 35 : 31;
+    } // the collapsed height changes depending on the state of the presBox.  We could store this on the presentation element template if it's used by only one presentation - but if it's shared by multiple, then this value must be looked up
+    @computed get presStatus() {
+        return this.presBox.presStatus;
+    }
+    @computed get presBox() {
+        return (this.props.DocumentView?.().props.treeViewDoc ?? this.props.ContainingCollectionDoc)!;
+    }
+    @computed get targetDoc() {
+        return Cast(this.rootDoc.presentationTargetDoc, Doc, null) || this.rootDoc;
+    }
 
     componentDidMount() {
         this.layoutDoc.hideLinkButton = true;
-        this._heightDisposer = reaction(() => [this.rootDoc.presExpandInlineButton, this.collapsedHeight],
-            params => this.layoutDoc._height = NumCast(params[1]) + (Number(params[0]) ? 100 : 0), { fireImmediately: true });
+        this._heightDisposer = reaction(
+            () => [this.rootDoc.presExpandInlineButton, this.collapsedHeight],
+            params => (this.layoutDoc._height = NumCast(params[1]) + (Number(params[0]) ? 100 : 0)),
+            { fireImmediately: true }
+        );
     }
     componentWillUnmount() {
         this._heightDisposer?.();
@@ -60,26 +74,26 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
     @action
     presExpandDocumentClick = () => {
         this.rootDoc.presExpandInlineButton = !this.rootDoc.presExpandInlineButton;
-    }
+    };
 
     embedHeight = (): number => 97;
     // embedWidth = () => this.props.PanelWidth();
     // embedHeight = () => Math.min(this.props.PanelWidth() - 20, this.props.PanelHeight() - this.collapsedHeight);
     embedWidth = (): number => this.props.PanelWidth() - 35;
-    styleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => {
+    styleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
         if (property === StyleProp.Opacity) return 1;
         return this.props.styleProvider?.(doc, props, property);
-    }
+    };
     /**
      * The function that is responsible for rendering a preview or not for this
      * presentation element.
      */
     @computed get renderEmbeddedInline() {
-        return !this.rootDoc.presExpandInlineButton || !this.targetDoc ? (null) :
+        return !this.rootDoc.presExpandInlineButton || !this.targetDoc ? null : (
             <div className="presItem-embedded" style={{ height: this.embedHeight(), width: this.embedWidth() }}>
                 <DocumentView
                     Document={this.rootDoc}
-                    DataDoc={undefined}//this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
+                    DataDoc={undefined} //this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]}
                     styleProvider={this.styleProvider}
                     docViewPath={returnEmptyDoclist}
                     rootSelected={returnTrue}
@@ -105,22 +119,23 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                     hideLinkButton={true}
                 />
                 <div className="presItem-embeddedMask" />
-            </div>;
+            </div>
+        );
     }
 
     @computed get renderGroupSlides() {
         const childDocs = DocListCast(this.targetDoc.data);
-        const groupSlides = childDocs.map((doc: Doc, ind: number) =>
-            <div className="presItem-groupSlide" onClick={(e) => {
-                console.log("Clicked on slide with index: ", ind);
-                e.stopPropagation();
-                e.preventDefault();
-                PresBox.Instance.modifierSelect(doc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
-                this.presExpandDocumentClick();
-            }}>
-                <div className="presItem-groupNum">
-                    {`${ind + 1}.`}
-                </div>
+        const groupSlides = childDocs.map((doc: Doc, ind: number) => (
+            <div
+                className="presItem-groupSlide"
+                onClick={e => {
+                    console.log('Clicked on slide with index: ', ind);
+                    e.stopPropagation();
+                    e.preventDefault();
+                    PresBox.Instance.modifierSelect(doc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey);
+                    this.presExpandDocumentClick();
+                }}>
+                <div className="presItem-groupNum">{`${ind + 1}.`}</div>
                 {/* style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105, cursor: isSelected ? 'text' : 'grab' }} */}
                 <div className="presItem-name">
                     <EditableView
@@ -130,37 +145,36 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                         overflow={'ellipsis'}
                         GetValue={() => StrCast(doc.title)}
                         SetValue={(value: string) => {
-                            doc.title = !value.trim().length ? "-untitled-" : value;
+                            doc.title = !value.trim().length ? '-untitled-' : value;
                             return true;
                         }}
                     />
                 </div>
             </div>
-        );
-        return (
-            groupSlides
-        );
+        ));
+        return groupSlides;
     }
     @computed get duration() {
         let durationInS: number;
-        if (this.rootDoc.type === DocumentType.AUDIO || this.rootDoc.type === DocumentType.VID) { durationInS = NumCast(this.rootDoc.presEndTime) - NumCast(this.rootDoc.presStartTime); durationInS = Math.round(durationInS * 10) / 10; }
-        else if (this.rootDoc.presDuration) durationInS = NumCast(this.rootDoc.presDuration) / 1000;
+        if (this.rootDoc.type === DocumentType.AUDIO || this.rootDoc.type === DocumentType.VID) {
+            durationInS = NumCast(this.rootDoc.presEndTime) - NumCast(this.rootDoc.presStartTime);
+            durationInS = Math.round(durationInS * 10) / 10;
+        } else if (this.rootDoc.presDuration) durationInS = NumCast(this.rootDoc.presDuration) / 1000;
         else durationInS = 2;
-        return "D: " + durationInS + "s";
+        return 'D: ' + durationInS + 's';
     }
 
     @computed get transition() {
         let transitionInS: number;
         if (this.rootDoc.presTransition) transitionInS = NumCast(this.rootDoc.presTransition) / 1000;
         else transitionInS = 0.5;
-        return this.rootDoc.presMovement === PresMovement.Jump || this.rootDoc.presMovement === PresMovement.None ? (null) : "M: " + transitionInS + "s";
+        return this.rootDoc.presMovement === PresMovement.Jump || this.rootDoc.presMovement === PresMovement.None ? null : 'M: ' + transitionInS + 's';
     }
 
     private _itemRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _dragRef: React.RefObject<HTMLDivElement> = React.createRef();
     private _titleRef: React.RefObject<EditableView> = React.createRef();
 
-
     @action
     headerDown = (e: React.PointerEvent<HTMLDivElement>) => {
         const element = e.target as any;
@@ -171,18 +185,24 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 PresBox.Instance._selectedArray.size === 1 && PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false);
                 setupMoveUpEvents(this, e, this.startDrag, emptyFunction, emptyFunction);
             } else {
-                setupMoveUpEvents(this, e, ((e: PointerEvent) => {
-                    PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false);
-                    return this.startDrag(e);
-                }), emptyFunction, emptyFunction);
+                setupMoveUpEvents(
+                    this,
+                    e,
+                    (e: PointerEvent) => {
+                        PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false);
+                        return this.startDrag(e);
+                    },
+                    emptyFunction,
+                    emptyFunction
+                );
             }
         }
-    }
+    };
 
     headerUp = (e: React.PointerEvent<HTMLDivElement>) => {
         e.stopPropagation();
         e.preventDefault();
-    }
+    };
 
     /**
      * Function to drag and drop the pres element to a diferent location
@@ -193,37 +213,43 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const dragArray = PresBox.Instance._dragArray;
         const dragData = new DragManager.DocumentDragData(PresBox.Instance.sortArray());
         if (!dragData.draggedDocuments.length) dragData.draggedDocuments.push(this.rootDoc);
-        dragData.dropAction = "move";
+        dragData.dropAction = 'move';
         dragData.treeViewDoc = this.props.docViewPath().lastElement()?.props.treeViewDoc;
         dragData.moveDocument = this.props.docViewPath().lastElement()?.props.moveDocument;
         const dragItem: HTMLElement[] = [];
         if (dragArray.length === 1) {
             const doc = this._itemRef.current || dragArray[0];
-            doc.className = miniView ? "presItem-miniSlide" : "presItem-slide";
+            doc.className = miniView ? 'presItem-miniSlide' : 'presItem-slide';
             dragItem.push(doc);
         } else if (dragArray.length >= 1) {
             const doc = document.createElement('div');
-            doc.className = "presItem-multiDrag";
-            doc.innerText = "Move " + PresBox.Instance._selectedArray.size + " slides";
+            doc.className = 'presItem-multiDrag';
+            doc.innerText = 'Move ' + PresBox.Instance._selectedArray.size + ' slides';
             doc.style.position = 'absolute';
-            doc.style.top = (e.clientY) + 'px';
-            doc.style.left = (e.clientX - 50) + 'px';
+            doc.style.top = e.clientY + 'px';
+            doc.style.left = e.clientX - 50 + 'px';
             dragItem.push(doc);
         }
 
         // const dropEvent = () => runInAction(() => this._dragging = false);
         if (activeItem) {
-            DragManager.StartDocumentDrag(dragItem.map(ele => ele), dragData, e.clientX, e.clientY, undefined);
+            DragManager.StartDocumentDrag(
+                dragItem.map(ele => ele),
+                dragData,
+                e.clientX,
+                e.clientY,
+                undefined
+            );
             // runInAction(() => this._dragging = true);
             return true;
         }
         return false;
-    }
+    };
 
     onPointerOver = (e: any) => {
-        document.removeEventListener("pointermove", this.onPointerMove);
-        document.addEventListener("pointermove", this.onPointerMove);
-    }
+        document.removeEventListener('pointermove', this.onPointerMove);
+        document.addEventListener('pointermove', this.onPointerMove);
+    };
 
     onPointerMove = (e: PointerEvent) => {
         const slide = this._itemRef.current!;
@@ -233,32 +259,32 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         }
         if (slide && dragIsPresItem) {
             const rect = slide.getBoundingClientRect();
-            const y = e.clientY - rect.top;  //y position within the element.
+            const y = e.clientY - rect.top; //y position within the element.
             const height = slide.clientHeight;
             const halfLine = height / 2;
             if (y <= halfLine) {
                 slide.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`;
-                slide.style.borderBottom = "0px";
+                slide.style.borderBottom = '0px';
             } else if (y > halfLine) {
-                slide.style.borderTop = "0px";
+                slide.style.borderTop = '0px';
                 slide.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`;
             }
         }
-        document.removeEventListener("pointermove", this.onPointerMove);
-    }
+        document.removeEventListener('pointermove', this.onPointerMove);
+    };
 
     onPointerLeave = (e: any) => {
-        this._itemRef.current!.style.borderTop = "0px";
-        this._itemRef.current!.style.borderBottom = "0px";
-        document.removeEventListener("pointermove", this.onPointerMove);
-    }
+        this._itemRef.current!.style.borderTop = '0px';
+        this._itemRef.current!.style.borderBottom = '0px';
+        document.removeEventListener('pointermove', this.onPointerMove);
+    };
 
     @action
     toggleProperties = () => {
-        if (CurrentUserUtils.propertiesWidth < 5) {
-            action(() => (CurrentUserUtils.propertiesWidth = 250));
+        if (SettingsManager.propertiesWidth < 5) {
+            action(() => (SettingsManager.propertiesWidth = 250));
         }
-    }
+    };
 
     @undoBatch
     removeItem = action((e: React.MouseEvent) => {
@@ -267,32 +293,35 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         if (PresBox.Instance._selectedArray.has(this.rootDoc)) {
             PresBox.Instance._selectedArray.delete(this.rootDoc);
         }
-        this.removeAllRecordingInOverlay()
+        this.removeAllRecordingInOverlay();
     });
 
     // set the value/title of the individual pres element
     @undoBatch
     @action
     onSetValue = (value: string) => {
-        this.rootDoc.title = !value.trim().length ? "-untitled-" : value;
+        this.rootDoc.title = !value.trim().length ? '-untitled-' : value;
         return true;
-    }
+    };
 
     /**
      * Method called for updating the view of the currently selected document
-     * 
-     * @param targetDoc 
-     * @param activeItem 
+     *
+     * @param targetDoc
+     * @param activeItem
      */
     @undoBatch
     @action
     updateView = (targetDoc: Doc, activeItem: Doc) => {
         switch (targetDoc.type) {
-            case DocumentType.PDF: case DocumentType.WEB: case DocumentType.RTF:
+            case DocumentType.PDF:
+            case DocumentType.WEB:
+            case DocumentType.RTF:
                 const scroll = targetDoc._scrollTop;
                 activeItem.presPinViewScroll = scroll;
                 break;
-            case DocumentType.VID: case DocumentType.AUDIO:
+            case DocumentType.VID:
+            case DocumentType.AUDIO:
                 activeItem.presStartTime = targetDoc._currentTimecode;
                 break;
             case DocumentType.COMPARISON:
@@ -307,53 +336,53 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 activeItem.presPinViewY = y;
                 activeItem.presPinViewScale = scale;
         }
-    }
+    };
 
     @computed get recordingIsInOverlay() {
-        let isInOverlay = false
-        DocListCast(CurrentUserUtils.MyOverlayDocs.data).forEach((doc) => {
+        let isInOverlay = false;
+        DocListCast(Doc.MyOverlayDocs.data).forEach(doc => {
             if (doc.slides === this.rootDoc) {
-                isInOverlay = true
+                isInOverlay = true;
                 // return
             }
-        })
-        return isInOverlay
+        });
+        return isInOverlay;
     }
 
     // a previously recorded video will have timecode defined
     static videoIsRecorded = (activeItem: Doc) => {
         const casted = Cast(activeItem.recording, Doc, null);
         return casted && 'currentTimecode' in casted;
-    }
+    };
 
     removeAllRecordingInOverlay = () => {
-        DocListCast(CurrentUserUtils.MyOverlayDocs.data).forEach((doc) => {
+        DocListCast(Doc.MyOverlayDocs.data).forEach(doc => {
             if (doc.slides === this.rootDoc) {
-                Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, doc);
+                Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, doc);
             }
-        })
-    }
+        });
+    };
 
     static removeEveryExistingRecordingInOverlay = () => {
         // Remove every recording that already exists in overlay view
-        DocListCast(CurrentUserUtils.MyOverlayDocs.data).forEach((doc) => {
+        DocListCast(Doc.MyOverlayDocs.data).forEach(doc => {
             // if it's a recording video, don't remove from overlay (user can lose data)
-            if (!PresElementBox.videoIsRecorded(DocCast(doc.slides))) return
+            if (!PresElementBox.videoIsRecorded(DocCast(doc.slides))) return;
 
             if (doc.slides !== null) {
-                Doc.RemoveDocFromList(CurrentUserUtils.MyOverlayDocs, undefined, doc);
+                Doc.RemoveDocFromList(Doc.MyOverlayDocs, undefined, doc);
             }
-        })
-    }
+        });
+    };
 
     @undoBatch
     @action
     hideRecording = (e: React.MouseEvent, iconClick: boolean = false) => {
-        e.stopPropagation()
-        this.removeAllRecordingInOverlay()
+        e.stopPropagation();
+        this.removeAllRecordingInOverlay();
 
         // if (iconClick) PresElementBox.showVideo = false;
-    }
+    };
 
     @undoBatch
     @action
@@ -364,47 +393,46 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         // remove the overlays on switch *IF* not opened from the specific icon
         if (!iconClick) PresElementBox.removeEveryExistingRecordingInOverlay();
 
-
         if (activeItem.recording) {
-            Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, Cast(activeItem.recording, Doc, null));
+            Doc.AddDocToList(Doc.MyOverlayDocs, undefined, Cast(activeItem.recording, Doc, null));
         }
-    }
+    };
 
     @undoBatch
     @action
     startRecording = (activeItem: Doc) => {
-        console.log('start recording', 'activeItem', activeItem)
+        console.log('start recording', 'activeItem', activeItem);
         if (PresElementBox.videoIsRecorded(activeItem)) {
             // if we already have an existing recording
             this.showRecording(activeItem, true);
             //   // if we already have an existing recording
-            //   Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, Cast(activeItem.recording, Doc, null));
-
+            //   Doc.AddDocToList(Doc.MyOverlayDocs, undefined, Cast(activeItem.recording, Doc, null));
         } else {
             // Remove every recording that already exists in overlay view
             // this is a design decision to clear to focus in on the recoding mode
             PresElementBox.removeEveryExistingRecordingInOverlay();
 
             // if we dont have any recording
-            const recording = Docs.Create.WebCamDocument("", {
-                _width: 384, _height: 216,
+            const recording = Docs.Create.WebCamDocument('', {
+                _width: 384,
+                _height: 216,
                 hideDocumentButtonBar: true,
                 hideDecorationTitle: true,
                 hideOpenButton: true,
                 // hideDeleteButton: true,
-                cloneFieldFilter: new List<string>(["system"])
+                cloneFieldFilter: new List<string>(['system']),
             });
 
             // attach the recording to the slide, and attach the slide to the recording
-            recording.slides = activeItem
-            activeItem.recording = recording
+            recording.slides = activeItem;
+            activeItem.recording = recording;
 
             // make recording box appear in the bottom right corner of the screen
             recording.x = window.innerWidth - recording[WidthSym]() - 20;
             recording.y = window.innerHeight - recording[HeightSym]() - 20;
-            Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, recording);
+            Doc.AddDocToList(Doc.MyOverlayDocs, undefined, recording);
         }
-    }
+    };
 
     @computed
     get toolbarWidth(): number {
@@ -422,17 +450,18 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const miniView: boolean = this.toolbarWidth <= 110;
         const presBox: Doc = this.presBox; //presBox
         const presBoxColor: string = StrCast(presBox._backgroundColor);
-        const presColorBool: boolean = presBoxColor ? (presBoxColor !== Colors.WHITE && presBoxColor !== "transparent") : false;
+        const presColorBool: boolean = presBoxColor ? presBoxColor !== Colors.WHITE && presBoxColor !== 'transparent' : false;
         const targetDoc: Doc = this.targetDoc;
         const activeItem: Doc = this.rootDoc;
 
         return (
-            <div className={`presItem-container`}
+            <div
+                className={`presItem-container`}
                 key={this.props.Document[Id] + this.indexInPres}
                 ref={this._itemRef}
                 style={{
-                    backgroundColor: presColorBool ? isSelected ? "rgba(250,250,250,0.3)" : "transparent" : isSelected ? Colors.LIGHT_BLUE : "transparent",
-                    opacity: this._dragging ? 0.3 : 1
+                    backgroundColor: presColorBool ? (isSelected ? 'rgba(250,250,250,0.3)' : 'transparent') : isSelected ? Colors.LIGHT_BLUE : 'transparent',
+                    opacity: this._dragging ? 0.3 : 1,
                 }}
                 onClick={e => {
                     e.stopPropagation();
@@ -447,8 +476,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 onPointerOver={this.onPointerOver}
                 onPointerLeave={this.onPointerLeave}
                 onPointerDown={this.headerDown}
-                onPointerUp={this.headerUp}
-            >
+                onPointerUp={this.headerUp}>
                 {/* {miniView ?
                               // when width is LESS than 110 px 
                               <div className={`presItem-miniSlide ${isSelected ? "active" : ""}`} ref={miniView ? this._dragRef : null}>
@@ -462,50 +490,63 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                 {/* <div className="presItem-number">
                               {`${this.indexInPres + 1}.`}
                         </div> */}
-                {miniView ? (null) : <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "active" : ""}`}
-                    style={{
-                        backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
-                        boxShadow: presBoxColor && presBoxColor !== "white" && presBoxColor !== "transparent" ? isSelected ? "0 0 0px 1.5px" + presBoxColor : undefined : undefined
-                    }}>
-                    <div className="presItem-name" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105, cursor: isSelected ? 'text' : 'grab' }}>
-                        <div>{`${this.indexInPres + 1}.  `}</div>
-                        <EditableView
-                            ref={this._titleRef}
-                            editing={!isSelected ? false : undefined}
-                            contents={activeItem.title}
-                            overflow={'ellipsis'}
-                            GetValue={() => StrCast(activeItem.title)}
-                            SetValue={this.onSetValue}
-                        />
-                    </div>
-                    {/* <Tooltip title={<><div className="dash-tooltip">{"Movement speed"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.transition}</div></Tooltip> */}
-                    {/* <Tooltip title={<><div className="dash-tooltip">{"Duration"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.duration}</div></Tooltip> */}
-                    <div className={"presItem-slideButtons"}>
-                        <Tooltip title={<><div className="dash-tooltip">{"Update view"}</div></>}>
-                            <div className="slideButton"
-                                onClick={() => this.updateView(targetDoc, activeItem)}
-                                style={{ fontWeight: 700, display: activeItem.presPinView ? "flex" : "none" }}>V</div>
-                        </Tooltip>
-
-                        {this.recordingIsInOverlay ?
-                            <Tooltip title={<><div className="dash-tooltip">{"Hide Recording"}</div></>}>
-                                <div className="slideButton"
-                                    onClick={(e) => this.hideRecording(e, true)}
-                                    style={{ fontWeight: 700 }}>
-                                    <FontAwesomeIcon icon={"video-slash"} onPointerDown={e => e.stopPropagation()} />
-                                </div>
-                            </Tooltip> :
-                            <Tooltip title={<><div className="dash-tooltip">{`${PresElementBox.videoIsRecorded(activeItem) ? "Show" : "Start"} recording`}</div></>}>
-                                <div className="slideButton"
-                                    onClick={(e) => { e.stopPropagation(); this.startRecording(activeItem); }}
-                                    style={{ fontWeight: 700 }}>
-                                    <FontAwesomeIcon icon={"video"} onPointerDown={e => e.stopPropagation()} />
+                {miniView ? null : (
+                    <div
+                        ref={miniView ? null : this._dragRef}
+                        className={`presItem-slide ${isSelected ? 'active' : ''}`}
+                        style={{
+                            backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor),
+                            boxShadow: presBoxColor && presBoxColor !== 'white' && presBoxColor !== 'transparent' ? (isSelected ? '0 0 0px 1.5px' + presBoxColor : undefined) : undefined,
+                        }}>
+                        <div className="presItem-name" style={{ maxWidth: showMore ? toolbarWidth - 195 : toolbarWidth - 105, cursor: isSelected ? 'text' : 'grab' }}>
+                            <div>{`${this.indexInPres + 1}.  `}</div>
+                            <EditableView ref={this._titleRef} editing={!isSelected ? false : undefined} contents={activeItem.title} overflow={'ellipsis'} GetValue={() => StrCast(activeItem.title)} SetValue={this.onSetValue} />
+                        </div>
+                        {/* <Tooltip title={<><div className="dash-tooltip">{"Movement speed"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.transition}</div></Tooltip> */}
+                        {/* <Tooltip title={<><div className="dash-tooltip">{"Duration"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.duration}</div></Tooltip> */}
+                        <div className={'presItem-slideButtons'}>
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">{'Update view'}</div>
+                                    </>
+                                }>
+                                <div className="slideButton" onClick={() => this.updateView(targetDoc, activeItem)} style={{ fontWeight: 700, display: activeItem.presPinView ? 'flex' : 'none' }}>
+                                    V
                                 </div>
                             </Tooltip>
-                        }
-
 
-                        {/* {this.indexInPres === 0 ? (null) : <Tooltip title={<><div className="dash-tooltip">{activeItem.groupWithUp ? "Ungroup" : "Group with up"}</div></>}>
+                            {this.recordingIsInOverlay ? (
+                                <Tooltip
+                                    title={
+                                        <>
+                                            <div className="dash-tooltip">{'Hide Recording'}</div>
+                                        </>
+                                    }>
+                                    <div className="slideButton" onClick={e => this.hideRecording(e, true)} style={{ fontWeight: 700 }}>
+                                        <FontAwesomeIcon icon={'video-slash'} onPointerDown={e => e.stopPropagation()} />
+                                    </div>
+                                </Tooltip>
+                            ) : (
+                                <Tooltip
+                                    title={
+                                        <>
+                                            <div className="dash-tooltip">{`${PresElementBox.videoIsRecorded(activeItem) ? 'Show' : 'Start'} recording`}</div>
+                                        </>
+                                    }>
+                                    <div
+                                        className="slideButton"
+                                        onClick={e => {
+                                            e.stopPropagation();
+                                            this.startRecording(activeItem);
+                                        }}
+                                        style={{ fontWeight: 700 }}>
+                                        <FontAwesomeIcon icon={'video'} onPointerDown={e => e.stopPropagation()} />
+                                    </div>
+                                </Tooltip>
+                            )}
+
+                            {/* {this.indexInPres === 0 ? (null) : <Tooltip title={<><div className="dash-tooltip">{activeItem.groupWithUp ? "Ungroup" : "Group with up"}</div></>}>
                                           <div className="slideButton"
                                                 onClick={() => activeItem.groupWithUp = !activeItem.groupWithUp}
                                                 style={{
@@ -520,22 +561,41 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() {
                                                 </div>
                                           </div>
                                     </Tooltip>} */}
-                        <Tooltip title={<><div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? "Minimize" : "Expand"}</div></>}><div className={"slideButton"} onClick={e => { e.stopPropagation(); this.presExpandDocumentClick(); }}>
-                            <FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? "eye-slash" : "eye"} onPointerDown={e => e.stopPropagation()} />
-                        </div></Tooltip>
-                        <Tooltip title={<><div className="dash-tooltip">{"Remove from presentation"}</div></>}><div
-                            className={"slideButton"}
-                            onClick={this.removeItem}>
-                            <FontAwesomeIcon icon={"trash"} onPointerDown={e => e.stopPropagation()} />
-                        </div></Tooltip>
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? 'Minimize' : 'Expand'}</div>
+                                    </>
+                                }>
+                                <div
+                                    className={'slideButton'}
+                                    onClick={e => {
+                                        e.stopPropagation();
+                                        this.presExpandDocumentClick();
+                                    }}>
+                                    <FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? 'eye-slash' : 'eye'} onPointerDown={e => e.stopPropagation()} />
+                                </div>
+                            </Tooltip>
+                            <Tooltip
+                                title={
+                                    <>
+                                        <div className="dash-tooltip">{'Remove from presentation'}</div>
+                                    </>
+                                }>
+                                <div className={'slideButton'} onClick={this.removeItem}>
+                                    <FontAwesomeIcon icon={'trash'} onPointerDown={e => e.stopPropagation()} />
+                                </div>
+                            </Tooltip>
+                        </div>
+                        {/* <div className="presItem-docName" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105 }}>{activeItem.presPinView ? (<><i>View of </i> {targetDoc.title}</>) : targetDoc.title}</div> */}
+                        {this.renderEmbeddedInline}
                     </div>
-                    {/* <div className="presItem-docName" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105 }}>{activeItem.presPinView ? (<><i>View of </i> {targetDoc.title}</>) : targetDoc.title}</div> */}
-                    {this.renderEmbeddedInline}
-                </div>}
-            </div >);
+                )}
+            </div>
+        );
     }
 
     render() {
-        return !(this.rootDoc instanceof Doc) || this.targetDoc instanceof Promise ? (null) : this.mainItem;
+        return !(this.rootDoc instanceof Doc) || this.targetDoc instanceof Promise ? null : this.mainItem;
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 5bdce273d..44f815336 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -1,15 +1,15 @@
-import React = require("react");
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
-import { observer } from "mobx-react";
-import { Doc, DocListCast, Opt } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
-import { List } from "../../../fields/List";
-import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
-import { LinkManager } from "../../util/LinkManager";
-import { undoBatch } from "../../util/UndoManager";
-import { FieldViewProps } from "../nodes/FieldView";
-import { AnchorMenu } from "./AnchorMenu";
-import "./Annotation.scss";
+import React = require('react');
+import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { List } from '../../../fields/List';
+import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
+import { LinkFollower } from '../../util/LinkFollower';
+import { undoBatch } from '../../util/UndoManager';
+import { FieldViewProps } from '../nodes/FieldView';
+import { AnchorMenu } from './AnchorMenu';
+import './Annotation.scss';
 
 interface IAnnotationProps extends FieldViewProps {
     anno: Doc;
@@ -19,14 +19,15 @@ interface IAnnotationProps extends FieldViewProps {
     pointerEvents?: () => Opt<string>;
 }
 @observer
-export
-    class Annotation extends React.Component<IAnnotationProps> {
+export class Annotation extends React.Component<IAnnotationProps> {
     render() {
-        return <div>
-            {DocListCast(this.props.anno.textInlineAnnotations).map(a =>
-                <RegionAnnotation pointerEvents={this.props.pointerEvents} {...this.props} document={a} key={a[Id]} />
-            )}
-        </div>;
+        return (
+            <div>
+                {DocListCast(this.props.anno.textInlineAnnotations).map(a => (
+                    <RegionAnnotation pointerEvents={this.props.pointerEvents} {...this.props} document={a} key={a[Id]} />
+                ))}
+            </div>
+        );
     }
 }
 
@@ -38,7 +39,9 @@ interface IRegionAnnotationProps extends IAnnotationProps {
 class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
     private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
 
-    @computed get annoTextRegion() { return Cast(this.props.document.annoTextRegion, Doc, null) || this.props.document; }
+    @computed get annoTextRegion() {
+        return Cast(this.props.document.annoTextRegion, Doc, null) || this.props.document;
+    }
 
     @undoBatch
     deleteAnnotation = () => {
@@ -46,20 +49,20 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
         this.props.dataDoc[this.props.fieldKey] = new List<Doc>(docAnnotations.filter(a => a !== this.annoTextRegion));
         AnchorMenu.Instance.fadeOut(true);
         this.props.select(false);
-    }
+    };
 
     @undoBatch
-    pinToPres = () => this.props.pinToPres(this.annoTextRegion)
+    pinToPres = () => this.props.pinToPres(this.annoTextRegion);
 
     @undoBatch
-    makePushpin = () => this.annoTextRegion.isPushpin = !this.annoTextRegion.isPushpin
+    makePushpin = () => (this.annoTextRegion.isPushpin = !this.annoTextRegion.isPushpin);
 
     isPushpin = () => BoolCast(this.annoTextRegion.isPushpin);
 
     @action
     onPointerDown = (e: React.PointerEvent) => {
         if (e.button === 2 || e.ctrlKey) {
-            AnchorMenu.Instance.Status = "annotation";
+            AnchorMenu.Instance.Status = 'annotation';
             AnchorMenu.Instance.Delete = this.deleteAnnotation.bind(this);
             AnchorMenu.Instance.Pinned = false;
             AnchorMenu.Instance.AddTag = this.addTag.bind(this);
@@ -68,42 +71,43 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
             AnchorMenu.Instance.IsPushpin = this.isPushpin;
             AnchorMenu.Instance.jumpTo(e.clientX, e.clientY, true);
             e.stopPropagation();
-        }
-        else if (e.button === 0) {
+        } else if (e.button === 0) {
             e.stopPropagation();
-            LinkManager.FollowLink(undefined, this.annoTextRegion, this.props, false);
+            LinkFollower.FollowLink(undefined, this.annoTextRegion, this.props, false);
         }
-    }
+    };
 
     addTag = (key: string, value: string): boolean => {
         const valNum = parseInt(value);
         this.annoTextRegion[key] = isNaN(valNum) ? value : valNum;
         return true;
-    }
+    };
 
     render() {
         const brushed = this.annoTextRegion && Doc.isBrushedHighlightedDegree(this.annoTextRegion);
-        return (<div className="htmlAnnotation" ref={this._mainCont}
-            onPointerEnter={action(() => {
-                Doc.BrushDoc(this.props.anno);
-                this.props.showInfo(this.props.anno);
-            })}
-            onPointerLeave={action(() => {
-                Doc.UnBrushDoc(this.props.anno);
-                this.props.showInfo(undefined);
-            })}
-            onPointerDown={this.onPointerDown}
-            style={{
-                left: NumCast(this.props.document.x),
-                top: NumCast(this.props.document.y),
-                width: NumCast(this.props.document._width),
-                height: NumCast(this.props.document._height),
-                opacity: brushed === Doc.DocBrushStatus.highlighted ? 0.5 : undefined,
-                pointerEvents: this.props.pointerEvents?.() as any,
-                outline: brushed === Doc.DocBrushStatus.linkHighlighted ? "solid 1px lightBlue" : undefined,
-                backgroundColor: brushed === Doc.DocBrushStatus.highlighted ? "orange" :
-                    StrCast(this.props.document.backgroundColor),
-            }} >
-        </div>);
+        return (
+            <div
+                className="htmlAnnotation"
+                ref={this._mainCont}
+                onPointerEnter={action(() => {
+                    Doc.BrushDoc(this.props.anno);
+                    this.props.showInfo(this.props.anno);
+                })}
+                onPointerLeave={action(() => {
+                    Doc.UnBrushDoc(this.props.anno);
+                    this.props.showInfo(undefined);
+                })}
+                onPointerDown={this.onPointerDown}
+                style={{
+                    left: NumCast(this.props.document.x),
+                    top: NumCast(this.props.document.y),
+                    width: NumCast(this.props.document._width),
+                    height: NumCast(this.props.document._height),
+                    opacity: brushed === Doc.DocBrushStatus.highlighted ? 0.5 : undefined,
+                    pointerEvents: this.props.pointerEvents?.() as any,
+                    outline: brushed === Doc.DocBrushStatus.linkHighlighted ? 'solid 1px lightBlue' : undefined,
+                    backgroundColor: brushed === Doc.DocBrushStatus.highlighted ? 'orange' : StrCast(this.props.document.backgroundColor),
+                }}></div>
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 510c5c385..837734edf 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -1,36 +1,35 @@
-import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from "mobx";
-import { observer } from "mobx-react";
-import * as Pdfjs from "pdfjs-dist";
-import "pdfjs-dist/web/pdf_viewer.css";
-import { Doc, DocListCast, Field, HeightSym, Opt } from "../../../fields/Doc";
-import { Id } from "../../../fields/FieldSymbols";
-import { InkTool } from "../../../fields/InkField";
-import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { TraceMobx } from "../../../fields/util";
-import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, returnFalse, smoothScroll, Utils } from "../../../Utils";
-import { DocUtils } from "../../documents/Documents";
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { SelectionManager } from "../../util/SelectionManager";
-import { SharingManager } from "../../util/SharingManager";
-import { SnappingManager } from "../../util/SnappingManager";
-import { MarqueeOptionsMenu } from "../collections/collectionFreeForm";
-import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView";
-import { MarqueeAnnotator } from "../MarqueeAnnotator";
-import { DocumentViewProps } from "../nodes/DocumentView";
-import { FieldViewProps } from "../nodes/FieldView";
-import { LinkDocPreview } from "../nodes/LinkDocPreview";
-import { StyleProp } from "../StyleProvider";
-import { AnchorMenu } from "./AnchorMenu";
-import { Annotation } from "./Annotation";
-import "./PDFViewer.scss";
-import React = require("react");
-const PDFJSViewer = require("pdfjs-dist/web/pdf_viewer");
-const pdfjsLib = require("pdfjs-dist");
-const _global = (window /* browser */ || global /* node */) as any;
+import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as Pdfjs from 'pdfjs-dist';
+import 'pdfjs-dist/web/pdf_viewer.css';
+import { Doc, DocListCast, Field, HeightSym, Opt } from '../../../fields/Doc';
+import { Id } from '../../../fields/FieldSymbols';
+import { InkTool } from '../../../fields/InkField';
+import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { TraceMobx } from '../../../fields/util';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, OmitKeys, smoothScroll, Utils } from '../../../Utils';
+import { DocUtils } from '../../documents/Documents';
+import { SelectionManager } from '../../util/SelectionManager';
+import { SharingManager } from '../../util/SharingManager';
+import { SnappingManager } from '../../util/SnappingManager';
+import { MarqueeOptionsMenu } from '../collections/collectionFreeForm';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView';
+import { MarqueeAnnotator } from '../MarqueeAnnotator';
+import { DocumentViewProps } from '../nodes/DocumentView';
+import { FieldViewProps } from '../nodes/FieldView';
+import { LinkDocPreview } from '../nodes/LinkDocPreview';
+import { StyleProp } from '../StyleProvider';
+import { AnchorMenu } from './AnchorMenu';
+import { Annotation } from './Annotation';
+import './PDFViewer.scss';
+import React = require('react');
+const PDFJSViewer = require('pdfjs-dist/web/pdf_viewer');
+const pdfjsLib = require('pdfjs-dist');
+const _global = (window /* browser */ || global) /* node */ as any;
 
 //pdfjsLib.GlobalWorkerOptions.workerSrc = `/assets/pdf.worker.js`;
 // The workerSrc property shall be specified.
-pdfjsLib.GlobalWorkerOptions.workerSrc = "https://unpkg.com/pdfjs-dist@2.14.305/build/pdf.worker.js";
+pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://unpkg.com/pdfjs-dist@2.14.305/build/pdf.worker.js';
 
 interface IViewerProps extends FieldViewProps {
     Document: Doc;
@@ -53,7 +52,7 @@ interface IViewerProps extends FieldViewProps {
 @observer
 export class PDFViewer extends React.Component<IViewerProps> {
     static _annotationStyle: any = addStyleSheet();
-    @observable private _pageSizes: { width: number, height: number }[] = [];
+    @observable private _pageSizes: { width: number; height: number }[] = [];
     @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
     @observable private _marqueeing: number[] | undefined;
     @observable private _textSelecting = true;
@@ -70,7 +69,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
     private _viewer: React.RefObject<HTMLDivElement> = React.createRef();
     public _getAnchor: (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>) => Opt<Doc> = () => undefined;
     _mainCont: React.RefObject<HTMLDivElement> = React.createRef();
-    private _selectionText: string = "";
+    private _selectionText: string = '';
     private _downX: number = 0;
     private _downY: number = 0;
     private _lastSearch = false;
@@ -82,69 +81,80 @@ export class PDFViewer extends React.Component<IViewerProps> {
     @observable isAnnotating = false;
     // key where data is stored
     @computed get allAnnotations() {
-        return DocUtils.FilterDocs(DocListCast(this.props.dataDoc[this.props.fieldKey + "-annotations"]), this.props.docFilters(), this.props.docRangeFilters(), undefined);
+        return DocUtils.FilterDocs(DocListCast(this.props.dataDoc[this.props.fieldKey + '-annotations']), this.props.docFilters(), this.props.docRangeFilters(), undefined);
+    }
+    @computed get inlineTextAnnotations() {
+        return this.allAnnotations.filter(a => a.textInlineAnnotations);
     }
-    @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); }
 
     componentDidMount = async () => {
-        runInAction(() => this._showWaiting = true);
+        runInAction(() => (this._showWaiting = true));
         this.setupPdfJsViewer();
-        this._mainCont.current?.addEventListener("scroll", e => (e.target as any).scrollLeft = 0);
+        this._mainCont.current?.addEventListener('scroll', e => ((e.target as any).scrollLeft = 0));
 
-        this._disposers.autoHeight = reaction(() => this.props.layoutDoc._autoHeight,
+        this._disposers.autoHeight = reaction(
+            () => this.props.layoutDoc._autoHeight,
             autoHeight => {
                 if (autoHeight) {
-                    this.props.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]);
-                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + "-nativeHeight"]) * (this.props.scaling?.() || 1));
+                    this.props.layoutDoc._nativeHeight = NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']);
+                    this.props.setHeight?.(NumCast(this.props.Document[this.props.fieldKey + '-nativeHeight']) * (this.props.scaling?.() || 1));
                 }
-            });
+            }
+        );
 
-        this._disposers.selected = reaction(() => this.props.isSelected(),
+        this._disposers.selected = reaction(
+            () => this.props.isSelected(),
             selected => {
                 // if (!selected) {
                 //     Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove()));
                 //     Array.from(this._savedAnnotations.keys()).forEach(k => this._savedAnnotations.set(k, []));
                 // }
-                (SelectionManager.Views().length === 1) && this.setupPdfJsViewer();
+                SelectionManager.Views().length === 1 && this.setupPdfJsViewer();
             },
-            { fireImmediately: true });
-        this._disposers.curPage = reaction(() => Cast(this.props.Document._curPage, "number", null),
-            (page) => page !== undefined && page !== this._pdfViewer?.currentPageNumber && this.gotoPage(page),
             { fireImmediately: true }
         );
-    }
+        this._disposers.curPage = reaction(
+            () => Cast(this.props.Document._curPage, 'number', null),
+            page => page !== undefined && page !== this._pdfViewer?.currentPageNumber && this.gotoPage(page),
+            { fireImmediately: true }
+        );
+    };
 
     componentWillUnmount = () => {
         Object.values(this._disposers).forEach(disposer => disposer?.());
-        document.removeEventListener("copy", this.copy);
-    }
+        document.removeEventListener('copy', this.copy);
+    };
 
     copy = (e: ClipboardEvent) => {
         if (this.props.isContentActive() && e.clipboardData) {
-            e.clipboardData.setData("text/plain", this._selectionText);
+            e.clipboardData.setData('text/plain', this._selectionText);
             e.preventDefault();
         }
-    }
+    };
 
     @action
     initialLoad = async () => {
         if (this._pageSizes.length === 0) {
-            this._pageSizes = Array<{ width: number, height: number }>(this.props.pdf.numPages);
-            await Promise.all(this._pageSizes.map((val, i) =>
-                this.props.pdf.getPage(i + 1).then(action((page: Pdfjs.PDFPageProxy) => {
-                    const page0or180 = page.rotate === 0 || page.rotate === 180;
-                    this._pageSizes.splice(i, 1, {
-                        width: (page.view[page0or180 ? 2 : 3] - page.view[page0or180 ? 0 : 1]),
-                        height: (page.view[page0or180 ? 3 : 2] - page.view[page0or180 ? 1 : 0])
-                    });
-                    if (i === this.props.pdf.numPages - 1) {
-                        this.props.loaded?.(page.view[page0or180 ? 2 : 3] - page.view[page0or180 ? 0 : 1],
-                            page.view[page0or180 ? 3 : 2] - page.view[page0or180 ? 1 : 0], this.props.pdf.numPages);
-                    }
-                }))));
-            this.props.Document.scrollHeight = this._pageSizes.reduce((size, page) => size + page.height, 0) * 96 / 72;
+            this._pageSizes = Array<{ width: number; height: number }>(this.props.pdf.numPages);
+            await Promise.all(
+                this._pageSizes.map((val, i) =>
+                    this.props.pdf.getPage(i + 1).then(
+                        action((page: Pdfjs.PDFPageProxy) => {
+                            const page0or180 = page.rotate === 0 || page.rotate === 180;
+                            this._pageSizes.splice(i, 1, {
+                                width: page.view[page0or180 ? 2 : 3] - page.view[page0or180 ? 0 : 1],
+                                height: page.view[page0or180 ? 3 : 2] - page.view[page0or180 ? 1 : 0],
+                            });
+                            if (i === this.props.pdf.numPages - 1) {
+                                this.props.loaded?.(page.view[page0or180 ? 2 : 3] - page.view[page0or180 ? 0 : 1], page.view[page0or180 ? 3 : 2] - page.view[page0or180 ? 1 : 0], this.props.pdf.numPages);
+                            }
+                        })
+                    )
+                )
+            );
+            this.props.Document.scrollHeight = (this._pageSizes.reduce((size, page) => size + page.height, 0) * 96) / 72;
         }
-    }
+    };
 
     // scrolls to focus on a nested annotation document.  if this is part a link preview then it will jump to the scroll location,
     // otherwise it will scroll smoothly.
@@ -153,7 +163,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
         let focusSpeed: Opt<number>;
         if (doc !== this.props.rootDoc && mainCont) {
             const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1);
-            const scrollTo = doc.unrendered ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, .1 * windowHeight, NumCast(this.props.Document.scrollHeight));
+            const scrollTo = doc.unrendered ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, 0.1 * windowHeight, NumCast(this.props.Document.scrollHeight));
             if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._scrollTop) {
                 focusSpeed = 500;
 
@@ -165,10 +175,10 @@ export class PDFViewer extends React.Component<IViewerProps> {
             this._initialScroll = NumCast(this.props.layoutDoc._scrollTop);
         }
         return focusSpeed;
-    }
+    };
     crop = (region: Doc | undefined, addCrop?: boolean) => {
         return this.props.crop(region, addCrop);
-    }
+    };
 
     @action
     setupPdfJsViewer = async () => {
@@ -179,18 +189,18 @@ export class PDFViewer extends React.Component<IViewerProps> {
         await this.initialLoad();
 
         this.createPdfViewer();
-    }
+    };
 
     pagesinit = () => {
         if (this._pdfViewer._setDocumentViewerElement?.offsetParent) {
-            runInAction(() => this._pdfViewer.currentScaleValue = this.props.layoutDoc._viewScale = 1);
+            runInAction(() => (this._pdfViewer.currentScaleValue = this.props.layoutDoc._viewScale = 1));
             this.gotoPage(NumCast(this.props.Document._curPage, 1));
         }
-        document.removeEventListener("pagesinit", this.pagesinit);
-        var quickScroll: string | undefined = this._initialScroll ? this._initialScroll.toString() : "";
+        document.removeEventListener('pagesinit', this.pagesinit);
+        var quickScroll: string | undefined = this._initialScroll ? this._initialScroll.toString() : '';
         this._disposers.scroll = reaction(
             () => Math.abs(NumCast(this.props.Document._scrollTop)),
-            (pos) => {
+            pos => {
                 if (!this._ignoreScroll) {
                     this._showWaiting && this.setupPdfJsViewer();
                     const viewTrans = quickScroll ?? StrCast(this.props.Document._viewTransition);
@@ -199,10 +209,13 @@ export class PDFViewer extends React.Component<IViewerProps> {
                     const duration = durationMiliStr ? Number(durationMiliStr[1]) : durationSecStr ? Number(durationSecStr[1]) * 1000 : 0;
                     this._forcedScroll = true;
                     if (duration) {
-                        setTimeout(() => {
-                            this._mainCont.current && smoothScroll(duration, this._mainCont.current, pos);
-                            setTimeout(() => this._forcedScroll = false, duration);
-                        }, this._mainCont.current ? 0 : 250); // wait for mainCont and try again to scroll
+                        setTimeout(
+                            () => {
+                                this._mainCont.current && smoothScroll(duration, this._mainCont.current, pos);
+                                setTimeout(() => (this._forcedScroll = false), duration);
+                            },
+                            this._mainCont.current ? 0 : 250
+                        ); // wait for mainCont and try again to scroll
                     } else {
                         this._mainCont.current?.scrollTo({ top: pos });
                         this._forcedScroll = false;
@@ -216,23 +229,27 @@ export class PDFViewer extends React.Component<IViewerProps> {
             this._mainCont.current?.scrollTo({ top: Math.abs(this._initialScroll || 0) });
             this._initialScroll = undefined;
         }
-    }
+    };
 
     createPdfViewer() {
-        if (!this._mainCont.current) { // bcz: I don't think this is ever triggered or needed
-            console.log("PDFViewer- I guess we got here");
+        if (!this._mainCont.current) {
+            // bcz: I don't think this is ever triggered or needed
+            console.log('PDFViewer- I guess we got here');
             if (this._retries < 5) {
                 this._retries++;
-                console.log("PDFViewer- retry num:" + this._retries);
+                console.log('PDFViewer- retry num:' + this._retries);
                 setTimeout(() => this.createPdfViewer(), 1000);
             }
             return;
         }
-        document.removeEventListener("copy", this.copy);
-        document.addEventListener("copy", this.copy);
+        document.removeEventListener('copy', this.copy);
+        document.addEventListener('copy', this.copy);
         const eventBus = new PDFJSViewer.EventBus(true);
-        eventBus._on("pagesinit", this.pagesinit);
-        eventBus._on("pagerendered", action(() => this._showWaiting = false));
+        eventBus._on('pagesinit', this.pagesinit);
+        eventBus._on(
+            'pagerendered',
+            action(() => (this._showWaiting = false))
+        );
         const pdfLinkService = new PDFJSViewer.PDFLinkService({ eventBus });
         const pdfFindController = new PDFJSViewer.PDFFindController({ linkService: pdfLinkService, eventBus });
         this._pdfViewer = new PDFJSViewer.PDFViewer({
@@ -240,31 +257,30 @@ export class PDFViewer extends React.Component<IViewerProps> {
             viewer: this._viewer.current,
             linkService: pdfLinkService,
             findController: pdfFindController,
-            renderer: "canvas",
-            eventBus
+            renderer: 'canvas',
+            eventBus,
         });
         pdfLinkService.setViewer(this._pdfViewer);
         pdfLinkService.setDocument(this.props.pdf, null);
         this._pdfViewer.setDocument(this.props.pdf);
     }
 
-
     @action
     prevAnnotation = () => {
         this.Index = Math.max(this.Index - 1, 0);
         this.scrollToAnnotation(this.allAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y))[this.Index]);
-    }
+    };
 
     @action
     nextAnnotation = () => {
         this.Index = Math.min(this.Index + 1, this.allAnnotations.length - 1);
         this.scrollToAnnotation(this.allAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y))[this.Index]);
-    }
+    };
 
     @action
     gotoPage = (p: number) => {
         this._pdfViewer?.scrollPageIntoView({ pageNumber: Math.min(Math.max(1, p), this._pageSizes.length) });
-    }
+    };
 
     @action
     scrollToAnnotation = (scrollToAnnotation: Doc) => {
@@ -272,7 +288,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             this.scrollFocus(scrollToAnnotation, true);
             Doc.linkFollowHighlight(scrollToAnnotation);
         }
-    }
+    };
 
     @observable private _scrollTimer: any;
 
@@ -289,7 +305,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 this._scrollTimer = undefined;
             }, 200);
         }
-    }
+    };
 
     // get the page index that the vertical offset passed in is on
     getPageFromScroll = (vOffset: number) => {
@@ -299,7 +315,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
             currOffset -= this._pageSizes[index++].height;
         }
         return index;
-    }
+    };
 
     @action
     search = (searchString: string, bwd?: boolean, clear: boolean = false) => {
@@ -308,22 +324,21 @@ export class PDFViewer extends React.Component<IViewerProps> {
             findPrevious: bwd,
             highlightAll: true,
             phraseSearch: true,
-            query: searchString
+            query: searchString,
         };
         if (clear) {
-            this._pdfViewer?.findController.executeCommand('reset', { query: "" });
+            this._pdfViewer?.findController.executeCommand('reset', { query: '' });
         } else if (!searchString) {
             bwd ? this.prevAnnotation() : this.nextAnnotation();
         } else if (this._pdfViewer?.pageViewsReady) {
             this._pdfViewer.findController.executeCommand('findagain', findOpts);
-        }
-        else if (this._mainCont.current) {
+        } else if (this._mainCont.current) {
             const executeFind = () => this._pdfViewer.findController.executeCommand('find', findOpts);
-            this._mainCont.current.addEventListener("pagesloaded", executeFind);
-            this._mainCont.current.addEventListener("pagerendered", executeFind);
+            this._mainCont.current.addEventListener('pagesloaded', executeFind);
+            this._mainCont.current.addEventListener('pagerendered', executeFind);
         }
         return true;
-    }
+    };
 
     @action
     onPointerDown = (e: React.PointerEvent): void => {
@@ -340,24 +355,27 @@ export class PDFViewer extends React.Component<IViewerProps> {
         if ((e.button !== 0 || e.altKey) && this.props.isContentActive(true)) {
             this._setPreviewCursor?.(e.clientX, e.clientY, true, false);
         }
-        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) {
+        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
             this.props.select(false);
             MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
             this._marqueeing = [e.clientX, e.clientY];
             this.isAnnotating = true;
-            if (e.target && ((e.target as any).className.includes("endOfContent") || ((e.target as any).parentElement.className !== "textLayer"))) {
+            if (e.target && ((e.target as any).className.includes('endOfContent') || (e.target as any).parentElement.className !== 'textLayer')) {
                 this._textSelecting = false;
-                document.addEventListener("pointermove", this.onSelectMove); // need this to prevent document from being dragged if stopPropagation doesn't get called
+                document.addEventListener('pointermove', this.onSelectMove); // need this to prevent document from being dragged if stopPropagation doesn't get called
             } else {
                 // if textLayer is hit, then we select text instead of using a marquee so clear out the marquee.
-                setTimeout(action(() => this._marqueeing = undefined), 100); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
-
-                this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, "htmlAnnotation", { "pointer-events": "none" });
-                document.addEventListener("pointerup", this.onSelectEnd);
-                document.addEventListener("pointermove", this.onSelectMove);
+                setTimeout(
+                    action(() => (this._marqueeing = undefined)),
+                    100
+                ); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
+
+                this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, 'htmlAnnotation', { 'pointer-events': 'none' });
+                document.addEventListener('pointerup', this.onSelectEnd);
+                document.addEventListener('pointermove', this.onSelectMove);
             }
         }
-    }
+    };
 
     @action
     finishMarquee = (x?: number, y?: number) => {
@@ -365,8 +383,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
         this.isAnnotating = false;
         this._marqueeing = undefined;
         this._textSelecting = true;
-        document.removeEventListener("pointermove", this.onSelectMove);
-    }
+        document.removeEventListener('pointermove', this.onSelectMove);
+    };
 
     onSelectMove = (e: PointerEvent) => e.stopPropagation();
 
@@ -375,15 +393,15 @@ export class PDFViewer extends React.Component<IViewerProps> {
         this.isAnnotating = false;
         clearStyleSheetRules(PDFViewer._annotationStyle);
         this.props.select(false);
-        document.removeEventListener("pointermove", this.onSelectMove);
-        document.removeEventListener("pointerup", this.onSelectEnd);
+        document.removeEventListener('pointermove', this.onSelectMove);
+        document.removeEventListener('pointerup', this.onSelectEnd);
 
         const sel = window.getSelection();
-        if (sel?.type === "Range") {
+        if (sel?.type === 'Range') {
             this.createTextAnnotation(sel, sel.getRangeAt(0));
             AnchorMenu.Instance.jumpTo(e.clientX, e.clientY);
         }
-    }
+    };
 
     @action
     createTextAnnotation = (sel: Selection, selRange: Range) => {
@@ -395,41 +413,41 @@ export class PDFViewer extends React.Component<IViewerProps> {
                 if (rect && rect.width !== this._mainCont.current.clientWidth && rect.width) {
                     const scaleX = this._mainCont.current.offsetWidth / boundingRect.width;
                     const pdfScale = NumCast(this.props.layoutDoc._viewScale, 1);
-                    const annoBox = document.createElement("div");
-                    annoBox.className = "marqueeAnnotator-annotationBox";
+                    const annoBox = document.createElement('div');
+                    annoBox.className = 'marqueeAnnotator-annotationBox';
                     // transforms the positions from screen onto the pdf div
-                    annoBox.style.top = ((rect.top - boundingRect.top) * scaleX / pdfScale + this._mainCont.current.scrollTop).toString();
-                    annoBox.style.left = ((rect.left - boundingRect.left) * scaleX / pdfScale).toString();
-                    annoBox.style.width = (rect.width * this._mainCont.current.offsetWidth / boundingRect.width / pdfScale).toString();
-                    annoBox.style.height = (rect.height * this._mainCont.current.offsetHeight / boundingRect.height / pdfScale).toString();
+                    annoBox.style.top = (((rect.top - boundingRect.top) * scaleX) / pdfScale + this._mainCont.current.scrollTop).toString();
+                    annoBox.style.left = (((rect.left - boundingRect.left) * scaleX) / pdfScale).toString();
+                    annoBox.style.width = ((rect.width * this._mainCont.current.offsetWidth) / boundingRect.width / pdfScale).toString();
+                    annoBox.style.height = ((rect.height * this._mainCont.current.offsetHeight) / boundingRect.height / pdfScale).toString();
                     this._annotationLayer.current && MarqueeAnnotator.previewNewAnnotation(this._savedAnnotations, this._annotationLayer.current, annoBox, this.getPageFromScroll(rect.top));
                 }
             }
         }
-        this._selectionText = selRange.cloneContents().textContent || "";
+        this._selectionText = selRange.cloneContents().textContent || '';
 
         // clear selection
-        if (sel.empty) {  // Chrome
+        if (sel.empty) {
+            // Chrome
             sel.empty();
-        } else if (sel.removeAllRanges) {  // Firefox
+        } else if (sel.removeAllRanges) {
+            // Firefox
             sel.removeAllRanges();
         }
-    }
+    };
 
     scrollXf = () => {
         return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, NumCast(this.props.layoutDoc._scrollTop)) : this.props.ScreenToLocalTransform();
-    }
+    };
 
     onClick = (e: React.MouseEvent) => {
-        if (this._setPreviewCursor && e.button === 0 &&
-            Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD &&
-            Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
+        if (this._setPreviewCursor && e.button === 0 && Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
             this._setPreviewCursor(e.clientX, e.clientY, false, false);
         }
         // e.stopPropagation();  // bcz: not sure why this was here.  We need to allow the DocumentView to get clicks to process doubleClicks
-    }
+    };
 
-    setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => this._setPreviewCursor = func;
+    setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);
 
     @action
     onZoomWheel = (e: React.WheelEvent) => {
@@ -437,52 +455,59 @@ export class PDFViewer extends React.Component<IViewerProps> {
             e.stopPropagation();
             if (e.ctrlKey) {
                 const curScale = Number(this._pdfViewer.currentScaleValue);
-                this._pdfViewer.currentScaleValue = Math.max(1, Math.min(10, curScale - curScale * e.deltaY / 1000));
+                this._pdfViewer.currentScaleValue = Math.max(1, Math.min(10, curScale - (curScale * e.deltaY) / 1000));
                 this.props.layoutDoc._viewScale = Number(this._pdfViewer.currentScaleValue);
             }
         }
-    }
+    };
 
-    pointerEvents = () => this.props.isContentActive() && this.props.pointerEvents?.() !== "none" && !MarqueeOptionsMenu.Instance.isShown() ? "all" : SnappingManager.GetIsDragging() ? undefined : "none";
+    pointerEvents = () => (this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none');
     @computed get annotationLayer() {
-        return <div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this.props.Document), transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})` }} ref={this._annotationLayer}>
-            {this.inlineTextAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno =>
-                <Annotation {...this.props} fieldKey={this.props.fieldKey + "-annotations"} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.props.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />)}
-        </div>;
+        return (
+            <div className="pdfViewerDash-annotationLayer" style={{ height: Doc.NativeHeight(this.props.Document), transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})` }} ref={this._annotationLayer}>
+                {this.inlineTextAnnotations
+                    .sort((a, b) => NumCast(a.y) - NumCast(b.y))
+                    .map(anno => (
+                        <Annotation {...this.props} fieldKey={this.props.fieldKey + '-annotations'} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.props.dataDoc} anno={anno} key={`${anno[Id]}-annotation`} />
+                    ))}
+            </div>
+        );
     }
 
     @computed get overlayInfo() {
-        return !this._overlayAnnoInfo ? (null) :
+        return !this._overlayAnnoInfo ? null : (
             <div className="pdfViewerDash-overlayAnno" style={{ top: NumCast(this._overlayAnnoInfo.y), left: NumCast(this._overlayAnnoInfo.x) }}>
                 <div className="pdfViewerDash-overlayAnno" style={{ right: -50, background: SharingManager.Instance.users.find(users => users.user.email === this._overlayAnnoInfo!.author)?.userColor }}>
-                    {this._overlayAnnoInfo.author + " " + Field.toString(this._overlayAnnoInfo.creationDate as Field)}
+                    {this._overlayAnnoInfo.author + ' ' + Field.toString(this._overlayAnnoInfo.creationDate as Field)}
                 </div>
-            </div>;
+            </div>
+        );
     }
 
-    showInfo = action((anno: Opt<Doc>) => this._overlayAnnoInfo = anno);
+    showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
     overlayTransform = () => this.scrollXf().scale(1 / NumCast(this.props.layoutDoc._viewScale, 1));
     panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0);
     panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document);
-    basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter("textInlineAnnotations")];
+    basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter('textInlineAnnotations')];
     transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()];
     opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()];
-    childStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => {
+    childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
         if (doc instanceof Doc && property === StyleProp.PointerEvents) {
-            if (doc.textInlineAnnotations) return "none";
-            return "all";
+            if (doc.textInlineAnnotations) return 'none';
+            return 'all';
         }
         return this.props.styleProvider?.(doc, props, property);
-    }
+    };
 
-    renderAnnotations = (docFilters?: () => string[], dontRender?: boolean) =>
-        <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
+    renderAnnotations = (docFilters?: () => string[], dontRender?: boolean) => (
+        <CollectionFreeFormView
+            {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
             isAnnotationOverlay={true}
-            fieldKey={this.props.fieldKey + "-annotations"}
+            fieldKey={this.props.fieldKey + '-annotations'}
             setPreviewCursor={this.setPreviewCursor}
             PanelHeight={this.panelHeight}
             PanelWidth={this.panelWidth}
-            dropAction={"alias"}
+            dropAction={'alias'}
             select={emptyFunction}
             ContentScaling={this.contentZoom}
             bringToFront={emptyFunction}
@@ -493,70 +518,88 @@ export class PDFViewer extends React.Component<IViewerProps> {
             ScreenToLocalTransform={this.overlayTransform}
             renderDepth={this.props.renderDepth + 1}
         />
-    @computed get overlayTransparentAnnotations() { return this.renderAnnotations(this.transparentFilter, false); }
-    @computed get overlayOpaqueAnnotations() { return this.renderAnnotations(this.opaqueFilter, false); }
+    );
+    @computed get overlayTransparentAnnotations() {
+        return this.renderAnnotations(this.transparentFilter, false);
+    }
+    @computed get overlayOpaqueAnnotations() {
+        return this.renderAnnotations(this.opaqueFilter, false);
+    }
     @computed get overlayClickableAnnotations() {
-        return <div style={{ height: NumCast(this.props.rootDoc.scrollHeight) }}>
-            {this.renderAnnotations(undefined, true)}
-        </div>;
+        return <div style={{ height: NumCast(this.props.rootDoc.scrollHeight) }}>{this.renderAnnotations(undefined, true)}</div>;
     }
     @computed get overlayLayer() {
-        return <div style={{ pointerEvents: SnappingManager.GetIsDragging() ? "all" : "none" }}>
-            <div className="pdfViewerDash-overlay"
-                style={{
-                    pointerEvents: SnappingManager.GetIsDragging() ? "all" : "none",
-                    mixBlendMode: "multiply",
-                    transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})`
-                }}>
-                {this.overlayTransparentAnnotations}
-            </div>
-            <div className="pdfViewerDash-overlay"
-                style={{
-                    pointerEvents: SnappingManager.GetIsDragging() ? "all" : "none",
-                    mixBlendMode: this.allAnnotations.some(anno => anno.mixBlendMode) ? "hard-light" : undefined,
-                    transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})`
-                }}>
-                {this.overlayOpaqueAnnotations}
-                {this.overlayClickableAnnotations}
+        return (
+            <div style={{ pointerEvents: SnappingManager.GetIsDragging() ? 'all' : 'none' }}>
+                <div
+                    className="pdfViewerDash-overlay"
+                    style={{
+                        pointerEvents: SnappingManager.GetIsDragging() ? 'all' : 'none',
+                        mixBlendMode: 'multiply',
+                        transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})`,
+                    }}>
+                    {this.overlayTransparentAnnotations}
+                </div>
+                <div
+                    className="pdfViewerDash-overlay"
+                    style={{
+                        pointerEvents: SnappingManager.GetIsDragging() ? 'all' : 'none',
+                        mixBlendMode: this.allAnnotations.some(anno => anno.mixBlendMode) ? 'hard-light' : undefined,
+                        transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})`,
+                    }}>
+                    {this.overlayOpaqueAnnotations}
+                    {this.overlayClickableAnnotations}
+                </div>
             </div>
-        </div>;
+        );
     }
     @computed get pdfViewerDiv() {
-        return <div className={"pdfViewerDash-text" + (this.props.pointerEvents?.() !== "none" && this._textSelecting && this.props.isContentActive() ? "-selected" : "")} ref={this._viewer} />;
+        return <div className={'pdfViewerDash-text' + (this.props.pointerEvents?.() !== 'none' && this._textSelecting && this.props.isContentActive() ? '-selected' : '')} ref={this._viewer} />;
+    }
+    @computed get contentScaling() {
+        return this.props.ContentScaling?.() || 1;
     }
-    @computed get contentScaling() { return this.props.ContentScaling?.() || 1; }
     contentZoom = () => NumCast(this.props.layoutDoc._viewScale, 1);
     savedAnnotations = () => this._savedAnnotations;
     render() {
         TraceMobx();
-        return <div className="pdfViewer-content">
-            <div className={`pdfViewerDash${this.props.isContentActive() && this.props.pointerEvents?.() !== "none" ? "-interactive" : ""}`} ref={this._mainCont}
-                onScroll={this.onScroll} onWheel={this.onZoomWheel} onPointerDown={this.onPointerDown} onClick={this.onClick}
-                style={{
-                    overflowX: NumCast(this.props.layoutDoc._viewScale, 1) !== 1 ? "scroll" : undefined,
-                    height: !this.props.Document._fitWidth && (window.screen.width > 600) ? Doc.NativeHeight(this.props.Document) : `${100 / this.contentScaling}%`,
-                    transform: `scale(${this.contentScaling})`
-                }}  >
-                {this.pdfViewerDiv}
-                {this.annotationLayer}
-                {this.overlayLayer}
-                {this.overlayInfo}
-                {this._showWaiting ? <img className="pdfViewerDash-waiting" src={"/assets/loading.gif"} /> : (null)}
-                {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? (null) :
-                    <MarqueeAnnotator rootDoc={this.props.rootDoc}
-                        getPageFromScroll={this.getPageFromScroll}
-                        anchorMenuClick={this.props.anchorMenuClick}
-                        scrollTop={0}
-                        down={this._marqueeing}
-                        addDocument={(doc: Doc | Doc[]) => this.props.addDocument!(doc)}
-                        docView={this.props.docViewPath().lastElement()}
-                        finishMarquee={this.finishMarquee}
-                        savedAnnotations={this.savedAnnotations}
-                        annotationLayer={this._annotationLayer.current}
-                        mainCont={this._mainCont.current}
-                        anchorMenuCrop={this._textSelecting ? undefined : this.crop}
-                    />}
+        return (
+            <div className="pdfViewer-content">
+                <div
+                    className={`pdfViewerDash${this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' ? '-interactive' : ''}`}
+                    ref={this._mainCont}
+                    onScroll={this.onScroll}
+                    onWheel={this.onZoomWheel}
+                    onPointerDown={this.onPointerDown}
+                    onClick={this.onClick}
+                    style={{
+                        overflowX: NumCast(this.props.layoutDoc._viewScale, 1) !== 1 ? 'scroll' : undefined,
+                        height: !this.props.Document._fitWidth && window.screen.width > 600 ? Doc.NativeHeight(this.props.Document) : `${100 / this.contentScaling}%`,
+                        transform: `scale(${this.contentScaling})`,
+                    }}>
+                    {this.pdfViewerDiv}
+                    {this.annotationLayer}
+                    {this.overlayLayer}
+                    {this.overlayInfo}
+                    {this._showWaiting ? <img className="pdfViewerDash-waiting" src={'/assets/loading.gif'} /> : null}
+                    {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
+                        <MarqueeAnnotator
+                            rootDoc={this.props.rootDoc}
+                            getPageFromScroll={this.getPageFromScroll}
+                            anchorMenuClick={this.props.anchorMenuClick}
+                            scrollTop={0}
+                            down={this._marqueeing}
+                            addDocument={(doc: Doc | Doc[]) => this.props.addDocument!(doc)}
+                            docView={this.props.docViewPath().lastElement()}
+                            finishMarquee={this.finishMarquee}
+                            savedAnnotations={this.savedAnnotations}
+                            annotationLayer={this._annotationLayer.current}
+                            mainCont={this._mainCont.current}
+                            anchorMenuCrop={this._textSelecting ? undefined : this.crop}
+                        />
+                    )}
+                </div>
             </div>
-        </div>;
+        );
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index b447bdc19..0bf1575fb 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -1,94 +1,106 @@
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { Tooltip } from "@material-ui/core";
-import { action } from "mobx";
-import { observer } from "mobx-react";
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
+import { action } from 'mobx';
+import { observer } from 'mobx-react';
 import * as React from 'react';
-import { AclAdmin, Doc, DocListCast } from '../../../fields/Doc';
-import { Id } from '../../../fields/FieldSymbols';
-import { Cast, StrCast } from '../../../fields/Types';
-import { GetEffectiveAcl } from "../../../fields/util";
+import { AclAdmin, Doc } from '../../../fields/Doc';
+import { StrCast } from '../../../fields/Types';
+import { GetEffectiveAcl } from '../../../fields/util';
 import { Utils } from '../../../Utils';
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { DocumentManager } from "../../util/DocumentManager";
-import { SelectionManager } from "../../util/SelectionManager";
-import { SettingsManager } from "../../util/SettingsManager";
-import { SharingManager } from "../../util/SharingManager";
-import { undoBatch, UndoManager } from "../../util/UndoManager";
-import { CollectionDockingView } from "../collections/CollectionDockingView";
-import { ContextMenu } from "../ContextMenu";
-import { Borders, Colors } from "../global/globalEnums";
-import { MainView } from "../MainView";
-import "./TopBar.scss";
+import { DocumentManager } from '../../util/DocumentManager';
+import { SelectionManager } from '../../util/SelectionManager';
+import { SettingsManager } from '../../util/SettingsManager';
+import { SharingManager } from '../../util/SharingManager';
+import { UndoManager } from '../../util/UndoManager';
+import { CollectionDockingView } from '../collections/CollectionDockingView';
+import { ContextMenu } from '../ContextMenu';
+import { DashboardView } from '../DashboardView';
+import { Borders, Colors } from '../global/globalEnums';
+import { MainView } from '../MainView';
+import './TopBar.scss';
 
 /**
  * ABOUT: This is the topbar in Dash, which included the current Dashboard as well as access to information on the user
- * and settings and help buttons. Future scope for this bar is to include the collaborators that are on the same Dashboard. 
+ * and settings and help buttons. Future scope for this bar is to include the collaborators that are on the same Dashboard.
  */
 @observer
 export class TopBar extends React.Component {
-      navigateToHome = () => {
-            CollectionDockingView.Instance.CaptureThumbnail()?.then(() => {
-                CurrentUserUtils.ActivePage = "home";
-                CurrentUserUtils.closeActiveDashboard(); // bcz: if we do this, we need some other way to keep track, for user convenience, of the last dashboard in use
-            });
-      }
-      render() {
-            const activeDashboard = CurrentUserUtils.ActiveDashboard;
-            return (
-                  //TODO:glr Add support for light / dark mode
-                  <div style={{ pointerEvents: "all", background: Colors.DARK_GRAY, borderBottom: Borders.STANDARD }} className="topbar-container">
-                        <div className="topbar-inner-container">
-                              <div className="topbar-left">
-                                    {activeDashboard ?
-                                          <>
-                                                <div className="topbar-button-icon" onClick={e => {
-                                                      ContextMenu.Instance.addItem({ description: "Logout", event: () => window.location.assign(Utils.prepend("/logout")), icon: "edit" });
-                                                      ContextMenu.Instance.displayMenu(e.clientX + 5, e.clientY + 10);
-                                                }}>{Doc.CurrentUserEmail}</div>
-                                                <div className="topbar-button-icon" onClick={this.navigateToHome}>
-                                                      <FontAwesomeIcon icon="home" />
-                                                </div>
-                                          </>
-                                          : (null)}
-
-                              </div>
-                              <div className="topbar-center" >
-                                    <div className="topbar-title" onClick={() => activeDashboard && SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(activeDashboard)!, false)}>
-                                          {activeDashboard ? StrCast(activeDashboard.title) : "Dash"}
-                                    </div>
-                                    <div className="topbar-button-icon" onClick={e => {
-                                        const dashView = activeDashboard && DocumentManager.Instance.getDocumentView(activeDashboard);
-                                        ContextMenu.Instance.addItem({ description: "Open Dashboard View", event: this.navigateToHome, icon: "edit" });
-                                        ContextMenu.Instance.addItem({ description: "Snapshot Dashboard", event: async () => {
-                                            const batch = UndoManager.StartBatch("snapshot");
-                                            await CurrentUserUtils.snapshotDashboard();
-                                            batch.end();
-                                        }, icon: "edit" });
-                                        dashView?.showContextMenu(e.clientX+20, e.clientY+30);
+    navigateToHome = () => {
+        CollectionDockingView.Instance.CaptureThumbnail()?.then(() => {
+            Doc.ActivePage = 'home';
+            DashboardView.closeActiveDashboard(); // bcz: if we do this, we need some other way to keep track, for user convenience, of the last dashboard in use
+        });
+    };
+    render() {
+        const activeDashboard = Doc.ActiveDashboard;
+        return (
+            //TODO:glr Add support for light / dark mode
+            <div style={{ pointerEvents: 'all', background: Colors.DARK_GRAY, borderBottom: Borders.STANDARD }} className="topbar-container">
+                <div className="topbar-inner-container">
+                    <div className="topbar-left">
+                        {activeDashboard ? (
+                            <>
+                                <div
+                                    className="topbar-button-icon"
+                                    onClick={e => {
+                                        ContextMenu.Instance.addItem({ description: 'Logout', event: () => window.location.assign(Utils.prepend('/logout')), icon: 'edit' });
+                                        ContextMenu.Instance.displayMenu(e.clientX + 5, e.clientY + 10);
                                     }}>
-                                          <FontAwesomeIcon color="white" size="lg" icon="bars" />
-                                    </div>
-                                    <Tooltip title={<div className="dash-tooltip">Browsing mode for directly navigating to documents</div>} placement="bottom">
-                                          <div className="topbar-button-icon" style={{ background: MainView.Instance._exploreMode ? Colors.LIGHT_BLUE : undefined }} onClick={action(() => MainView.Instance._exploreMode = !MainView.Instance._exploreMode)}>
-                                                <FontAwesomeIcon color={MainView.Instance._exploreMode ? "red" : "white"} icon="eye" size="lg" />
-                                          </div>
-                                    </Tooltip>
-                              </div>
-                              <div className="topbar-right" >
-                                    {CurrentUserUtils.ActiveDashboard ? <div className="topbar-button-icon" onClick={() => { SharingManager.Instance.open(undefined, activeDashboard) }}>
-                                          {GetEffectiveAcl(Doc.GetProto(CurrentUserUtils.ActiveDashboard)) === AclAdmin ? "Share" : "view original"}
-                                    </div> : (null)}
-                                    <div className="topbar-button-icon" onClick={() => window.open(
-                                          "https://brown-dash.github.io/Dash-Documentation/", "_blank")}>
-                                          <FontAwesomeIcon icon="question-circle" />
-                                    </div>
-                                    <div className="topbar-button-icon" onClick={() => SettingsManager.Instance.open()}>
-                                          <FontAwesomeIcon icon="cog" />
-                                    </div>
-                              </div>
+                                    {Doc.CurrentUserEmail}
+                                </div>
+                                <div className="topbar-button-icon" onClick={this.navigateToHome}>
+                                    <FontAwesomeIcon icon="home" />
+                                </div>
+                            </>
+                        ) : null}
+                    </div>
+                    <div className="topbar-center">
+                        <div className="topbar-title" onClick={() => activeDashboard && SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(activeDashboard)!, false)}>
+                            {activeDashboard ? StrCast(activeDashboard.title) : 'Dash'}
                         </div>
-                  </div>
-
-            );
-      }
-}
\ No newline at end of file
+                        <div
+                            className="topbar-button-icon"
+                            onClick={e => {
+                                const dashView = activeDashboard && DocumentManager.Instance.getDocumentView(activeDashboard);
+                                ContextMenu.Instance.addItem({ description: 'Open Dashboard View', event: this.navigateToHome, icon: 'edit' });
+                                ContextMenu.Instance.addItem({
+                                    description: 'Snapshot Dashboard',
+                                    event: async () => {
+                                        const batch = UndoManager.StartBatch('snapshot');
+                                        await DashboardView.snapshotDashboard();
+                                        batch.end();
+                                    },
+                                    icon: 'edit',
+                                });
+                                dashView?.showContextMenu(e.clientX + 20, e.clientY + 30);
+                            }}>
+                            <FontAwesomeIcon color="white" size="lg" icon="bars" />
+                        </div>
+                        <Tooltip title={<div className="dash-tooltip">Browsing mode for directly navigating to documents</div>} placement="bottom">
+                            <div className="topbar-button-icon" style={{ background: MainView.Instance._exploreMode ? Colors.LIGHT_BLUE : undefined }} onClick={action(() => (MainView.Instance._exploreMode = !MainView.Instance._exploreMode))}>
+                                <FontAwesomeIcon color={MainView.Instance._exploreMode ? 'red' : 'white'} icon="eye" size="lg" />
+                            </div>
+                        </Tooltip>
+                    </div>
+                    <div className="topbar-right">
+                        {Doc.ActiveDashboard ? (
+                            <div
+                                className="topbar-button-icon"
+                                onClick={() => {
+                                    SharingManager.Instance.open(undefined, activeDashboard);
+                                }}>
+                                {GetEffectiveAcl(Doc.GetProto(Doc.ActiveDashboard)) === AclAdmin ? 'Share' : 'view original'}
+                            </div>
+                        ) : null}
+                        <div className="topbar-button-icon" onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/', '_blank')}>
+                            <FontAwesomeIcon icon="question-circle" />
+                        </div>
+                        <div className="topbar-button-icon" onClick={() => SettingsManager.Instance.open()}>
+                            <FontAwesomeIcon icon="cog" />
+                        </div>
+                    </div>
+                </div>
+            </div>
+        );
+    }
+}
diff --git a/src/client/views/webcam/DashWebRTCVideo.tsx b/src/client/views/webcam/DashWebRTCVideo.tsx
index 6c0c9b301..02e44a793 100644
--- a/src/client/views/webcam/DashWebRTCVideo.tsx
+++ b/src/client/views/webcam/DashWebRTCVideo.tsx
@@ -1,86 +1,84 @@
-import { faPhoneSlash, faSync } from "@fortawesome/free-solid-svg-icons";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { action, observable } from "mobx";
-import { observer } from "mobx-react";
-import { Doc } from "../../../fields/Doc";
-import { InkTool } from "../../../fields/InkField";
-import "../../views/nodes/WebBox.scss";
-import { DocumentDecorations } from "../DocumentDecorations";
-import { CollectionFreeFormDocumentViewProps } from "../nodes/CollectionFreeFormDocumentView";
-import { FieldView, FieldViewProps } from "../nodes/FieldView";
-import "./DashWebRTCVideo.scss";
-import { hangup, initialize, refreshVideos } from "./WebCamLogic";
-import React = require("react");
-import { CurrentUserUtils } from "../../util/CurrentUserUtils";
-import { IconLookup } from "@fortawesome/fontawesome-svg-core";
-
+import { IconLookup } from '@fortawesome/fontawesome-svg-core';
+import { faPhoneSlash, faSync } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { action, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import { Doc } from '../../../fields/Doc';
+import { InkTool } from '../../../fields/InkField';
+import '../../views/nodes/WebBox.scss';
+import { DocumentDecorations } from '../DocumentDecorations';
+import { CollectionFreeFormDocumentViewProps } from '../nodes/CollectionFreeFormDocumentView';
+import { FieldView, FieldViewProps } from '../nodes/FieldView';
+import './DashWebRTCVideo.scss';
+import { hangup, initialize, refreshVideos } from './WebCamLogic';
+import React = require('react');
 
 /**
  * This models the component that will be rendered, that can be used as a doc that will reflect the video cams.
  */
 @observer
 export class DashWebRTCVideo extends React.Component<CollectionFreeFormDocumentViewProps & FieldViewProps> {
-
     private roomText: HTMLInputElement | undefined;
     @observable remoteVideoAdded: boolean = false;
 
     @action
     changeUILook = () => {
         this.remoteVideoAdded = true;
-    }
+    };
 
     /**
-      * Function that submits the title entered by user on enter press.
-      */
+     * Function that submits the title entered by user on enter press.
+     */
     private onEnterKeyDown = (e: React.KeyboardEvent) => {
         if (e.keyCode === 13) {
             const submittedTitle = this.roomText!.value;
-            this.roomText!.value = "";
+            this.roomText!.value = '';
             this.roomText!.blur();
             initialize(submittedTitle, this.changeUILook);
         }
-    }
-
+    };
 
-    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DashWebRTCVideo, fieldKey); }
+    public static LayoutString(fieldKey: string) {
+        return FieldView.LayoutString(DashWebRTCVideo, fieldKey);
+    }
 
     @action
     onClickRefresh = () => {
         refreshVideos();
-    }
+    };
 
     onClickHangUp = () => {
         hangup();
-    }
+    };
 
     render() {
-        const content =
-            <div className="webcam-cont" style={{ width: "100%", height: "100%" }}>
+        const content = (
+            <div className="webcam-cont" style={{ width: '100%', height: '100%' }}>
                 <div className="webcam-header">DashWebRTC</div>
-                <input id="roomName" type="text" placeholder="Enter room name" ref={(e) => this.roomText = e!} onKeyDown={this.onEnterKeyDown} />
+                <input id="roomName" type="text" placeholder="Enter room name" ref={e => (this.roomText = e!)} onKeyDown={this.onEnterKeyDown} />
                 <div className="videoContainer">
-                    <video id="localVideo" className={"RTCVideo" + (this.remoteVideoAdded ? " side" : " main")} autoPlay playsInline muted ref={(e) => {
-                    }}></video>
-                    <video id="remoteVideo" className="RTCVideo main" autoPlay playsInline ref={(e) => {
-                    }}></video>
+                    <video id="localVideo" className={'RTCVideo' + (this.remoteVideoAdded ? ' side' : ' main')} autoPlay playsInline muted ref={e => {}}></video>
+                    <video id="remoteVideo" className="RTCVideo main" autoPlay playsInline ref={e => {}}></video>
                 </div>
                 <div className="buttonContainer">
-                    <div className="videoButtons" style={{ background: "red" }} onClick={this.onClickHangUp}><FontAwesomeIcon icon={faPhoneSlash as IconLookup} color="white" /></div>
-                    <div className="videoButtons" style={{ background: "green" }} onClick={this.onClickRefresh}><FontAwesomeIcon icon={faSync as IconLookup} color="white" /></div>
+                    <div className="videoButtons" style={{ background: 'red' }} onClick={this.onClickHangUp}>
+                        <FontAwesomeIcon icon={faPhoneSlash as IconLookup} color="white" />
+                    </div>
+                    <div className="videoButtons" style={{ background: 'green' }} onClick={this.onClickRefresh}>
+                        <FontAwesomeIcon icon={faSync as IconLookup} color="white" />
+                    </div>
                 </div>
-            </div >;
+            </div>
+        );
 
         const frozen = !this.props.isSelected() || DocumentDecorations.Instance.Interacting;
-        const classname = "webBox-cont" + (this.props.isSelected() && CurrentUserUtils.ActiveTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? "-interactive" : "");
+        const classname = 'webBox-cont' + (this.props.isSelected() && Doc.ActiveTool === InkTool.None && !DocumentDecorations.Instance.Interacting ? '-interactive' : '');
 
         return (
             <>
-                <div className={classname}  >
-                    {content}
-                </div>
-                {!frozen ? (null) : <div className="webBox-overlay" />}
-            </>);
+                <div className={classname}>{content}</div>
+                {!frozen ? null : <div className="webBox-overlay" />}
+            </>
+        );
     }
-
-
-}
\ No newline at end of file
+}
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index 31a50301a..0c7504913 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -5,7 +5,6 @@ import { computedFn } from 'mobx-utils';
 import { alias, map, serializable } from 'serializr';
 import { DocServer } from '../client/DocServer';
 import { DocumentType } from '../client/documents/DocumentTypes';
-import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
 import { LinkManager } from '../client/util/LinkManager';
 import { scriptingGlobal, ScriptingGlobals } from '../client/util/ScriptingGlobals';
 import { SelectionManager } from '../client/util/SelectionManager';
@@ -14,6 +13,7 @@ import { UndoManager } from '../client/util/UndoManager';
 import { DashColor, incrementTitleCopy, intersectRect, Utils } from '../Utils';
 import { DateField } from './DateField';
 import { Copy, HandleUpdate, Id, OnUpdate, Parent, Self, SelfProxy, ToScriptString, ToString, Update } from './FieldSymbols';
+import { InkTool } from './InkField';
 import { List } from './List';
 import { ObjectField } from './ObjectField';
 import { PrefetchProxy, ProxyField } from './Proxy';
@@ -21,7 +21,7 @@ import { FieldId, RefField } from './RefField';
 import { RichTextField } from './RichTextField';
 import { listSpec } from './Schema';
 import { ComputedField, ScriptField } from './ScriptField';
-import { Cast, FieldValue, NumCast, StrCast, ToConstructor } from './Types';
+import { Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor } from './Types';
 import { AudioField, ImageField, MapField, PdfField, VideoField, WebField } from './URLField';
 import { deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, updateFunction } from './util';
 import JSZip = require('jszip');
@@ -141,6 +141,95 @@ export function updateCachedAcls(doc: Doc) {
 @scriptingGlobal
 @Deserializable('Doc', updateCachedAcls).withFields(['id'])
 export class Doc extends RefField {
+    //TODO tfs: these should be temporary...
+    private static mainDocId: string | undefined;
+    public static get MainDocId() {
+        return this.mainDocId;
+    }
+    public static set MainDocId(id: string | undefined) {
+        this.mainDocId = id;
+    }
+    @observable public static GuestDashboard: Doc | undefined;
+    @observable public static GuestTarget: Doc | undefined;
+    @observable public static GuestMobile: Doc | undefined;
+    public static get MySharedDocs() {
+        return DocCast(Doc.UserDoc().mySharedDocs);
+    }
+    public static get MyUserDocView() {
+        return DocCast(Doc.UserDoc().myUserDocView);
+    }
+    public static get MyDockedBtns() {
+        return DocCast(Doc.UserDoc().myDockedBtns);
+    }
+    public static get MySearcher() {
+        return DocCast(Doc.UserDoc().mySearcher);
+    }
+    public static get MyHeaderBar() {
+        return DocCast(Doc.UserDoc().myHeaderBar);
+    }
+    public static get MyLeftSidebarMenu() {
+        return DocCast(Doc.UserDoc().myLeftSidebarMenu);
+    }
+    public static get MyLeftSidebarPanel() {
+        return DocCast(Doc.UserDoc().myLeftSidebarPanel);
+    }
+    public static get MyContextMenuBtns() {
+        return DocCast(Doc.UserDoc().myContextMenuBtns);
+    }
+    public static get MyRecentlyClosed() {
+        return DocCast(Doc.UserDoc().myRecentlyClosed);
+    }
+    public static get MyTrails() {
+        return DocCast(Doc.UserDoc().myTrails);
+    }
+    public static get MyOverlayDocs() {
+        return DocCast(Doc.UserDoc().myOverlayDocs);
+    }
+    public static get MyPublishedDocs() {
+        return DocCast(Doc.UserDoc().myPublishedDocs);
+    }
+    public static get MyDashboards() {
+        return DocCast(Doc.UserDoc().myDashboards);
+    }
+    public static get MyTemplates() {
+        return DocCast(Doc.UserDoc().myTemplates);
+    }
+    public static get MyImports() {
+        return DocCast(Doc.UserDoc().myImports);
+    }
+    public static get MyFilesystem() {
+        return DocCast(Doc.UserDoc().myFilesystem);
+    }
+    public static get MyFileOrphans() {
+        return DocCast(Doc.UserDoc().myFileOrphans);
+    }
+    public static get MyTools() {
+        return DocCast(Doc.UserDoc().myTools);
+    }
+    public static get ActivePage() {
+        return StrCast(Doc.UserDoc().activePage);
+    }
+    public static set ActivePage(val) {
+        Doc.UserDoc().activePage = val;
+    }
+    public static get ActiveDashboard() {
+        return DocCast(Doc.UserDoc().activeDashboard);
+    }
+    public static set ActiveDashboard(val: Doc | undefined) {
+        Doc.UserDoc().activeDashboard = val;
+    }
+    public static set ActiveTool(tool: InkTool) {
+        Doc.UserDoc().activeTool = tool;
+    }
+    public static get ActiveTool(): InkTool {
+        return StrCast(Doc.UserDoc().activeTool, InkTool.None) as InkTool;
+    }
+    public static get ActivePresentation() {
+        return DocCast(Doc.UserDoc().activePresentation);
+    }
+    public static set ActivePresentation(val) {
+        Doc.UserDoc().activePresentation = val;
+    }
     constructor(id?: FieldId, forceSave?: boolean) {
         super(id);
         const doc = new Proxy<this>(this, {
@@ -1035,7 +1124,7 @@ export namespace Doc {
         return manager._user_doc;
     }
     export function SharingDoc(): Doc {
-        return CurrentUserUtils.MySharedDocs;
+        return Doc.MySharedDocs;
     }
     export function LinkDBDoc(): Doc {
         return Cast(Doc.UserDoc().myLinkDatabase, Doc, null);
@@ -1295,50 +1384,34 @@ export namespace Doc {
 
     export function isDocPinned(doc: Doc) {
         //add this new doc to props.Document
-        const curPres = CurrentUserUtils.ActivePresentation;
+        const curPres = Doc.ActivePresentation;
         return !curPres ? false : DocListCast(curPres.data).findIndex(val => Doc.AreProtosEqual(val, doc)) !== -1;
     }
 
+    // prettier-ignore
     export function toIcon(doc?: Doc, isOpen?: boolean) {
         switch (StrCast(doc?.type)) {
-            case DocumentType.IMG:
-                return 'image';
-            case DocumentType.COMPARISON:
-                return 'columns';
-            case DocumentType.RTF:
-                return 'sticky-note';
+            case DocumentType.IMG:  return 'image';
+            case DocumentType.COMPARISON: return 'columns';
+            case DocumentType.RTF: return 'sticky-note';
             case DocumentType.COL:
                 const folder: IconProp = isOpen ? 'folder-open' : 'folder';
                 const chevron: IconProp = isOpen ? 'chevron-down' : 'chevron-right';
                 return !doc?.isFolder ? folder : chevron;
-            case DocumentType.WEB:
-                return 'globe-asia';
-            case DocumentType.SCREENSHOT:
-                return 'photo-video';
-            case DocumentType.WEBCAM:
-                return 'video';
-            case DocumentType.AUDIO:
-                return 'microphone';
-            case DocumentType.BUTTON:
-                return 'bolt';
-            case DocumentType.PRES:
-                return 'tv';
-            case DocumentType.SCRIPTING:
-                return 'terminal';
-            case DocumentType.IMPORT:
-                return 'cloud-upload-alt';
-            case DocumentType.VID:
-                return 'video';
-            case DocumentType.INK:
-                return 'pen-nib';
-            case DocumentType.PDF:
-                return 'file-pdf';
-            case DocumentType.LINK:
-                return 'link';
-            case DocumentType.MAP:
-                return 'map-marker-alt';
-            default:
-                return 'question';
+            case DocumentType.WEB: return 'globe-asia';
+            case DocumentType.SCREENSHOT: return 'photo-video';
+            case DocumentType.WEBCAM: return 'video';
+            case DocumentType.AUDIO: return 'microphone';
+            case DocumentType.BUTTON: return 'bolt';
+            case DocumentType.PRES: return 'tv';
+            case DocumentType.SCRIPTING: return 'terminal';
+            case DocumentType.IMPORT: return 'cloud-upload-alt';
+            case DocumentType.VID: return 'video';
+            case DocumentType.INK: return 'pen-nib';
+            case DocumentType.PDF: return 'file-pdf';
+            case DocumentType.LINK: return 'link';
+            case DocumentType.MAP: return 'map-marker-alt';
+            default: return 'question';
         }
     }
 
@@ -1551,7 +1624,7 @@ ScriptingGlobals.add(function docCast(doc: FieldResult): any {
     return DocCastAsync(doc);
 });
 ScriptingGlobals.add(function activePresentationItem() {
-    const curPres = CurrentUserUtils.ActivePresentation;
+    const curPres = Doc.ActivePresentation;
     return curPres && DocListCast(curPres[Doc.LayoutFieldKey(curPres)])[NumCast(curPres._itemIndex)];
 });
 ScriptingGlobals.add(function selectedDocs(container: Doc, excludeCollections: boolean, prevValue: any) {
diff --git a/src/mobile/MobileInterface.tsx b/src/mobile/MobileInterface.tsx
index e1360553a..f19496d25 100644
--- a/src/mobile/MobileInterface.tsx
+++ b/src/mobile/MobileInterface.tsx
@@ -104,14 +104,13 @@ import { action, computed, observable, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Docs, DocumentOptions, DocUtils } from '../client/documents/Documents';
-import { DocumentType } from '../client/documents/DocumentTypes';
+import { CollectionViewType, DocumentType } from '../client/documents/DocumentTypes';
 import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
 import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
 import { SettingsManager, ColorScheme } from '../client/util/SettingsManager';
 import { Transform } from '../client/util/Transform';
 import { UndoManager } from '../client/util/UndoManager';
 import { TabDocView } from '../client/views/collections/TabDocView';
-import { CollectionViewType } from '../client/views/collections/CollectionView';
 import { GestureOverlay } from '../client/views/GestureOverlay';
 import { AudioBox } from '../client/views/nodes/AudioBox';
 import { DocumentView } from '../client/views/nodes/DocumentView';
@@ -249,7 +248,7 @@ export class MobileInterface extends React.Component {
     @observable private _parents: Array<Doc> = []; // array of parent docs (for pathbar)
 
     @computed private get mainContainer() {
-        return Doc.UserDoc() ? FieldValue(Cast(Doc.UserDoc().activeMobile, Doc)) : CurrentUserUtils.GuestMobile;
+        return Doc.UserDoc() ? FieldValue(Cast(Doc.UserDoc().activeMobile, Doc)) : Doc.GuestMobile;
     }
 
     constructor(props: Readonly<{}>) {
@@ -262,7 +261,7 @@ export class MobileInterface extends React.Component {
     componentDidMount = () => {
         // if the home menu is in list view -> adjust the menu toggle appropriately
         this._menuListView = this._homeDoc._viewType === 'stacking' ? true : false;
-        CurrentUserUtils.ActiveTool = InkTool.None; // ink should intially be set to none
+        Doc.ActiveTool = InkTool.None; // ink should intially be set to none
         Doc.UserDoc().activeMobile = this._homeDoc; // active mobile set to home
         AudioBox.Enabled = true;
 
@@ -511,7 +510,7 @@ export class MobileInterface extends React.Component {
             );
         }
         // stores dashboards documents as 'dashboards' variable
-        let dashboards = CurrentUserUtils.MyDashboards;
+        let dashboards = Doc.MyDashboards;
         if (this.dashboards) {
             dashboards = this.dashboards;
         }
@@ -580,14 +579,14 @@ export class MobileInterface extends React.Component {
      */
     @action
     createNewDashboard = (id?: string) => {
-        const scens = CurrentUserUtils.MyDashboards;
+        const scens = Doc.MyDashboards;
         const dashboardCount = DocListCast(scens.data).length + 1;
         const freeformOptions: DocumentOptions = {
             x: 0,
             y: 400,
             title: 'Collection ' + dashboardCount,
         };
-        const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
+        const freeformDoc = Doc.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions);
         const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, 'row');
 
         const toggleTheme = ScriptField.MakeScript(`self.colorScheme = self.colorScheme ? undefined: ${ColorScheme.Dark}}`);
@@ -607,10 +606,10 @@ export class MobileInterface extends React.Component {
         button.style.color = this._ink ? 'black' : 'white';
 
         if (!this._ink) {
-            CurrentUserUtils.ActiveTool = InkTool.Pen;
+            Doc.ActiveTool = InkTool.Pen;
             this._ink = true;
         } else {
-            CurrentUserUtils.ActiveTool = InkTool.None;
+            Doc.ActiveTool = InkTool.None;
             this._ink = false;
         }
     };
@@ -762,7 +761,7 @@ export class MobileInterface extends React.Component {
     // For setting up the presentation document for the home menu
     @action
     setupDefaultPresentation = () => {
-        const presentation = CurrentUserUtils.ActivePresentation;
+        const presentation = Doc.ActivePresentation;
 
         if (presentation) {
             this.switchCurrentView(presentation);
diff --git a/src/mobile/MobileMain.tsx b/src/mobile/MobileMain.tsx
index 4a1e26078..f85f05f53 100644
--- a/src/mobile/MobileMain.tsx
+++ b/src/mobile/MobileMain.tsx
@@ -1,25 +1,29 @@
-import { MobileInterface } from "./MobileInterface";
-import { Docs } from "../client/documents/Documents";
-import { CurrentUserUtils } from "../client/util/CurrentUserUtils";
-import * as ReactDOM from 'react-dom';
 import * as React from 'react';
-import { DocServer } from "../client/DocServer";
-import { AssignAllExtensions } from "../extensions/General/Extensions";
+import * as ReactDOM from 'react-dom';
+import { DocServer } from '../client/DocServer';
+import { Docs } from '../client/documents/Documents';
+import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
+import { AssignAllExtensions } from '../extensions/General/Extensions';
+import { MobileInterface } from './MobileInterface';
 
 AssignAllExtensions();
 
 (async () => {
     const info = await CurrentUserUtils.loadCurrentUser();
-    DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email + " (mobile)");
+    DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email + ' (mobile)');
     await Docs.Prototypes.initialize();
-    if (info.id !== "__guest__") {
+    if (info.id !== '__guest__') {
         // a guest will not have an id registered
         await CurrentUserUtils.loadUserDocument(info.id);
     }
-    document.getElementById('root')!.addEventListener('wheel', event => {
-        if (event.ctrlKey) {
-            event.preventDefault();
-        }
-    }, true);
+    document.getElementById('root')!.addEventListener(
+        'wheel',
+        event => {
+            if (event.ctrlKey) {
+                event.preventDefault();
+            }
+        },
+        true
+    );
     ReactDOM.render(<MobileInterface />, document.getElementById('root'));
-})();
\ No newline at end of file
+})();
-- 
cgit v1.2.3-70-g09d2


From 5628b585fa6356d66cf2e7454be20e3b847ad22e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Fri, 8 Jul 2022 12:37:06 -0400
Subject: fixes for drawing ink on pdf/image/etc.  fixes for showing
 contextMenu.  moved gestureOverlay into main dashboard area to avoid drawing
 on UI widgets.  more code cleanup to put things in reasonable places and
 avoid importing too much stuff.

---
 src/Utils.ts                                       | 348 ++++++++++++---------
 src/client/util/CurrentUserUtils.ts                |  57 +---
 src/client/util/SelectionManager.ts                |   6 +
 src/client/util/SnappingManager.ts                 |  49 +--
 src/client/views/ContextMenu.tsx                   |  89 +++---
 src/client/views/DashboardView.tsx                 |  17 +
 src/client/views/GestureOverlay.scss               |  11 +-
 src/client/views/GestureOverlay.tsx                |  20 +-
 src/client/views/Main.scss                         |   7 +-
 src/client/views/MainView.scss                     |  26 +-
 src/client/views/MainView.tsx                      |  32 +-
 src/client/views/_nodeModuleOverrides.scss         |  15 +-
 .../views/collections/CollectionDockingView.scss   |  10 +-
 .../views/collections/CollectionDockingView.tsx    |   2 +-
 src/client/views/collections/CollectionView.tsx    |   1 -
 src/client/views/collections/TreeView.tsx          |   7 +
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  37 +--
 src/client/views/nodes/DocumentView.tsx            |  24 +-
 src/fields/util.ts                                 | 304 ++++++++++--------
 19 files changed, 579 insertions(+), 483 deletions(-)

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

diff --git a/src/Utils.ts b/src/Utils.ts
index 6699aa133..528a429d0 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -1,5 +1,5 @@
 import v4 = require('uuid/v4');
-import v5 = require("uuid/v5");
+import v5 = require('uuid/v5');
 import { ColorState } from 'react-color';
 import { Socket } from 'socket.io';
 import { Colors } from './client/views/global/globalEnums';
@@ -16,7 +16,7 @@ export namespace Utils {
         return new Promise((resolve, reject) => {
             temporaryFileReader.onerror = () => {
                 temporaryFileReader.abort();
-                reject(new DOMException("Problem parsing input file."));
+                reject(new DOMException('Problem parsing input file.'));
             };
 
             temporaryFileReader.onload = () => {
@@ -34,7 +34,7 @@ export namespace Utils {
         return v5(seed, v5.URL);
     }
 
-    export function GetScreenTransform(ele?: HTMLElement): { scale: number, translateX: number, translateY: number } {
+    export function GetScreenTransform(ele?: HTMLElement): { scale: number; translateX: number; translateY: number } {
         if (!ele) {
             return { scale: 1, translateX: 1, translateY: 1 };
         }
@@ -50,12 +50,12 @@ export namespace Utils {
         ['log', 'warn'].forEach(function (method) {
             const old = (console as any)[method];
             (console as any)[method] = function () {
-                let stack = new Error("").stack?.split(/\n/);
+                let stack = new Error('').stack?.split(/\n/);
                 // Chrome includes a single "Error" line, FF doesn't.
                 if (stack && stack[0].indexOf('Error') === 0) {
                     stack = stack.slice(1);
                 }
-                const message = (stack?.[1] || "Stack undefined!").trim();
+                const message = (stack?.[1] || 'Stack undefined!').trim();
                 const args = ([] as any[]).slice.apply(arguments).concat([message]);
                 return old.apply(console, args);
             };
@@ -79,7 +79,7 @@ export namespace Utils {
     }
 
     export function CorsProxy(url: string): string {
-        return prepend("/corsProxy/") + encodeURIComponent(url);
+        return prepend('/corsProxy/') + encodeURIComponent(url);
     }
 
     export function CopyText(text: string) {
@@ -88,14 +88,13 @@ export namespace Utils {
 
     export function decimalToHexString(number: number) {
         if (number < 0) {
-            number = 0xFFFFFFFF + number + 1;
+            number = 0xffffffff + number + 1;
         }
-        return (number < 16 ? "0" : "") + number.toString(16).toUpperCase();
+        return (number < 16 ? '0' : '') + number.toString(16).toUpperCase();
     }
 
     export function colorString(color: ColorState) {
-        return color.hex.startsWith("#") ?
-            color.hex + (color.rgb.a ? decimalToHexString(Math.round(color.rgb.a * 255)) : "ff") : color.hex;
+        return color.hex.startsWith('#') ? color.hex + (color.rgb.a ? decimalToHexString(Math.round(color.rgb.a * 255)) : 'ff') : color.hex;
     }
 
     export function fromRGBAstr(rgba: string) {
@@ -110,8 +109,8 @@ export namespace Utils {
         return { r: r, g: g, b: b, a: a };
     }
 
-    const isTransparentFunctionHack = "isTransparent(__value__)";
-    export const noRecursionHack = "__noRecursion";
+    const isTransparentFunctionHack = 'isTransparent(__value__)';
+    export const noRecursionHack = '__noRecursion';
     export function IsRecursiveFilter(val: string) {
         return !val.includes(noRecursionHack);
     }
@@ -120,18 +119,18 @@ export namespace Utils {
     }
     export function IsTransparentFilter() {
         // bcz: isTransparent(__value__) is a hack.  it would be nice to have acual functions be parsed, but now Doc.matchFieldValue is hardwired to recognize just this one
-        return `backgroundColor:${isTransparentFunctionHack},${noRecursionHack}:check`;// bcz: hack.  noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field
+        return `backgroundColor:${isTransparentFunctionHack},${noRecursionHack}:check`; // bcz: hack.  noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field
     }
     export function IsOpaqueFilter() {
         // bcz: isTransparent(__value__) is a hack.  it would be nice to have acual functions be parsed, but now Doc.matchFieldValue is hardwired to recognize just this one
-        return `backgroundColor:${isTransparentFunctionHack},${noRecursionHack}:x`;// bcz: hack.  noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field
+        return `backgroundColor:${isTransparentFunctionHack},${noRecursionHack}:x`; // bcz: hack.  noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field
     }
     export function PropUnsetFilter(prop: string) {
         return `${prop}:any,${noRecursionHack}:unset`;
     }
 
-    export function toRGBAstr(col: { r: number, g: number, b: number, a?: number }) {
-        return "rgba(" + col.r + "," + col.g + "," + col.b + (col.a !== undefined ? "," + col.a : "") + ")";
+    export function toRGBAstr(col: { r: number; g: number; b: number; a?: number }) {
+        return 'rgba(' + col.r + ',' + col.g + ',' + col.b + (col.a !== undefined ? ',' + col.a : '') + ')';
     }
 
     export function HSLtoRGB(h: number, s: number, l: number) {
@@ -140,23 +139,35 @@ export namespace Utils {
         // l /= 100;
 
         const c = (1 - Math.abs(2 * l - 1)) * s,
-            x = c * (1 - Math.abs((h / 60) % 2 - 1)),
+            x = c * (1 - Math.abs(((h / 60) % 2) - 1)),
             m = l - c / 2;
         let r = 0,
             g = 0,
             b = 0;
         if (0 <= h && h < 60) {
-            r = c; g = x; b = 0;
+            r = c;
+            g = x;
+            b = 0;
         } else if (60 <= h && h < 120) {
-            r = x; g = c; b = 0;
+            r = x;
+            g = c;
+            b = 0;
         } else if (120 <= h && h < 180) {
-            r = 0; g = c; b = x;
+            r = 0;
+            g = c;
+            b = x;
         } else if (180 <= h && h < 240) {
-            r = 0; g = x; b = c;
+            r = 0;
+            g = x;
+            b = c;
         } else if (240 <= h && h < 300) {
-            r = x; g = 0; b = c;
+            r = x;
+            g = 0;
+            b = c;
         } else if (300 <= h && h < 360) {
-            r = c; g = 0; b = x;
+            r = c;
+            g = 0;
+            b = x;
         }
         r = Math.round((r + m) * 255);
         g = Math.round((g + m) * 255);
@@ -221,18 +232,17 @@ export namespace Utils {
     export function distanceBetweenHorizontalLines(xs: number, xe: number, y: number, xs2: number, xe2: number, y2: number): [number, number[]] {
         if ((xs2 <= xs && xe2 >= xs) || (xs2 <= xe && xe2 >= xe) || (xs2 >= xs && xe2 <= xe)) return [Math.abs(y - y2), [Math.max(xs, xs2), y, Math.min(xe, xe2), y]];
         if (xe2 <= xs) return [Math.sqrt((xe2 - xs) * (xe2 - xs) + (y2 - y) * (y2 - y)), [xs, y, xs, y]];
-        //if (xs2 > xe) 
+        //if (xs2 > xe)
         return [Math.sqrt((xs2 - xe) * (xs2 - xe) + (y2 - y) * (y2 - y)), [xe, y, xe, y]];
     }
     export function distanceBetweenVerticalLines(x: number, ys: number, ye: number, x2: number, ys2: number, ye2: number): [number, number[]] {
         if ((ys2 <= ys && ye2 >= ys) || (ys2 <= ye && ye2 >= ye) || (ys2 >= ys && ye2 <= ye)) return [Math.abs(x - x2), [x, Math.max(ys, ys2), x, Math.min(ye, ye2)]];
         if (ye2 <= ys) return [Math.sqrt((ye2 - ys) * (ye2 - ys) + (x2 - x) * (x2 - x)), [x, ys, x, ys]];
-        //if (ys2 > ye) 
+        //if (ys2 > ye)
         return [Math.sqrt((ys2 - ye) * (ys2 - ye) + (x2 - x) * (x2 - x)), [x, ye, x, ye]];
     }
 
     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 };
         const atob = { x: bx - ax, y: by - ay };
         const atop = { x: px - ax, y: py - ay };
@@ -245,30 +255,41 @@ export namespace Utils {
         return {
             point: {
                 x: ax + atob.x * t,
-                y: ay + atob.y * t
+                y: ay + atob.y * t,
             },
             left: dot < 1,
             dot: dot,
-            t: t
+            t: t,
         };
     }
 
-    export function closestPtBetweenRectangles(l: number, t: number, w: number, h: number,
-        l1: number, t1: number, w1: number, h1: number,
-        x: number, y: number) {
+    export function closestPtBetweenRectangles(l: number, t: number, w: number, h: number, l1: number, t1: number, w1: number, h1: number, x: number, y: number) {
         const r = l + w,
             b = t + h;
         const r1 = l1 + w1,
             b1 = t1 + h1;
-        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[]]);
+        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[]]
+        );
         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;
+            return res[0] < closest[0] ? res : closest;
         }, res);
 
         const near = project(x, y, fres[1][0], fres[1][1], fres[1][2], fres[1][3]);
@@ -279,8 +300,7 @@ export namespace Utils {
         const r = l + w,
             b = t + h;
 
-        x = clamp(x, l, r),
-            y = clamp(y, t, b);
+        (x = clamp(x, l, r)), (y = clamp(y, t, b));
 
         const dl = Math.abs(x - l),
             dr = Math.abs(x - r),
@@ -289,18 +309,18 @@ export namespace Utils {
 
         const m = Math.min(dl, dr, dt, db);
 
-        return (m === dt) ? [x, t] :
-            (m === db) ? [x, b] :
-                (m === dl) ? [l, y] : [r, y];
+        return m === dt ? [x, t] : m === db ? [x, b] : m === dl ? [l, y] : [r, y];
     }
 
     export function GetClipboardText(): string {
-        const textArea = document.createElement("textarea");
+        const textArea = document.createElement('textarea');
         document.body.appendChild(textArea);
         textArea.focus();
         textArea.select();
 
-        try { document.execCommand('paste'); } catch (err) { }
+        try {
+            document.execCommand('paste');
+        } catch (err) {}
 
         const val = textArea.value;
         document.body.removeChild(textArea);
@@ -318,7 +338,7 @@ export namespace Utils {
         if (logFilter !== undefined && logFilter !== message.type) {
             return;
         }
-        const 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)} `);
     }
@@ -331,14 +351,14 @@ export namespace Utils {
     }
 
     export function Emit<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T) {
-        log("Emit", message.Name, args, false);
+        log('Emit', message.Name, args, false);
         socket.emit(message.Message, args);
     }
 
     export function EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T): Promise<any>;
     export function EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T, fn: (args: any) => any): void;
     export function EmitCallback<T>(socket: Socket | SocketIOClient.Socket, message: Message<T>, args: T, fn?: (args: any) => any): void | Promise<any> {
-        log("Emit", message.Name, args, false);
+        log('Emit', message.Name, args, false);
         if (fn) {
             socket.emit(message.Message, args, loggingCallback('Receiving', fn, message.Name));
         } else {
@@ -358,30 +378,33 @@ export namespace Utils {
     }
     export type RoomHandler = (socket: Socket, room: string) => any;
     export type UsedSockets = Socket | SocketIOClient.Socket;
-    export type RoomMessage = "create or join" | "created" | "joined";
+    export type RoomMessage = 'create or join' | 'created' | 'joined';
     export function AddRoomHandler(socket: Socket, message: RoomMessage, handler: RoomHandler) {
         socket.on(message, room => handler(socket, room));
     }
 }
 
-export function OmitKeys(obj: any, keys: string[], pattern?: string, addKeyFunc?: (dup: any) => void): { omit: any, extract: any } {
+export function OmitKeys(obj: any, keys: string[], pattern?: string, addKeyFunc?: (dup: any) => void): { omit: any; extract: any } {
     const omit: any = { ...obj };
     const extract: any = {};
     keys.forEach(key => {
         extract[key] = omit[key];
         delete omit[key];
     });
-    pattern && Array.from(Object.keys(omit)).filter(key => key.match(pattern)).forEach(key => {
-        extract[key] = omit[key];
-        delete omit[key];
-    });
+    pattern &&
+        Array.from(Object.keys(omit))
+            .filter(key => key.match(pattern))
+            .forEach(key => {
+                extract[key] = omit[key];
+                delete omit[key];
+            });
     addKeyFunc?.(omit);
     return { omit, extract };
 }
 
 export function WithKeys(obj: any, keys: string[], addKeyFunc?: (dup: any) => void) {
     const dup: any = {};
-    keys.forEach(key => dup[key] = obj[key]);
+    keys.forEach(key => (dup[key] = obj[key]));
     addKeyFunc && addKeyFunc(dup);
     return dup;
 }
@@ -402,31 +425,39 @@ export function timenow() {
 
 export function incrementTitleCopy(title: string) {
     const numstr = title.match(/.*(\{([0-9]*)\})+/);
-    const copyNumStr = `{${1 + (numstr ? (+numstr[2]) : 0)}}`;
-    return (numstr ? title.replace(numstr[1], "") : title) + copyNumStr;
+    const copyNumStr = `{${1 + (numstr ? +numstr[2] : 0)}}`;
+    return (numstr ? title.replace(numstr[1], '') : title) + copyNumStr;
 }
 
 export function formatTime(time: number) {
     time = Math.round(time);
     const hours = Math.floor(time / 60 / 60);
-    const minutes = Math.floor(time / 60) - (hours * 60);
+    const minutes = Math.floor(time / 60) - hours * 60;
     const seconds = time % 60;
-    return (hours ? hours.toString() + ":" : "") + minutes.toString().padStart(2, '0') + ':' + seconds.toString().padStart(2, '0');
+    return (hours ? hours.toString() + ':' : '') + minutes.toString().padStart(2, '0') + ':' + seconds.toString().padStart(2, '0');
 }
 
 // x is furthest left, y is furthest top, r is furthest right, b is furthest bottom
-export function aggregateBounds(boundsList: { x: number, y: number, width?: number, height?: number }[], xpad: number, ypad: number) {
-    const bounds = boundsList.map(b => ({ x: b.x, y: b.y, r: b.x + (b.width || 0), b: b.y + (b.height || 0) })).reduce((bounds, b) => ({
-        x: Math.min(b.x, bounds.x), y: Math.min(b.y, bounds.y),
-        r: Math.max(b.r, bounds.r), b: Math.max(b.b, bounds.b)
-    }), { x: Number.MAX_VALUE, y: Number.MAX_VALUE, r: -Number.MAX_VALUE, b: -Number.MAX_VALUE });
+export function aggregateBounds(boundsList: { x: number; y: number; width?: number; height?: number }[], xpad: number, ypad: number) {
+    const bounds = boundsList
+        .map(b => ({ x: b.x, y: b.y, r: b.x + (b.width || 0), b: b.y + (b.height || 0) }))
+        .reduce(
+            (bounds, b) => ({
+                x: Math.min(b.x, bounds.x),
+                y: Math.min(b.y, bounds.y),
+                r: Math.max(b.r, bounds.r),
+                b: Math.max(b.b, 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 + xpad : bounds.r, b: bounds.b !== -Number.MAX_VALUE ? bounds.b + ypad : bounds.b
+        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 + xpad : bounds.r,
+        b: bounds.b !== -Number.MAX_VALUE ? bounds.b + ypad : bounds.b,
     };
 }
-export function intersectRect(r1: { left: number, top: number, width: number, height: number },
-    r2: { left: number, top: number, width: number, height: number }) {
+export function intersectRect(r1: { left: number; top: number; width: number; height: number }, r2: { left: number; top: number; width: number; height: number }) {
     return !(r2.left > r1.left + r1.width || r2.left + r2.width < r1.left || r2.top > r1.top + r1.height || r2.top + r2.height < r1.top);
 }
 
@@ -434,36 +465,63 @@ export function percent2frac(percent: string) {
     return Number(percent.substr(0, percent.length - 1)) / 100;
 }
 
-export function numberRange(num: number) { return num > 0 && num < 1000 ? Array.from(Array(num)).map((v, i) => i) : []; }
+export function numberRange(num: number) {
+    return num > 0 && num < 1000 ? Array.from(Array(num)).map((v, i) => i) : [];
+}
 
-export function returnTransparent() { return "transparent"; }
+export function returnTransparent() {
+    return 'transparent';
+}
 
-export function returnTrue() { return true; }
+export function returnTrue() {
+    return true;
+}
 
-export function returnFalse() { return false; }
+export function returnFalse() {
+    return false;
+}
 
-export function returnAll() { return "all"; }
+export function returnAll() {
+    return 'all';
+}
 
-export function returnNone() { return "none"; }
+export function returnNone() {
+    return 'none';
+}
 
-export function returnVal(val1?: number, val2?: number) { return val1 !== undefined ? val1 : val2 !== undefined ? val2 : 0; }
+export function returnVal(val1?: number, val2?: number) {
+    return val1 !== undefined ? val1 : val2 !== undefined ? val2 : 0;
+}
 
-export function returnOne() { return 1; }
+export function returnOne() {
+    return 1;
+}
 
-export function returnZero() { return 0; }
+export function returnZero() {
+    return 0;
+}
 
-export function returnEmptyString() { return ""; }
+export function returnEmptyString() {
+    return '';
+}
 
-export function returnEmptyFilter() { return [] as string[]; }
+export function returnEmptyFilter() {
+    return [] as string[];
+}
 
-export function returnEmptyDoclist() { return [] as any[]; }
+export function returnEmptyDoclist() {
+    return [] as any[];
+}
 
 export let emptyPath = [];
 
-export function emptyFunction() { return undefined; }
-
-export function unimplementedFunction() { throw new Error("This function is not implemented, but should be."); }
+export function emptyFunction() {
+    return undefined;
+}
 
+export function unimplementedFunction() {
+    throw new Error('This function is not implemented, but should be.');
+}
 
 export type Without<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
 
@@ -484,7 +542,6 @@ export function DeepCopy<K, V>(source: Map<K, V>, predicate?: Predicate<K, V>) {
 }
 
 export namespace JSONUtils {
-
     export function tryParse(source: string) {
         let results: any;
         try {
@@ -494,7 +551,6 @@ export namespace JSONUtils {
         }
         return results;
     }
-
 }
 
 const easeInOutQuad = (currentTime: number, start: number, change: number, duration: number) => {
@@ -509,52 +565,57 @@ const easeInOutQuad = (currentTime: number, start: number, change: number, durat
 };
 
 export function smoothScroll(duration: number, element: HTMLElement | HTMLElement[], to: number) {
-    const elements = (element instanceof HTMLElement ? [element] : element);
+    const elements = element instanceof HTMLElement ? [element] : element;
     const starts = elements.map(element => element.scrollTop);
     const startDate = new Date().getTime();
 
     const animateScroll = () => {
         const currentDate = new Date().getTime();
         const currentTime = currentDate - startDate;
-        elements.map((element, i) => element.scrollTop = easeInOutQuad(currentTime, starts[i], to - starts[i], duration));
+        elements.map((element, i) => (element.scrollTop = easeInOutQuad(currentTime, starts[i], to - starts[i], duration)));
 
         if (currentTime < duration) {
             requestAnimationFrame(animateScroll);
         } else {
-            elements.forEach(element => element.scrollTop = to);
+            elements.forEach(element => (element.scrollTop = to));
         }
     };
     animateScroll();
 }
 
 export function smoothScrollHorizontal(duration: number, element: HTMLElement | HTMLElement[], to: number) {
-    const elements = (element instanceof HTMLElement ? [element] : element);
+    const elements = element instanceof HTMLElement ? [element] : element;
     const starts = elements.map(element => element.scrollLeft);
     const startDate = new Date().getTime();
 
     const animateScroll = () => {
         const currentDate = new Date().getTime();
         const currentTime = currentDate - startDate;
-        elements.map((element, i) => element.scrollLeft = easeInOutQuad(currentTime, starts[i], to - starts[i], duration));
+        elements.map((element, i) => (element.scrollLeft = easeInOutQuad(currentTime, starts[i], to - starts[i], duration)));
 
         if (currentTime < duration) {
             requestAnimationFrame(animateScroll);
         } else {
-            elements.forEach(element => element.scrollLeft = to);
+            elements.forEach(element => (element.scrollLeft = to));
         }
     };
     animateScroll();
 }
 
-export function addStyleSheet(styleType: string = "text/css") {
-    const style = document.createElement("style");
+export function addStyleSheet(styleType: string = 'text/css') {
+    const style = document.createElement('style');
     style.type = styleType;
     const sheets = document.head.appendChild(style);
     return (sheets as any).sheet;
 }
-export function addStyleSheetRule(sheet: any, selector: any, css: any, selectorPrefix = ".") {
-    const propText = typeof css === "string" ? css : Object.keys(css).map(p => p + ":" + (p === "content" ? "'" + css[p] + "'" : css[p])).join(";");
-    return sheet.insertRule(selectorPrefix + selector + "{" + propText + "}", sheet.cssRules.length);
+export function addStyleSheetRule(sheet: any, selector: any, css: any, selectorPrefix = '.') {
+    const propText =
+        typeof css === 'string'
+            ? css
+            : Object.keys(css)
+                  .map(p => p + ':' + (p === 'content' ? "'" + css[p] + "'" : css[p]))
+                  .join(';');
+    return sheet.insertRule(selectorPrefix + selector + '{' + propText + '}', sheet.cssRules.length);
 }
 export function removeStyleSheetRule(sheet: any, rule: number) {
     if (sheet.rules.length) {
@@ -573,33 +634,35 @@ export function clearStyleSheetRules(sheet: any) {
 
 export function simulateMouseClick(element: Element | null | undefined, x: number, y: number, sx: number, sy: number, rightClick = true) {
     if (!element) return;
-    ["pointerdown", "pointerup"].map(event => element.dispatchEvent(
-        new PointerEvent(event, {
+    ['pointerdown', 'pointerup'].map(event => {
+        const me = new PointerEvent(event, {
             view: window,
             bubbles: true,
             cancelable: true,
             button: 2,
-            pointerType: "mouse",
+            pointerType: 'mouse',
             clientX: x,
             clientY: y,
             screenX: sx,
             screenY: sy,
-        })));
+        });
+        (me as any).dash = true;
+        element.dispatchEvent(me);
+    });
 
     if (rightClick) {
-        const me =
-            new MouseEvent("contextmenu", {
-                view: window,
-                bubbles: true,
-                cancelable: true,
-                button: 2,
-                clientX: x,
-                clientY: y,
-                movementX: 0,
-                movementY: 0,
-                screenX: sx,
-                screenY: sy,
-            });
+        const me = new MouseEvent('contextmenu', {
+            view: window,
+            bubbles: true,
+            cancelable: true,
+            button: 2,
+            clientX: x,
+            clientY: y,
+            movementX: 0,
+            movementY: 0,
+            screenX: sx,
+            screenY: sy,
+        });
         (me as any).dash = true;
         element.dispatchEvent(me);
     }
@@ -607,27 +670,25 @@ export function simulateMouseClick(element: Element | null | undefined, x: numbe
 
 export function DashColor(color: string) {
     try {
-        return color ? Color(color.toLowerCase()) : Color("transparent");
+        return color ? Color(color.toLowerCase()) : Color('transparent');
     } catch (e) {
-        console.log("COLOR error:", e);
-        return Color("red");
+        console.log('COLOR error:', e);
+        return Color('red');
     }
 }
 
 export function lightOrDark(color: any) {
-    if (color === "transparent") return "gray";
-    if (color.startsWith?.("linear")) return "black";
-    const nonAlphaColor = color.startsWith("#") ? (color as string).substring(0, 7) :
-        color.startsWith("rgba") ? color.replace(/,.[^,]*\)/, ")").replace("rgba", "rgb") : color;
+    if (color === 'transparent') return 'gray';
+    if (color.startsWith?.('linear')) return 'black';
+    const nonAlphaColor = color.startsWith('#') ? (color as string).substring(0, 7) : color.startsWith('rgba') ? color.replace(/,.[^,]*\)/, ')').replace('rgba', 'rgb') : color;
     const col = DashColor(nonAlphaColor).rgb();
-    const colsum = (col.red() + col.green() + col.blue());
+    const colsum = col.red() + col.green() + col.blue();
     if (colsum / col.alpha() > 400 || col.alpha() < 0.25) return Colors.DARK_GRAY;
     else return Colors.WHITE;
 }
 
-
 export function getWordAtPoint(elem: any, x: number, y: number): string | undefined {
-    if (elem.tagName === "INPUT") return "input";
+    if (elem.tagName === 'INPUT') return 'input';
     if (elem.nodeType === elem.TEXT_NODE) {
         const range = elem.ownerDocument.createRange();
         range.selectNodeContents(elem);
@@ -637,12 +698,11 @@ export function getWordAtPoint(elem: any, x: number, y: number): string | undefi
             range.setStart(elem, currentPos);
             range.setEnd(elem, currentPos + 1);
             const rangeRect = range.getBoundingClientRect();
-            if (rangeRect.left <= x && rangeRect.right >= x &&
-                rangeRect.top <= y && rangeRect.bottom >= y) {
-                range.expand?.("word"); // doesn't exist in firefox
+            if (rangeRect.left <= x && rangeRect.right >= x && rangeRect.top <= y && rangeRect.bottom >= y) {
+                range.expand?.('word'); // doesn't exist in firefox
                 const ret = range.toString();
                 range.detach();
-                return (ret);
+                return ret;
             }
             currentPos += 1;
         }
@@ -651,8 +711,7 @@ export function getWordAtPoint(elem: any, x: number, y: number): string | undefi
             const range = childNode.ownerDocument.createRange();
             range.selectNodeContents(childNode);
             const rangeRect = range.getBoundingClientRect();
-            if (rangeRect.left <= x && rangeRect.right >= x &&
-                rangeRect.top <= y && rangeRect.bottom >= y) {
+            if (rangeRect.left <= x && rangeRect.right >= x && rangeRect.top <= y && rangeRect.bottom >= y) {
                 range.detach();
                 const word = getWordAtPoint(childNode, x, y);
                 if (word) return word;
@@ -681,17 +740,17 @@ export function StopEvent(e: React.PointerEvent | React.MouseEvent) {
  * Helper method for converting pixel string eg. '32px' into number eg. 32
  * @param value: string with 'px' ending
  * @returns value: number
- * 
+ *
  * Example:
  * '32px' -> 32
  */
-export function numberValue(value: string | undefined):number {
+export function numberValue(value: string | undefined): number {
     if (value == undefined) return 0;
     return parseInt(value);
 }
 
 export function numbersAlmostEqual(num1: number, num2: number) {
-    return Math.abs( num1 - num2 ) < 0.2;
+    return Math.abs(num1 - num2) < 0.2;
 }
 
 export function setupMoveUpEvents(
@@ -705,7 +764,7 @@ export function setupMoveUpEvents(
     noDoubleTapTimeout?: () => void
 ) {
     const doubleTapTimeout = 300;
-    (target as any)._doubleTap = (Date.now() - (target as any)._lastTap < doubleTapTimeout);
+    (target as any)._doubleTap = Date.now() - (target as any)._lastTap < doubleTapTimeout;
     (target as any)._lastTap = Date.now();
     (target as any)._downX = (target as any)._lastX = e.clientX;
     (target as any)._downY = (target as any)._lastY = e.clientY;
@@ -717,10 +776,9 @@ export function setupMoveUpEvents(
                 clearTimeout((target as any)._doubleTime);
                 (target as any)._doubleTime = undefined;
             }
-            if (moveEvent(e, [(target as any)._downX, (target as any)._downY],
-                [e.clientX - (target as any)._lastX, e.clientY - (target as any)._lastY])) {
-                document.removeEventListener("pointermove", _moveEvent);
-                document.removeEventListener("pointerup", _upEvent);
+            if (moveEvent(e, [(target as any)._downX, (target as any)._downY], [e.clientX - (target as any)._lastX, e.clientY - (target as any)._lastY])) {
+                document.removeEventListener('pointermove', _moveEvent);
+                document.removeEventListener('pointerup', _upEvent);
             }
         }
         (target as any)._lastX = e.clientX;
@@ -743,18 +801,18 @@ export function setupMoveUpEvents(
             }
             (target as any)._noClick = clickEvent(e, (target as any)._doubleTap);
         }
-        document.removeEventListener("pointermove", _moveEvent);
-        document.removeEventListener("pointerup", _upEvent);
+        document.removeEventListener('pointermove', _moveEvent);
+        document.removeEventListener('pointerup', _upEvent);
     };
     const _clickEvent = (e: MouseEvent): void => {
         if ((target as any)._noClick) e.stopPropagation();
-        document.removeEventListener("click", _clickEvent, true);
-    }
+        document.removeEventListener('click', _clickEvent, true);
+    };
     if (stopPropagation) {
         e.stopPropagation();
         e.preventDefault();
     }
-    document.addEventListener("pointermove", _moveEvent);
-    document.addEventListener("pointerup", _upEvent);
-    document.addEventListener("click", _clickEvent, true);
-}
\ No newline at end of file
+    document.addEventListener('pointermove', _moveEvent);
+    document.addEventListener('pointerup', _upEvent);
+    document.addEventListener('click', _clickEvent, true);
+}
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 02d43088d..6c80cf0f4 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1,37 +1,28 @@
-import { computed, observable, reaction } from "mobx";
+import { reaction } from "mobx";
 import * as rp from 'request-promise';
-import { DataSym, Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc";
-import { Id } from "../../fields/FieldSymbols";
+import { Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc";
 import { List } from "../../fields/List";
 import { PrefetchProxy } from "../../fields/Proxy";
 import { RichTextField } from "../../fields/RichTextField";
 import { listSpec } from "../../fields/Schema";
-import { ComputedField, ScriptField } from "../../fields/ScriptField";
-import { Cast, DateCast, DocCast, FieldValue, NumCast, PromiseValue, ScriptCast, StrCast } from "../../fields/Types";
+import { ScriptField } from "../../fields/ScriptField";
+import { Cast, DateCast, DocCast, PromiseValue, StrCast } from "../../fields/Types";
 import { nullAudio } from "../../fields/URLField";
-import { SharingPermissions } from "../../fields/util";
+import { SetCachedGroups, SharingPermissions } from "../../fields/util";
 import { OmitKeys, Utils } from "../../Utils";
 import { DocServer } from "../DocServer";
 import { Docs, DocumentOptions, DocUtils, FInfo } from "../documents/Documents";
 import { CollectionViewType, DocumentType } from "../documents/DocumentTypes";
-import { CollectionDockingView } from "../views/collections/CollectionDockingView";
 import { TreeViewType } from "../views/collections/CollectionTreeView";
-import { CollectionView } from "../views/collections/CollectionView";
-import { TreeView } from "../views/collections/TreeView";
-import { DashboardView } from "../views/DashboardView";
 import { Colors } from "../views/global/globalEnums";
 import { MainView } from "../views/MainView";
 import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox";
 import { OverlayView } from "../views/OverlayView";
 import { DragManager } from "./DragManager";
 import { MakeTemplate } from "./DropConverter";
-import { HistoryUtil } from "./History";
 import { LinkManager } from "./LinkManager";
 import { ScriptingGlobals } from "./ScriptingGlobals";
-import { SelectionManager } from "./SelectionManager";
 import { ColorScheme } from "./SettingsManager";
-import { SharingManager } from "./SharingManager";
-import { SnappingManager } from "./SnappingManager";
 import { UndoManager } from "./UndoManager";
 
 interface Button {
@@ -488,7 +479,7 @@ export class CurrentUserUtils {
         var myFilesystem = DocCast(doc[field]);
         const myFileOrphans = DocUtils.AssignDocField(doc, "myFileOrphans", (opts) => Docs.Create.TreeDocument([], opts), { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true });
         
-        const newFolder = `makeTopLevelFolder()`;
+        const newFolder = `TreeView_addNewFolder()`;
         const newFolderOpts: DocumentOptions = {
             _forceActive: true, _stayInCollection: true, _hideContextMenu: true, _width: 30, _height: 30,
             title: "New folder", btnType: ButtonType.ClickButton, toolTip: "Create new folder", buttonText: "New folder", icon: "folder-plus", system: true
@@ -638,16 +629,16 @@ export class CurrentUserUtils {
                     CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel,
                     CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map,
                     CollectionViewType.Grid]),
-              title: "Perspective",                    toolTip: "View",                 width: 100,btnType: ButtonType.DropdownList,ignoreClick: true, scripts: { script: 'setView(value, _readOnly_)'}},
-            { title: "Back",    icon: "chevron-left",  toolTip: "Prev Animation Frame", width: 20, btnType: ButtonType.ClickButton,                    scripts: { onClick: 'prevKeyFrame(_readOnly_)'},             funcs: {hidden: 'IsNoviceMode() || !selectedDocumentType(undefined, "freeform")'}},
-            { title: "Fwd",     icon: "chevron-right", toolTip: "Next Animation Frame", width: 20, btnType: ButtonType.ClickButton,                    scripts: { onClick: 'nextKeyFrame(_readOnly_)'},             funcs: {hidden: 'IsNoviceMode() || !selectedDocumentType(undefined, "freeform")'}},
-            { title: "Fill",    icon: "fill-drip",     toolTip: "Background Fill Color",width: 20, btnType: ButtonType.ColorButton, ignoreClick: true, scripts: { script:  'setBackgroundColor(value, _readOnly_)'},funcs: {hidden: '!selectedDocumentType()'}}, // Only when a document is selected
-            { title: "Header",  icon: "heading",       toolTip: "Header Color",                    btnType: ButtonType.ColorButton, ignoreClick: true, scripts: { script:  'setHeaderColor(value, _readOnly_)'},    funcs: {hidden: '!selectedDocumentType()'}},
-            { title: "Overlay", icon: "layer-group",   toolTip: "Overlay",                         btnType: ButtonType.ToggleButton,                   scripts: { onClick: 'toggleOverlay(_readOnly_)'},            funcs: {hidden: '!selectedDocumentType(undefined, "freeform", true)'}}, // Only when floating document is selected in freeform
-            { title: "Text",    icon: "text",   subMenu: CurrentUserUtils.textTools(),   funcs: {linearViewIsExpanded: `selectedDocumentType("${DocumentType.RTF}")`} }, // Always available
-            { title: "Ink",     icon: "ink",    subMenu: CurrentUserUtils.inkTools(),    funcs: {linearViewIsExpanded: `selectedDocumentType("${DocumentType.INK}")`} }, // Always available
-            { title: "Web",     icon: "web",    subMenu: CurrentUserUtils.webTools(),    funcs: {linearViewIsExpanded: `selectedDocumentType("${DocumentType.WEB}")`,                     hidden: `!selectedDocumentType("${DocumentType.WEB}")`} }, // Only when Web is selected
-            { title: "Schema",  icon: "schema", subMenu: CurrentUserUtils.schemaTools(), funcs: {linearViewIsExpanded: `selectedDocumentType(undefined, "${CollectionViewType.Schema}")`, hidden: `!selectedDocumentType(undefined, "${CollectionViewType.Schema}")`} } // Only when Schema is selected
+              title: "Perspective",                    toolTip: "View",                 btnType: ButtonType.DropdownList,                                                                                    ignoreClick: true, width: 100, scripts: { script: 'setView(value, _readOnly_)'}},
+            { title: "Back",    icon: "chevron-left",  toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton,         funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}, width: 20,  scripts: { onClick: 'prevKeyFrame(_readOnly_)'}},
+            { title: "Fwd",     icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton,         funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}, width: 20,  scripts: { onClick: 'nextKeyFrame(_readOnly_)'}},
+            { title: "Fill",    icon: "fill-drip",     toolTip: "Background Fill Color",btnType: ButtonType.ColorButton,         funcs: {hidden: '!SelectionManager_selectedDocType()'},                     ignoreClick: true, width: 20,  scripts: { script:  'setBackgroundColor(value, _readOnly_)'}}, // Only when a document is selected
+            { title: "Header",  icon: "heading",       toolTip: "Header Color",         btnType: ButtonType.ColorButton,         funcs: {hidden: '!SelectionManager_selectedDocType()'},                     ignoreClick: true,             scripts: { script:  'setHeaderColor(value, _readOnly_)'}},
+            { title: "Overlay", icon: "layer-group",   toolTip: "Overlay",              btnType: ButtonType.ToggleButton,        funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform", true)'},                         scripts: { onClick: 'toggleOverlay(_readOnly_)'}}, // Only when floating document is selected in freeform
+            { title: "Text",    icon: "text",          toolTip: "Text functions",       subMenu: CurrentUserUtils.textTools(),   funcs: {hidden: 'false',                                                                        linearViewIsExpanded: `SelectionManager_selectedDocType("${DocumentType.RTF}")`} }, // Always available
+            { title: "Ink",     icon: "ink",           toolTip: "Ink functions",        subMenu: CurrentUserUtils.inkTools(),    funcs: {hidden: 'false',                                                                        inearViewIsExpanded: `SelectionManager_selectedDocType("${DocumentType.INK}")`} }, // Always available
+            { title: "Web",     icon: "web",           toolTip: "Web functions",        subMenu: CurrentUserUtils.webTools(),    funcs: {hidden: `!SelectionManager_selectedDocType("${DocumentType.WEB}")`,                     linearViewIsExpanded: `SelectionManager_selectedDocType("${DocumentType.WEB}")`,                     } }, // Only when Web is selected
+            { title: "Schema",  icon: "schema",        toolTip: "Schema functions",     subMenu: CurrentUserUtils.schemaTools(), funcs: {hidden: `!SelectionManager_selectedDocType(undefined, "${CollectionViewType.Schema}")`, linearViewIsExpanded: `SelectionManager_selectedDocType(undefined, "${CollectionViewType.Schema}")`} } // Only when Schema is selected
         ];
     }
 
@@ -812,7 +803,7 @@ export class CurrentUserUtils {
             async () => {
                 const groups = await DocListCastAsync(DocCast(doc.globalGroupDatabase).data);
                 const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(Doc.CurrentUserEmail)) || [];
-                SnappingManager.SetCachedGroups(["Public", ...mygroups?.map(g => StrCast(g.title))]);
+                SetCachedGroups(["Public", ...mygroups?.map(g => StrCast(g.title))]);
             }, { fireImmediately: true });
         doc.system ?? (doc.system = true);
         doc.title ?? (doc.title = Doc.CurrentUserEmail);
@@ -947,21 +938,7 @@ export class CurrentUserUtils {
 ScriptingGlobals.add(function MySharedDocs() { return Doc.MySharedDocs; }, "document containing all shared Docs");
 ScriptingGlobals.add(function IsNoviceMode() { return Doc.noviceMode; }, "is Dash in novice mode");
 ScriptingGlobals.add(function toggleComicMode() { Doc.UserDoc().renderStyle = Doc.UserDoc().renderStyle === "comic" ? undefined : "comic"; }, "switches between comic and normal document rendering");
-ScriptingGlobals.add(function snapshotDashboard() { DashboardView.snapshotDashboard(); }, "creates a snapshot copy of a dashboard");
-ScriptingGlobals.add(function createNewDashboard() { return DashboardView.createNewDashboard(); }, "creates a new dashboard when called");
 ScriptingGlobals.add(function createNewPresentation() { return MainView.Instance.createNewPresentation(); }, "creates a new presentation when called");
 ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called");
 ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, "returns all the links to the document or its annotations", "(doc: any)");
 ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar");
-ScriptingGlobals.add(function shareDashboard(dashboard: Doc) { SharingManager.Instance.open(undefined, dashboard); }, "opens sharing dialog for Dashboard");
-ScriptingGlobals.add(function removeDashboard(dashboard: Doc) { DashboardView.removeDashboard(dashboard); }, "Remove Dashboard from Dashboards");
-ScriptingGlobals.add(function addToDashboards(dashboard: Doc) {  DashboardView.openDashboard( Doc.MakeAlias(dashboard)); },  "adds Dashboard to set of Dashboards");
-ScriptingGlobals.add(function selectedDocumentType(docType?: DocumentType, colType?: CollectionViewType, checkContext?: boolean) {
-    let selected = (sel => checkContext ? DocCast(sel?.context) : sel)(SelectionManager.SelectedSchemaDoc() ?? SelectionManager.Docs().lastElement());
-    return docType ? selected?.type === docType : colType ? selected?.viewType === colType : true;
-});
-ScriptingGlobals.add(function makeTopLevelFolder() {
-    TreeView._editTitleOnLoad = { id: Utils.GenerateGuid(), parent: undefined };
-    const opts = { title: "Untitled folder", _stayInCollection: true, isFolder: true };
-    return Doc.AddDocToList(Doc.MyFilesystem, "data", Docs.Create.TreeDocument([], opts, TreeView._editTitleOnLoad.id));
-});
\ No newline at end of file
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index d67ce6f6a..1c84af94a 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,8 +1,10 @@
 import { action, observable, ObservableMap } from 'mobx';
 import { computedFn } from 'mobx-utils';
 import { Doc, Opt } from '../../fields/Doc';
+import { DocCast } from '../../fields/Types';
 import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
 import { DocumentView } from '../views/nodes/DocumentView';
+import { ScriptingGlobals } from './ScriptingGlobals';
 
 export namespace SelectionManager {
     class Manager {
@@ -98,3 +100,7 @@ export namespace SelectionManager {
         return Array.from(manager.SelectedViews.values()).filter(doc => doc?._viewType !== CollectionViewType.Docking);
     }
 }
+ScriptingGlobals.add(function SelectionManager_selectedDocType(docType?: DocumentType, colType?: CollectionViewType, checkContext?: boolean) {
+    let selected = (sel => (checkContext ? DocCast(sel?.context) : sel))(SelectionManager.SelectedSchemaDoc() ?? SelectionManager.Docs().lastElement());
+    return docType ? selected?.type === docType : colType ? selected?.viewType === colType : true;
+});
diff --git a/src/client/util/SnappingManager.ts b/src/client/util/SnappingManager.ts
index 057843c68..2c0a1da8b 100644
--- a/src/client/util/SnappingManager.ts
+++ b/src/client/util/SnappingManager.ts
@@ -1,9 +1,8 @@
-import { observable, action, runInAction } from "mobx";
-import { computedFn } from "mobx-utils";
-import { Doc } from "../../fields/Doc";
+import { observable, action, runInAction } from 'mobx';
+import { computedFn } from 'mobx-utils';
+import { Doc } from '../../fields/Doc';
 
 export namespace SnappingManager {
-
     class Manager {
         @observable IsDragging: boolean = false;
         @observable public horizSnapLines: number[] = [];
@@ -16,28 +15,34 @@ export namespace SnappingManager {
             this.horizSnapLines = horizLines;
             this.vertSnapLines = vertLines;
         }
-
-        @observable cachedGroups: string[] = [];
-        @action setCachedGroups(groups: string[]) { this.cachedGroups = groups; }
     }
 
     const manager = new Manager();
 
-    export function clearSnapLines() { manager.clearSnapLines(); }
-    export function setSnapLines(horizLines: number[], vertLines: number[]) { manager.setSnapLines(horizLines, vertLines); }
-    export function horizSnapLines() { return manager.horizSnapLines; }
-    export function vertSnapLines() { return manager.vertSnapLines; }
-
-    export function SetIsDragging(dragging: boolean) { runInAction(() => manager.IsDragging = dragging); }
-    export function GetIsDragging() { return manager.IsDragging; }
+    export function clearSnapLines() {
+        manager.clearSnapLines();
+    }
+    export function setSnapLines(horizLines: number[], vertLines: number[]) {
+        manager.setSnapLines(horizLines, vertLines);
+    }
+    export function horizSnapLines() {
+        return manager.horizSnapLines;
+    }
+    export function vertSnapLines() {
+        return manager.vertSnapLines;
+    }
 
-    export function SetShowSnapLines(show: boolean) { runInAction(() => Doc.UserDoc().showSnapLines = show); }
-    export function GetShowSnapLines() { return Doc.UserDoc().showSnapLines; }
+    export function SetIsDragging(dragging: boolean) {
+        runInAction(() => (manager.IsDragging = dragging));
+    }
+    export function GetIsDragging() {
+        return manager.IsDragging;
+    }
 
-    /// bcz; argh!! TODO;   These do not belong here, but there were include order problems with leaving them in util.ts
-    // need to investigate further what caused the mobx update problems and move to a better location.
-    const getCachedGroupByNameCache = computedFn(function (name: string) { return manager.cachedGroups.includes(name); }, true);
-    export function GetCachedGroupByName(name: string) { return getCachedGroupByNameCache(name); }
-    export function SetCachedGroups(groups: string[]) { manager.setCachedGroups(groups); }
+    export function SetShowSnapLines(show: boolean) {
+        runInAction(() => (Doc.UserDoc().showSnapLines = show));
+    }
+    export function GetShowSnapLines() {
+        return Doc.UserDoc().showSnapLines;
+    }
 }
-
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index cffcd0f17..c9908b4b0 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -1,10 +1,10 @@
-import React = require("react");
+import React = require('react');
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, observable } from "mobx";
-import { observer } from "mobx-react";
-import "./ContextMenu.scss";
-import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from "./ContextMenuItem";
-import { Utils } from "../../Utils";
+import { action, computed, IReactionDisposer, observable } from 'mobx';
+import { observer } from 'mobx-react';
+import './ContextMenu.scss';
+import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from './ContextMenuItem';
+import { Utils } from '../../Utils';
 
 @observer
 export class ContextMenu extends React.Component {
@@ -14,7 +14,7 @@ export class ContextMenu extends React.Component {
     @observable private _pageX: number = 0;
     @observable private _pageY: number = 0;
     @observable private _display: boolean = false;
-    @observable private _searchString: string = "";
+    @observable private _searchString: string = '';
     @observable private _showSearch: boolean = false;
     // afaik displaymenu can be called before all the items are added to the menu, so can't determine in displayMenu what the height of the menu will be
     @observable private _yRelativeToTop: boolean = true;
@@ -43,9 +43,10 @@ export class ContextMenu extends React.Component {
     onPointerDown = (e: PointerEvent) => {
         this._mouseX = e.clientX;
         this._mouseY = e.clientY;
-    }
+    };
     @action
     onPointerUp = (e: PointerEvent) => {
+        if (e.button !== 2 && !e.ctrlKey) return;
         const curX = e.clientX;
         const curY = e.clientY;
         if (this._ignoreUp) {
@@ -63,27 +64,27 @@ export class ContextMenu extends React.Component {
                 this._display = true;
             }
         }
-    }
+    };
     componentWillUnmount() {
-        document.removeEventListener("pointerdown", this.onPointerDown);
-        document.removeEventListener("pointerup", this.onPointerUp);
+        document.removeEventListener('pointerdown', this.onPointerDown, true);
+        document.removeEventListener('pointerup', this.onPointerUp);
         this._reactionDisposer?.();
     }
 
     @action
     componentDidMount = () => {
-        document.addEventListener("pointerdown", this.onPointerDown);
-        document.addEventListener("pointerup", this.onPointerUp);
-    }
+        document.addEventListener('pointerdown', this.onPointerDown, true);
+        document.addEventListener('pointerup', this.onPointerUp);
+    };
 
     @action
     clearItems() {
         this._items = [];
-        this._defaultPrefix = "";
+        this._defaultPrefix = '';
         this._defaultItem = undefined;
     }
 
-    _defaultPrefix: string = "";
+    _defaultPrefix: string = '';
     _defaultItem: ((name: string) => void) | undefined;
 
     findByDescription = (target: string, toLowerCase = false) => {
@@ -92,7 +93,7 @@ export class ContextMenu extends React.Component {
             toLowerCase && (reference = reference.toLowerCase());
             return reference === target;
         });
-    }
+    };
 
     @action
     addItem(item: ContextMenuProps) {
@@ -103,9 +104,9 @@ export class ContextMenu extends React.Component {
     @action
     moveAfter(item: ContextMenuProps, after: ContextMenuProps) {
         if (after && this.findByDescription(after.description)) {
-            const curInd = this._items.findIndex((i) => i.description === item.description);
+            const curInd = this._items.findIndex(i => i.description === item.description);
             this._items.splice(curInd, 1);
-            const afterInd = this._items.findIndex((i) => i.description === after.description);
+            const afterInd = this._items.findIndex(i => i.description === after.description);
             this._items.splice(afterInd + 1, 0, item);
         }
     }
@@ -142,7 +143,7 @@ export class ContextMenu extends React.Component {
 
     _onDisplay?: () => void = undefined;
     @action
-    displayMenu = (x: number, y: number, initSearch = "", showSearch = false, onDisplay?: () => void) => {
+    displayMenu = (x: number, y: number, initSearch = '', showSearch = false, onDisplay?: () => void) => {
         //maxX and maxY will change if the UI/font size changes, but will work for any amount
         //of items added to the menu
 
@@ -153,7 +154,7 @@ export class ContextMenu extends React.Component {
         this._shouldDisplay = true;
         this._onDisplay = onDisplay;
         this._display = !onDisplay;
-    }
+    };
 
     @action
     closeMenu = () => {
@@ -162,10 +163,10 @@ export class ContextMenu extends React.Component {
         this._display = false;
         this._shouldDisplay = false;
         return wasOpen;
-    }
+    };
 
     @computed get filteredItems(): (OriginalMenuProps | string[])[] {
-        const searchString = this._searchString.toLowerCase().split(" ");
+        const searchString = this._searchString.toLowerCase().split(' ');
         const matches = (descriptions: string[]): boolean => {
             return searchString.every(s => descriptions.some(desc => desc.toLowerCase().includes(s)));
         };
@@ -176,7 +177,7 @@ export class ContextMenu extends React.Component {
             for (const item of items) {
                 const description = item.description;
                 const path = groupFunc(description);
-                if ("subitems" in item) {
+                if ('subitems' in item) {
                     const children = flattenItems(item.subitems, name => [...groupFunc(description), name]);
                     if (children.length || matches(path)) {
                         eles.push(path);
@@ -205,13 +206,14 @@ export class ContextMenu extends React.Component {
         if (!this._searchString) {
             return this._items.map((item, ind) => <ContextMenuItem {...item} noexpand={this.itemsNeedSearch ? true : (item as any).noexpand} key={ind + item.description} closeMenu={this.closeMenu} />);
         }
-        return this.filteredItems.map((value, index) => 
-            Array.isArray(value)  ?
+        return this.filteredItems.map((value, index) =>
+            Array.isArray(value) ? (
                 <div className="contextMenu-group">
-                    <div className="contextMenu-description">{value.join(" -> ")}</div>
+                    <div className="contextMenu-description">{value.join(' -> ')}</div>
                 </div>
-                :
-                <ContextMenuItem {...value} key={index+value.description} closeMenu={this.closeMenu} selected={index === this.selectedIndex} />
+            ) : (
+                <ContextMenuItem {...value} key={index + value.description} closeMenu={this.closeMenu} selected={index === this.selectedIndex} />
+            )
         );
     }
 
@@ -220,32 +222,34 @@ export class ContextMenu extends React.Component {
     }
 
     render() {
-        return !this._display ? (null) : 
-            <div className="contextMenu-cont" style={{left: this.pageX, ...(this._yRelativeToTop ? { top: this.pageY } : { bottom: this.pageY })}}>
-                {!this.itemsNeedSearch ? (null) :
-                    <span className={"search-icon"}>
+        return !this._display ? null : (
+            <div className="contextMenu-cont" style={{ left: this.pageX, ...(this._yRelativeToTop ? { top: this.pageY } : { bottom: this.pageY }) }}>
+                {!this.itemsNeedSearch ? null : (
+                    <span className={'search-icon'}>
                         <span className="icon-background">
                             <FontAwesomeIcon icon="search" size="lg" />
                         </span>
                         <input className="contextMenu-item contextMenu-description search" type="text" placeholder="Filter Menu..." value={this._searchString} onKeyDown={this.onKeyDown} onChange={this.onChange} autoFocus />
-                    </span>}
+                    </span>
+                )}
                 {this.menuItems}
-            </div>;
+            </div>
+        );
     }
 
     @action
     onKeyDown = (e: React.KeyboardEvent) => {
-        if (e.key === "ArrowDown") {
+        if (e.key === 'ArrowDown') {
             if (this.selectedIndex < this.flatItems.length - 1) {
                 this.selectedIndex++;
             }
             e.preventDefault();
-        } else if (e.key === "ArrowUp") {
+        } else if (e.key === 'ArrowUp') {
             if (this.selectedIndex > 0) {
                 this.selectedIndex--;
             }
             e.preventDefault();
-        } else if (e.key === "Enter" || e.key === "Tab") {
+        } else if (e.key === 'Enter' || e.key === 'Tab') {
             const item = this.flatItems[this.selectedIndex];
             if (item) {
                 item.event({ x: this.pageX, y: this.pageY });
@@ -256,20 +260,19 @@ export class ContextMenu extends React.Component {
             e.preventDefault();
             e.stopPropagation();
         }
-    }
+    };
 
     @action
     onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         this._searchString = e.target.value;
         if (!this._searchString) {
             this.selectedIndex = -1;
-        }
-        else {
+        } else {
             if (this.selectedIndex === -1) {
                 this.selectedIndex = 0;
             } else {
                 this.selectedIndex = Math.min(this.flatItems.length - 1, this.selectedIndex);
             }
         }
-    }
-}
\ No newline at end of file
+    };
+}
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index c59c37488..526a32f5a 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -10,6 +10,7 @@ import { DocServer } from '../DocServer';
 import { Docs, DocumentOptions } from '../documents/Documents';
 import { CollectionViewType } from '../documents/DocumentTypes';
 import { HistoryUtil } from '../util/History';
+import { ScriptingGlobals } from '../util/ScriptingGlobals';
 import { SharingManager } from '../util/SharingManager';
 import { undoBatch } from '../util/UndoManager';
 import { CollectionDockingView } from './collections/CollectionDockingView';
@@ -288,3 +289,19 @@ export class DashboardView extends React.Component {
 export function AddToList(MySharedDocs: Doc, arg1: string, dash: any) {
     throw new Error('Function not implemented.');
 }
+
+ScriptingGlobals.add(function createNewDashboard() {
+    return DashboardView.createNewDashboard();
+}, 'creates a new dashboard when called');
+ScriptingGlobals.add(function shareDashboard(dashboard: Doc) {
+    SharingManager.Instance.open(undefined, dashboard);
+}, 'opens sharing dialog for Dashboard');
+ScriptingGlobals.add(function removeDashboard(dashboard: Doc) {
+    DashboardView.removeDashboard(dashboard);
+}, 'Remove Dashboard from Dashboards');
+ScriptingGlobals.add(function addToDashboards(dashboard: Doc) {
+    DashboardView.openDashboard(Doc.MakeAlias(dashboard));
+}, 'adds Dashboard to set of Dashboards');
+ScriptingGlobals.add(function snapshotDashboard() {
+    DashboardView.snapshotDashboard();
+}, 'creates a snapshot copy of a dashboard');
diff --git a/src/client/views/GestureOverlay.scss b/src/client/views/GestureOverlay.scss
index 16a4c74d1..f5cbbffb1 100644
--- a/src/client/views/GestureOverlay.scss
+++ b/src/client/views/GestureOverlay.scss
@@ -1,11 +1,8 @@
 .gestureOverlay-cont {
     width: 100vw;
     height: 100vh;
-    position: relative;
-    top: 0;
-    left: 0;
+    position: absolute;
     touch-action: none;
-    z-index: -1;
 
     .pointerBubbles {
         width: 100%;
@@ -18,7 +15,7 @@
             width: 15px;
             height: 15px;
             border-radius: 100%;
-            border: .5px solid grey;
+            border: 0.5px solid grey;
         }
     }
 }
@@ -44,7 +41,7 @@
             width: 100%;
             height: 25px;
             padding: 2.5px;
-            border-bottom: .5px solid black;
+            border-bottom: 0.5px solid black;
         }
     }
 
@@ -62,4 +59,4 @@
     position: absolute;
     background-color: transparent;
     border: 1px solid black;
-}
\ No newline at end of file
+}
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 5a68e9091..5ec4de953 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -582,13 +582,15 @@ export class GestureOverlay extends Touchable {
                 })
             );
         }
-        if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
-            if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
-                Doc.ActiveTool = InkTool.Write;
+        if (!(e.target as any)?.className?.startsWith('lm_')) {
+            if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+                if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) {
+                    Doc.ActiveTool = InkTool.Write;
+                }
+                this._points.push({ X: e.clientX, Y: e.clientY });
+                setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
+                // if (Doc.ActiveTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)");
             }
-            this._points.push({ X: e.clientX, Y: e.clientY });
-            setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
-            // if (Doc.ActiveTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)");
         }
     };
 
@@ -965,7 +967,7 @@ export class GestureOverlay extends Touchable {
                 this._strokes.map((l, i) => {
                     const b = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 }; //this.getBounds(l, true);
                     return (
-                        <svg key={i} width={b.width} height={b.height} style={{ transform: `translate(${b.left}px, ${b.top}px)`, pointerEvents: 'none', position: 'absolute', zIndex: 30000, overflow: 'visible' }}>
+                        <svg key={i} width={b.width} height={b.height} style={{ top: 0, left: 0, transform: `translate(${b.left}px, ${b.top}px)`, pointerEvents: 'none', position: 'absolute', zIndex: 30000, overflow: 'visible' }}>
                             {InteractionUtils.CreatePolyline(
                                 l,
                                 b.left,
@@ -992,9 +994,9 @@ export class GestureOverlay extends Touchable {
                     );
                 }),
                 this._points.length <= 1 ? null : (
-                    <svg key="svg" width={B.width} height={B.height} style={{ transform: `translate(${B.left}px, ${B.top}px)`, pointerEvents: 'none', position: 'absolute', zIndex: 30000, overflow: 'visible' }}>
+                    <svg key="svg" width={B.width} height={B.height} style={{ top: 0, left: 0, transform: `translate(${B.left}px, ${B.top}px)`, pointerEvents: 'none', position: 'absolute', zIndex: 30000, overflow: 'visible' }}>
                         {InteractionUtils.CreatePolyline(
-                            this._points.map(p => ({ X: p.X, Y: p.Y - (rect?.y || 0) })),
+                            this._points.map(p => ({ X: p.X - (rect?.x || 0), Y: p.Y - (rect?.y || 0) })),
                             B.left,
                             B.top,
                             ActiveInkColor(),
diff --git a/src/client/views/Main.scss b/src/client/views/Main.scss
index c8e64b5c4..c7a7614ac 100644
--- a/src/client/views/Main.scss
+++ b/src/client/views/Main.scss
@@ -1,5 +1,5 @@
-@import "global/globalCssVariables";
-@import "nodeModuleOverrides";
+@import 'global/globalCssVariables';
+@import 'nodeModuleOverrides';
 
 :root {
     --flyoutHandleWidth: 28px;
@@ -24,7 +24,6 @@ body {
 //     -ms-user-select: none;
 // }
 
-
 .jsx-parser {
     width: 100%;
     height: 100%;
@@ -70,4 +69,4 @@ button:hover {
 
 .svg-inline--fa {
     vertical-align: unset;
-}
\ No newline at end of file
+}
diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss
index a695577d0..ad87fb874 100644
--- a/src/client/views/MainView.scss
+++ b/src/client/views/MainView.scss
@@ -1,6 +1,5 @@
-@import "global/globalCssVariables";
-@import "nodeModuleOverrides";
-
+@import 'global/globalCssVariables';
+@import 'nodeModuleOverrides';
 
 .dash-tooltip {
     font-size: 11px;
@@ -63,6 +62,17 @@
     }
 }
 
+.mainView-container,
+.mainView-container-Dark {
+    .lm_header .lm_tab {
+        padding: 0px;
+        opacity: 0.7;
+        box-shadow: none;
+        height: 25px;
+        border-bottom: black solid;
+    }
+}
+
 .mainView-container {
     color: $dark-gray;
 
@@ -183,7 +193,6 @@
             background-color: $light-gray;
         }
     }
-
 }
 
 .mainView-libraryHandle {
@@ -252,7 +261,6 @@
 }
 
 .buttonContainer {
-
     position: absolute;
     bottom: 0;
 
@@ -270,8 +278,6 @@
     }
 }
 
-
-
 .mainView-logout {
     position: absolute;
     right: 0;
@@ -309,11 +315,10 @@
 }
 
 .mainView-libraryFlyout-out {
-    transition: width .25s;
+    transition: width 0.25s;
     box-shadow: rgb(156, 147, 150) 0.2vw 0.2vw 0.2vw;
 }
 
-
 .mainView-libraryHandle {
     width: var(--flyoutHandleWidth);
     height: 55px;
@@ -334,7 +339,6 @@
         margin-right: 3px;
         padding-top: 19px;
     }
-
 }
 
 .mainView-dashboard {
@@ -371,4 +375,4 @@
     display: block;
     width: 500px;
     height: 1000px;
-}
\ No newline at end of file
+}
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index edc16d9a6..b61cd3409 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -465,7 +465,7 @@ export class MainView extends React.Component {
         window.addEventListener('drop', e => e.preventDefault(), false); // prevent default behavior of navigating to a new web page
         window.addEventListener('dragover', e => e.preventDefault(), false);
         // document.addEventListener("pointermove", action(e => SearchBox.Instance._undoBackground = UndoManager.batchCounter ? "#000000a8" : undefined));
-        document.addEventListener('pointerdown', this.globalPointerDown);
+        document.addEventListener('pointerdown', this.globalPointerDown, true);
         document.addEventListener(
             'click',
             (e: MouseEvent) => {
@@ -590,19 +590,21 @@ export class MainView extends React.Component {
 
     @computed get dockingContent() {
         return (
-            <div
-                key="docking"
-                className={`mainView-dockingContent${this._leftMenuFlyoutWidth ? '-flyout' : ''}`}
-                onDrop={e => {
-                    e.stopPropagation();
-                    e.preventDefault();
-                }}
-                style={{
-                    minWidth: `calc(100% - ${this._leftMenuFlyoutWidth + this.leftMenuWidth() + this.propertiesWidth()}px)`,
-                    transform: LightboxView.LightboxDoc ? 'scale(0.0001)' : undefined,
-                }}>
-                {!this.mainContainer ? null : this.mainDocView}
-            </div>
+            <GestureOverlay>
+                <div
+                    key="docking"
+                    className={`mainView-dockingContent${this._leftMenuFlyoutWidth ? '-flyout' : ''}`}
+                    onDrop={e => {
+                        e.stopPropagation();
+                        e.preventDefault();
+                    }}
+                    style={{
+                        minWidth: `calc(100% - ${this._leftMenuFlyoutWidth + this.leftMenuWidth() + this.propertiesWidth()}px)`,
+                        transform: LightboxView.LightboxDoc ? 'scale(0.0001)' : undefined,
+                    }}>
+                    {!this.mainContainer ? null : this.mainDocView}
+                </div>
+            </GestureOverlay>
         );
     }
 
@@ -974,7 +976,7 @@ export class MainView extends React.Component {
                                     <div style={{ position: 'relative', display: LightboxView.LightboxDoc ? 'none' : undefined, zIndex: 1999 }}>
                                         <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
                                     </div>
-                                    <GestureOverlay> {this.mainDashboardArea} </GestureOverlay>
+                                    {this.mainDashboardArea}
                                 </>
                             );
                         case 'home':
diff --git a/src/client/views/_nodeModuleOverrides.scss b/src/client/views/_nodeModuleOverrides.scss
index fd0ac9d5c..17eff022f 100644
--- a/src/client/views/_nodeModuleOverrides.scss
+++ b/src/client/views/_nodeModuleOverrides.scss
@@ -1,4 +1,4 @@
-@import "./global/globalCssVariables";
+@import './global/globalCssVariables';
 // this file is for overriding all the css from installed node modules
 
 // goldenlayout stuff
@@ -56,16 +56,5 @@ div .lm_header {
     font-family: $sans-serif !important;
 }
 
-.lm_header .lm_controls {
-    align-items: center;
-    position: absolute;
-    background-color: $dark-gray;
-    border-radius: 5px;
-    display: flex;
-    justify-content: space-evenly;
-    height: 23px;
-    width: 65px;
-}
-
 // @TODO the ril__navgiation buttons in the img gallery are a lil messed up but I can't figure out
-// why. Low priority for now but it's bugging me. --Julie
\ No newline at end of file
+// why. Low priority for now but it's bugging me. --Julie
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index 6d5a39bc2..091ba8e74 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -37,11 +37,11 @@
 }
 
 .lm_header .lm_tab {
-    padding: 0px;
-    opacity: 0.7;
-    box-shadow: none;
-    height: 25px;
-    border-bottom: black solid;
+    // padding: 0px;  // moved to MainView.scss, othwerise they get overridden by default stylings
+    // opacity: 0.7;
+    // box-shadow: none;
+    // height: 25px;
+    // border-bottom: black solid;
 
     .collectionDockingView-gear {
         display: none;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index d47dfbea0..42f9bb981 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -377,7 +377,7 @@ export class CollectionDockingView extends CollectionSubView() {
                 }
             }
         }
-        if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+        if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
             e.stopPropagation();
         }
     };
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index f38efe578..2ab5f6247 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -160,7 +160,6 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
 
     onContextMenu = (e: React.MouseEvent): void => {
         const cm = ContextMenu.Instance;
-        if (e.nativeEvent.cancelBubble) return; // nested calls to React to render can cause the same event to trigger in the outer view even if the inner view has handled it.  This avoid CollectionDockingView menu options from being added when the event has been handled by a sub-document.
         if (cm && !e.isPropagationStopped() && this.rootDoc[Id] !== Doc.MainDocId) {
             // need to test this because GoldenLayout causes a parallel hierarchy in the React DOM for its children and the main document view7
             this.setupViewTypes(
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index eb5faf4e1..5a2103e98 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -31,6 +31,7 @@ import { CollectionTreeView, TreeViewType } from './CollectionTreeView';
 import { CollectionView } from './CollectionView';
 import './TreeView.scss';
 import React = require('react');
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
 
 export interface TreeViewProps {
     treeView: CollectionTreeView;
@@ -1223,3 +1224,9 @@ export class TreeView extends React.Component<TreeViewProps> {
             });
     }
 }
+
+ScriptingGlobals.add(function TreeView_addNewFolder() {
+    TreeView._editTitleOnLoad = { id: Utils.GenerateGuid(), parent: undefined };
+    const opts = { title: 'Untitled folder', _stayInCollection: true, isFolder: true };
+    return Doc.AddDocToList(Doc.MyFilesystem, 'data', Docs.Create.TreeDocument([], opts, TreeView._editTitleOnLoad.id));
+});
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 07ea26346..4174661d8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -509,7 +509,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         this._downY = this._lastY = e.pageY;
         if (e.button === 0 && !e.altKey && !e.ctrlKey && this.props.isContentActive(true)) {
             if (
-                !e.nativeEvent.cancelBubble &&
                 !this.props.Document._isGroup && // group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag
                 !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) &&
                 !InteractionUtils.IsType(e, InteractionUtils.PENTYPE)
@@ -543,24 +542,22 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     handle1PointerDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => {
-        if (!e.nativeEvent.cancelBubble) {
-            // const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
-            const pt = me.changedTouches[0];
-            if (pt) {
-                this._hitCluster = this.pickCluster(this.getTransform().transformPoint(pt.clientX, pt.clientY));
-                if (!e.shiftKey && !e.altKey && !e.ctrlKey && this.props.isContentActive(true)) {
-                    this.removeMoveListeners();
-                    this.addMoveListeners();
-                    this.removeEndListeners();
-                    this.addEndListeners();
-                    if (Doc.ActiveTool === InkTool.None) {
-                        this._lastX = pt.pageX;
-                        this._lastY = pt.pageY;
-                        e.preventDefault();
-                        e.stopPropagation();
-                    } else {
-                        e.preventDefault();
-                    }
+        // const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
+        const pt = me.changedTouches[0];
+        if (pt) {
+            this._hitCluster = this.pickCluster(this.getTransform().transformPoint(pt.clientX, pt.clientY));
+            if (!e.shiftKey && !e.altKey && !e.ctrlKey && this.props.isContentActive(true)) {
+                this.removeMoveListeners();
+                this.addMoveListeners();
+                this.removeEndListeners();
+                this.addEndListeners();
+                if (Doc.ActiveTool === InkTool.None) {
+                    this._lastX = pt.pageX;
+                    this._lastY = pt.pageY;
+                    e.preventDefault();
+                    e.stopPropagation();
+                } else {
+                    e.preventDefault();
                 }
             }
         }
@@ -976,7 +973,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     handle2PointersDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => {
-        if (!e.nativeEvent.cancelBubble && this.props.isContentActive(true)) {
+        if (this.props.isContentActive(true)) {
             // const pt1: React.Touch | null = e.targetTouches.item(0);
             // const pt2: React.Touch | null = e.targetTouches.item(1);
             // // if (!pt1 || !pt2) return;
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f89c65052..dea718a0d 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -362,7 +362,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     };
 
     handle2PointersDown = (e: React.TouchEvent, me: InteractionUtils.MultiTouchEvent<React.TouchEvent>) => {
-        if (!e.nativeEvent.cancelBubble && !this.props.isSelected()) {
+        if (!this.props.isSelected()) {
             e.stopPropagation();
             e.preventDefault();
 
@@ -380,14 +380,14 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         if (touch) {
             this._downX = touch.clientX;
             this._downY = touch.clientY;
-            if (!e.nativeEvent.cancelBubble) {
-                if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc._lockedPosition && !DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) e.stopPropagation();
-                this.removeMoveListeners();
-                this.addMoveListeners();
-                this.removeEndListeners();
-                this.addEndListeners();
+            if ((this.props.isDocumentActive?.() || this.layoutDoc.onDragStart || this.onClickHandler) && !e.ctrlKey && !this.layoutDoc._lockedPosition && !DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) {
                 e.stopPropagation();
             }
+            this.removeMoveListeners();
+            this.addMoveListeners();
+            this.removeEndListeners();
+            this.addEndListeners();
+            e.stopPropagation();
         }
     };
 
@@ -510,7 +510,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
     }
 
     onKeyDown = (e: React.KeyboardEvent) => {
-        if (e.altKey && !e.nativeEvent.cancelBubble) {
+        if (e.altKey) {
             e.stopPropagation();
             e.preventDefault();
             if (e.key === '†' || e.key === 't') {
@@ -543,7 +543,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         });
     };
     onClick = action((e: React.MouseEvent | React.PointerEvent) => {
-        if (!e.nativeEvent.cancelBubble && !this.Document.ignoreClick && this.props.renderDepth >= 0 && Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
+        if (!this.Document.ignoreClick && this.props.renderDepth >= 0 && Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) {
             let stopPropagate = true;
             let preventDefault = true;
             const isScriptBox = () => StrCast(Doc.LayoutField(this.layoutDoc))?.includes(ScriptingBox.name);
@@ -643,11 +643,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
         }
         this._downX = e.clientX;
         this._downY = e.clientY;
-        if (
-            (!e.nativeEvent.cancelBubble || this.onClickHandler || this.layoutDoc.onDragStart) &&
+        if (Doc.ActiveTool === InkTool.None && !(this.props.Document.rootDocument && !(e.ctrlKey || e.button > 0))) {
             // if this is part of a template, let the event go up to the tempalte root unless right/ctrl clicking
-            !(this.props.Document.rootDocument && !(e.ctrlKey || e.button > 0))
-        ) {
             if (
                 (this.props.isDocumentActive?.() || this.layoutDoc.onDragStart) &&
                 !this.props.onBrowseClick?.() &&
@@ -792,7 +789,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
 
     @action
     onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => {
-        if (e?.nativeEvent.cancelBubble) return;
         if (e && this.rootDoc._hideContextMenu && Doc.noviceMode) {
             e.preventDefault();
             e.stopPropagation();
diff --git a/src/fields/util.ts b/src/fields/util.ts
index 8fb35981b..cbb560114 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -1,20 +1,41 @@
-import { UndoManager } from "../client/util/UndoManager";
-import { Doc, FieldResult, UpdatingFromServer, LayoutSym, AclPrivate, AclEdit, AclReadonly, AclAugment, AclSym, DataSym, DocListCast, AclAdmin, HeightSym, WidthSym, updateCachedAcls, AclUnset, DocListCastAsync, ForceServerWrite, Initializing, AclSelfEdit } from "./Doc";
-import { SerializationHelper } from "../client/util/SerializationHelper";
-import { ProxyField, PrefetchProxy } from "./Proxy";
-import { RefField } from "./RefField";
-import { ObjectField } from "./ObjectField";
-import { action, trace, } from "mobx";
-import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from "./FieldSymbols";
-import { DocServer } from "../client/DocServer";
-import { ComputedField } from "./ScriptField";
-import { ScriptCast, StrCast } from "./Types";
-import { returnZero } from "../Utils";
-import CursorField from "./CursorField";
-import { List } from "./List";
-import { SnappingManager } from "../client/util/SnappingManager";
-import { computedFn } from "mobx-utils";
-import { RichTextField } from "./RichTextField";
+import { UndoManager } from '../client/util/UndoManager';
+import {
+    Doc,
+    FieldResult,
+    UpdatingFromServer,
+    LayoutSym,
+    AclPrivate,
+    AclEdit,
+    AclReadonly,
+    AclAugment,
+    AclSym,
+    DataSym,
+    DocListCast,
+    AclAdmin,
+    HeightSym,
+    WidthSym,
+    updateCachedAcls,
+    AclUnset,
+    DocListCastAsync,
+    ForceServerWrite,
+    Initializing,
+    AclSelfEdit,
+} from './Doc';
+import { SerializationHelper } from '../client/util/SerializationHelper';
+import { ProxyField, PrefetchProxy } from './Proxy';
+import { RefField } from './RefField';
+import { ObjectField } from './ObjectField';
+import { action, observable, runInAction, trace } from 'mobx';
+import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from './FieldSymbols';
+import { DocServer } from '../client/DocServer';
+import { ComputedField } from './ScriptField';
+import { ScriptCast, StrCast } from './Types';
+import { returnZero } from '../Utils';
+import CursorField from './CursorField';
+import { List } from './List';
+import { SnappingManager } from '../client/util/SnappingManager';
+import { computedFn } from 'mobx-utils';
+import { RichTextField } from './RichTextField';
 
 function _readOnlySetter(): never {
     throw new Error("Documents can't be modified in read-only mode");
@@ -44,7 +65,7 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
         return true;
     }
 
-    if (typeof prop === "symbol") {
+    if (typeof prop === 'symbol') {
         target[prop] = value;
         return true;
     }
@@ -76,15 +97,13 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
 
     const writeMode = DocServer.getFieldWriteMode(prop as string);
     const fromServer = target[UpdatingFromServer];
-    const sameAuthor = fromServer || (receiver.author === Doc.CurrentUserEmail);
-    const writeToDoc = sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (writeMode !== DocServer.WriteMode.LiveReadonly);
-    const writeToServer =
-        (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (effectiveAcl === AclSelfEdit && (value instanceof RichTextField))) &&
-        !DocServer.Control.isReadOnly();
+    const sameAuthor = fromServer || receiver.author === Doc.CurrentUserEmail;
+    const writeToDoc = sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || writeMode !== DocServer.WriteMode.LiveReadonly;
+    const writeToServer = (sameAuthor || effectiveAcl === AclEdit || effectiveAcl === AclAdmin || (effectiveAcl === AclSelfEdit && value instanceof RichTextField)) && !DocServer.Control.isReadOnly();
 
     if (writeToDoc) {
         if (value === undefined) {
-            target.__fieldKeys && (delete target.__fieldKeys[prop]);
+            target.__fieldKeys && delete target.__fieldKeys[prop];
             delete target.__fields[prop];
         } else {
             target.__fieldKeys && (target.__fieldKeys[prop] = true);
@@ -96,16 +115,17 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number
         //if (typeof value === "object" && !(value instanceof ObjectField)) debugger;
 
         if (writeToServer) {
-            if (value === undefined) target[Update]({ '$unset': { ["fields." + prop]: "" } });
-            else target[Update]({ '$set': { ["fields." + prop]: value instanceof ObjectField ? SerializationHelper.Serialize(value) : (value === undefined ? null : value) } });
+            if (value === undefined) target[Update]({ $unset: { ['fields.' + prop]: '' } });
+            else target[Update]({ $set: { ['fields.' + prop]: value instanceof ObjectField ? SerializationHelper.Serialize(value) : value === undefined ? null : value } });
         } else {
             DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue);
         }
-        !receiver[Initializing] && (!receiver[UpdatingFromServer] || receiver[ForceServerWrite]) &&
+        !receiver[Initializing] &&
+            (!receiver[UpdatingFromServer] || receiver[ForceServerWrite]) &&
             UndoManager.AddEvent({
-                redo: () => receiver[prop] = value,
-                undo: () => receiver[prop] = curValue,
-                prop: prop?.toString()
+                redo: () => (receiver[prop] = value),
+                undo: () => (receiver[prop] = curValue),
+                prop: prop?.toString(),
             });
         return true;
     }
@@ -136,7 +156,6 @@ export function denormalizeEmail(email: string) {
 //     playgroundMode = !playgroundMode;
 // }
 
-
 /**
  * Copies parent's acl fields to the child
  */
@@ -145,35 +164,37 @@ export function inheritParentAcls(parent: Doc, child: Doc) {
     const dataDoc = parent[DataSym];
     for (const key of Object.keys(dataDoc)) {
         // if the default acl mode is private, then don't inherit the acl-Public permission, but set it to private.
-        const permission = (key === "acl-Public" && Doc.defaultAclPrivate) ? AclPrivate : dataDoc[key];
-        key.startsWith("acl") && distributeAcls(key, permission, child);
+        const permission = key === 'acl-Public' && Doc.defaultAclPrivate ? AclPrivate : dataDoc[key];
+        key.startsWith('acl') && distributeAcls(key, permission, child);
     }
 }
 
 /**
  * These are the various levels of access a user can have to a document.
- * 
+ *
  * Admin: a user with admin access to a document can remove/edit that document, add/remove/edit annotations (depending on permissions), as well as change others' access rights to that document.
- * 
+ *
  * Edit: a user with edit access to a document can remove/edit that document, add/remove/edit annotations (depending on permissions), but not change any access rights to that document.
- * 
+ *
  * Add: a user with add access to a document can augment documents/annotations to that document but cannot edit or delete anything.
- * 
+ *
  * View: a user with view access to a document can only view it - they cannot add/remove/edit anything.
- * 
+ *
  * None: the document is not shared with that user.
  */
 export enum SharingPermissions {
-    Admin = "Admin",
-    Edit = "Edit",
-    SelfEdit = "Self Edit",
-    Augment = "Augment",
-    View = "View",
-    None = "Not Shared"
+    Admin = 'Admin',
+    Edit = 'Edit',
+    SelfEdit = 'Self Edit',
+    Augment = 'Augment',
+    View = 'View',
+    None = 'Not Shared',
 }
 
 // return acl from cache or cache the acl and return.
-const getEffectiveAclCache = computedFn(function (target: any, user?: string) { return getEffectiveAcl(target, user); }, true);
+const getEffectiveAclCache = computedFn(function (target: any, user?: string) {
+    return getEffectiveAcl(target, user);
+}, true);
 
 /**
  * Calculates the effective access right to a document for the current user.
@@ -183,33 +204,47 @@ export function GetEffectiveAcl(target: any, user?: string): symbol {
     if (target[UpdatingFromServer]) return AclAdmin;
     // authored documents are private until an ACL is set.
     if (!target[AclSym] && target.author && target.author !== Doc.CurrentUserEmail) return AclPrivate;
-    return getEffectiveAclCache(target, user);// all changes received from the server must be processed as Admin.  return this directly so that the acls aren't cached (UpdatingFromServer is not observable)
+    return getEffectiveAclCache(target, user); // all changes received from the server must be processed as Admin.  return this directly so that the acls aren't cached (UpdatingFromServer is not observable)
 }
 
 function getPropAcl(target: any, prop: string | symbol | number) {
-    if (prop === UpdatingFromServer || prop === Initializing || target[UpdatingFromServer] || prop === AclSym) return AclAdmin;  // requesting the UpdatingFromServer prop or AclSym must always go through to keep the local DB consistent
+    if (prop === UpdatingFromServer || prop === Initializing || target[UpdatingFromServer] || prop === AclSym) return AclAdmin; // requesting the UpdatingFromServer prop or AclSym must always go through to keep the local DB consistent
     if (prop && DocServer.IsPlaygroundField(prop.toString())) return AclEdit; // playground props are always editable
     return GetEffectiveAcl(target);
 }
 
 let HierarchyMapping: Map<symbol, number> | undefined;
 
+let cachedGroups = observable([] as string[]);
+/// bcz; argh!! TODO;   These do not belong here, but there were include order problems with leaving them in util.ts
+// need to investigate further what caused the mobx update problems and move to a better location.
+const getCachedGroupByNameCache = computedFn(function (name: string) {
+    return cachedGroups.includes(name);
+}, true);
+export function GetCachedGroupByName(name: string) {
+    return getCachedGroupByNameCache(name);
+}
+export function SetCachedGroups(groups: string[]) {
+    runInAction(() => cachedGroups.push(...groups));
+}
 function getEffectiveAcl(target: any, user?: string): symbol {
     const targetAcls = target[AclSym];
-    const userChecked = user || Doc.CurrentUserEmail;    // if the current user is the author of the document / the current user is a member of the admin group
-    const targetAuthor = (target.__fields?.author || target.author); // target may be a Doc of Proxy, so check __fields.author and .author
+    const userChecked = user || Doc.CurrentUserEmail; // if the current user is the author of the document / the current user is a member of the admin group
+    const targetAuthor = target.__fields?.author || target.author; // target may be a Doc of Proxy, so check __fields.author and .author
     if (userChecked === targetAuthor || !targetAuthor) return AclAdmin;
-    if (SnappingManager.GetCachedGroupByName("Admin")) return AclAdmin;
+    if (GetCachedGroupByName('Admin')) return AclAdmin;
 
     if (targetAcls && Object.keys(targetAcls).length) {
-        HierarchyMapping = HierarchyMapping || new Map<symbol, number>([
-            [AclPrivate, 0],
-            [AclReadonly, 1],
-            [AclAugment, 2],
-            [AclSelfEdit, 2.5],
-            [AclEdit, 3],
-            [AclAdmin, 4]
-        ]);
+        HierarchyMapping =
+            HierarchyMapping ||
+            new Map<symbol, number>([
+                [AclPrivate, 0],
+                [AclReadonly, 1],
+                [AclAugment, 2],
+                [AclSelfEdit, 2.5],
+                [AclEdit, 3],
+                [AclAdmin, 4],
+            ]);
 
         let effectiveAcl = AclPrivate;
         for (const [key, value] of Object.entries(targetAcls)) {
@@ -217,14 +252,14 @@ function getEffectiveAcl(target: any, user?: string): symbol {
             // as a result we need to restore them again during this comparison.
             const entity = denormalizeEmail(key.substring(4)); // an individual or a group
             if (HierarchyMapping.get(value as symbol)! > HierarchyMapping.get(effectiveAcl)!) {
-                if (SnappingManager.GetCachedGroupByName(entity) || userChecked === entity) {
+                if (GetCachedGroupByName(entity) || userChecked === entity) {
                     effectiveAcl = value as symbol;
                 }
             }
         }
 
         // if there's an overriding acl set through the properties panel or sharing menu, that's what's returned if the user isn't an admin of the document
-        const override = targetAcls["acl-Override"];
+        const override = targetAcls['acl-Override'];
         if (override !== AclUnset && override !== undefined) effectiveAcl = override;
 
         // if we're in playground mode, return AclEdit (or AclAdmin if that's the user's effectiveAcl)
@@ -246,12 +281,12 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
     visited.push(target);
 
     const HierarchyMapping = new Map<string, number>([
-        ["Not Shared", 0],
-        ["Can View", 1],
-        ["Can Augment", 2],
-        ["Self Edit", 2.5],
-        ["Can Edit", 3],
-        ["Admin", 4]
+        ['Not Shared', 0],
+        ['Can View', 1],
+        ['Can Augment', 2],
+        ['Self Edit', 2.5],
+        ['Can Edit', 3],
+        ['Admin', 4],
     ]);
 
     let layoutDocChanged = false; // determines whether fetchProto should be called or not (i.e. is there a change that should be reflected in target[AclSym])
@@ -271,7 +306,6 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
     }
 
     if (dataDoc && (!inheritingFromCollection || !dataDoc[key] || HierarchyMapping.get(StrCast(dataDoc[key]))! > HierarchyMapping.get(acl)!)) {
-
         if (GetEffectiveAcl(dataDoc) === AclAdmin) {
             dataDoc[key] = acl;
             dataDocChanged = true;
@@ -282,7 +316,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
         links.forEach(link => distributeAcls(key, acl, link, inheritingFromCollection, visited));
 
         // maps over the children of the document
-        DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + (isDashboard ? "-all" : "")]).map(d => {
+        DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + (isDashboard ? '-all' : '')]).map(d => {
             distributeAcls(key, acl, d, inheritingFromCollection, visited);
             // }
             const data = d[DataSym];
@@ -292,7 +326,7 @@ export function distributeAcls(key: string, acl: SharingPermissions, target: Doc
         });
 
         // maps over the annotations of the document
-        DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + "-annotations"]).map(d => {
+        DocListCast(dataDoc[Doc.LayoutFieldKey(dataDoc) + '-annotations']).map(d => {
             distributeAcls(key, acl, d, inheritingFromCollection, visited);
             // }
             const data = d[DataSym];
@@ -311,11 +345,11 @@ export function setter(target: any, in_prop: string | symbol | number, value: an
     const effectiveAcl = getPropAcl(target, prop);
     if (effectiveAcl !== AclEdit && effectiveAcl !== AclAdmin && !(effectiveAcl === AclSelfEdit && value instanceof RichTextField)) return true;
     // if you're trying to change an acl but don't have Admin access / you're trying to change it to something that isn't an acceptable acl, you can't
-    if (typeof prop === "string" && prop.startsWith("acl") && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined, "None"].includes(value))) return true;
+    if (typeof prop === 'string' && prop.startsWith('acl') && (effectiveAcl !== AclAdmin || ![...Object.values(SharingPermissions), undefined, 'None'].includes(value))) return true;
     // if (typeof prop === "string" && prop.startsWith("acl") && !["Can Edit", "Can Augment", "Can View", "Not Shared", undefined].includes(value)) return true;
 
-    if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && prop.startsWith("_")) {
-        if (!prop.startsWith("__")) prop = prop.substring(1);
+    if (typeof prop === 'string' && prop !== '__id' && prop !== '__fields' && prop.startsWith('_')) {
+        if (!prop.startsWith('__')) prop = prop.substring(1);
         if (target.__LAYOUT__) {
             target.__LAYOUT__[prop] = value;
             return true;
@@ -331,17 +365,18 @@ export function getter(target: any, in_prop: string | symbol | number, receiver:
     let prop = in_prop;
 
     if (in_prop === AclSym) return target[AclSym];
-    if (in_prop === "toString" || (in_prop !== HeightSym && in_prop !== WidthSym && in_prop !== LayoutSym && typeof prop === "symbol")) return target.__fields[prop] || target[prop];
+    if (in_prop === 'toString' || (in_prop !== HeightSym && in_prop !== WidthSym && in_prop !== LayoutSym && typeof prop === 'symbol')) return target.__fields[prop] || target[prop];
     if (GetEffectiveAcl(target) === AclPrivate) return prop === HeightSym || prop === WidthSym ? returnZero : undefined;
     if (prop === LayoutSym) return target.__LAYOUT__;
-    if (typeof prop === "string" && prop !== "__id" && prop !== "__fields" && prop.startsWith("_")) {
-        if (!prop.startsWith("__")) prop = prop.substring(1);
+    if (typeof prop === 'string' && prop !== '__id' && prop !== '__fields' && prop.startsWith('_')) {
+        if (!prop.startsWith('__')) prop = prop.substring(1);
         if (target.__LAYOUT__) return target.__LAYOUT__[prop];
     }
-    if (prop === "then") {//If we're being awaited
+    if (prop === 'then') {
+        //If we're being awaited
         return undefined;
     }
-    if (typeof prop === "symbol") {
+    if (typeof prop === 'symbol') {
         return target.__fields[prop] || target[prop];
     }
     if (SerializationHelper.IsSerializing()) {
@@ -362,22 +397,21 @@ function getFieldImpl(target: any, prop: string | number, receiver: any, ignoreP
             field = res.value;
         }
     }
-    if (field === undefined && !ignoreProto && prop !== "proto") {
-        const proto = getFieldImpl(target, "proto", receiver, true);//TODO tfs: instead of receiver we could use target[SelfProxy]... I don't which semantics we want or if it really matters
+    if (field === undefined && !ignoreProto && prop !== 'proto') {
+        const proto = getFieldImpl(target, 'proto', receiver, true); //TODO tfs: instead of receiver we could use target[SelfProxy]... I don't which semantics we want or if it really matters
         if (proto instanceof Doc && GetEffectiveAcl(proto) !== AclPrivate) {
             return getFieldImpl(proto[Self], prop, receiver, ignoreProto);
         }
         return undefined;
     }
     return field;
-
 }
 export function getField(target: any, prop: string | number, ignoreProto: boolean = false): any {
     return getFieldImpl(target, prop, undefined, ignoreProto);
 }
 
 export function deleteProperty(target: any, prop: string | number | symbol) {
-    if (typeof prop === "symbol") {
+    if (typeof prop === 'symbol') {
         delete target[prop];
         return true;
     }
@@ -389,64 +423,68 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any
     let lastValue = ObjectField.MakeCopy(value);
     return (diff?: any) => {
         const op =
-            diff?.op === "$addToSet" ? { '$addToSet': { ["fields." + prop]: SerializationHelper.Serialize(new List<Doc>(diff.items)) } } :
-                diff?.op === "$remFromSet" ? { '$remFromSet': { ["fields." + prop]: SerializationHelper.Serialize(new List<Doc>(diff.items)) } }
-                    : { '$set': { ["fields." + prop]: SerializationHelper.Serialize(value) } };
+            diff?.op === '$addToSet'
+                ? { $addToSet: { ['fields.' + prop]: SerializationHelper.Serialize(new List<Doc>(diff.items)) } }
+                : diff?.op === '$remFromSet'
+                ? { $remFromSet: { ['fields.' + prop]: SerializationHelper.Serialize(new List<Doc>(diff.items)) } }
+                : { $set: { ['fields.' + prop]: SerializationHelper.Serialize(value) } };
         !op.$set && ((op as any).length = diff.length);
         const prevValue = ObjectField.MakeCopy(lastValue as List<any>);
         lastValue = ObjectField.MakeCopy(value);
         const newValue = ObjectField.MakeCopy(value);
 
-        if (!(value instanceof CursorField) && !(value?.some?.((v: any) => v instanceof CursorField))) {
-            !receiver[UpdatingFromServer] && UndoManager.AddEvent(
-                diff?.op === "$addToSet" ?
-                    {
-                        redo: () => {
-                            receiver[prop].push(...diff.items.map((item: any) => item.value ? item.value() : item));
-                            lastValue = ObjectField.MakeCopy(receiver[prop]);
-                        },
-                        undo: action(() => {
-                            // console.log("undo $add: " + prop, diff.items) // bcz: uncomment to log undo 
-                            diff.items.forEach((item: any) => {
-                                const ind = receiver[prop].indexOf(item.value ? item.value() : item);
-                                ind !== -1 && receiver[prop].splice(ind, 1);
-                            });
-                            lastValue = ObjectField.MakeCopy(receiver[prop]);
-                        }),
-                        prop: ""
-                    } :
-                    diff?.op === "$remFromSet" ?
-                        {
-                            redo: action(() => {
-                                diff.items.forEach((item: any) => {
-                                    const ind = receiver[prop].indexOf(item.value ? item.value() : item);
-                                    ind !== -1 && receiver[prop].splice(ind, 1);
-                                });
-                                lastValue = ObjectField.MakeCopy(receiver[prop]);
-                            }),
-                            undo: () => {
-                                // console.log("undo $rem: " + prop, diff.items) // bcz: uncomment to log undo 
-                                diff.items.forEach((item: any) => {
-                                    const ind = (prevValue as List<any>).indexOf(item.value ? item.value() : item);
-                                    ind !== -1 && receiver[prop].indexOf(item.value ? item.value() : item) === -1 && receiver[prop].splice(ind, 0, item);
-                                });
-                                lastValue = ObjectField.MakeCopy(receiver[prop]);
-                            },
-                            prop: ""
-                        }
+        if (!(value instanceof CursorField) && !value?.some?.((v: any) => v instanceof CursorField)) {
+            !receiver[UpdatingFromServer] &&
+                UndoManager.AddEvent(
+                    diff?.op === '$addToSet'
+                        ? {
+                              redo: () => {
+                                  receiver[prop].push(...diff.items.map((item: any) => (item.value ? item.value() : item)));
+                                  lastValue = ObjectField.MakeCopy(receiver[prop]);
+                              },
+                              undo: action(() => {
+                                  // console.log("undo $add: " + prop, diff.items) // bcz: uncomment to log undo
+                                  diff.items.forEach((item: any) => {
+                                      const ind = receiver[prop].indexOf(item.value ? item.value() : item);
+                                      ind !== -1 && receiver[prop].splice(ind, 1);
+                                  });
+                                  lastValue = ObjectField.MakeCopy(receiver[prop]);
+                              }),
+                              prop: '',
+                          }
+                        : diff?.op === '$remFromSet'
+                        ? {
+                              redo: action(() => {
+                                  diff.items.forEach((item: any) => {
+                                      const ind = receiver[prop].indexOf(item.value ? item.value() : item);
+                                      ind !== -1 && receiver[prop].splice(ind, 1);
+                                  });
+                                  lastValue = ObjectField.MakeCopy(receiver[prop]);
+                              }),
+                              undo: () => {
+                                  // console.log("undo $rem: " + prop, diff.items) // bcz: uncomment to log undo
+                                  diff.items.forEach((item: any) => {
+                                      const ind = (prevValue as List<any>).indexOf(item.value ? item.value() : item);
+                                      ind !== -1 && receiver[prop].indexOf(item.value ? item.value() : item) === -1 && receiver[prop].splice(ind, 0, item);
+                                  });
+                                  lastValue = ObjectField.MakeCopy(receiver[prop]);
+                              },
+                              prop: '',
+                          }
                         : {
-                            redo: () => {
-                                receiver[prop] = ObjectField.MakeCopy(newValue as List<any>);
-                                lastValue = ObjectField.MakeCopy(receiver[prop]);
-                            },
-                            undo: () => {
-                                // console.log("undo list: " + prop, receiver[prop]) // bcz: uncomment to log undo 
-                                receiver[prop] = ObjectField.MakeCopy(prevValue as List<any>);
-                                lastValue = ObjectField.MakeCopy(receiver[prop]);
-                            },
-                            prop: ""
-                        });
+                              redo: () => {
+                                  receiver[prop] = ObjectField.MakeCopy(newValue as List<any>);
+                                  lastValue = ObjectField.MakeCopy(receiver[prop]);
+                              },
+                              undo: () => {
+                                  // console.log("undo list: " + prop, receiver[prop]) // bcz: uncomment to log undo
+                                  receiver[prop] = ObjectField.MakeCopy(prevValue as List<any>);
+                                  lastValue = ObjectField.MakeCopy(receiver[prop]);
+                              },
+                              prop: '',
+                          }
+                );
         }
         target[Update](op);
     };
-}
\ No newline at end of file
+}
-- 
cgit v1.2.3-70-g09d2


From 724c1c1228aa601fdbbed93dde68a5a9fa9c5ae9 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 20 Jul 2022 09:12:11 -0400
Subject: added a 'guest' login mode.  added ability for mainView to be any
 doc, not just a docking collection.

---
 src/client/DocServer.ts                            |   4 +-
 src/client/util/CurrentUserUtils.ts                |  18 +-
 src/client/util/GroupManager.tsx                   |   9 +-
 src/client/util/SettingsManager.tsx                |   3 +-
 src/client/util/SharingManager.tsx                 |   3 +-
 src/client/util/TrackMovements.ts                  | 141 ++++++++-------
 src/client/views/ContextMenu.scss                  |  22 +--
 src/client/views/ContextMenuItem.tsx               |  84 +++++----
 src/client/views/DashboardView.tsx                 |   2 +-
 src/client/views/GlobalKeyHandler.ts               |   4 +-
 src/client/views/Main.tsx                          |  51 +++---
 src/client/views/MainView.tsx                      |  53 ++++--
 src/client/views/PropertiesDocContextSelector.tsx  |   2 +-
 .../views/collections/CollectionDockingView.tsx    |  70 ++++----
 src/client/views/collections/CollectionView.tsx    |   2 +-
 src/client/views/collections/TabDocView.tsx        |  15 +-
 src/client/views/nodes/MapBox/MapBox.tsx           |  48 ++---
 src/client/views/nodes/PDFBox.tsx                  |   5 +-
 .../views/nodes/formattedText/FormattedTextBox.tsx |   2 +-
 src/client/views/nodes/trails/PresBox.tsx          |   2 +-
 src/client/views/topbar/TopBar.tsx                 |   2 +-
 src/fields/util.ts                                 |  47 +++--
 src/mobile/MobileInkOverlay.tsx                    | 119 ++++++-------
 src/mobile/MobileMain.tsx                          |   5 +-
 src/server/ApiManagers/UserManager.ts              |  77 ++++----
 src/server/RouteManager.ts                         |  58 +++---
 src/server/authentication/AuthenticationManager.ts |  31 ++--
 src/server/authentication/DashUserModel.ts         | 106 ++++++-----
 src/server/index.ts                                | 108 +++++-------
 src/server/websocket.ts                            | 194 +++++++++++----------
 views/login.pug                                    |   4 +-
 31 files changed, 682 insertions(+), 609 deletions(-)

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

diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index 1b0ba6bc3..5a34fcf11 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -77,7 +77,7 @@ export namespace DocServer {
     }
 
     export function getFieldWriteMode(field: string) {
-        return fieldWriteModes[field] || WriteMode.Default;
+        return Doc.CurrentUserEmail === 'guest' ? WriteMode.LiveReadonly : fieldWriteModes[field] || WriteMode.Default;
     }
 
     export function registerDocWithCachedUpdate(doc: Doc, field: string, oldValue: any) {
@@ -178,7 +178,7 @@ export namespace DocServer {
                 _isReadOnly = true;
                 _CreateField = field => (_cache[field[Id]] = field);
                 _UpdateField = emptyFunction;
-                _RespondToUpdate = emptyFunction; // bcz: option: don't clear RespondToUpdate to continue to receive updates as others change the DB
+                // _RespondToUpdate = emptyFunction; // bcz: option: don't clear RespondToUpdate to continue to receive updates as others change the DB
             }
         }
 
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 6c80cf0f4..bbf2ff3f9 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -1,6 +1,7 @@
 import { reaction } from "mobx";
 import * as rp from 'request-promise';
 import { Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc";
+import { Id } from "../../fields/FieldSymbols";
 import { List } from "../../fields/List";
 import { PrefetchProxy } from "../../fields/Proxy";
 import { RichTextField } from "../../fields/RichTextField";
@@ -14,6 +15,7 @@ import { DocServer } from "../DocServer";
 import { Docs, DocumentOptions, DocUtils, FInfo } from "../documents/Documents";
 import { CollectionViewType, DocumentType } from "../documents/DocumentTypes";
 import { TreeViewType } from "../views/collections/CollectionTreeView";
+import { DashboardView } from "../views/DashboardView";
 import { Colors } from "../views/global/globalEnums";
 import { MainView } from "../views/MainView";
 import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox";
@@ -762,8 +764,6 @@ export class CurrentUserUtils {
 
             doc["clickFuncs-child"] = Docs.Create.TreeDocument([openInTarget, openDetail], { title: "on Child Click function templates", system: true });
         }
-        // this is equivalent to using PrefetchProxies to make sure all the childClickFuncs have been retrieved.
-        PromiseValue(Cast(doc["clickFuncs-child"], Doc)).then(func => func && PromiseValue(func.data).then(DocListCast));
 
         if (doc.clickFuncs === undefined) {
             const onClick = Docs.Create.ScriptingDocument(undefined, {
@@ -789,7 +789,6 @@ export class CurrentUserUtils {
             }, "onCheckedClick");
             doc.clickFuncs = Docs.Create.TreeDocument([onClick, onChildClick, onDoubleClick, onCheckedClick], { title: "onClick funcs", system: true });
         }
-        PromiseValue(Cast(doc.clickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast));
 
         return doc.clickFuncs as Doc;
     }
@@ -886,13 +885,20 @@ export class CurrentUserUtils {
     public static async loadUserDocument(id: string) {
         await rp.get(Utils.prepend("/getUserDocumentIds")).then(ids => {
             const { userDocumentId, sharingDocumentId, linkDatabaseId } = JSON.parse(ids);
-            if (userDocumentId !== "guest") {
+            if (userDocumentId) {
                 return DocServer.GetRefField(userDocumentId).then(async field => {
                     Docs.newAccount = !(field instanceof Doc);
                     await Docs.Prototypes.initialize();
                     const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc;
-                    Docs.newAccount &&(userDoc.activePage = "home");
-                    return this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId);
+                    this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId);
+                    if (Docs.newAccount) {
+                        if (Doc.CurrentUserEmail === "guest") {
+                            DashboardView.createNewDashboard(undefined, "guest dashboard");
+                        } else {
+                            userDoc.activePage = "home";
+                        }
+                    }
+                    return userDoc;
                 });
             } else {
                 throw new Error("There should be a user id! Why does Dash think there isn't one?");
diff --git a/src/client/util/GroupManager.tsx b/src/client/util/GroupManager.tsx
index 59334f6a2..c8b784390 100644
--- a/src/client/util/GroupManager.tsx
+++ b/src/client/util/GroupManager.tsx
@@ -14,6 +14,7 @@ import { GroupMemberView } from './GroupMemberView';
 import { SharingManager, User } from './SharingManager';
 import { listSpec } from '../../fields/Schema';
 import { DateField } from '../../fields/DateField';
+import { Id } from '../../fields/FieldSymbols';
 
 /**
  * Interface for options for the react-select component
@@ -49,9 +50,11 @@ export class GroupManager extends React.Component<{}> {
      * Fetches the list of users stored on the database.
      */
     populateUsers = async () => {
-        const userList = await RequestPromise.get(Utils.prepend('/getUsers'));
-        const raw = JSON.parse(userList) as User[];
-        raw.map(action(user => !this.users.some(umail => umail === user.email) && this.users.push(user.email)));
+        if (Doc.UserDoc()[Id] !== '__guest__') {
+            const userList = await RequestPromise.get(Utils.prepend('/getUsers'));
+            const raw = JSON.parse(userList) as User[];
+            raw.map(action(user => !this.users.some(umail => umail === user.email) && this.users.push(user.email)));
+        }
     };
 
     /**
diff --git a/src/client/util/SettingsManager.tsx b/src/client/util/SettingsManager.tsx
index 12d1793af..cf143c5e8 100644
--- a/src/client/util/SettingsManager.tsx
+++ b/src/client/util/SettingsManager.tsx
@@ -4,6 +4,7 @@ import { observer } from 'mobx-react';
 import * as React from 'react';
 import { ColorState, SketchPicker } from 'react-color';
 import { Doc } from '../../fields/Doc';
+import { Id } from '../../fields/FieldSymbols';
 import { BoolCast, Cast, StrCast } from '../../fields/Types';
 import { addStyleSheet, addStyleSheetRule, Utils } from '../../Utils';
 import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager';
@@ -81,7 +82,7 @@ export class SettingsManager extends React.Component<{}> {
         if (this.playgroundMode) {
             DocServer.Control.makeReadOnly();
             addStyleSheetRule(SettingsManager._settingsStyle, 'topbar-inner-container', { background: 'red !important' });
-        } else DocServer.Control.makeEditable();
+        } else Doc.CurrentUserEmail !== 'guest' && DocServer.Control.makeEditable();
     });
 
     @undoBatch
diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx
index 793027ea1..895bd3374 100644
--- a/src/client/util/SharingManager.tsx
+++ b/src/client/util/SharingManager.tsx
@@ -23,6 +23,7 @@ import { GroupMemberView } from './GroupMemberView';
 import { SelectionManager } from './SelectionManager';
 import './SharingManager.scss';
 import { LinkManager } from './LinkManager';
+import { Id } from '../../fields/FieldSymbols';
 
 export interface User {
     email: string;
@@ -136,7 +137,7 @@ export class SharingManager extends React.Component<{}> {
      * Populates the list of validated users (this.users) by adding registered users which have a sharingDocument.
      */
     populateUsers = async () => {
-        if (!this.populating) {
+        if (!this.populating && Doc.UserDoc()[Id] !== '__guest__') {
             this.populating = true;
             const userList = await RequestPromise.get(Utils.prepend('/getUsers'));
             const raw = JSON.parse(userList) as User[];
diff --git a/src/client/util/TrackMovements.ts b/src/client/util/TrackMovements.ts
index d512e4802..4a2ccd706 100644
--- a/src/client/util/TrackMovements.ts
+++ b/src/client/util/TrackMovements.ts
@@ -1,27 +1,26 @@
-import { IReactionDisposer, observable, observe, reaction } from "mobx";
-import { NumCast } from "../../fields/Types";
-import { Doc, DocListCast } from "../../fields/Doc";
-import { CollectionDockingView } from "../views/collections/CollectionDockingView";
-import { Id } from "../../fields/FieldSymbols";
+import { IReactionDisposer, observable, observe, reaction } from 'mobx';
+import { NumCast } from '../../fields/Types';
+import { Doc, DocListCast } from '../../fields/Doc';
+import { CollectionDockingView } from '../views/collections/CollectionDockingView';
+import { Id } from '../../fields/FieldSymbols';
 
 export type Movement = {
-    time: number,
-    panX: number,
-    panY: number,
-    scale: number,
-    docId: string,
-}
+    time: number;
+    panX: number;
+    panY: number;
+    scale: number;
+    docId: string;
+};
 
 export type Presentation = {
-    movements: Movement[] | null,
-    totalTime: number,
-    meta: Object | Object[],
-}
+    movements: Movement[] | null;
+    totalTime: number;
+    meta: Object | Object[];
+};
 
 export class TrackMovements {
-
     private static get NULL_PRESENTATION(): Presentation {
-        return { movements: null, meta: {}, totalTime: -1, }
+        return { movements: null, meta: {}, totalTime: -1 };
     }
 
     // instance variables
@@ -32,16 +31,17 @@ export class TrackMovements {
     private recordingFFViews: Map<string, IReactionDisposer> | null;
     private tabChangeDisposeFunc: IReactionDisposer | null;
 
-
     // create static instance and getter for global use
     @observable static _instance: TrackMovements;
-    static get Instance(): TrackMovements { return TrackMovements._instance }
+    static get Instance(): TrackMovements {
+        return TrackMovements._instance;
+    }
     constructor() {
         // init the global instance
         TrackMovements._instance = this;
 
         // init the instance variables
-        this.currentPresentation = TrackMovements.NULL_PRESENTATION
+        this.currentPresentation = TrackMovements.NULL_PRESENTATION;
         this.tracking = false;
         this.absoluteStart = -1;
 
@@ -52,28 +52,37 @@ export class TrackMovements {
 
     // little helper :)
     private get nullPresentation(): boolean {
-        return this.currentPresentation.movements === null
+        return this.currentPresentation.movements === null;
     }
 
     private addRecordingFFView(doc: Doc, key: string = doc[Id]): void {
         // console.info('adding dispose func : docId', key, 'doc', doc);
 
-        if (this.recordingFFViews === null) { console.warn('addFFView on null RecordingApi'); return; }
-        if (this.recordingFFViews.has(key)) { console.warn('addFFView : key already in map'); return; }
+        if (this.recordingFFViews === null) {
+            console.warn('addFFView on null RecordingApi');
+            return;
+        }
+        if (this.recordingFFViews.has(key)) {
+            console.warn('addFFView : key already in map');
+            return;
+        }
 
         const disposeFunc = reaction(
-            () => ({ x: NumCast(doc.panX, -1), y: NumCast(doc.panY, -1), scale: NumCast(doc.viewScale, 0)}),
-            (res) => (res.x !== -1 && res.y !== -1 && this.tracking) && this.trackMovement(res.x, res.y, key, res.scale),
+            () => ({ x: NumCast(doc.panX, -1), y: NumCast(doc.panY, -1), scale: NumCast(doc.viewScale, 0) }),
+            res => res.x !== -1 && res.y !== -1 && this.tracking && this.trackMovement(res.x, res.y, key, res.scale)
         );
         this.recordingFFViews?.set(key, disposeFunc);
     }
 
     private removeRecordingFFView = (key: string) => {
         // console.info('removing dispose func : docId', key);
-        if (this.recordingFFViews === null) { console.warn('removeFFView on null RecordingApi'); return; }
+        if (this.recordingFFViews === null) {
+            console.warn('removeFFView on null RecordingApi');
+            return;
+        }
         this.recordingFFViews.get(key)?.();
         this.recordingFFViews.delete(key);
-    }
+    };
 
     // in the case where only one tab was changed (updates not across dashboards), set only one to true
     private updateRecordingFFViewsFromTabs = (tabbedDocs: Doc[], onlyOne = false) => {
@@ -86,7 +95,6 @@ export class TrackMovements {
             if (isFFView(DashDoc)) tabbedFFViews.add(DashDoc[Id]);
         }
 
-
         // new tab was added - need to add it
         if (tabbedFFViews.size > this.recordingFFViews.size) {
             for (const DashDoc of tabbedDocs) {
@@ -103,13 +111,13 @@ export class TrackMovements {
         // tab was removed - need to remove it from recordingFFViews
         else if (tabbedFFViews.size < this.recordingFFViews.size) {
             for (const [key] of this.recordingFFViews) {
-                if (!tabbedFFViews.has(key)) { 
+                if (!tabbedFFViews.has(key)) {
                     this.removeRecordingFFView(key);
                     if (onlyOne) return;
-                } 
+                }
             }
         }
-    }
+    };
 
     private initTabTracker = () => {
         if (this.recordingFFViews === null) {
@@ -117,18 +125,19 @@ export class TrackMovements {
         }
 
         // init the dispose funcs on the page
-        const docList = DocListCast(CollectionDockingView.Instance.props.Document.data);
+        const docList = DocListCast(CollectionDockingView.Instance?.props.Document.data);
         this.updateRecordingFFViewsFromTabs(docList);
 
         // create a reaction to monitor changes in tabs
-        this.tabChangeDisposeFunc =
-            reaction(() => CollectionDockingView.Instance.props.Document.data,
-                (change) => {
-                    // TODO: consider changing between dashboards
-                    // console.info('change in tabs', change);
-                    this.updateRecordingFFViewsFromTabs(DocListCast(change), true);
-                });
-    }
+        this.tabChangeDisposeFunc = reaction(
+            () => CollectionDockingView.Instance?.props.Document.data,
+            change => {
+                // TODO: consider changing between dashboards
+                // console.info('change in tabs', change);
+                this.updateRecordingFFViewsFromTabs(DocListCast(change), true);
+            }
+        );
+    };
 
     start = (meta?: Object) => {
         this.initTabTracker();
@@ -142,12 +151,12 @@ export class TrackMovements {
         this.absoluteStart = startDate.getTime();
 
         // (2) assign meta content if it exists
-        this.currentPresentation.meta = meta || {}
+        this.currentPresentation.meta = meta || {};
         // (3) assign start date to currentPresenation
-        this.currentPresentation.movements = []
+        this.currentPresentation.movements = [];
         // (4) set tracking true to allow trackMovements
-        this.tracking = true
-    }
+        this.tracking = true;
+    };
 
     /* stops the video and returns the presentatation; if no presentation, returns undefined */
     yieldPresentation(clearData: boolean = true): Presentation | null {
@@ -157,7 +166,7 @@ export class TrackMovements {
         // set the previus recording view to the play view
         // this.playFFView = this.recordingFFView;
 
-        // ensure we add the endTime now that they are done recording    
+        // ensure we add the endTime now that they are done recording
         const cpy = { ...this.currentPresentation, totalTime: new Date().getTime() - this.absoluteStart };
 
         // reset the current presentation
@@ -169,10 +178,10 @@ export class TrackMovements {
 
     finish = (): void => {
         // make is tracking false
-        this.tracking = false
+        this.tracking = false;
         // reset the RecordingApi instance
         this.clear();
-    }
+    };
 
     private clear = (): void => {
         // clear the disposeFunc if we are done (not tracking)
@@ -187,44 +196,46 @@ export class TrackMovements {
         }
 
         // clear presenation data
-        this.currentPresentation = TrackMovements.NULL_PRESENTATION
+        this.currentPresentation = TrackMovements.NULL_PRESENTATION;
         // clear absoluteStart
-        this.absoluteStart = -1
-    }
+        this.absoluteStart = -1;
+    };
 
     removeAllRecordingFFViews = () => {
-        if (this.recordingFFViews === null) { console.warn('removeAllFFViews on null RecordingApi'); return; }
+        if (this.recordingFFViews === null) {
+            console.warn('removeAllFFViews on null RecordingApi');
+            return;
+        }
 
         for (const [id, disposeFunc] of this.recordingFFViews) {
             // console.info('calling dispose func : docId', id);
             disposeFunc();
             this.recordingFFViews.delete(id);
         }
-    }
+    };
 
     private trackMovement = (panX: number, panY: number, docId: string, scale: number = 0) => {
         // ensure we are recording to track
         if (!this.tracking) {
-            console.error('[recordingApi.ts] trackMovements(): tracking is false')
+            console.error('[recordingApi.ts] trackMovements(): tracking is false');
             return;
         }
         // check to see if the presetation is init - if not, we are between segments
         // TODO: make this more clear - tracking should be "live tracking", not always true when the recording api being used (between start and yieldPres)
-        // bacuse tracking should be false inbetween segments high key 
+        // bacuse tracking should be false inbetween segments high key
         if (this.nullPresentation) {
-            console.warn('[recordingApi.ts] trackMovements(): trying to store movemetns between segments')
+            console.warn('[recordingApi.ts] trackMovements(): trying to store movemetns between segments');
             return;
         }
 
         // get the time
-        const time = new Date().getTime() - this.absoluteStart
+        const time = new Date().getTime() - this.absoluteStart;
         // make new movement object
-        const movement: Movement = { time, panX, panY, scale, docId }
+        const movement: Movement = { time, panX, panY, scale, docId };
 
         // add that movement to the current presentation data's movement array
-        this.currentPresentation.movements && this.currentPresentation.movements.push(movement)
-    }
-
+        this.currentPresentation.movements && this.currentPresentation.movements.push(movement);
+    };
 
     // method that concatenates an array of presentatations into one
     public concatPresentations = (presentations: Presentation[]): Presentation => {
@@ -233,13 +244,15 @@ export class TrackMovements {
         let sumTime = 0;
         let combinedMetas: any[] = [];
 
-        presentations.forEach((presentation) => {
+        presentations.forEach(presentation => {
             const { movements, totalTime, meta } = presentation;
 
             // update movements if they had one
             if (movements) {
                 // add the summed time to the movements
-                const addedTimeMovements = movements.map(move => { return { ...move, time: move.time + sumTime } });
+                const addedTimeMovements = movements.map(move => {
+                    return { ...move, time: move.time + sumTime };
+                });
                 // concat the movements already in the combined presentation with these new ones
                 combinedMovements.push(...addedTimeMovements);
             }
@@ -252,6 +265,6 @@ export class TrackMovements {
         });
 
         // return the combined presentation with the updated total summed time
-		return { movements: combinedMovements, totalTime: sumTime, meta: combinedMetas };
-    }
+        return { movements: combinedMovements, totalTime: sumTime, meta: combinedMetas };
+    };
 }
diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss
index ea24dbf6d..1e6a377de 100644
--- a/src/client/views/ContextMenu.scss
+++ b/src/client/views/ContextMenu.scss
@@ -1,10 +1,10 @@
-@import "global/globalCssVariables";
+@import 'global/globalCssVariables';
 
 .contextMenu-cont {
     position: absolute;
     display: flex;
     z-index: 100000;
-    box-shadow: 0px 3px 4px rgba(0,0,0,30%);
+    box-shadow: 0px 3px 4px rgba(0, 0, 0, 30%);
     flex-direction: column;
     background: whitesmoke;
     border-radius: 3px;
@@ -28,9 +28,9 @@
     position: absolute;
     display: flex;
     z-index: 1000;
-    box-shadow: #AAAAAA .2vw .2vw .4vw;
+    box-shadow: #aaaaaa 0.2vw 0.2vw 0.4vw;
     flex-direction: column;
-    border: 1px solid #BBBBBBBB;
+    border: 1px solid #bbbbbbbb;
     border-radius: 15px;
     padding-top: 10px;
     padding-bottom: 10px;
@@ -49,7 +49,7 @@
     -moz-user-select: none;
     -ms-user-select: none;
     user-select: none;
-    transition: all .1s;
+    transition: all 0.1s;
     border-style: none;
     // padding: 10px 0px 10px 0px;
     white-space: nowrap;
@@ -58,7 +58,7 @@
     text-transform: uppercase;
     padding-right: 30px;
 
-    .icon-background {
+    .contextMenu-item-icon-background {
         pointer-events: all;
         background-color: transparent;
         width: 35px;
@@ -78,7 +78,7 @@
     -moz-user-select: none;
     -ms-user-select: none;
     user-select: none;
-    transition: all .1s;
+    transition: all 0.1s;
     border-style: none;
     // padding: 10px 0px 10px 0px;
     white-space: nowrap;
@@ -89,7 +89,7 @@
 }
 
 .contextMenu-item:hover {
-    border-width: .11px;
+    border-width: 0.11px;
     border-style: none;
     border-color: $medium-gray; //  rgb(187, 186, 186);
     border-bottom-style: solid;
@@ -116,8 +116,8 @@
     -moz-user-select: none;
     -ms-user-select: none;
     user-select: none;
-    transition: all .1s;
-    border-width: .11px;
+    transition: all 0.1s;
+    border-width: 0.11px;
     border-style: none;
     border-color: $medium-gray; //  rgb(187, 186, 186);
     // padding: 10px 0px 10px 0px;
@@ -152,4 +152,4 @@
     padding-left: 10px;
     border: solid black 1px;
     border-radius: 5px;
-}
\ No newline at end of file
+}
diff --git a/src/client/views/ContextMenuItem.tsx b/src/client/views/ContextMenuItem.tsx
index 30073e21f..dc9c2eb6c 100644
--- a/src/client/views/ContextMenuItem.tsx
+++ b/src/client/views/ContextMenuItem.tsx
@@ -1,9 +1,9 @@
-import React = require("react");
-import { observable, action, runInAction } from "mobx";
-import { observer } from "mobx-react";
+import React = require('react');
+import { observable, action, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { UndoManager } from "../util/UndoManager";
+import { UndoManager } from '../util/UndoManager';
 
 export interface OriginalMenuProps {
     description: string;
@@ -37,7 +37,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
     }
 
     handleEvent = async (e: React.MouseEvent<HTMLDivElement>) => {
-        if ("event" in this.props) {
+        if ('event' in this.props) {
             this.props.closeMenu?.();
             let batch: UndoManager.Batch | undefined;
             if (this.props.undoable !== false) {
@@ -46,7 +46,7 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
             await this.props.event({ x: e.clientX, y: e.clientY });
             batch?.end();
         }
-    }
+    };
 
     currentTimeout?: any;
     static readonly timeout = 300;
@@ -62,8 +62,11 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
         }
         this._overPosY = e.clientY;
         this._overPosX = e.clientX;
-        this.currentTimeout = setTimeout(action(() => this.overItem = true), ContextMenuItem.timeout);
-    }
+        this.currentTimeout = setTimeout(
+            action(() => (this.overItem = true)),
+            ContextMenuItem.timeout
+        );
+    };
 
     onPointerLeave = () => {
         if (this.currentTimeout) {
@@ -73,57 +76,68 @@ export class ContextMenuItem extends React.Component<ContextMenuProps & { select
         if (!this.overItem) {
             return;
         }
-        this.currentTimeout = setTimeout(action(() => this.overItem = false), ContextMenuItem.timeout);
-    }
+        this.currentTimeout = setTimeout(
+            action(() => (this.overItem = false)),
+            ContextMenuItem.timeout
+        );
+    };
 
     render() {
-        if ("event" in this.props) {
+        if ('event' in this.props) {
             return (
-                <div className={"contextMenu-item" + (this.props.selected ? " contextMenu-itemSelected" : "")} onPointerDown={this.handleEvent}>
+                <div className={'contextMenu-item' + (this.props.selected ? ' contextMenu-itemSelected' : '')} onPointerDown={this.handleEvent}>
                     {this.props.icon ? (
-                        <span className="icon-background">
+                        <span className="contextMenu-item-icon-background">
                             <FontAwesomeIcon icon={this.props.icon} size="sm" />
                         </span>
                     ) : null}
-                    <div className="contextMenu-description">
-                        {this.props.description.replace(":", "")}
-                    </div>
+                    <div className="contextMenu-description">{this.props.description.replace(':', '')}</div>
                 </div>
             );
-        } else if ("subitems" in this.props) {
-            const where = !this.overItem ? "" : this._overPosY < window.innerHeight / 3 ? "flex-start" : this._overPosY > window.innerHeight * 2 / 3 ? "flex-end" : "center";
-            const marginTop = !this.overItem ? "" : this._overPosY < window.innerHeight / 3 ? "20px" : this._overPosY > window.innerHeight * 2 / 3 ? "-20px" : "";
+        } else if ('subitems' in this.props) {
+            const where = !this.overItem ? '' : this._overPosY < window.innerHeight / 3 ? 'flex-start' : this._overPosY > (window.innerHeight * 2) / 3 ? 'flex-end' : 'center';
+            const marginTop = !this.overItem ? '' : this._overPosY < window.innerHeight / 3 ? '20px' : this._overPosY > (window.innerHeight * 2) / 3 ? '-20px' : '';
 
             // here
-            const submenu = !this.overItem ? (null) :
-                <div className="contextMenu-subMenu-cont"
+            const submenu = !this.overItem ? null : (
+                <div
+                    className="contextMenu-subMenu-cont"
                     style={{
-                        marginLeft: window.innerHeight - this._overPosX - 50 > 0 ? "90%" : "20%", marginTop
+                        marginLeft: window.innerHeight - this._overPosX - 50 > 0 ? '90%' : '20%',
+                        marginTop,
                     }}>
-                    {this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />)}
-                </div>;
+                    {this._items.map(prop => (
+                        <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />
+                    ))}
+                </div>
+            );
             if (!(this.props as SubmenuProps).noexpand) {
-                return <div className="contextMenu-inlineMenu">
-                    {this._items.map(prop => <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />)}
-                </div>;
+                return (
+                    <div className="contextMenu-inlineMenu">
+                        {this._items.map(prop => (
+                            <ContextMenuItem {...prop} key={prop.description} closeMenu={this.props.closeMenu} />
+                        ))}
+                    </div>
+                );
             }
             return (
-                <div className={"contextMenu-item" + (this.props.selected ? " contextMenu-itemSelected" : "")}
-                    style={{ alignItems: where, borderTop: this.props.addDivider ? "solid 1px" : undefined }}
-                    onMouseLeave={this.onPointerLeave} onMouseEnter={this.onPointerEnter}>
+                <div
+                    className={'contextMenu-item' + (this.props.selected ? ' contextMenu-itemSelected' : '')}
+                    style={{ alignItems: where, borderTop: this.props.addDivider ? 'solid 1px' : undefined }}
+                    onMouseLeave={this.onPointerLeave}
+                    onMouseEnter={this.onPointerEnter}>
                     {this.props.icon ? (
-                        <span className="icon-background" onMouseEnter={this.onPointerLeave} style={{ alignItems: "center", alignSelf: "center" }}>
+                        <span className="contextMenu-item-icon-background" onMouseEnter={this.onPointerLeave} style={{ alignItems: 'center', alignSelf: 'center' }}>
                             <FontAwesomeIcon icon={this.props.icon} size="sm" />
                         </span>
                     ) : null}
-                    <div className="contextMenu-description" onMouseEnter={this.onPointerEnter}
-                        style={{ alignItems: "center", alignSelf: "center" }} >
+                    <div className="contextMenu-description" onMouseEnter={this.onPointerEnter} style={{ alignItems: 'center', alignSelf: 'center' }}>
                         {this.props.description}
-                        <FontAwesomeIcon icon={"angle-right"} size="lg" style={{ position: "absolute", right: "10px" }} />
+                        <FontAwesomeIcon icon={'angle-right'} size="lg" style={{ position: 'absolute', right: '10px' }} />
                     </div>
                     {submenu}
                 </div>
             );
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 526a32f5a..9ea9128bd 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -238,7 +238,7 @@ export class DashboardView extends React.Component {
             } else if (doc.readOnly) {
                 DocServer.Control.makeReadOnly();
             } else {
-                DocServer.Control.makeEditable();
+                Doc.CurrentUserEmail !== 'guest' && DocServer.Control.makeEditable();
             }
         }
 
diff --git a/src/client/views/GlobalKeyHandler.ts b/src/client/views/GlobalKeyHandler.ts
index 73e0c9933..85f579975 100644
--- a/src/client/views/GlobalKeyHandler.ts
+++ b/src/client/views/GlobalKeyHandler.ts
@@ -116,8 +116,8 @@ export class KeyManager {
                 var doDeselect = true;
                 if (SnappingManager.GetIsDragging()) {
                     DragManager.AbortDrag();
-                } else if (CollectionDockingView.Instance.HasFullScreen) {
-                    CollectionDockingView.Instance.CloseFullScreen();
+                } else if (CollectionDockingView.Instance?.HasFullScreen) {
+                    CollectionDockingView.Instance?.CloseFullScreen();
                 } else if (CollectionStackedTimeline.SelectingRegion) {
                     CollectionStackedTimeline.SelectingRegion = undefined;
                     doDeselect = false;
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index e998f1fb9..4cb1183d0 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -5,6 +5,8 @@
 import * as React from 'react';
 import * as ReactDOM from 'react-dom';
 import { AssignAllExtensions } from '../../extensions/General/Extensions';
+import { Utils } from '../../Utils';
+import { DocServer } from '../DocServer';
 import { Docs } from '../documents/Documents';
 import { CurrentUserUtils } from '../util/CurrentUserUtils';
 import { LinkManager } from '../util/LinkManager'; // this must come before importing Docs and CurrentUserUtils
@@ -19,31 +21,28 @@ AssignAllExtensions();
     MainView.Live = window.location.search.includes('live');
     window.location.search.includes('safe') && CollectionView.SetSafeMode(true);
     const info = await CurrentUserUtils.loadCurrentUser();
-    if (info.id !== '__guest__') {
-        // a guest will not have an id registered
-        await CurrentUserUtils.loadUserDocument(info.id);
-    } else {
-        await Docs.Prototypes.initialize();
+    if (info.email === 'guest') DocServer.Control.makeReadOnly();
+    await CurrentUserUtils.loadUserDocument(info.id);
+    setTimeout(() => {
+        document.getElementById('root')!.addEventListener(
+            'wheel',
+            event => {
+                if (event.ctrlKey) {
+                    event.preventDefault();
+                }
+            },
+            true
+        );
+        const startload = (document as any).startLoad;
+        const loading = Date.now() - (startload ? Number(startload) : Date.now() - 3000);
+        console.log('Loading Time = ' + loading);
+        const d = new Date();
+        d.setTime(d.getTime() + 100 * 24 * 60 * 60 * 1000);
+        const expires = 'expires=' + d.toUTCString();
+        document.cookie = `loadtime=${loading};${expires};path=/`;
         new LinkManager();
-    }
-    document.getElementById('root')!.addEventListener(
-        'wheel',
-        event => {
-            if (event.ctrlKey) {
-                event.preventDefault();
-            }
-        },
-        true
-    );
-    const startload = (document as any).startLoad;
-    const loading = Date.now() - (startload ? Number(startload) : Date.now() - 3000);
-    console.log('Loading Time = ' + loading);
-    const d = new Date();
-    d.setTime(d.getTime() + 100 * 24 * 60 * 60 * 1000);
-    const expires = 'expires=' + d.toUTCString();
-    document.cookie = `loadtime=${loading};${expires};path=/`;
-    new LinkManager();
-    new TrackMovements();
-    new ReplayMovements();
-    ReactDOM.render(<MainView />, document.getElementById('root'));
+        new TrackMovements();
+        new ReplayMovements();
+        ReactDOM.render(<MainView />, document.getElementById('root'));
+    }, 0);
 })();
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index b61cd3409..7e032af5e 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -3,7 +3,7 @@ import { faBuffer, faHireAHelper } from '@fortawesome/free-brands-svg-icons';
 import * as far from '@fortawesome/free-regular-svg-icons';
 import * as fa from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, configure, observable, reaction } from 'mobx';
+import { action, computed, configure, observable, reaction, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import * as ReactDOM from 'react-dom';
@@ -77,12 +77,15 @@ export class MainView extends React.Component {
     @observable private _panelContent: string = 'none';
     @observable private _sidebarContent: any = Doc.MyLeftSidebarPanel;
     @observable private _leftMenuFlyoutWidth: number = 0;
+    @computed get _hideUI() {
+        return this.mainDoc && this.mainDoc._viewType !== CollectionViewType.Docking;
+    }
 
     @computed private get dashboardTabHeight() {
-        return 27;
+        return this._hideUI ? 0 : 27;
     } //  27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js
     @computed private get topOfDashUI() {
-        return Number(DASHBOARD_SELECTOR_HEIGHT.replace('px', ''));
+        return this._hideUI ? 0 : Number(DASHBOARD_SELECTOR_HEIGHT.replace('px', ''));
     }
     @computed private get topOfHeaderBarDoc() {
         return this.topOfDashUI;
@@ -105,7 +108,12 @@ export class MainView extends React.Component {
     @computed private get colorScheme() {
         return StrCast(Doc.ActiveDashboard?.colorScheme);
     }
+    @observable mainDoc: Opt<Doc>;
     @computed private get mainContainer() {
+        if (window.location.pathname.startsWith('/doc/')) {
+            DocServer.GetRefField(window.location.pathname.substring('/doc/'.length)).then(main => runInAction(() => (this.mainDoc = main as Doc)));
+            return this.mainDoc;
+        }
         return this.userDoc ? Doc.ActiveDashboard : Doc.GuestDashboard;
     }
     @computed private get headerBarDoc() {
@@ -116,10 +124,10 @@ export class MainView extends React.Component {
     }
 
     headerBarDocWidth = () => this.mainDocViewWidth();
-    headerBarDocHeight = () => SettingsManager.headerBarHeight ?? 0;
-    topMenuHeight = () => 35;
+    headerBarDocHeight = () => (this._hideUI ? 0 : SettingsManager.headerBarHeight ?? 0);
+    topMenuHeight = () => (this._hideUI ? 0 : 35);
     topMenuWidth = returnZero; // value is ignored ...
-    leftMenuWidth = () => Number(LEFT_MENU_WIDTH.replace('px', ''));
+    leftMenuWidth = () => (this._hideUI ? 0 : Number(LEFT_MENU_WIDTH.replace('px', '')));
     leftMenuHeight = () => this._dashUIHeight;
     leftMenuFlyoutWidth = () => this._leftMenuFlyoutWidth;
     leftMenuFlyoutHeight = () => this._dashUIHeight;
@@ -212,7 +220,8 @@ export class MainView extends React.Component {
             const pathname = window.location.pathname.substr(1).split('/');
             if (pathname.length > 1 && pathname[0] === 'doc') {
                 Doc.MainDocId = pathname[1];
-                !this.userDoc && DocServer.GetRefField(pathname[1]).then(action(field => field instanceof Doc && (Doc.GuestTarget = field)));
+                //!this.userDoc &&
+                DocServer.GetRefField(pathname[1]).then(action(field => field instanceof Doc && (Doc.GuestTarget = field)));
             }
         }
 
@@ -455,7 +464,11 @@ export class MainView extends React.Component {
         AudioBox.Enabled = true;
         const targets = document.elementsFromPoint(e.x, e.y);
         if (targets.length) {
-            const targClass = targets[0].className.toString();
+            let targClass = targets[0].className.toString();
+            for (let i = 0; i < targets.length - 1; i++) {
+                if (typeof targets[i].className === 'object') targClass = targets[i + 1].className.toString();
+                else break;
+            }
             !targClass.includes('contextMenu') && ContextMenu.Instance.closeMenu();
             !['timeline-menu-desc', 'timeline-menu-item', 'timeline-menu-input'].includes(targClass) && TimelineMenu.Instance.closeMenu();
         }
@@ -517,7 +530,7 @@ export class MainView extends React.Component {
         return () => (this._exploreMode ? ScriptField.MakeScript('CollectionBrowseClick(documentView, clientX, clientY)', { documentView: 'any', clientX: 'number', clientY: 'number' })! : undefined);
     }
     headerBarScreenXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.headerBarDocHeight(), 1);
-
+    mainScreenToLocalXf = () => new Transform(-this.leftScreenOffsetOfMainDocView - this.leftMenuFlyoutWidth(), -this.topOfMainDocContent, 1);
     @computed get headerBarDocView() {
         return (
             <div className="mainView-headerBar" style={{ height: this.headerBarDocHeight() }}>
@@ -557,7 +570,7 @@ export class MainView extends React.Component {
     @computed get mainDocView() {
         return (
             <>
-                {this.headerBarDocView}
+                {this._hideUI ? null : this.headerBarDocView}
                 <DocumentView
                     key="main"
                     Document={this.mainContainer!}
@@ -566,11 +579,11 @@ export class MainView extends React.Component {
                     addDocTab={this.addDocTabFunc}
                     pinToPres={emptyFunction}
                     docViewPath={returnEmptyDoclist}
-                    styleProvider={undefined}
+                    styleProvider={this._hideUI ? DefaultStyleProvider : undefined}
                     rootSelected={returnTrue}
                     isContentActive={returnTrue}
                     removeDocument={undefined}
-                    ScreenToLocalTransform={Transform.Identity}
+                    ScreenToLocalTransform={this._hideUI ? this.mainScreenToLocalXf : Transform.Identity}
                     PanelWidth={this.mainDocViewWidth}
                     PanelHeight={this.mainDocViewHeight}
                     focus={DocUtils.DefaultFocus}
@@ -582,7 +595,7 @@ export class MainView extends React.Component {
                     ContainingCollectionView={undefined}
                     ContainingCollectionDoc={undefined}
                     suppressSetHeight={true}
-                    renderDepth={-1}
+                    renderDepth={this._hideUI ? 0 : -1}
                 />
             </>
         );
@@ -750,7 +763,7 @@ export class MainView extends React.Component {
         const width = this.propertiesWidth() + leftMenuFlyoutWidth;
         return (
             <>
-                {this.leftMenuPanel}
+                {this._hideUI ? null : this.leftMenuPanel}
                 <div key="inner" className={`mainView-innerContent${this.colorScheme}`}>
                     {this.flyout}
                     <div className="mainView-libraryHandle" style={{ left: leftMenuFlyoutWidth - 10 /* ~half width of handle */, display: !this._leftMenuFlyoutWidth ? 'none' : undefined }} onPointerDown={this.onFlyoutPointerDown}>
@@ -759,9 +772,11 @@ export class MainView extends React.Component {
                     <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)` }}>
                         {this.dockingContent}
 
-                        <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}>
-                            <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? 'chevron-left' : 'chevron-right'} color={this.colorScheme === ColorScheme.Dark ? Colors.WHITE : Colors.BLACK} size="sm" />
-                        </div>
+                        {this._hideUI ? null : (
+                            <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}>
+                                <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? 'chevron-left' : 'chevron-right'} color={this.colorScheme === ColorScheme.Dark ? Colors.WHITE : Colors.BLACK} size="sm" />
+                            </div>
+                        )}
                         <div className="properties-container" style={{ width: this.propertiesWidth() }}>
                             {this.propertiesWidth() < 10 ? null : <PropertiesView styleProvider={DefaultStyleProvider} addDocTab={this.addDocTabFunc} width={this.propertiesWidth()} height={this.propertiesHeight()} />}
                         </div>
@@ -962,7 +977,7 @@ export class MainView extends React.Component {
                 <GoogleAuthenticationManager />
                 <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfHeaderBarDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} />
                 <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} />
-                <TopBar />
+                {this._hideUI ? null : <TopBar />}
                 {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null}
                 {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => (DocumentLinksButton.LinkEditorDocView = undefined))} docView={DocumentLinksButton.LinkEditorDocView} /> : null}
                 {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : null}
@@ -973,7 +988,7 @@ export class MainView extends React.Component {
                         default:
                             return (
                                 <>
-                                    <div style={{ position: 'relative', display: LightboxView.LightboxDoc ? 'none' : undefined, zIndex: 1999 }}>
+                                    <div style={{ position: 'relative', display: this._hideUI || LightboxView.LightboxDoc ? 'none' : undefined, zIndex: 1999 }}>
                                         <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} />
                                     </div>
                                     {this.mainDashboardArea}
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index 0f63ebc1d..9d89ee036 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -40,7 +40,7 @@ export class PropertiesDocContextSelector extends React.Component<PropertiesDocC
                 .keys()
         );
         return doclayouts
-            .filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance.props.Document))
+            .filter(doc => !Doc.AreProtosEqual(doc, CollectionDockingView.Instance?.props.Document))
             .filter(doc => !Doc.IsSystem(doc))
             .map(doc => ({ col: doc, target }));
     }
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 42f9bb981..ed9054107 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -29,7 +29,7 @@ const _global = (window /* browser */ || global) /* node */ as any;
 
 @observer
 export class CollectionDockingView extends CollectionSubView() {
-    @observable public static Instance: CollectionDockingView;
+    @observable public static Instance: CollectionDockingView | undefined;
     public static makeDocumentConfig(document: Doc, panelName?: string, width?: number) {
         return {
             type: 'react-component',
@@ -103,12 +103,14 @@ export class CollectionDockingView extends CollectionSubView() {
 
     @undoBatch
     public static CloseSplit(document: Opt<Doc>, panelName?: string): boolean {
-        const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find(tab => (panelName ? tab.contentItem.config.props.panelName === panelName : tab.DashDoc === document));
-        if (tab) {
-            const j = tab.header.parent.contentItems.indexOf(tab.contentItem);
-            if (j !== -1) {
-                tab.header.parent.contentItems[j].remove();
-                return CollectionDockingView.Instance.layoutChanged();
+        if (CollectionDockingView.Instance) {
+            const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find(tab => (panelName ? tab.contentItem.config.props.panelName === panelName : tab.DashDoc === document));
+            if (tab) {
+                const j = tab.header.parent.contentItems.indexOf(tab.contentItem);
+                if (j !== -1) {
+                    tab.header.parent.contentItems[j].remove();
+                    return CollectionDockingView.Instance.layoutChanged();
+                }
             }
         }
 
@@ -119,19 +121,21 @@ export class CollectionDockingView extends CollectionSubView() {
     public static OpenFullScreen(doc: Doc) {
         SelectionManager.DeselectAll();
         const instance = CollectionDockingView.Instance;
-        if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === 'layout') {
-            return DashboardView.openDashboard(doc);
+        if (instance) {
+            if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === 'layout') {
+                return DashboardView.openDashboard(doc);
+            }
+            const newItemStackConfig = {
+                type: 'stack',
+                content: [CollectionDockingView.makeDocumentConfig(Doc.MakeAlias(doc))],
+            };
+            const docconfig = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
+            instance._goldenLayout.root.contentItems[0].addChild(docconfig);
+            docconfig.callDownwards('_$init');
+            instance._goldenLayout._$maximiseItem(docconfig);
+            instance._goldenLayout.emit('stateChanged');
+            instance.stateChanged();
         }
-        const newItemStackConfig = {
-            type: 'stack',
-            content: [CollectionDockingView.makeDocumentConfig(Doc.MakeAlias(doc))],
-        };
-        const docconfig = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout);
-        instance._goldenLayout.root.contentItems[0].addChild(docconfig);
-        docconfig.callDownwards('_$init');
-        instance._goldenLayout._$maximiseItem(docconfig);
-        instance._goldenLayout.emit('stateChanged');
-        instance.stateChanged();
         return true;
     }
 
@@ -146,21 +150,23 @@ export class CollectionDockingView extends CollectionSubView() {
             const newContentItem = stack.layoutManager.createContentItem(newConfig, instance._goldenLayout);
             stack.addChild(newContentItem.contentItems[0], undefined);
             stack.contentItems[activeContentItemIndex].remove();
-            return CollectionDockingView.Instance.layoutChanged();
+            return instance.layoutChanged();
         }
-        const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find(tab => tab.contentItem.config.props.panelName === panelName);
+        const tab = Array.from(instance.tabMap.keys()).find(tab => tab.contentItem.config.props.panelName === panelName);
         if (tab) {
             tab.header.parent.addChild(newConfig, undefined);
             const j = tab.header.parent.contentItems.indexOf(tab.contentItem);
             !addToSplit && j !== -1 && tab.header.parent.contentItems[j].remove();
-            return CollectionDockingView.Instance.layoutChanged();
+            return instance.layoutChanged();
         }
         return CollectionDockingView.AddSplit(document, panelName, stack, panelName);
     }
 
     @undoBatch
     public static ToggleSplit(doc: Doc, location: string, stack?: any, panelName?: string) {
-        return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex(tab => tab.DashDoc === doc) !== -1 ? CollectionDockingView.CloseSplit(doc) : CollectionDockingView.AddSplit(doc, location, stack, panelName);
+        return CollectionDockingView.Instance && Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex(tab => tab.DashDoc === doc) !== -1
+            ? CollectionDockingView.CloseSplit(doc)
+            : CollectionDockingView.AddSplit(doc, location, stack, panelName);
     }
 
     //
@@ -170,7 +176,7 @@ export class CollectionDockingView extends CollectionSubView() {
     @action
     public static AddSplit(document: Doc, pullSide: string, stack?: any, panelName?: string) {
         if (document._viewType === CollectionViewType.Docking) return DashboardView.openDashboard(document);
-
+        if (!CollectionDockingView.Instance) return false;
         const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document);
         if (tab) {
             tab.header.parent.setActiveContentItem(tab.contentItem);
@@ -453,13 +459,15 @@ export class CollectionDockingView extends CollectionSubView() {
             Doc.AddDocToList(Doc.MyHeaderBar, 'data', tab.DashDoc);
             Doc.AddDocToList(Doc.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true);
         }
-        const dview = CollectionDockingView.Instance.props.Document;
-        const fieldKey = CollectionDockingView.Instance.props.fieldKey;
-        Doc.RemoveDocFromList(dview, fieldKey, tab.DashDoc);
-        this.tabMap.delete(tab);
-        tab._disposers && Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
-        tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele));
-        this.stateChanged();
+        if (CollectionDockingView.Instance) {
+            const dview = CollectionDockingView.Instance.props.Document;
+            const fieldKey = CollectionDockingView.Instance.props.fieldKey;
+            Doc.RemoveDocFromList(dview, fieldKey, tab.DashDoc);
+            this.tabMap.delete(tab);
+            tab._disposers && Object.values(tab._disposers).forEach((disposer: any) => disposer?.());
+            tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele));
+            this.stateChanged();
+        }
     };
     tabCreated = (tab: any) => {
         this.tabMap.add(tab);
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 2ab5f6247..f0cb23eab 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -225,7 +225,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
                         },
                     })
                 );
-                DocListCast(Cast(Doc.UserDoc()['clickFuncs-child'], Doc, null).data).forEach(childClick =>
+                DocListCast(Cast(Doc.UserDoc()['clickFuncs-child'], Doc, null)?.data).forEach(childClick =>
                     onClicks.push({
                         description: `Set child ${childClick.title}`,
                         icon: 'edit',
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index b8aaea622..2d08b1c09 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -302,6 +302,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                 pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array
             });
         if (
+            CollectionDockingView.Instance &&
             !Array.from(CollectionDockingView.Instance.tabMap)
                 .map(d => d.DashDoc)
                 .includes(curPres)
@@ -420,7 +421,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
     ScreenToLocalTransform = () => {
         this._forceInvalidateScreenToLocal;
         const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont?.children?.[0] as HTMLElement);
-        return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY);
+        return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY) ?? Transform.Identity();
     };
     PanelWidth = () => this._panelWidth;
     PanelHeight = () => this._panelHeight;
@@ -449,9 +450,9 @@ export class TabDocView extends React.Component<TabDocViewProps> {
                     PanelWidth={this.PanelWidth}
                     PanelHeight={this.PanelHeight}
                     styleProvider={DefaultStyleProvider}
-                    docFilters={CollectionDockingView.Instance.childDocFilters}
-                    docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
-                    searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
+                    docFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist}
+                    docRangeFilters={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist}
+                    searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist}
                     addDocument={undefined}
                     removeDocument={this.remDocTab}
                     addDocTab={this.addDocTab}
@@ -624,9 +625,9 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
                     styleProvider={TabMinimapView.miniStyleProvider}
                     addDocTab={this.props.addDocTab}
                     pinToPres={TabDocView.PinDoc}
-                    docFilters={CollectionDockingView.Instance.childDocFilters}
-                    docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
-                    searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
+                    docFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist}
+                    docRangeFilters={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist}
+                    searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist}
                     fitContentsToBox={returnTrue}
                 />
                 <div className="miniOverlay" onPointerDown={this.miniDown}>
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index bf4c029b2..c4b42301e 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -1,6 +1,6 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Autocomplete, GoogleMap, GoogleMapProps, Marker } from '@react-google-maps/api';
-import { action, computed, IReactionDisposer, observable, ObservableMap } from 'mobx';
+import { action, computed, IReactionDisposer, observable, ObservableMap, runInAction } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { Doc, DocListCast, Opt, WidthSym } from '../../../../fields/Doc';
@@ -12,6 +12,7 @@ import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Uti
 import { Docs } from '../../../documents/Documents';
 import { DragManager } from '../../../util/DragManager';
 import { SnappingManager } from '../../../util/SnappingManager';
+import { UndoManager } from '../../../util/UndoManager';
 import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
 import { Colors } from '../../global/globalEnums';
@@ -368,23 +369,27 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
         setupMoveUpEvents(
             this,
             e,
-            (e, down, delta) => {
-                const localDelta = this.props
-                    .ScreenToLocalTransform()
-                    .scale(this.props.scaling?.() || 1)
-                    .transformDirection(delta[0], delta[1]);
-                const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + '-nativeWidth']);
-                const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
-                const ratio = (curNativeWidth + localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
-                if (ratio >= 1) {
-                    this.layoutDoc.nativeWidth = nativeWidth * ratio;
-                    this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0];
-                    this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
-                }
-                return false;
-            },
+            (e, down, delta) =>
+                runInAction(() => {
+                    const localDelta = this.props
+                        .ScreenToLocalTransform()
+                        .scale(this.props.scaling?.() || 1)
+                        .transformDirection(delta[0], delta[1]);
+                    const fullWidth = this.layoutDoc[WidthSym]();
+                    const mapWidth = fullWidth - this.sidebarWidth();
+                    if (this.sidebarWidth() + localDelta[0] > 0) {
+                        this._showSidebar = true;
+                        this.layoutDoc._width = fullWidth + localDelta[0];
+                        this.layoutDoc._sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
+                    } else {
+                        this._showSidebar = false;
+                        this.layoutDoc._width = mapWidth;
+                        this.layoutDoc._sidebarWidthPercent = '0%';
+                    }
+                    return false;
+                }),
             emptyFunction,
-            this.toggleSidebar
+            () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar map')
         );
     };
 
@@ -470,13 +475,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
     // TODO: Adding highlight box layer to Maps
     @action
     toggleSidebar = () => {
+        //1.2 * w * ? = .2 * w      .2/1.2
         const prevWidth = this.sidebarWidth();
-        this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? '50%' : '0%') !== '0%';
-        this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
+        this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%';
+        this.layoutDoc._width = this.layoutDoc._showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
     };
 
     sidebarDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), false);
+        setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true);
     };
     sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
         const bounds = this._ref.current!.getBoundingClientRect();
@@ -605,7 +611,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     {this.annotationLayer}
                     <GoogleMap mapContainerStyle={mapContainerStyle} onZoomChanged={this.zoomChanged} onCenterChanged={this.centered} onLoad={this.loadHandler} options={mapOptions}>
                         <Autocomplete onLoad={this.setSearchBox} onPlaceChanged={this.handlePlaceChanged}>
-                            <input className="mapBox-input" ref={this.inputRef} type="text" placeholder="Enter location" />
+                            <input className="mapBox-input" ref={this.inputRef} type="text" onKeyDown={e => e.stopPropagation()} placeholder="Enter location" />
                         </Autocomplete>
 
                         {this.renderMarkers()}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 5b9d90780..f41f6a1ad 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -13,20 +13,19 @@ import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
 import { KeyCodes } from '../../util/KeyCodes';
 import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { CollectionFreeFormView } from '../collections/collectionFreeForm';
 import { ContextMenu } from '../ContextMenu';
 import { ContextMenuProps } from '../ContextMenuItem';
 import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent';
 import { Colors } from '../global/globalEnums';
 import { CreateImage } from '../nodes/WebBoxRenderer';
-import { AnchorMenu } from '../pdf/AnchorMenu';
 import { PDFViewer } from '../pdf/PDFViewer';
 import { SidebarAnnos } from '../SidebarAnnos';
 import { FieldView, FieldViewProps } from './FieldView';
 import { ImageBox } from './ImageBox';
-import './PDFBox.scss';
 import { VideoBox } from './VideoBox';
+import './PDFBox.scss';
 import React = require('react');
-import { CollectionFreeFormView } from '../collections/collectionFreeForm';
 
 @observer
 export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index cfaa428f9..e945920da 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -640,7 +640,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         this.layoutDoc._width = !preview && this.SidebarShown ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
     };
     sidebarDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), false);
+        setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true);
     };
     sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
         const bounds = this._ref.current!.getBoundingClientRect();
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 292c187e4..6e5eb3300 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -399,7 +399,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
         const presCollection = Cast(this.layoutDoc.presCollection, Doc, null);
         const collectionDocView = presCollection ? DocumentManager.Instance.getDocumentView(presCollection) : undefined;
         const includesDoc: boolean = DocListCast(presCollection?.data).includes(targetDoc);
-        const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === srcContext);
+        const tab = CollectionDockingView.Instance && Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === srcContext);
         this.turnOffEdit();
         // Handles the setting of presCollection
         if (includesDoc) {
diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx
index 0bf1575fb..3fc0a237e 100644
--- a/src/client/views/topbar/TopBar.tsx
+++ b/src/client/views/topbar/TopBar.tsx
@@ -26,7 +26,7 @@ import './TopBar.scss';
 @observer
 export class TopBar extends React.Component {
     navigateToHome = () => {
-        CollectionDockingView.Instance.CaptureThumbnail()?.then(() => {
+        CollectionDockingView.Instance?.CaptureThumbnail()?.then(() => {
             Doc.ActivePage = 'home';
             DashboardView.closeActiveDashboard(); // bcz: if we do this, we need some other way to keep track, for user convenience, of the last dashboard in use
         });
diff --git a/src/fields/util.ts b/src/fields/util.ts
index cbb560114..41e723119 100644
--- a/src/fields/util.ts
+++ b/src/fields/util.ts
@@ -1,41 +1,40 @@
+import { action, observable, runInAction, trace } from 'mobx';
+import { computedFn } from 'mobx-utils';
+import { DocServer } from '../client/DocServer';
+import { SerializationHelper } from '../client/util/SerializationHelper';
 import { UndoManager } from '../client/util/UndoManager';
+import { returnZero } from '../Utils';
+import CursorField from './CursorField';
 import {
-    Doc,
-    FieldResult,
-    UpdatingFromServer,
-    LayoutSym,
-    AclPrivate,
+    AclAdmin,
+    AclAugment,
     AclEdit,
+    AclPrivate,
     AclReadonly,
-    AclAugment,
+    AclSelfEdit,
     AclSym,
+    AclUnset,
     DataSym,
+    Doc,
     DocListCast,
-    AclAdmin,
-    HeightSym,
-    WidthSym,
-    updateCachedAcls,
-    AclUnset,
     DocListCastAsync,
+    FieldResult,
     ForceServerWrite,
+    HeightSym,
     Initializing,
-    AclSelfEdit,
+    LayoutSym,
+    updateCachedAcls,
+    UpdatingFromServer,
+    WidthSym,
 } from './Doc';
-import { SerializationHelper } from '../client/util/SerializationHelper';
-import { ProxyField, PrefetchProxy } from './Proxy';
-import { RefField } from './RefField';
+import { Id, OnUpdate, Parent, Self, SelfProxy, Update } from './FieldSymbols';
+import { List } from './List';
 import { ObjectField } from './ObjectField';
-import { action, observable, runInAction, trace } from 'mobx';
-import { Parent, OnUpdate, Update, Id, SelfProxy, Self } from './FieldSymbols';
-import { DocServer } from '../client/DocServer';
+import { PrefetchProxy, ProxyField } from './Proxy';
+import { RefField } from './RefField';
+import { RichTextField } from './RichTextField';
 import { ComputedField } from './ScriptField';
 import { ScriptCast, StrCast } from './Types';
-import { returnZero } from '../Utils';
-import CursorField from './CursorField';
-import { List } from './List';
-import { SnappingManager } from '../client/util/SnappingManager';
-import { computedFn } from 'mobx-utils';
-import { RichTextField } from './RichTextField';
 
 function _readOnlySetter(): never {
     throw new Error("Documents can't be modified in read-only mode");
diff --git a/src/mobile/MobileInkOverlay.tsx b/src/mobile/MobileInkOverlay.tsx
index d668d134e..6415099fd 100644
--- a/src/mobile/MobileInkOverlay.tsx
+++ b/src/mobile/MobileInkOverlay.tsx
@@ -1,13 +1,12 @@
 import React = require('react');
-import { observer } from "mobx-react";
-import { MobileInkOverlayContent, GestureContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent } from "../server/Message";
-import { observable, action } from "mobx";
-import { GestureUtils } from "../pen-gestures/GestureUtils";
-import "./MobileInkOverlay.scss";
-import { DragManager } from "../client/util/DragManager";
+import { action, observable } from 'mobx';
+import { observer } from 'mobx-react';
 import { DocServer } from '../client/DocServer';
+import { DragManager } from '../client/util/DragManager';
 import { Doc } from '../fields/Doc';
-
+import { GestureUtils } from '../pen-gestures/GestureUtils';
+import { GestureContent, MobileDocumentUploadContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent } from '../server/Message';
+import './MobileInkOverlay.scss';
 
 @observer
 export default class MobileInkOverlay extends React.Component {
@@ -18,7 +17,7 @@ export default class MobileInkOverlay extends React.Component {
     @observable private _height: number = 0;
     @observable private _x: number = -300;
     @observable private _y: number = -300;
-    @observable private _text: string = "";
+    @observable private _text: string = '';
 
     @observable private _offsetX: number = 0;
     @observable private _offsetY: number = 0;
@@ -49,7 +48,7 @@ export default class MobileInkOverlay extends React.Component {
         this._scale = scaledSize.scale;
         this._x = 300; // TODO: center on screen
         this._y = 25; // TODO: center on screen
-        this._text = text ? text : "";
+        this._text = text ? text : '';
     }
 
     @action
@@ -64,31 +63,29 @@ export default class MobileInkOverlay extends React.Component {
         // TODO: figure out why strokes drawn in corner of mobile interface dont get inserted
 
         const { points, bounds } = content;
-        console.log("received points", points, bounds);
+        console.log('received points', points, bounds);
 
         const B = {
-            right: (bounds.right * this._scale) + this._x,
-            left: (bounds.left * this._scale) + this._x, // TODO: scale
-            bottom: (bounds.bottom * this._scale) + this._y,
-            top: (bounds.top * this._scale) + this._y, // TODO: scale
+            right: bounds.right * this._scale + this._x,
+            left: bounds.left * this._scale + this._x, // TODO: scale
+            bottom: bounds.bottom * this._scale + this._y,
+            top: bounds.top * this._scale + this._y, // TODO: scale
             width: bounds.width * this._scale,
             height: bounds.height * this._scale,
         };
 
         const target = document.elementFromPoint(this._x + 10, this._y + 10);
         target?.dispatchEvent(
-            new CustomEvent<GestureUtils.GestureEvent>("dashOnGesture",
-                {
-                    bubbles: true,
-                    detail: {
-                        points: points,
-                        gesture: GestureUtils.Gestures.Stroke,
-                        bounds: B
-                    }
-                }
-            )
+            new CustomEvent<GestureUtils.GestureEvent>('dashOnGesture', {
+                bubbles: true,
+                detail: {
+                    points: points,
+                    gesture: GestureUtils.Gestures.Stroke,
+                    bounds: B,
+                },
+            })
         );
-    }
+    };
 
     uploadDocument = async (content: MobileDocumentUploadContent) => {
         const { docId } = content;
@@ -100,36 +97,34 @@ export default class MobileInkOverlay extends React.Component {
             const complete = new DragManager.DragCompleteEvent(false, dragData);
 
             if (target) {
-                console.log("dispatching upload doc!!!!", target, doc);
+                console.log('dispatching upload doc!!!!', target, doc);
                 target.dispatchEvent(
-                    new CustomEvent<DragManager.DropEvent>("dashOnDrop",
-                        {
-                            bubbles: true,
-                            detail: {
-                                x: this._x,
-                                y: this._y,
-                                complete: complete,
-                                altKey: false,
-                                metaKey: false,
-                                ctrlKey: false,
-                                shiftKey: false,
-                                embedKey: false
-                            }
-                        }
-                    )
+                    new CustomEvent<DragManager.DropEvent>('dashOnDrop', {
+                        bubbles: true,
+                        detail: {
+                            x: this._x,
+                            y: this._y,
+                            complete: complete,
+                            altKey: false,
+                            metaKey: false,
+                            ctrlKey: false,
+                            shiftKey: false,
+                            embedKey: false,
+                        },
+                    })
                 );
             } else {
-                alert("TARGET IS UNDEFINED");
+                alert('TARGET IS UNDEFINED');
             }
         }
-    }
+    };
 
     @action
     dragStart = (e: React.PointerEvent) => {
-        document.removeEventListener("pointermove", this.dragging);
-        document.removeEventListener("pointerup", this.dragEnd);
-        document.addEventListener("pointermove", this.dragging);
-        document.addEventListener("pointerup", this.dragEnd);
+        document.removeEventListener('pointermove', this.dragging);
+        document.removeEventListener('pointerup', this.dragEnd);
+        document.addEventListener('pointermove', this.dragging);
+        document.addEventListener('pointerup', this.dragEnd);
 
         this._isDragging = true;
         this._offsetX = e.pageX - this._mainCont.current!.getBoundingClientRect().left;
@@ -137,7 +132,7 @@ export default class MobileInkOverlay extends React.Component {
 
         e.preventDefault();
         e.stopPropagation();
-    }
+    };
 
     @action
     dragging = (e: PointerEvent) => {
@@ -150,41 +145,39 @@ export default class MobileInkOverlay extends React.Component {
 
         e.preventDefault();
         e.stopPropagation();
-    }
+    };
 
     @action
     dragEnd = (e: PointerEvent) => {
-        document.removeEventListener("pointermove", this.dragging);
-        document.removeEventListener("pointerup", this.dragEnd);
+        document.removeEventListener('pointermove', this.dragging);
+        document.removeEventListener('pointerup', this.dragEnd);
 
         this._isDragging = false;
 
         e.preventDefault();
         e.stopPropagation();
-    }
+    };
 
     render() {
-
         return (
-            <div className="mobileInkOverlay"
+            <div
+                className="mobileInkOverlay"
                 style={{
                     width: this._width,
                     height: this._height,
-                    position: "absolute",
+                    position: 'absolute',
                     transform: `translate(${this._x}px, ${this._y}px)`,
                     zIndex: 30000,
-                    pointerEvents: "none",
-                    borderStyle: this._isDragging ? "solid" : "dashed",
-                }
-                }
-                ref={this._mainCont}
-            >
+                    pointerEvents: 'none',
+                    borderStyle: this._isDragging ? 'solid' : 'dashed',
+                }}
+                ref={this._mainCont}>
                 <p>{this._text}</p>
                 <div className="mobileInkOverlay-border top" onPointerDown={this.dragStart}></div>
                 <div className="mobileInkOverlay-border bottom" onPointerDown={this.dragStart}></div>
                 <div className="mobileInkOverlay-border left" onPointerDown={this.dragStart}></div>
                 <div className="mobileInkOverlay-border right" onPointerDown={this.dragStart}></div>
-            </div >
+            </div>
         );
     }
-}
\ No newline at end of file
+}
diff --git a/src/mobile/MobileMain.tsx b/src/mobile/MobileMain.tsx
index f85f05f53..6cbf86f77 100644
--- a/src/mobile/MobileMain.tsx
+++ b/src/mobile/MobileMain.tsx
@@ -12,10 +12,7 @@ AssignAllExtensions();
     const info = await CurrentUserUtils.loadCurrentUser();
     DocServer.init(window.location.protocol, window.location.hostname, 4321, info.email + ' (mobile)');
     await Docs.Prototypes.initialize();
-    if (info.id !== '__guest__') {
-        // a guest will not have an id registered
-        await CurrentUserUtils.loadUserDocument(info.id);
-    }
+    await CurrentUserUtils.loadUserDocument(info.id);
     document.getElementById('root')!.addEventListener(
         'wheel',
         event => {
diff --git a/src/server/ApiManagers/UserManager.ts b/src/server/ApiManagers/UserManager.ts
index 7be8a1e9f..53e55c1c3 100644
--- a/src/server/ApiManagers/UserManager.ts
+++ b/src/server/ApiManagers/UserManager.ts
@@ -1,10 +1,10 @@
-import ApiManager, { Registration } from "./ApiManager";
-import { Method } from "../RouteManager";
-import { Database } from "../database";
-import { msToTime } from "../ActionUtilities";
-import * as bcrypt from "bcrypt-nodejs";
-import { Opt } from "../../fields/Doc";
-import { WebSocket } from "../websocket";
+import ApiManager, { Registration } from './ApiManager';
+import { Method } from '../RouteManager';
+import { Database } from '../database';
+import { msToTime } from '../ActionUtilities';
+import * as bcrypt from 'bcrypt-nodejs';
+import { Opt } from '../../fields/Doc';
+import { WebSocket } from '../websocket';
 
 export const timeMap: { [id: string]: number } = {};
 interface ActivityUnit {
@@ -13,28 +13,26 @@ interface ActivityUnit {
 }
 
 export default class UserManager extends ApiManager {
-
     protected initialize(register: Registration): void {
-
         register({
             method: Method.GET,
-            subscription: "/getUsers",
+            subscription: '/getUsers',
             secureHandler: async ({ res }) => {
-                const cursor = await Database.Instance.query({}, { email: 1, linkDatabaseId: 1, sharingDocumentId: 1 }, "users");
+                const cursor = await Database.Instance.query({}, { email: 1, linkDatabaseId: 1, sharingDocumentId: 1 }, 'users');
                 const results = await cursor.toArray();
                 res.send(results.map((user: any) => ({ email: user.email, linkDatabaseId: user.linkDatabaseId, sharingDocumentId: user.sharingDocumentId })));
-            }
+            },
         });
 
         register({
             method: Method.POST,
-            subscription: "/setCacheDocumentIds",
+            subscription: '/setCacheDocumentIds',
             secureHandler: async ({ user, req, res }) => {
                 const result: any = {};
                 user.cacheDocumentIds = req.body.cacheDocumentIds;
                 user.save(err => {
                     if (err) {
-                        result.error = [{ msg: "Error while caching documents" }];
+                        result.error = [{ msg: 'Error while caching documents' }];
                     }
                 });
 
@@ -42,32 +40,35 @@ export default class UserManager extends ApiManager {
                 //     console.log(e);
                 // });
                 res.send(result);
-            }
+            },
         });
 
         register({
             method: Method.GET,
-            subscription: "/getUserDocumentIds",
-            secureHandler: ({ res, user }) => res.send({ userDocumentId: user.userDocumentId, linkDatabaseId: user.linkDatabaseId, sharingDocumentId: user.sharingDocumentId })
+            subscription: '/getUserDocumentIds',
+            secureHandler: ({ res, user }) => res.send({ userDocumentId: user.userDocumentId, linkDatabaseId: user.linkDatabaseId, sharingDocumentId: user.sharingDocumentId }),
+            publicHandler: ({ res }) => res.send({ userDocumentId: '__guest__', linkDatabaseId: 3, sharingDocumentId: 2 }),
         });
 
         register({
             method: Method.GET,
-            subscription: "/getSharingDocumentId",
-            secureHandler: ({ res, user }) => res.send(user.sharingDocumentId)
+            subscription: '/getSharingDocumentId',
+            secureHandler: ({ res, user }) => res.send(user.sharingDocumentId),
+            publicHandler: ({ res }) => res.send(2),
         });
 
         register({
             method: Method.GET,
-            subscription: "/getLinkDatabaseId",
-            secureHandler: ({ res, user }) => res.send(user.linkDatabaseId)
+            subscription: '/getLinkDatabaseId',
+            secureHandler: ({ res, user }) => res.send(user.linkDatabaseId),
+            publicHandler: ({ res }) => res.send(3),
         });
 
         register({
             method: Method.GET,
-            subscription: "/getCurrentUser",
+            subscription: '/getCurrentUser',
             secureHandler: ({ res, user: { _id, email, cacheDocumentIds } }) => res.send(JSON.stringify({ id: _id, email, cacheDocumentIds })),
-            publicHandler: ({ res }) => res.send(JSON.stringify({ id: "__guest__", email: "" }))
+            publicHandler: ({ res }) => res.send(JSON.stringify({ id: '__guest__', email: 'guest' })),
         });
 
         register({
@@ -80,7 +81,7 @@ export default class UserManager extends ApiManager {
                 const validated = await new Promise<Opt<boolean>>(resolve => {
                     bcrypt.compare(curr_pass, user.password, (err, passwords_match) => {
                         if (err || !passwords_match) {
-                            result.error = [{ msg: "Incorrect current password" }];
+                            result.error = [{ msg: 'Incorrect current password' }];
                             res.send(result);
                             resolve(undefined);
                         } else {
@@ -93,10 +94,10 @@ export default class UserManager extends ApiManager {
                     return;
                 }
 
-                req.assert("new_pass", "Password must be at least 4 characters long").len({ min: 4 });
-                req.assert("new_confirm", "Passwords do not match").equals(new_pass);
+                req.assert('new_pass', 'Password must be at least 4 characters long').len({ min: 4 });
+                req.assert('new_confirm', 'Passwords do not match').equals(new_pass);
                 if (curr_pass === new_pass) {
-                    result.error = [{ msg: "Current and new password are the same" }];
+                    result.error = [{ msg: 'Current and new password are the same' }];
                 }
                 // was there error in validating new passwords?
                 if (req.validationErrors()) {
@@ -113,17 +114,17 @@ export default class UserManager extends ApiManager {
 
                 user.save(err => {
                     if (err) {
-                        result.error = [{ msg: "Error while saving new password" }];
+                        result.error = [{ msg: 'Error while saving new password' }];
                     }
                 });
 
                 res.send(result);
-            }
+            },
         });
 
         register({
             method: Method.GET,
-            subscription: "/activity",
+            subscription: '/activity',
             secureHandler: ({ res }) => {
                 const now = Date.now();
 
@@ -135,25 +136,23 @@ export default class UserManager extends ApiManager {
                     const socketPair = Array.from(WebSocket.socketMap).find(pair => pair[1] === user);
                     if (socketPair && !socketPair[0].disconnected) {
                         const duration = now - time;
-                        const target = (duration / 1000) < (60 * 5) ? activeTimes : inactiveTimes;
+                        const target = duration / 1000 < 60 * 5 ? activeTimes : inactiveTimes;
                         target.push({ user, duration });
                     }
                 }
 
-                const process = (target: { user: string, duration: number }[]) => {
+                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} (${msToTime(duration)})`);
                 };
 
-                res.render("user_activity.pug", {
-                    title: "User Activity",
+                res.render('user_activity.pug', {
+                    title: 'User Activity',
                     active: process(activeTimes),
-                    inactive: process(inactiveTimes)
+                    inactive: process(inactiveTimes),
                 });
-            }
+            },
         });
-
     }
-
-}
\ No newline at end of file
+}
diff --git a/src/server/RouteManager.ts b/src/server/RouteManager.ts
index aa9bfcfa7..5683cd539 100644
--- a/src/server/RouteManager.ts
+++ b/src/server/RouteManager.ts
@@ -1,12 +1,12 @@
 import { cyan, green, red } from 'colors';
 import { Express, Request, Response } from 'express';
-import { AdminPriviliges } from ".";
-import { DashUserModel } from "./authentication/DashUserModel";
-import RouteSubscriber from "./RouteSubscriber";
+import { AdminPriviliges } from '.';
+import { DashUserModel } from './authentication/DashUserModel';
+import RouteSubscriber from './RouteSubscriber';
 
 export enum Method {
     GET,
-    POST
+    POST,
 }
 
 export interface CoreArguments {
@@ -33,13 +33,13 @@ const registered = new Map<string, Set<Method>>();
 
 enum RegistrationError {
     Malformed,
-    Duplicate
+    Duplicate,
 }
 
 export default class RouteManager {
     private server: Express;
     private _isRelease: boolean;
-    private failedRegistrations: { route: string, reason: RegistrationError }[] = [];
+    private failedRegistrations: { route: string; reason: RegistrationError }[] = [];
 
     public get isRelease() {
         return this._isRelease;
@@ -74,39 +74,42 @@ export default class RouteManager {
             }
             process.exit(1);
         } else {
-            console.log(green("all server routes have been successfully registered:"));
-            Array.from(registered.keys()).sort().forEach(route => console.log(cyan(route)));
+            console.log(green('all server routes have been successfully registered:'));
+            Array.from(registered.keys())
+                .sort()
+                .forEach(route => console.log(cyan(route)));
             console.log();
         }
-    }
+    };
 
     static routes: string[] = [];
     /**
-     * 
-     * @param initializer 
+     *
+     * @param initializer
      */
     addSupervisedRoute = (initializer: RouteInitializer): void => {
         const { method, subscription, secureHandler, publicHandler, errorHandler, requireAdminInRelease: requireAdmin } = initializer;
 
-        typeof (initializer.subscription) === "string" && RouteManager.routes.push(initializer.subscription);
+        typeof initializer.subscription === 'string' && RouteManager.routes.push(initializer.subscription);
         initializer.subscription instanceof RouteSubscriber && RouteManager.routes.push(initializer.subscription.root);
-        initializer.subscription instanceof Array && initializer.subscription.map(sub => {
-            typeof (sub) === "string" && RouteManager.routes.push(sub);
-            sub instanceof RouteSubscriber && RouteManager.routes.push(sub.root);
-        });
+        initializer.subscription instanceof Array &&
+            initializer.subscription.map(sub => {
+                typeof sub === 'string' && RouteManager.routes.push(sub);
+                sub instanceof RouteSubscriber && RouteManager.routes.push(sub.root);
+            });
         const isRelease = this._isRelease;
         const supervised = async (req: Request, res: Response) => {
             let user = req.user as Partial<DashUserModel> | undefined;
             const { originalUrl: target } = req;
-            if (process.env.DB === "MEM" && !user) {
-                user = { id: "guest", email: "", userDocumentId: "guestDocId" };
+            if (process.env.DB === 'MEM' && !user) {
+                user = { id: 'guest', email: 'guest', userDocumentId: '__guest__' };
             }
             const core = { req, res, isRelease };
             const tryExecute = async (toExecute: (args: any) => any | Promise<any>, args: any) => {
                 try {
                     await toExecute(args);
                 } catch (e) {
-                    console.log(red(target), user && ("email" in user) ? "<user logged out>" : undefined);
+                    console.log(red(target), user && 'email' in user ? '<user logged out>' : undefined);
                     if (errorHandler) {
                         errorHandler({ ...core, error: e });
                     } else {
@@ -119,7 +122,7 @@ export default class RouteManager {
                     if (AdminPriviliges.get(user.id)) {
                         AdminPriviliges.delete(user.id);
                     } else {
-                        return res.redirect(`/admin/${req.originalUrl.substring(1).replace("/", ":")}`);
+                        return res.redirect(`/admin/${req.originalUrl.substring(1).replace('/', ':')}`);
                     }
                 }
                 await tryExecute(secureHandler, { ...core, user });
@@ -128,10 +131,10 @@ export default class RouteManager {
                 if (publicHandler) {
                     await tryExecute(publicHandler, core);
                     if (!res.headersSent) {
-                        res.redirect("/login");
+                        // res.redirect("/login");
                     }
                 } else {
-                    res.redirect("/login");
+                    res.redirect('/login');
                 }
             }
             setTimeout(() => {
@@ -144,7 +147,7 @@ export default class RouteManager {
         };
         const subscribe = (subscriber: RouteSubscriber | string) => {
             let route: string;
-            if (typeof subscriber === "string") {
+            if (typeof subscriber === 'string') {
                 route = subscriber;
             } else {
                 route = subscriber.build;
@@ -152,7 +155,7 @@ export default class RouteManager {
             if (!/^\/$|^\/[A-Za-z\*]+(\/\:[A-Za-z?_\*]+)*$/g.test(route)) {
                 this.failedRegistrations.push({
                     reason: RegistrationError.Malformed,
-                    route
+                    route,
                 });
             } else {
                 const existing = registered.get(route);
@@ -160,7 +163,7 @@ export default class RouteManager {
                     if (existing.has(method)) {
                         this.failedRegistrations.push({
                             reason: RegistrationError.Duplicate,
-                            route
+                            route,
                         });
                         return;
                     }
@@ -184,15 +187,14 @@ export default class RouteManager {
         } else {
             subscribe(subscription);
         }
-    }
-
+    };
 }
 
 export const STATUS = {
     OK: 200,
     BAD_REQUEST: 400,
     EXECUTION_ERROR: 500,
-    PERMISSION_DENIED: 403
+    PERMISSION_DENIED: 403,
 };
 
 export function _error(res: Response, message: string, error?: any) {
diff --git a/src/server/authentication/AuthenticationManager.ts b/src/server/authentication/AuthenticationManager.ts
index 3622be4c5..52d876e95 100644
--- a/src/server/authentication/AuthenticationManager.ts
+++ b/src/server/authentication/AuthenticationManager.ts
@@ -1,4 +1,4 @@
-import { default as User, DashUserModel } from './DashUserModel';
+import { default as User, DashUserModel, initializeGuest } from './DashUserModel';
 import { Request, Response, NextFunction } from 'express';
 import * as passport from 'passport';
 import { IVerifyOptions } from 'passport-local';
@@ -30,6 +30,7 @@ export let getSignup = (req: Request, res: Response) => {
  * Create a new local account.
  */
 export let postSignup = (req: Request, res: Response, next: NextFunction) => {
+    const email = req.body.email as String;
     req.assert('email', 'Email is not valid').isEmail();
     req.assert('password', 'Password must be at least 4 characters long').len({ min: 4 });
     req.assert('confirmPassword', 'Passwords do not match').equals(req.body.password);
@@ -41,15 +42,14 @@ export let postSignup = (req: Request, res: Response, next: NextFunction) => {
         return res.redirect('/signup');
     }
 
-    const email = req.body.email as String;
     const password = req.body.password;
 
     const model = {
         email,
         password,
-        userDocumentId: Utils.GenerateGuid(),
-        sharingDocumentId: Utils.GenerateGuid(),
-        linkDatabaseId: Utils.GenerateGuid(),
+        userDocumentId: email === 'guest' ? '__guest__' : Utils.GenerateGuid(),
+        sharingDocumentId: email === 'guest' ? 2 : Utils.GenerateGuid(),
+        linkDatabaseId: email === 'guest' ? 3 : Utils.GenerateGuid(),
         cacheDocumentIds: '',
     } as Partial<DashUserModel>;
 
@@ -106,18 +106,22 @@ export let getLogin = (req: Request, res: Response) => {
  * On failure, redirect to signup page
  */
 export let postLogin = (req: Request, res: Response, next: NextFunction) => {
-    req.assert('email', 'Email is not valid').isEmail();
-    req.assert('password', 'Password cannot be blank').notEmpty();
-    req.sanitize('email').normalizeEmail({ gmail_remove_dots: false });
-
-    const errors = req.validationErrors();
+    if (req.body.email === '') {
+        User.findOne({ email: 'guest' }, (err: any, user: DashUserModel) => !user && initializeGuest());
+        req.body.email = 'guest';
+        req.body.password = 'guest';
+    } else {
+        req.assert('email', 'Email is not valid').isEmail();
+        req.assert('password', 'Password cannot be blank').notEmpty();
+        req.sanitize('email').normalizeEmail({ gmail_remove_dots: false });
+    }
 
-    if (errors) {
+    if (req.validationErrors()) {
         req.flash('errors', 'Unable to login at this time. Please try again.');
         return res.redirect('/signup');
     }
 
-    passport.authenticate('local', (err: Error, user: DashUserModel, _info: IVerifyOptions) => {
+    const callback = (err: Error, user: DashUserModel, _info: IVerifyOptions) => {
         if (err) {
             next(err);
             return;
@@ -132,7 +136,8 @@ export let postLogin = (req: Request, res: Response, next: NextFunction) => {
             }
             tryRedirectToTarget(req, res);
         });
-    })(req, res, next);
+    };
+    setTimeout(() => passport.authenticate('local', callback)(req, res, next), 500);
 };
 
 /**
diff --git a/src/server/authentication/DashUserModel.ts b/src/server/authentication/DashUserModel.ts
index bee28b96d..a1883beab 100644
--- a/src/server/authentication/DashUserModel.ts
+++ b/src/server/authentication/DashUserModel.ts
@@ -1,13 +1,13 @@
 //@ts-ignore
-import * as bcrypt from "bcrypt-nodejs";
+import * as bcrypt from 'bcrypt-nodejs';
 //@ts-ignore
 import * as mongoose from 'mongoose';
 
 export type DashUserModel = mongoose.Document & {
-    email: String,
-    password: string,
-    passwordResetToken?: string,
-    passwordResetExpires?: Date,
+    email: String;
+    password: string;
+    passwordResetToken?: string;
+    passwordResetExpires?: Date;
 
     userDocumentId: string;
     sharingDocumentId: string;
@@ -15,66 +15,74 @@ export type DashUserModel = mongoose.Document & {
     cacheDocumentIds: string;
 
     profile: {
-        name: string,
-        gender: string,
-        location: string,
-        website: string,
-        picture: string
-    },
+        name: string;
+        gender: string;
+        location: string;
+        website: string;
+        picture: string;
+    };
 
-    comparePassword: comparePasswordFunction,
+    comparePassword: comparePasswordFunction;
 };
 
 type comparePasswordFunction = (candidatePassword: string, cb: (err: any, isMatch: any) => {}) => void;
 
 export type AuthToken = {
-    accessToken: string,
-    kind: string
+    accessToken: string;
+    kind: string;
 };
 
-const userSchema = new mongoose.Schema({
-    email: String,
-    password: String,
-    passwordResetToken: String,
-    passwordResetExpires: Date,
+const userSchema = new mongoose.Schema(
+    {
+        email: String,
+        password: String,
+        passwordResetToken: String,
+        passwordResetExpires: Date,
 
-    userDocumentId: String,    // id that identifies a document which hosts all of a user's account data
-    sharingDocumentId: String, // id that identifies a document that stores documents shared to a user, their user color, and any additional info needed to communicate between users
-    linkDatabaseId: String,
-    cacheDocumentIds: String, // set of document ids to retreive on startup
+        userDocumentId: String, // id that identifies a document which hosts all of a user's account data
+        sharingDocumentId: String, // id that identifies a document that stores documents shared to a user, their user color, and any additional info needed to communicate between users
+        linkDatabaseId: String,
+        cacheDocumentIds: String, // set of document ids to retreive on startup
 
-    facebook: String,
-    twitter: String,
-    google: String,
+        facebook: String,
+        twitter: String,
+        google: String,
 
-    profile: {
-        name: String,
-        gender: String,
-        location: String,
-        website: String,
-        picture: String
-    }
-}, { timestamps: true });
+        profile: {
+            name: String,
+            gender: String,
+            location: String,
+            website: String,
+            picture: String,
+        },
+    },
+    { timestamps: true }
+);
 
 /**
  * Password hash middleware.
  */
-userSchema.pre("save", function save(next) {
+userSchema.pre('save', function save(next) {
     const user = this as DashUserModel;
-    if (!user.isModified("password")) {
+    if (!user.isModified('password')) {
         return next();
     }
     bcrypt.genSalt(10, (err: any, salt: string) => {
         if (err) {
             return next(err);
         }
-        bcrypt.hash(user.password, salt, () => void {}, (err: mongoose.Error, hash: string) => {
-            if (err) {
-                return next(err);
+        bcrypt.hash(
+            user.password,
+            salt,
+            () => void {},
+            (err: mongoose.Error, hash: string) => {
+                if (err) {
+                    return next(err);
+                }
+                user.password = hash;
+                next();
             }
-            user.password = hash;
-            next();
-        });
+        );
     });
 });
 
@@ -88,5 +96,15 @@ const comparePassword: comparePasswordFunction = function (this: DashUserModel,
 
 userSchema.methods.comparePassword = comparePassword;
 
-const User = mongoose.model("User", userSchema);
-export default User;
\ No newline at end of file
+const User = mongoose.model('User', userSchema);
+export function initializeGuest() {
+    new User({
+        email: 'guest',
+        password: 'guest',
+        userDocumentId: '__guest__',
+        sharingDocumentId: '2',
+        linkDatabaseId: '3',
+        cacheDocumentIds: '',
+    }).save();
+}
+export default User;
diff --git a/src/server/index.ts b/src/server/index.ts
index f8c32103b..6e6bde3cb 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -1,35 +1,35 @@
 require('dotenv').config();
-import { yellow } from "colors";
+import { yellow } from 'colors';
 import * as mobileDetect from 'mobile-detect';
 import * as path from 'path';
 import * as qs from 'query-string';
-import { log_execution } from "./ActionUtilities";
-import DeleteManager from "./ApiManagers/DeleteManager";
+import { log_execution } from './ActionUtilities';
+import DeleteManager from './ApiManagers/DeleteManager';
 import DownloadManager from './ApiManagers/DownloadManager';
-import GeneralGoogleManager from "./ApiManagers/GeneralGoogleManager";
-import GooglePhotosManager from "./ApiManagers/GooglePhotosManager";
-import PDFManager from "./ApiManagers/PDFManager";
+import GeneralGoogleManager from './ApiManagers/GeneralGoogleManager';
+import GooglePhotosManager from './ApiManagers/GooglePhotosManager';
+import PDFManager from './ApiManagers/PDFManager';
 import { SearchManager } from './ApiManagers/SearchManager';
-import SessionManager from "./ApiManagers/SessionManager";
-import UploadManager from "./ApiManagers/UploadManager";
+import SessionManager from './ApiManagers/SessionManager';
+import UploadManager from './ApiManagers/UploadManager';
 import UserManager from './ApiManagers/UserManager';
 import UtilManager from './ApiManagers/UtilManager';
 import { GoogleCredentialsLoader, SSL } from './apis/google/CredentialsLoader';
-import { GoogleApiServerUtils } from "./apis/google/GoogleApiServerUtils";
-import { DashSessionAgent } from "./DashSession/DashSessionAgent";
-import { AppliedSessionAgent } from "./DashSession/Session/agents/applied_session_agent";
+import { GoogleApiServerUtils } from './apis/google/GoogleApiServerUtils';
+import { DashSessionAgent } from './DashSession/DashSessionAgent';
+import { AppliedSessionAgent } from './DashSession/Session/agents/applied_session_agent';
 import { DashUploadUtils } from './DashUploadUtils';
 import { Database } from './database';
-import { Logger } from "./ProcessFactory";
+import { Logger } from './ProcessFactory';
 import RouteManager, { Method, PublicHandler } from './RouteManager';
 import RouteSubscriber from './RouteSubscriber';
 import initializeServer, { resolvedPorts } from './server_Initialization';
 
 export const AdminPriviliges: Map<string, boolean> = new Map();
-export const onWindows = process.platform === "win32";
+export const onWindows = process.platform === 'win32';
 export let sessionAgent: AppliedSessionAgent;
-export const publicDirectory = path.resolve(__dirname, "public");
-export const filesDirectory = path.resolve(publicDirectory, "files");
+export const publicDirectory = path.resolve(__dirname, 'public');
+export const filesDirectory = path.resolve(publicDirectory, 'files');
 
 /**
  * These are the functions run before the server starts
@@ -43,11 +43,11 @@ async function preliminaryFunctions() {
     await GoogleCredentialsLoader.loadCredentials();
     SSL.loadCredentials();
     GoogleApiServerUtils.processProjectCredentials();
-    if (process.env.DB !== "MEM") {
+    if (process.env.DB !== 'MEM') {
         await log_execution({
-            startMessage: "attempting to initialize mongodb connection",
-            endMessage: "connection outcome determined",
-            action: Database.tryInitializeConnection
+            startMessage: 'attempting to initialize mongodb connection',
+            endMessage: 'connection outcome determined',
+            action: Database.tryInitializeConnection,
         });
     }
 }
@@ -56,27 +56,16 @@ async function preliminaryFunctions() {
  * Either clustered together as an API manager
  * or individually referenced below, by the completion
  * of this function's execution, all routes will
- * be registered on the server 
+ * be registered on the server
  * @param router the instance of the route manager
  * that will manage the registration of new routes
  * with the server
  */
 function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }: RouteManager) {
-    const managers = [
-        new SessionManager(),
-        new UserManager(),
-        new UploadManager(),
-        new DownloadManager(),
-        new SearchManager(),
-        new PDFManager(),
-        new DeleteManager(),
-        new UtilManager(),
-        new GeneralGoogleManager(),
-        new GooglePhotosManager(),
-    ];
+    const managers = [new SessionManager(), new UserManager(), new UploadManager(), new DownloadManager(), new SearchManager(), new PDFManager(), new DeleteManager(), new UtilManager(), new GeneralGoogleManager(), new GooglePhotosManager()];
 
     // initialize API Managers
-    console.log(yellow("\nregistering server routes..."));
+    console.log(yellow('\nregistering server routes...'));
     managers.forEach(manager => manager.register(addSupervisedRoute));
 
     /**
@@ -84,88 +73,87 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }:
      */
     addSupervisedRoute({
         method: Method.GET,
-        subscription: "/",
-        secureHandler: ({ res }) => res.redirect("/home")
+        subscription: '/',
+        secureHandler: ({ res }) => res.redirect('/home'),
     });
 
-
     addSupervisedRoute({
         method: Method.GET,
-        subscription: "/serverHeartbeat",
-        secureHandler: ({ res }) => res.send(true)
+        subscription: '/serverHeartbeat',
+        secureHandler: ({ res }) => res.send(true),
     });
 
     addSupervisedRoute({
         method: Method.GET,
-        subscription: "/resolvedPorts",
-        secureHandler: ({ res }) => res.send(resolvedPorts)
+        subscription: '/resolvedPorts',
+        secureHandler: ({ res }) => res.send(resolvedPorts),
+        publicHandler: ({ res }) => res.send(resolvedPorts),
     });
 
     const serve: PublicHandler = ({ req, res }) => {
-        const detector = new mobileDetect(req.headers['user-agent'] || "");
+        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));
     };
 
     /**
-     * Serves a simple password input box for any 
+     * Serves a simple password input box for any
      */
     addSupervisedRoute({
         method: Method.GET,
-        subscription: new RouteSubscriber("admin").add("previous_target"),
+        subscription: new RouteSubscriber('admin').add('previous_target'),
         secureHandler: ({ res, isRelease }) => {
             const { PASSWORD } = process.env;
             if (!(isRelease && PASSWORD)) {
-                return res.redirect("/home");
+                return res.redirect('/home');
             }
-            res.render("admin.pug", { title: "Enter Administrator Password" });
-        }
+            res.render('admin.pug', { title: 'Enter Administrator Password' });
+        },
     });
 
     addSupervisedRoute({
         method: Method.POST,
-        subscription: new RouteSubscriber("admin").add("previous_target"),
+        subscription: new RouteSubscriber('admin').add('previous_target'),
         secureHandler: async ({ req, res, isRelease, user: { id } }) => {
             const { PASSWORD } = process.env;
             if (!(isRelease && PASSWORD)) {
-                return res.redirect("/home");
+                return res.redirect('/home');
             }
             const { password } = req.body;
             const { previous_target } = req.params;
             let redirect: string;
             if (password === PASSWORD) {
                 AdminPriviliges.set(id, true);
-                redirect = `/${previous_target.replace(":", "/")}`;
+                redirect = `/${previous_target.replace(':', '/')}`;
             } else {
                 redirect = `/admin/${previous_target}`;
             }
             res.redirect(redirect);
-        }
+        },
     });
 
     addSupervisedRoute({
         method: Method.GET,
-        subscription: ["/home", new RouteSubscriber("doc").add("docId")],
+        subscription: ['/home', new RouteSubscriber('doc').add('docId')],
         secureHandler: serve,
         publicHandler: ({ req, res, ...remaining }) => {
             const { originalUrl: target } = req;
-            const sharing = qs.parse(qs.extract(req.originalUrl), { sort: false }).sharing === "true";
-            const docAccess = target.startsWith("/doc/");
+            const sharing = qs.parse(qs.extract(req.originalUrl), { sort: false }).sharing === 'true';
+            const docAccess = target.startsWith('/doc/');
             // since this is the public handler, there's no meaning of '/home' to speak of
             // since there's no user logged in, so the only viable operation
             // for a guest is to look at a shared document
-            if (sharing && docAccess) {
+            if (docAccess) {
                 serve({ req, res, ...remaining });
             } else {
-                res.redirect("/login");
+                res.redirect('/login');
             }
-        }
+        },
     });
 
     logRegistrationOutcome();
 }
 
-
 /**
  * This function can be used in two different ways. If not in release mode,
  * this is simply the logic that is invoked to start the server. In release mode,
@@ -174,9 +162,9 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }:
  */
 export async function launchServer() {
     await log_execution({
-        startMessage: "\nstarting execution of preliminary functions",
-        endMessage: "completed preliminary functions\n",
-        action: preliminaryFunctions
+        startMessage: '\nstarting execution of preliminary functions',
+        endMessage: 'completed preliminary functions\n',
+        action: preliminaryFunctions,
     });
     await initializeServer(routeSetter);
 }
diff --git a/src/server/websocket.ts b/src/server/websocket.ts
index 1b7f5919f..9b91a35a6 100644
--- a/src/server/websocket.ts
+++ b/src/server/websocket.ts
@@ -1,24 +1,24 @@
-import * as express from "express";
-import { blue, green } from "colors";
-import { createServer, Server } from "https";
-import { networkInterfaces } from "os";
+import { blue } from 'colors';
+import * as express from 'express';
+import { createServer, Server } from 'https';
+import { networkInterfaces } from 'os';
 import * as sio from 'socket.io';
-import { Socket } from "socket.io";
-import { Utils } from "../Utils";
+import { Socket } from 'socket.io';
+import { Opt } from '../fields/Doc';
+import { Utils } from '../Utils';
 import { logPort } from './ActionUtilities';
-import { timeMap } from "./ApiManagers/UserManager";
-import { GoogleCredentialsLoader, SSL } from "./apis/google/CredentialsLoader";
-import YoutubeApi from "./apis/youtube/youtubeApiSample";
-import { Client } from "./Client";
-import { Database } from "./database";
-import { DocumentsCollection } from "./IDatabase";
-import { Diff, GestureContent, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, Transferable, Types, UpdateMobileInkOverlayPositionContent, YoutubeQueryInput, YoutubeQueryTypes } from "./Message";
-import { Search } from "./Search";
+import { timeMap } from './ApiManagers/UserManager';
+import { GoogleCredentialsLoader, SSL } from './apis/google/CredentialsLoader';
+import YoutubeApi from './apis/youtube/youtubeApiSample';
+import { initializeGuest } from './authentication/DashUserModel';
+import { Client } from './Client';
+import { Database } from './database';
+import { DocumentsCollection } from './IDatabase';
+import { Diff, GestureContent, MessageStore, MobileDocumentUploadContent, MobileInkOverlayContent, Transferable, Types, UpdateMobileInkOverlayPositionContent, YoutubeQueryInput, YoutubeQueryTypes } from './Message';
+import { Search } from './Search';
 import { resolvedPorts } from './server_Initialization';
-import { Opt } from "../fields/Doc";
 
 export namespace WebSocket {
-
     export let _socket: Socket;
     const clients: { [key: string]: Client } = {};
     export const socketMap = new Map<SocketIO.Socket, string>();
@@ -32,14 +32,14 @@ export namespace WebSocket {
                 resolvedPorts.socket = Number(socketPort);
             }
             let socketEndpoint: Opt<Server>;
-            await new Promise<void>(resolve => socketEndpoint = createServer(SSL.Credentials, app).listen(resolvedPorts.socket, resolve));
+            await new Promise<void>(resolve => (socketEndpoint = createServer(SSL.Credentials, app).listen(resolvedPorts.socket, resolve)));
             io = sio(socketEndpoint!, SSL.Credentials as any);
         } else {
             io = sio().listen(resolvedPorts.socket);
         }
-        logPort("websocket", resolvedPorts.socket);
+        logPort('websocket', resolvedPorts.socket);
 
-        io.on("connection", function (socket: Socket) {
+        io.on('connection', function (socket: Socket) {
             _socket = socket;
             socket.use((_packet, next) => {
                 const userEmail = socketMap.get(socket);
@@ -70,14 +70,14 @@ export namespace WebSocket {
                     socket.join(room);
                     console.log('Client ID ' + socket.id + ' created room ' + room);
                     socket.emit('created', room, socket.id);
-
                 } else if (numClients === 1) {
                     console.log('Client ID ' + socket.id + ' joined room ' + room);
                     socket.in(room).emit('join', room);
                     socket.join(room);
                     socket.emit('joined', room, socket.id);
                     socket.in(room).emit('ready');
-                } else { // max two clients
+                } else {
+                    // max two clients
                     socket.emit('full', room);
                 }
             });
@@ -97,10 +97,10 @@ export namespace WebSocket {
                 console.log('received bye');
             });
 
-            Utils.Emit(socket, MessageStore.Foo, "handshooken");
+            Utils.Emit(socket, MessageStore.Foo, 'handshooken');
 
             Utils.AddServerHandler(socket, MessageStore.Bar, guid => barReceived(socket, guid));
-            Utils.AddServerHandler(socket, MessageStore.SetField, (args) => setField(socket, args));
+            Utils.AddServerHandler(socket, MessageStore.SetField, args => setField(socket, args));
             Utils.AddServerHandlerCallback(socket, MessageStore.GetField, getField);
             Utils.AddServerHandlerCallback(socket, MessageStore.GetFields, getFields);
             if (isRelease) {
@@ -126,26 +126,26 @@ export namespace WebSocket {
              */
 
             disconnect = () => {
-                socket.broadcast.emit("connection_terminated", Date.now());
+                socket.broadcast.emit('connection_terminated', Date.now());
                 socket.disconnect(true);
             };
         });
     }
 
     function processGesturePoints(socket: Socket, content: GestureContent) {
-        socket.broadcast.emit("receiveGesturePoints", content);
+        socket.broadcast.emit('receiveGesturePoints', content);
     }
 
     function processOverlayTrigger(socket: Socket, content: MobileInkOverlayContent) {
-        socket.broadcast.emit("receiveOverlayTrigger", content);
+        socket.broadcast.emit('receiveOverlayTrigger', content);
     }
 
     function processUpdateOverlayPosition(socket: Socket, content: UpdateMobileInkOverlayPositionContent) {
-        socket.broadcast.emit("receiveUpdateOverlayPosition", content);
+        socket.broadcast.emit('receiveUpdateOverlayPosition', content);
     }
 
     function processMobileDocumentUpload(socket: Socket, content: MobileDocumentUploadContent) {
-        socket.broadcast.emit("receiveMobileDocumentUpload", content);
+        socket.broadcast.emit('receiveMobileDocumentUpload', content);
     }
 
     function HandleYoutubeQuery([query, callback]: [YoutubeQueryInput, (result?: any[]) => void]) {
@@ -165,27 +165,22 @@ export namespace WebSocket {
         const target: string[] = [];
         onlyFields && target.push(DocumentsCollection);
         await Database.Instance.dropSchema(...target);
-        if (process.env.DISABLE_SEARCH !== "true") {
+        if (process.env.DISABLE_SEARCH !== 'true') {
             await Search.clear();
         }
+        initializeGuest();
     }
 
     function barReceived(socket: SocketIO.Socket, userEmail: string) {
         clients[userEmail] = new Client(userEmail.toString());
         const currentdate = new Date();
-        const datetime = currentdate.getDate() + "/"
-            + (currentdate.getMonth() + 1) + "/"
-            + currentdate.getFullYear() + " @ "
-            + currentdate.getHours() + ":"
-            + currentdate.getMinutes() + ":"
-            + currentdate.getSeconds();
+        const datetime = currentdate.getDate() + '/' + (currentdate.getMonth() + 1) + '/' + currentdate.getFullYear() + ' @ ' + currentdate.getHours() + ':' + currentdate.getMinutes() + ':' + currentdate.getSeconds();
         console.log(blue(`user ${userEmail} has connected to the web socket at: ${datetime}`));
-        socketMap.set(socket, userEmail + " at " + datetime);
+        socketMap.set(socket, userEmail + ' at ' + datetime);
     }
 
     function getField([id, callback]: [string, (result?: Transferable) => void]) {
-        Database.Instance.getDocument(id, (result?: Transferable) =>
-            callback(result ? result : undefined));
+        Database.Instance.getDocument(id, (result?: Transferable) => callback(result ? result : undefined));
     }
 
     function getFields([ids, callback]: [string[], (result: Transferable[]) => void]) {
@@ -193,9 +188,9 @@ export namespace WebSocket {
     }
 
     function setField(socket: Socket, newValue: Transferable) {
-        Database.Instance.update(newValue.id, newValue, () =>
-            socket.broadcast.emit(MessageStore.SetField.Message, newValue));  // broadcast set value to all other clients
-        if (newValue.type === Types.Text) {  // if the newValue has sring type, then it's suitable for searching -- pass it to SOLR
+        Database.Instance.update(newValue.id, newValue, () => socket.broadcast.emit(MessageStore.SetField.Message, newValue)); // broadcast set value to all other clients
+        if (newValue.type === Types.Text) {
+            // if the newValue has sring type, then it's suitable for searching -- pass it to SOLR
             Search.updateDocument({ id: newValue.id, data: { set: (newValue as any).data } });
         }
     }
@@ -213,34 +208,36 @@ export namespace WebSocket {
         Database.Instance.getDocuments(ids, callback);
     }
 
-    const suffixMap: { [type: string]: (string | [string, string | ((json: any) => any)]) } = {
-        "number": "_n",
-        "string": "_t",
-        "boolean": "_b",
-        "image": ["_t", "url"],
-        "video": ["_t", "url"],
-        "pdf": ["_t", "url"],
-        "audio": ["_t", "url"],
-        "web": ["_t", "url"],
-        "map": ["_t", "url"],
-        "script": ["_t", value => value.script.originalScript],
-        "RichTextField": ["_t", value => value.Text],
-        "date": ["_d", value => new Date(value.date).toISOString()],
-        "proxy": ["_i", "fieldId"],
-        "list": ["_l", list => {
-            const results = [];
-            for (const value of list.fields) {
-                const term = ToSearchTerm(value);
-                if (term) {
-                    results.push(term.value);
+    const suffixMap: { [type: string]: string | [string, string | ((json: any) => any)] } = {
+        number: '_n',
+        string: '_t',
+        boolean: '_b',
+        image: ['_t', 'url'],
+        video: ['_t', 'url'],
+        pdf: ['_t', 'url'],
+        audio: ['_t', 'url'],
+        web: ['_t', 'url'],
+        map: ['_t', 'url'],
+        script: ['_t', value => value.script.originalScript],
+        RichTextField: ['_t', value => value.Text],
+        date: ['_d', value => new Date(value.date).toISOString()],
+        proxy: ['_i', 'fieldId'],
+        list: [
+            '_l',
+            list => {
+                const results = [];
+                for (const value of list.fields) {
+                    const term = ToSearchTerm(value);
+                    if (term) {
+                        results.push(term.value);
+                    }
                 }
-            }
-            return results.length ? results : null;
-        }]
+                return results.length ? results : null;
+            },
+        ],
     };
 
-    function ToSearchTerm(val: any): { suffix: string, value: any } | undefined {
-
+    function ToSearchTerm(val: any): { suffix: string; value: any } | undefined {
         if (val === null || val === undefined) {
             return;
         }
@@ -252,69 +249,79 @@ export namespace WebSocket {
         }
         if (Array.isArray(suffix)) {
             const accessor = suffix[1];
-            if (typeof accessor === "function") {
+            if (typeof accessor === 'function') {
                 val = accessor(val);
             } else {
                 val = val[accessor];
-
             }
             suffix = suffix[0];
-
         }
         return { suffix, value: val };
     }
 
     function getSuffix(value: string | [string, any]): string {
-        return typeof value === "string" ? value : value[0];
+        return typeof value === 'string' ? value : value[0];
     }
 
     function addToListField(socket: Socket, diff: Diff, curListItems?: Transferable): void {
-        diff.diff.$set = diff.diff.$addToSet; delete diff.diff.$addToSet;// convert add to set to a query of the current fields, and then a set of the composition of the new fields with the old ones
+        diff.diff.$set = diff.diff.$addToSet;
+        delete diff.diff.$addToSet; // convert add to set to a query of the current fields, and then a set of the composition of the new fields with the old ones
         const updatefield = Array.from(Object.keys(diff.diff.$set))[0];
         const newListItems = diff.diff.$set[updatefield]?.fields;
         if (!newListItems) {
-            console.log("Error: addToListField - no new list items");
+            console.log('Error: addToListField - no new list items');
             return;
         }
-        const curList = (curListItems as any)?.fields?.[updatefield.replace("fields.", "")]?.fields.filter((item: any) => item !== undefined) || [];
-        diff.diff.$set[updatefield].fields = [...curList, ...newListItems];//, ...newListItems.filter((newItem: any) => newItem === null || !curList.some((curItem: any) => curItem.fieldId ? curItem.fieldId === newItem.fieldId : curItem.heading ? curItem.heading === newItem.heading : curItem === newItem))];
+        const curList = (curListItems as any)?.fields?.[updatefield.replace('fields.', '')]?.fields.filter((item: any) => item !== undefined) || [];
+        diff.diff.$set[updatefield].fields = [...curList, ...newListItems]; //, ...newListItems.filter((newItem: any) => newItem === null || !curList.some((curItem: any) => curItem.fieldId ? curItem.fieldId === newItem.fieldId : curItem.heading ? curItem.heading === newItem.heading : curItem === newItem))];
         const sendBack = diff.diff.length !== diff.diff.$set[updatefield].fields.length;
         delete diff.diff.length;
-        Database.Instance.update(diff.id, diff.diff,
+        Database.Instance.update(
+            diff.id,
+            diff.diff,
             () => {
                 if (sendBack) {
-                    console.log("Warning: list modified during update. Composite list is being returned.");
+                    console.log('Warning: list modified during update. Composite list is being returned.');
                     const id = socket.id;
-                    socket.id = "";
+                    socket.id = '';
                     socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
                     socket.id = id;
                 } else socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
                 dispatchNextOp(diff.id);
-            }, false);
+            },
+            false
+        );
     }
 
     function remFromListField(socket: Socket, diff: Diff, curListItems?: Transferable): void {
-        diff.diff.$set = diff.diff.$remFromSet; delete diff.diff.$remFromSet;
+        diff.diff.$set = diff.diff.$remFromSet;
+        delete diff.diff.$remFromSet;
         const updatefield = Array.from(Object.keys(diff.diff.$set))[0];
         const remListItems = diff.diff.$set[updatefield].fields;
-        const curList = (curListItems as any)?.fields?.[updatefield.replace("fields.", "")]?.fields.filter((f: any) => f !== null) || [];
-        diff.diff.$set[updatefield].fields = curList?.filter((curItem: any) => !remListItems.some((remItem: any) => remItem.fieldId ? remItem.fieldId === curItem.fieldId : remItem.heading ? remItem.heading === curItem.heading : remItem === curItem));
+        const curList = (curListItems as any)?.fields?.[updatefield.replace('fields.', '')]?.fields.filter((f: any) => f !== null) || [];
+        diff.diff.$set[updatefield].fields = curList?.filter(
+            (curItem: any) => !remListItems.some((remItem: any) => (remItem.fieldId ? remItem.fieldId === curItem.fieldId : remItem.heading ? remItem.heading === curItem.heading : remItem === curItem))
+        );
         const sendBack = diff.diff.length !== diff.diff.$set[updatefield].fields.length;
         delete diff.diff.length;
-        Database.Instance.update(diff.id, diff.diff,
+        Database.Instance.update(
+            diff.id,
+            diff.diff,
             () => {
                 if (sendBack) {
-                    console.log("SEND BACK");
+                    console.log('SEND BACK');
                     const id = socket.id;
-                    socket.id = "";
+                    socket.id = '';
                     socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
                     socket.id = id;
                 } else socket.broadcast.emit(MessageStore.UpdateField.Message, diff);
                 dispatchNextOp(diff.id);
-            }, false);
+            },
+            false
+        );
     }
 
-    const pendingOps = new Map<string, { diff: Diff, socket: Socket }[]>();
+    const pendingOps = new Map<string, { diff: Diff; socket: Socket }[]>();
 
     function dispatchNextOp(id: string) {
         const next = pendingOps.get(id)!.shift();
@@ -341,7 +348,7 @@ export namespace WebSocket {
     function UpdateField(socket: Socket, diff: Diff) {
         if (CurUser !== socketMap.get(socket)) {
             CurUser = socketMap.get(socket);
-            console.log("Switch User: " + CurUser);
+            console.log('Switch User: ' + CurUser);
         }
         if (pendingOps.has(diff.id)) {
             pendingOps.get(diff.id)!.push({ diff, socket });
@@ -357,24 +364,25 @@ export namespace WebSocket {
         return GetRefFieldLocal([diff.id, (result?: Transferable) => SetField(socket, diff, result)]);
     }
     function SetField(socket: Socket, diff: Diff, curListItems?: Transferable) {
-        Database.Instance.update(diff.id, diff.diff,
-            () => socket.broadcast.emit(MessageStore.UpdateField.Message, diff), false);
+        Database.Instance.update(diff.id, diff.diff, () => socket.broadcast.emit(MessageStore.UpdateField.Message, diff), false);
         const docfield = diff.diff.$set || diff.diff.$unset;
         if (docfield) {
             const update: any = { id: diff.id };
             let dynfield = false;
             for (let key in docfield) {
-                if (!key.startsWith("fields.")) continue;
+                if (!key.startsWith('fields.')) continue;
                 dynfield = true;
                 const val = docfield[key];
                 key = key.substring(7);
-                Object.values(suffixMap).forEach(suf => { update[key + getSuffix(suf)] = { set: null }; });
+                Object.values(suffixMap).forEach(suf => {
+                    update[key + getSuffix(suf)] = { set: null };
+                });
                 const term = ToSearchTerm(val);
                 if (term !== undefined) {
                     const { suffix, value } = term;
                     update[key + suffix] = { set: value };
                     if (key.endsWith('lastModified')) {
-                        update["lastModified" + suffix] = value;
+                        update['lastModified' + suffix] = value;
                     }
                 }
             }
@@ -403,6 +411,4 @@ export namespace WebSocket {
     function CreateField(newValue: any) {
         Database.Instance.insert(newValue);
     }
-
 }
-
diff --git a/views/login.pug b/views/login.pug
index 98816e9c8..71c6933be 100644
--- a/views/login.pug
+++ b/views/login.pug
@@ -15,10 +15,10 @@ block content
                 h3.auth_header Log In
                 .form-group
                     .col-sm-7
-                    input.form-control(type='email', name='email', id='email', placeholder='Email', autofocus, required)
+                    input.form-control(type='email', name='email', id='email', placeholder='Email (or blank for "guest")', autofocus)
                 .form-group
                     .col-sm-7
-                    input.form-control(type='password', name='password', id='password', placeholder='Password', required)
+                    input.form-control(type='password', name='password', id='password', placeholder='Password')
                 .form-group
                     .col-sm-offset-3.col-sm-7
                     button.btn.btn-success(id='submit', type='submit')
-- 
cgit v1.2.3-70-g09d2


From dc0ecb47cd4e63e4861b79c00fe131de4450f46b Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 26 Jul 2022 09:27:12 -0400
Subject: turned off resetriting text box resizing to max of scroll height. 
 fixed placement/behavior of sidebar anno button for text.

---
 src/client/views/DocumentDecorations.tsx           |  2 +-
 src/client/views/MainView.tsx                      |  1 +
 src/client/views/SidebarAnnos.tsx                  |  8 +++---
 src/client/views/nodes/MapBox/MapBox.tsx           |  2 +-
 src/client/views/nodes/PDFBox.tsx                  | 30 ++++++++++----------
 src/client/views/nodes/WebBox.tsx                  | 32 ++++++++++++----------
 .../views/nodes/formattedText/FormattedTextBox.tsx |  8 ++++--
 7 files changed, 45 insertions(+), 38 deletions(-)

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

diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 922f037f4..432dd360a 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -521,7 +521,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P
                             } else doc._height = actualdH;
                         }
                     } else {
-                        const maxHeight = Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '-scrollHeight']))) * docView.NativeDimScaling();
+                        const maxHeight = 0; //Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '-scrollHeight']))) * docView.NativeDimScaling();
                         dH && (doc._height = actualdH > maxHeight && maxHeight ? maxHeight : actualdH);
                         dW && (doc._width = actualdW);
                         dH && (doc._autoHeight = false);
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 7e032af5e..a85460137 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -159,6 +159,7 @@ export class MainView extends React.Component {
                 'viewTransition',
                 'panX',
                 'panY',
+                'fitWidth',
                 'nativeWidth',
                 'nativeHeight',
                 'text-scrollHeight',
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 9fdf9d2be..1c14c7cd5 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -101,7 +101,7 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
         !this.props.showSidebar
             ? 0
             : this.props.layoutDoc.type === DocumentType.RTF || this.props.layoutDoc.type === DocumentType.MAP
-            ? this.props.PanelWidth()
+            ? this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1)
             : ((NumCast(this.props.nativeWidth) - Doc.NativeWidth(this.props.dataDoc)) * this.props.PanelWidth()) / NumCast(this.props.nativeWidth);
     panelHeight = () => this.props.PanelHeight() - this.filtersHeight();
     addDocument = (doc: Doc | Doc[]) => this.props.sidebarAddDocument(doc, this.sidebarKey);
@@ -144,14 +144,14 @@ export class SidebarAnnos extends React.Component<FieldViewProps & ExtraProps> {
                     top: this.props.rootDoc.type !== DocumentType.RTF && StrCast(this.props.rootDoc._showTitle) === 'title' ? 15 : 0,
                     right: 0,
                     background: this.props.styleProvider?.(this.props.rootDoc, this.props, StyleProp.WidgetColor),
-                    width: `${this.panelWidth()}px`,
+                    width: `100%`,
                     height: '100%',
                 }}>
-                <div className="sidebarAnnos-tagList" style={{ height: this.filtersHeight(), width: this.panelWidth() }} onWheel={e => e.stopPropagation()}>
+                <div className="sidebarAnnos-tagList" style={{ height: this.filtersHeight() }} onWheel={e => e.stopPropagation()}>
                     {this.allUsers.map(renderUsers)}
                     {this.allMetadata.map(renderMeta)}
                 </div>
-                <div style={{ width: '100%', height: this.panelHeight(), position: 'relative' }}>
+                <div style={{ width: '100%', height: `calc(100% - 38px)`, position: 'relative' }}>
                     <CollectionStackingView
                         {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
                         ref={this._stackRef}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index abd64908f..6479e933e 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -643,7 +643,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     )}
                 </div>
                 {/* </LoadScript > */}
-                <div className="mapBox-sidebar" style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
+                <div className="mapBox-sidebar" style={{ position: 'absolute', right: 0, height: '100%', width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
                     <SidebarAnnos
                         ref={this._sidebarRef}
                         {...this.props}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 42df2ac5d..345407c2f 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -479,20 +479,22 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                         crop={this.crop}
                     />
                 </div>
-                <SidebarAnnos
-                    ref={this._sidebarRef}
-                    {...this.props}
-                    rootDoc={this.rootDoc}
-                    layoutDoc={this.layoutDoc}
-                    dataDoc={this.dataDoc}
-                    setHeight={emptyFunction}
-                    nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth)}
-                    showSidebar={this.SidebarShown}
-                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                    sidebarAddDocument={this.sidebarAddDocument}
-                    moveDocument={this.moveDocument}
-                    removeDocument={this.removeDocument}
-                />
+                <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this.layoutDoc[WidthSym]()}%` }}>
+                    <SidebarAnnos
+                        ref={this._sidebarRef}
+                        {...this.props}
+                        rootDoc={this.rootDoc}
+                        layoutDoc={this.layoutDoc}
+                        dataDoc={this.dataDoc}
+                        setHeight={emptyFunction}
+                        nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth)}
+                        showSidebar={this.SidebarShown}
+                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                        sidebarAddDocument={this.sidebarAddDocument}
+                        moveDocument={this.moveDocument}
+                        removeDocument={this.removeDocument}
+                    />
+                </div>
                 {this.settingsPanel()}
             </div>
         );
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 55228c19f..dd14822af 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -956,21 +956,23 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
                     }}
                     onPointerDown={e => this.sidebarBtnDown(e, false)}
                 />
-                <SidebarAnnos
-                    ref={this._sidebarRef}
-                    {...this.props}
-                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
-                    fieldKey={this.fieldKey + '-' + this._urlHash}
-                    rootDoc={this.rootDoc}
-                    layoutDoc={this.layoutDoc}
-                    dataDoc={this.dataDoc}
-                    setHeight={emptyFunction}
-                    nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth) - WebBox.sidebarResizerWidth / (this.props.NativeDimScaling?.() || 1)}
-                    showSidebar={this.SidebarShown}
-                    sidebarAddDocument={this.sidebarAddDocument}
-                    moveDocument={this.moveDocument}
-                    removeDocument={this.removeDocument}
-                />
+                <div style={{ position: 'absolute', height: '100%', right: 0, top: 0, width: `calc(100 * ${this.sidebarWidth() / this.layoutDoc[WidthSym]()}%` }}>
+                    <SidebarAnnos
+                        ref={this._sidebarRef}
+                        {...this.props}
+                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
+                        fieldKey={this.fieldKey + '-' + this._urlHash}
+                        rootDoc={this.rootDoc}
+                        layoutDoc={this.layoutDoc}
+                        dataDoc={this.dataDoc}
+                        setHeight={emptyFunction}
+                        nativeWidth={this._previewNativeWidth ?? NumCast(this.layoutDoc._nativeWidth) - WebBox.sidebarResizerWidth / (this.props.NativeDimScaling?.() || 1)}
+                        showSidebar={this.SidebarShown}
+                        sidebarAddDocument={this.sidebarAddDocument}
+                        moveDocument={this.moveDocument}
+                        removeDocument={this.removeDocument}
+                    />
+                </div>
                 {this.sidebarHandle}
                 {!this.props.isContentActive() ? null : this.searchUI}
             </div>
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index ead086aca..fe121471a 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -124,6 +124,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
         return DocListCast(this.dataDoc[this.SidebarKey]);
     }
 
+    @computed get noSidebar() {
+        return this.props.docViewPath?.()[this.props.docViewPath().length - 2]?.rootDoc.type === DocumentType.RTF || this.props.noSidebar || this.Document._noSidebar;
+    }
     @computed get sidebarWidthPercent() {
         return this._showSidebar ? '20%' : StrCast(this.layoutDoc._sidebarWidthPercent, '0%');
     }
@@ -1752,7 +1755,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                     rootDoc={this.rootDoc}
                     layoutDoc={this.layoutDoc}
                     dataDoc={this.dataDoc}
-                    // usePanelWidth={true}
                     nativeWidth={NumCast(this.layoutDoc._nativeWidth)}
                     whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                     showSidebar={this.SidebarShown}
@@ -1866,8 +1868,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
                             }}
                         />
                     </div>
-                    {this.props.noSidebar || this.Document._noSidebar || this.props.dontSelectOnLoad || !this.SidebarShown || this.sidebarWidthPercent === '0%' ? null : this.sidebarCollection}
-                    {this.props.noSidebar || this.Document._noSidebar || this.props.dontSelectOnLoad || this.Document._singleLine ? null : this.sidebarHandle}
+                    {this.noSidebar || this.props.dontSelectOnLoad || !this.SidebarShown || this.sidebarWidthPercent === '0%' ? null : this.sidebarCollection}
+                    {this.noSidebar || this.Document._noSidebar || this.props.dontSelectOnLoad || this.Document._singleLine ? null : this.sidebarHandle}
                     {!this.layoutDoc._showAudio ? null : this.audioHandle}
                 </div>
             </div>
-- 
cgit v1.2.3-70-g09d2


From 2f9ed7522d0615dfc27b7382be8b66eb341722da Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 26 Jul 2022 11:16:39 -0400
Subject: made width a playground field so shared views can open annotation
 pane.  made title shrink for annotation button only when title doesn't add a
 margin.  Fixed closing annotation view in lightbox not to make docuent shrink
 to minimum size by using percents, not pixels.

---
 src/client/views/MainView.tsx                             | 1 +
 src/client/views/nodes/DocumentView.tsx                   | 2 +-
 src/client/views/nodes/formattedText/FormattedTextBox.tsx | 4 ++--
 3 files changed, 4 insertions(+), 3 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index a85460137..4dc8a2e4a 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -171,6 +171,7 @@ export class MainView extends React.Component {
                 'curPage',
                 'viewType',
                 'chromeHidden',
+                'width',
                 'nativeWidth',
             ]); // can play with these fields on someone else's
         }
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 7032abba2..2ad8b8af0 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -1229,7 +1229,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
                 style={{
                     position: this.headerMargin ? 'relative' : 'absolute',
                     height: this.titleHeight,
-                    width: this.isContentActive() ? `calc(100% - 18px)` : '100%', // leave room for annotation button
+                    width: !this.headerMargin ? `calc(100% - 18px)` : '100%', // leave room for annotation button
                     color: lightOrDark(background),
                     background,
                     pointerEvents: this.onClickHandler || this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined,
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index fe121471a..bbb7ddc85 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -637,11 +637,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps
 
     @action
     toggleSidebar = (preview: boolean = false) => {
-        const prevWidth = this.sidebarWidth();
+        const prevWidth = 1 - this.sidebarWidth() / Number(getComputedStyle(this._ref.current!).width.replace('px', ''));
         if (preview) this._showSidebar = true;
         else this.layoutDoc._showSidebar = (this.layoutDoc._sidebarWidthPercent = StrCast(this.layoutDoc._sidebarWidthPercent, '0%') === '0%' ? '50%' : '0%') !== '0%';
 
-        this.layoutDoc._width = !preview && this.SidebarShown ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
+        this.layoutDoc._width = !preview && this.SidebarShown ? NumCast(this.layoutDoc._width) * 2 : Math.max(20, NumCast(this.layoutDoc._width) * prevWidth);
     };
     sidebarDown = (e: React.PointerEvent) => {
         const batch = UndoManager.StartBatch('sidebar');
-- 
cgit v1.2.3-70-g09d2


From 5f8248acbc5b83a4e1571779913f9ed5700382de Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 26 Jul 2022 15:54:23 -0400
Subject: fixed clicking linkDocPreview to follow links.  switched target drag
 behavior from title to preview

---
 src/client/views/MainView.tsx              |  2 +-
 src/client/views/nodes/LinkDocPreview.scss | 18 +++++++----
 src/client/views/nodes/LinkDocPreview.tsx  | 52 +++++++++++++++++++++---------
 3 files changed, 49 insertions(+), 23 deletions(-)

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

diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 4dc8a2e4a..e96f65548 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -205,7 +205,7 @@ export class MainView extends React.Component {
     componentWillUnMount() {
         window.removeEventListener('keyup', KeyManager.Instance.unhandle);
         window.removeEventListener('keydown', KeyManager.Instance.handle);
-        window.removeEventListener('pointerdown', this.globalPointerDown);
+        window.removeEventListener('pointerdown', this.globalPointerDown, true);
         window.removeEventListener('paste', KeyManager.Instance.paste as any);
         document.removeEventListener('linkAnnotationToDash', Hypothesis.linkListener);
     }
diff --git a/src/client/views/nodes/LinkDocPreview.scss b/src/client/views/nodes/LinkDocPreview.scss
index 3febbcecb..c68e55f73 100644
--- a/src/client/views/nodes/LinkDocPreview.scss
+++ b/src/client/views/nodes/LinkDocPreview.scss
@@ -8,6 +8,7 @@
     border-bottom: 8px solid white;
     border-right: 8px solid white;
     z-index: 2004;
+    cursor: pointer;
 
     .linkDocPreview-inner {
         background-color: white;
@@ -63,13 +64,16 @@
                     cursor: pointer;
                 }
             }
+        }
 
-            .linkDocPreview-preview-wrapper {
-                overflow: hidden;
-                align-content: center;
-                justify-content: center;
-                background-color: rgb(160, 160, 160);
-            }
+        .linkDocPreview-preview-wrapper {
+            overflow: hidden;
+            align-content: center;
+            justify-content: center;
+            pointer-events: all;
+            background-color: rgb(160, 160, 160);
+            overflow: auto;
+            cursor: grab;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index d8b943ae4..a12564839 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -5,7 +5,7 @@ import { observer } from 'mobx-react';
 import wiki from 'wikijs';
 import { Doc, DocCastAsync, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc';
 import { Cast, NumCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, setupMoveUpEvents } from '../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnNone, setupMoveUpEvents } from '../../../Utils';
 import { DocServer } from '../../DocServer';
 import { Docs, DocUtils } from '../../documents/Documents';
 import { DocumentType } from '../../documents/DocumentTypes';
@@ -36,6 +36,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
     }
 
     _infoRef = React.createRef<HTMLDivElement>();
+    _linkDocRef = React.createRef<HTMLDivElement>();
     @observable public static LinkInfo: Opt<LinkDocPreviewProps>;
     @observable _targetDoc: Opt<Doc>;
     @observable _linkDoc: Opt<Doc>;
@@ -74,7 +75,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
     }
 
     onPointerDown = (e: PointerEvent) => {
-        !this._infoRef.current?.contains(e.target as any) && LinkDocPreview.Clear(); // close preview when not clicking anywhere other than the info bar of the preview
+        !this._linkDocRef.current?.contains(e.target as any) && LinkDocPreview.Clear(); // close preview when not clicking anywhere other than the info bar of the preview
     };
 
     @computed get href() {
@@ -143,7 +144,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
         );
     };
 
-    followLink = (e: React.PointerEvent) => {
+    followLink = () => {
         if (this._linkDoc && this._linkSrc) {
             LinkDocPreview.Clear();
             LinkFollower.FollowLink(this._linkDoc, this._linkSrc, this.props.docProps, false);
@@ -151,6 +152,9 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
             this.props.docProps?.addDocTab(Docs.Create.WebDocument(this.props.hrefs[0], { title: this.props.hrefs[0], _nativeWidth: 850, _width: 200, _height: 400, useCors: true }), 'add:right');
         }
     };
+
+    followLinkPointerDown = (e: React.PointerEvent) => setupMoveUpEvents(this, e, returnFalse, emptyFunction, this.followLink);
+
     width = () => {
         if (!this._targetDoc) return 225;
         if (this._targetDoc[WidthSym]() < this._targetDoc?.[HeightSym]()) {
@@ -167,15 +171,8 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
     };
     @computed get previewHeader() {
         return !this._linkDoc || !this._targetDoc || !this._linkSrc ? null : (
-            <div className="linkDocPreview-info" ref={this._infoRef}>
-                <div
-                    className="linkDocPreview-title"
-                    style={{ pointerEvents: 'all' }}
-                    onPointerDown={e => {
-                        DragManager.StartDocumentDrag([this._infoRef.current!], new DragManager.DocumentDragData([this._targetDoc!]), e.pageX, e.pageY);
-                        e.stopPropagation();
-                        LinkDocPreview.Clear();
-                    }}>
+            <div className="linkDocPreview-info">
+                <div className="linkDocPreview-title" style={{ pointerEvents: 'all' }}>
                     {StrCast(this._targetDoc.title).length > 16 ? StrCast(this._targetDoc.title).substr(0, 16) + '...' : StrCast(this._targetDoc.title)}
                     <p className="linkDocPreview-description"> {StrCast(this._linkDoc.description)}</p>
                 </div>
@@ -201,7 +198,27 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
         return (!this._linkDoc || !this._targetDoc || !this._linkSrc) && !this._toolTipText ? null : (
             <div className="linkDocPreview-inner">
                 {!this.props.showHeader ? null : this.previewHeader}
-                <div className="linkDocPreview-preview-wrapper" style={{ maxHeight: this._toolTipText ? '100%' : undefined, overflow: 'auto' }}>
+                <div
+                    className="linkDocPreview-preview-wrapper"
+                    onPointerDown={e =>
+                        setupMoveUpEvents(
+                            this,
+                            e,
+                            (e, down, delta) => {
+                                if (Math.abs(e.clientX - down[0]) + Math.abs(e.clientY - down[1]) > 100) {
+                                    DragManager.StartDocumentDrag([this._infoRef.current!], new DragManager.DocumentDragData([this._targetDoc!]), e.pageX, e.pageY);
+                                    LinkDocPreview.Clear();
+                                    return true;
+                                }
+                                return false;
+                            },
+                            emptyFunction,
+                            this.followLink,
+                            true
+                        )
+                    }
+                    ref={this._infoRef}
+                    style={{ maxHeight: this._toolTipText ? '100%' : undefined }}>
                     {this._toolTipText ? (
                         this._toolTipText
                     ) : (
@@ -217,7 +234,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                             docViewPath={returnEmptyDoclist}
                             ScreenToLocalTransform={Transform.Identity}
                             isDocumentActive={returnFalse}
-                            isContentActive={emptyFunction}
+                            isContentActive={returnFalse}
                             addDocument={returnFalse}
                             removeDocument={returnFalse}
                             addDocTab={returnFalse}
@@ -232,6 +249,7 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
                             suppressSetHeight={true}
                             PanelWidth={this.width}
                             PanelHeight={this.height}
+                            pointerEvents={returnNone}
                             focus={DocUtils.DefaultFocus}
                             whenChildContentsActiveChanged={returnFalse}
                             ignoreAutoHeight={true} // need to ignore autoHeight otherwise autoHeight text boxes will expand beyond the preview panel size.
@@ -248,7 +266,11 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> {
     render() {
         const borders = 16; // 8px border on each side
         return (
-            <div className="linkDocPreview" onPointerDown={this.followLink} style={{ left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
+            <div
+                className="linkDocPreview"
+                ref={this._linkDocRef}
+                onPointerDown={this.followLinkPointerDown}
+                style={{ left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}>
                 {this.docPreview}
             </div>
         );
-- 
cgit v1.2.3-70-g09d2

